Вы находитесь на странице: 1из 63

1 билет. Базовые типы данных. Константы.

Типы делятся на две группы: базовые(встроенные) и производные(пользовательские).

Базовые в свою очередь на void и скалярные, которые делятся на целочисленные и вещественные, а


целочисленные на целые(int), логические(bool) и символьные(char).

Тип void имеет пустое множество значений, объявить в программе переменную такого типа нельзя. Этот тип
используется при объявлении функции, чтобы показать функция не возвращает никакого значения. Также
используется при объявлении не типизированных показателей.

Логический тип boolхранит логические значения, может принимать одно из двух значений truefalse.
Логические значения допускают преобразование к целому типу, при этом true->1 и false->0. Также
допускается обратное преобразование 0->false (-беск.;0) и (0;+беск.)->true

Символьный типchar данных позволяет работать с символами из некоторого набора, каждому символу типа
charсоответствует некоторое число(код символа), поэтому допускается неявное преобразование между
целыми и символьными типами.

Целые типы данных. Shortlongintкаждыйизэтих типов может предваряться модификатором signed. По


умолчанию любой целый тип интерпретируется как знаковый.

Вещественные типы данных.

Flout одинарная точность

Double двойная точность

Longdouble

Константы. Это заранее определенные значения используемые в процессе программы(целочисленные,


целые, вещественные, символьные, строковые и логические)

Целочисленные константы. Если константы начинается с ох или оХ, то она считается в 16-ой системе
счисления.

Если константа начинается с лидирующего о, то она считается в 8-ой системе счисления.

Символьные константы. Записываются ‘a’ ‘A’ ‘E’

Так как некоторые символьные константы таким способом задать невозможно поскольку они представляют
собой управляющие символы, то используется escape-последовательность. В них используется обратная
косая черта за которой следует определенный символ.

\nперевод строки

\tгоризонтальная табуляция

\v вертикальная табуляция

\b возврат на позицию с удалением символа

\r перевод курсора в начало строки

\\

\’

\”

\0 окончание строки

Вещественные константы. Могут содержать мантиссу и экспоненту.

Мантисса представляет собой необязательный знак «-» за которым следует целая часть, дробная часть и
десятичная точка.
Экспоненциальная часть начинается с е или Е за которым следует необязательный знак «-» и порядок числа.

Строковые константы. Это последовательность символов которая может содержать escape-


последовательность заключенную в двойные кавычки.
2 билет. Операции.
Арифметические операции.

- унарный минус

+унарный плюс

*умножение

/ деление

% остаток от деления

+ сложение

- вычитание

++ префиксный инкремент

--префиксный декремент

++ постфиксный инкремент

-- постфиксный декремент

Если один из аргументов вещественный, то операция выполняется над вещественными, в противном случае
над целыми.

Операции постфиксного и префиксного выполняют увеличение или уменьшение аргумента на 1,


используется в выражениях.

Для префиксных операций сначала выполняют инкремент или декремент и уже новые значения участвуют в
вычислении выражения.

Для постфиксных операций сначала значение используют для вычисления выражения а затем
инкрементируют или декрементируют.

Логические операции.

&&и (13)

|| или (14)

! отрицание (3)

Все логические операции неявно приводят аргумент к логическому типу данных, соответственно эти
аргументы могут быть целочисленного или логического типа. Аргументами для рассматриваемых операций
могут быть подвыражения в этом случае если результат ясен после 1 операнда то вычисление 2 операнда не
производится.

Битовые операции.

&- если соответствующие биты операндов = 1, то и соответствующие биты в результате устанавливается=1,


в противном случае 0.

||- если хотя бы один из соответствующих битов операндов=1, то и соотв. Биты в результате
устанавливаются=1, в противном случае 0.

^(XOR)- если оба соответствующих бита операндов равны между собой, двоичный разряд результата равен
0; в противном случае, двоичный разряд результата равен 1.

Дополнение до 1-если бит операнда=1, то соответствующие биты в результате устанавливается=0, в


противном случае 1.

Операции битого сдвига.


Операции сдвига << и>> осуществляют соответственно сдвиг влево и вправо своего левого операнда на
число битовых позиций, задаваемых правым операндом. При сдвиге освобожденные биты заполняются 0,
т.е. сдвиг не циклический. Операции сдвига часто используют для выполнения * и : на степени двойки.

Операции сравнения. Могут выполнятся над любыми скалярными величинами. Результатом является
логическое значение trueили false.

< меньше (8)

<= меньше либо равно (8)

>больше(8)

>= больше либо равно(8)

==равно(9)

!= не равно(9)

Операции присваивания.

= простая операция присваивания(16)

+= , -= , *= , /= , %= составные операции присваивания(16)

&= , |= , ^= , <<= , >>= составные битовые операции присваивания(16)

Простые операции присваивания сохраняют значение правого операнда в объект обозначаемый левым
операндом.

Составные операции присваивания сначала выполняют над операндом соответствующие арифметические


или битовые операции а уже потом присваивает.

Отличительная особенность всех операций присваивания: все эти операции возвращают новое значение
левого операнда. Это позволяет использовать операции присваивания в любых выражениях, получая тем
самым компактный код.
3 билет. Описание переменных. Классы памяти.
Объявление переменных в языке Си имеет следующий формат:

[<спецификатор>][const]<тип><идентификатор>[<инициализатор>]

[] не обязательно

<> обязательно

Int a =0;

Спецификатор - необязательный спецификатор класса памяти

Const - необязательный модификатор, говорящий о том что значение переменной нельзя будет изменить.

Необязательный параметр инициализатор задает значение, которое получает переменная при


инициализации.

Переменные можно объявлять в любом месте программы.

Классы памяти.

Auto, register, static, extern

Auto- указывает компилятору на то что объявляется автоматическая(локальная) переменная. Такая


переменная размещена на стэке и ее время жизни ограничено временем исполнения того блока операторов в
котором она определена.

Инициализация автоматической переменной выполняется только в том случае если инициализатор указан
явным образом. Если инициализатор указан, инициализация выполняется каждый раз когда поток
исполнения команд проходит через объявление переменной. Можно сказать что память под переменную
выделяется при входе в блок в котором она объявлена и освобождается при выходе из блока.

Спецификатор auto в неявном виде используется редко поскольку все переменные, объявленные внутри
любой функции и блока операторов по умолчанию являются автоматическими.

Register указывает компилятору на то что объявленную переменную желательно разместить в регистре


процессора. Используется с целью оптимизации и может быть проигнорирован компилятором. В этом
случае переменная будет автоматической.

Static обозначает что объявляется статическая переменная. Время ее жизни определено временем работы
программы. Память выделяется под переменную при запуске программы и освобождается при завершении.
Значение статической переменной объявляется внутри функции и сохраняется при вызове функции.
Инициализация такой переменной выполняется 1 раз, в соответствии с инициализатором если он указан.
Если нет то переменная инициализируется 0 значением.

Externговорит о том что объявленная переменная является внешней т.е. определена в другом месте
программы. Внешняя переменная имеет статическое время жизни.

Глобальная переменная описана в программе вне каких либо пользовательских функций. Время ее жизни
определено временем выполнения программы, область видимости ничем не ограничена.
4 вопрос. Условный оператор. Тернарная условная операция.
Оператор безусловного перехода.
Условный оператор.

If(<условие>){<оператор>;}

[else{<операторы>;}]

Условие всегда записывается в ()

Тернарная условная операция может заменить условный оператор в случае выполнения сравнительно
простых вычислений. Результат этой операции может использоваться в выражениях, что способствует более
компактному коду.

<Выражение1>?<выражение2>:<выражение3>

Если выражение 1 истинно то результатом операции будет являться значение выражения 2, в противном
случае результатом будет выражение 3.

Оператор безусловного перехода.

Goto<метка>;

<метка>:

Метка это корректный идентификатор завершающийся двоеточием. Она должна находиться в той же в
функции, что и оператор goto , ее вызывающий.
5 вопрос. Цикл с предусловием. Цикл с постусловием.
Параметрический цикл.
Цикл с предусловием

While(<условие>){<оператор>;}

While(0/false)никогда

While(1/true) вечный

Цикл с постусловием

Do{<операторы>;}

While(<операторы>)

Параметрический цикл for

For(<инициализация>;<условие>;<модификация>){

<операторы>;

Инициализация- это задание параметру начального значения. Она выполняется 1 раз в начале выполнения
цикла.

Условие- выражение условия вычисляется перед каждой операцией цикла(если условие истинно-
выполняется, нет -не выполнится)

Модификация-это изменение значение параметра цикла. Рассчитывается после каждой итерации.


6 билет. Оператор множественного выбора. Операторы break и
continue
Оператор множественного выбора

Switch(<выражение>)

Case<значение1>:[<оператор>;][break];

Case<значение>:[<оператор>;][break];

[default:<оператор>;]

При выполнении этого оператора вычисляется значение выражения, стоящего в заголовке, после этого
выполняется операторы в той ветке case, для которого значение совпадает со значением выражения. Если
значение выражения не совпало ни с одним из значений, то выполнится операторы стоящие после оператора
default.

Операторы break и continue

Break- вызывает выход из ближайшего цикла не смотря на то что условие выхода из цикла еще не
достигнуто.

Continue- вызывает переход к следующей операции ближайшего цикла при этом все операторы после
оператора break выполняться не будут.
7 билет. Указатели. Объявление указателей.
Указатель - это переменная, которая содержит в качестве значения адрес памяти.

Когда в указателе содержится адрес другой переменной, говорят, что он указывает на эту переменную.

Многие типы данных занимают более 1 байта памяти, в таком случае указатель содержит адрес 1 байта
памяти, связанной с данными этого типа.

Для того чтобы иметь возможность интерпретации той области памяти с которой они связаны водится
понятие типизированного указателя. Предположим что типизированный указатель содержит адрес объекта
только такого типа с которым он связан. Помимо типизированных бывают не типизированные(свободные)
указатели. Для них не делают никаких предположений относительно интерпретации той области памяти с
которой они связаны. Соответственно, такие указатели могут адресовать любой объект в оперативной
памяти.

Объявление указателей.

<тип данных>*<имя переменной указателя>;

В зависимости от того какой тип мы указываем при объявлении указателя мы получаем типизированный/не
типизированный указатель. В том случае, если указан тип void, мы получим не типизированный указатель,
во всех остальных случаях– типизированный, связанный именно указанным типом.
8 билет. Операции с указателем. Использование модификатора
const при объявлении указателей.
Операция присваивания. «=» используется для задания значения указателю. В указатель может быть
помещен либо уже известный адрес некоторого объекта (содержащийся в некотором другом указателе),
либо предопределенная константа NULL, показывающая, что указатель не указывает ни на один из объектов
в оперативной памяти.

Следует отметить, что совместимыми по присваиванию считаются либо два указателя одного типа, либо
типизированный и свободный указатель. Типизированные указатели разных типов не являются
совместимыми.

В том случае, если адрес объекта заранее неизвестен, а сам объект уже существует, для получения адреса
служит операция определения адреса(&). Это унарная операция, возвращающая адрес своего операнда.

Операция разыменования (раскрытия) указателя(*) служит для получения самого объекта по его указателю.
Это унарная операция, операндом которой является указатель, а результатом– объект на который этот
указатель указывает.

Неявное приведение типов возможно только к указателю void*. Для остальных случаев используется
операция приведения типа «( )». С ее использованием можно выполнять приведение, как между указателями
различных типов, так и между указателями и интегральными типами.

Из бинарных арифметических операций к указателям применимы только сложение и вычитание. Вычитание


указателей разрешено только для типизированных указателей одного типа, при этом результат
определяется, как расстояние между двумя участками памяти на которые указывают указатели, деленное на
размер того типа, с которым связаны указатели.

Из указателя можно вычитать целочисленное значение, что означает уменьшение фактического адреса,
хранящегося в указателе на вычитаемое значение помноженное на размер того типа, с которым связан
указатель.

Сложение указателей не допускается. При сложении указателя с целым числом, значение адреса,
хранящееся в указателе увеличивается на добавляемое значение, помноженное на размер того типа, с
которым связан указатель.

Операции инкремента и декремента (++, --) увеличивают или уменьшают фактический адрес, хранящийся в
указателе на размер того типа, с которым связан указатель.

Использование модификатора const при объявлении указателей.

Если требуется, чтобы константным был сам указатель, const указывается перед названием переменной. В
этом случае cpsz является символической константой, значение которой должно быть задано сразу при
инициализации и изменено впоследствии быть не может.

char *constcpsz = cpsz;

В том случае, когда требуется, чтобы неизменяемым был сам объект, модификатор может указываться перед
или после базового типа указателя. Приведенные ниже два объявления эквивалентны:

constchar *pcsz= psz; charconst *pcsz2= psz;

Такое объявление говорит о том, что изменить сам объект с использованием указателя будет нельзя.

Указатель на константу запрещает модификацию объекта, но допускает модификацию указателя.


9 билет. Массивы(статические).
Массив - это упорядоченное множество располагающихся в смежных областях памяти однотипных
элементов, каждый из которых характеризуется индексом (или набором индексом).

<тип><имя массива> [<размер>] = [<инициализатор>] ;

Доступ к элементу массива осуществляется с помощью [], внутри них указывается индекс.

Элементы массива всегда индексируются с нуля. На практике обычно известен размер массива, однако
посчитать его можно с помощью оператора sizeof.

Пусть объявлен некоторый массив [N] типа Т. Так как элементы в массиве располагаются последовательно,
начиная с нулевого элемента, то доступ кi-ому элементу можно представить в виде*(p+i). Здесь –
типизированный указатель на первый элемент массива: T *p=&a[0].

Имя массива может использоваться в качестве указателя на его первый элемент(arr ==&arr[0]).

Другими словами, везде, где Вам необходим указатель на первый элемент, можете использовать имя
массива.
10 билет. Строки. Функции работы со строками.
Строка - это последовательность символов, завершающаяся символом с нулевым кодом.

char str[]={‘A’,’B’,’C’,0};

char str[] = “ABC”;

сhar *psz = “ABC”;

При работе со строками зачастую используются указатели и арифметика указателей.

Int strlen(const char*str){

const char *tmp = str;

while (*tmp) tmp++;

return tmp-str;

1. Копирование.

Void* memcpy(void*destination, const void*source, size_t mem)

Копирует участок памяти из sourceв destination. С помощью этой функции можно переносить участок
массива не используя поэлементное копирование.

Void memmove(void*destination, const void*source, size_t mem)

Копирует из sourceв destinationnumбайт памяти. В отличие от предыдущей функции использует


промежуточный буфер.

Char*strcpy(void*destination, const char*source)

Копирует данные из sourceв строку destinationвместе с 0 символом.

Char*strncpy(char*destination, const char*source, size_t num)

Копируетизsourceвdestinationnumбайтпамятибез 0 символа.

2. Конкатенация строк.

Char*strcat(char*destination, const char*source)

Добавляет в конец destination строку source, при этом затирает первый 0 символ.

Char*strncat(char* destination, const char* source, size_t num)

Добавляет в конец строки destinationnumсимволов строки source, в конце добавляет 0.

3. Сравнение строк.

Char strcmp(const char*str1,const char*str2)

Сравнивает содержимое строки str1 и str2. Возвращает 0, если строки=,

>0, если 1 строка>, <0, если 1 строка<.

Int strncmp(const char*str1, const char*str2, size_t num)

Производит сравнение строк по первым numсимволам.

4. Поиск

Void*memchr(void*ptr, int value, size_t num)


Производит поиск среди первых numбайт участка памяти на которые указывает ptr, первого вхождения
значения value. Возвращает указатель на найденный элемент, либо NULL.

Char*strchr(char*str, int character)

Производит поиск первого вхождения символа characterв строке str. Возвращает указатель на найденный
элемент или NULL.

Size_tstrcspr(const char*str1, const char*str2)

Возвращает индекс первого вхождения любого символа из строкиstr2 в строке str1. Если ни одно вхождение
не найдено то возвращается длина строки str1.

Char*strpbrk(const char*str1, const char*str2)

Возвращает указатель на первое вхождение символа из строки str2 в строке str1.

Char*strnchr(const char*str, int character)

Возвращает указатель на последнее вхождение символа в строке.

Size_tstrspn(const char*str1, const char*str2)

Возвращает длину фрагмента строки str1, начиная от начала который состоит только из символов строки
str2.

Char*strstr(char*str1, const char*str2)

Возвращает указатель на первое вхождение строки str2 в str1.

Char*strtok(char*str, const char*delimiters)

Разбивает строку на лексемы (последовательности символов, разделенных символами входящих в группу


разделителей)

5. Вспомогательные функции.

Void*memset(void*ptr, int value, size_t num)

Заполняет блок памяти на который указывает ptr значениями value num раз.

Size_tstrlen(const char*str)

Функция возвращает длину строки.


11 билет. Структуры и битовые поля.
Структура представляет собой набор именованных атрибутов различных типов, называемых полями.
Объявление структуры выглядит следующим образом:

struct [<имя структуры>]
{
[<тип поля><имяполя>[, <имя поля>, …];
[ <типполя><имя поля>[, <имя поля>, …];
…]
}[<списокпеременных>] ;

struct Stud { char* surname; char* name; int group;};

Объявление переменных типа структура может быть сделано не только при определении самого типа, как
было показано выше, но и в любом месте после определения структуры.

[struct] <тип структуры><имя переменной> = [<инициализатор>] ;

Обращение к отдельным полям структуры осуществляется с использованием операций доступа «.» или«->».
Первая из операций применяется к самим объектам структур, вторая– куказателям на такие объекты:

Person*pp=&p;

p.firstname= «Прохор»

pp->lastname= «Сидоров»
Битовые поля.

Используя структуры, можно упаковать целочисленные компоненты еще более плотно, чем это было
сделано с использованием массива.

Набор разрядов целого числа можно разбить на битовые поля, каждое из которых выделяется для
определенной переменной. При работе с битовыми полями количество битов, выделяемое для хранения
каждого поля отделяется от имени двоеточием.

<тип> имя: КоличествоБит;


12 билет. Объединения. (+следующий билет)
Объединение-это сложный тип данных, позволяющий размещать в одном и том же месте памяти
одновременно данные различных типов. Другими словами все поля объединения разделяют одну и ту же
область памяти. Размер всего объединения = max одного из своих полей.

Объединения по-разному интерпретируют одну и ту же область памяти. Объявление объединения выглядит


следующим образом:

union<имя типа>{

<тип поля><имя поля>;

[… <тип><имя>;…]

}<список переменных> ;
unionCellData {

int iVal; double dblVal; char cval;

char *pszVal;

};

13 билет. Перечисления и ссылки.


Перечисления - это набор именованных целочисленных констант, определяющий все допустимые значения,
которые может принимать переменная. Определение типа выглядит следующим образом:

Enum[имя типа]{

<имя>[=значение],

[<имя>[=значение],

…]

[<список переменных>];

Так как по сути значения целого типа является числами они могут использоваться в качестве аргумента
оператора switchи как параметр индекс. массива.

Ссылки-адреса других узлов этого же типа с которым данный элемент логически связан. для организации
ссылок используются переменные указатели. При добавлении нового узла в такую структуру выделяется
новый блок памяти и (с помощью ссылок) устанавливаются связи этого элемента с уже существующими.
Для обозначения конечного элемента в цепи используются нулевые ссылки (NULL).

14 билет. Функции. Определение и объявление функций.


<тип возвращаемого значения>имя функции([<тип параметра>имя параметра[,<тип параметра>имя
параметра…])

При определении функции существует возможность указать спецификатор также как и при определении
переменных.

[<спецификаторы>] <тип><название>( [<список параметров> ]){

//тело функции

Виды спецификаторов:

Extern-В том случае если функция объявлена со спецификатором extern, она может быть использована также
из других модулей.

Static- В случае если функция задается как static, она может быть использована только в том модуле, где она
описана.

Inline-Спецификатор inline служит для объявления так называемых встраиваемых (подставляемых)


функций.

Особенность обработки таких функций заключается в том, что вместо передачи управления единственному
экземпляру функции код функции подставляется в точку вызова. Это приводит к разрастанию кода
программы, но увеличивает скорость её выполнения. Следует отметить, что спецификатор inline лишь
указывает компилятору на Ваше желание генерировать встраиваемую функцию, которое компилятор может
проигнорировать из-за ограничений, накладываемых на такие функции. Объявлять со спецификатором inline
имеет смысл небольшие, часто вызываемые в программе функции.
15 билет. Вызов функции и оператор return. Передача
параметров функции.
Оператор вызова функции обозначается «( )». При вызове функции указывают имя функции затем (), если
функция требует указать параметры, то в круглых скобках указываются фактические параметры. Если
функция не имеет параметров, то скобки оставляют пустыми или указать внутри них void.

<Имя функции> (<параметры>)

<Имя функции>()

Параметры в скобках указываются через запятую.

Команда returnпомогает завершить выполнение функции и вернуть управление коду, вызвавшему функцию.
Кроме того он позволяет указать возвращаемое из функции значение.

Return [<выражение>];

Return;

Когда используется оператор return функции main, то программа возвращает код завершения вызывающему
процессу(операционной системе).

Как правило значение 0 трактуется как завершение программы, а все остальные как ошибка.

Передача параметров функции.

В языке си существует только один способ передачи параметров-по значению. Это значит что при вызове
функции создается локальная копия фактического параметра который существует пока выполняется
функция. В связи с этим изменение значения локальной копии фактического параметра никак не
отобразится на значении фактического параметра на вызываемой стороне. Так как по факту это 2 различной
области памяти. Если в результате работы функции значение фактического параметра должно быть
изменено, то его необходимо передать по указателю и работать с ним в теле функции соответственным
образом.
16 билет. Задание параметров по умолчанию. Перегрузка
функции.
В языке си существует возможность задавать значение параметров по умолчанию, то есть те значения,
которые передаются в качестве фактического параметра, если при вызове он явно не указывается.

<тип параметра><имя параметра>=<значение по умолчанию>

Использование такого синтаксиса оправдано, когда функция по своему смыслу может иметь некоторое
предопределенное значение параметра, который используется при вызове этой функции наиболее часто.
Функция возведения в степень:

Double power(int x, int a=2){

If((x==0)&&(a<0)) exit(-1);

Double res=1;

Int b=abs(a);

While(b>0){

Res*=x;

b--;

If(a<0)return1/res;

Return res;
}

Значения по умолчанию не обязательно обозначаются константными выражениями. В выражениях могут


участвовать константы, глобальные переменные, функции. Необходимо помнить, что проверяется
выражение на этапе компиляции, а вычисляется во время вызова функции. Если функция имеет два
параметра по умолчанию, то не заданная всегда считается последняя.

Перегрузка функции.

Перегруженными называют функции, объявленные в одной области видимости и имеющие одинаковые


имена. Перегружаемые функции должны различаться типом, количеством входных параметров или
наличием неопределенного числа аргументов. Как правило, перегруженные функции выполняют
аналогичные действия для аргументов различных типов. Возможность перегрузки функций весьма удобна–
программисту не нужно помнить названия различных функций, совершающих похожие действия,
достаточно запомнить одно название.

Void print(int val);

Void print(double val);

Void print(double val, int f);

При обращении к функции компилятор осуществляет поиск наиболее подходящей для вызова функции из
числа перегруженных. При этом возможно как неполное совпадение типов,разрешаемое с использованием
неявных преобразований типов, так и возникновение неоднозначности, какую из функций вызвать,
устранить которую можно либо выполнив явные преобразования типов при передаче параметров.
17 билет. Функции с неопределенным числом параметров.
В языке си существует возможность определения функций с неопределенным числом параметров. В
качестве примера функции с неопределенным числом параметров можно привести функцию printf.

<Тип><название><список фиксированных параметров>,…)

Таким образом, в функциях с неопределенным числом параметров тип и количество параметров во время
компиляции определить невозможно. Известными они становятся только при вызове функции. При этом за
определение количества фактически переданных параметров и их типов отвечает программист. Фактические
значения параметров при вызове функции размещаются на стеке последовательно. При этом параметры
размещаются в порядке их следования в списке параметров. Это означает, что установив указатель
непосредственно за последним фиксированным параметром, мы получим доступ к следующему параметру.
Далее, зная типы передаваемых параметров и последовательно смещая указатель, мы можем получить
доступ к остальным параметрам функции.

double average(int n, ...){

double *p = (double*)(&n + 1);

double s = 0.0;

for (int i=0; i<n; i++)

s += *p++;

return s/n;

}
18 билет. Указатели на функции. Массив указателей на
функции.
Каждая функция во время выполнения программы располагается в сегменте кода по определенному адресу.
В си существует возможность создания указателя на функцию. Это переменная которая содержит адрес
некоторой функции. Указатель на функцию объявляется следующим образом:

<тип возвращаемого значения> (*<имя указателя>) (<список параметров>)

Void(*pf1)(char*);

Double(*pf2)(int,int);

Int sum(int a,int b){return a+b;}

Int sqr(int a){return a*a;}

Int main(){

Int x=2;

Int y=5;

Intz=0;

Int(*pf1)(int,int);

Pf1=&sum;

Pf2=&sqr;

Z=(*pf1)((*pf2)(x),(*pf2)(y));

Printf(“%d”,z); //29

В си указатели на функции обрабатывается особым образом. Возможна упрощенная запись инициализации


указателя и вызова функции по указателю.

Pf1=sum;

Pf2=sqr;

Z=pf1(pf2(x),pf2(y));

Указатель на функцию позволяет передать функцию в качестве аргумента другой функции. Что в свою
очередь позволяет создать функции общего назначения и высших порядков(функции которые принимают в
качестве элементов другие функции). Указатель на функции позволяет упростить сложность программы и
повысить ее масштабируемость.

Массив указателей на функцию. Кроме одиночного указателя на функцию можно создать массив указателей
на функцию.

<тип возвращаемого значения>(*<имя массива>[<размер>](<спецификация параметров>);

Double sum(){…}

Double vych(){…}

Double umn(){…}

Double div(double a, double b){

If(fabs(b)<EPS)exit(ERROR_DIV_BY_ZERO);

Return a/b;
}
Int main(){

Double(*operations[4])(double, double)={sum,vych,umn,div};

Int op;

Double a,b;

//Ввод а b

Printf(«Choose operation[0-add,1-vych,2-umn,3-div»]);

Scanf(«%d»,&op);

If(op>=0&&op<4) printf(«Result %f»,operations[op](a,b));

Return 0;

}
19 билет. Динамическая память. Динамические переменные.
Статическими величинами называются такие, память под которые выделяется во время компиляции и
сохраняется в течение всей работы программы.

В языках программирования существует и другой способ выделения памяти под данные, который
называется динамическим. В этом случае память под величины отводится во время выполнения программы.
Такие величины будем называть динамическими. Раздел оперативной памяти, динамически распределяемый
называется динамической памятью (динамически распределяемой памятью).

Использование динамических величин предоставляет программисту ряд дополнительных возможностей. Во-


первых, подключение динамической памяти позволяет увеличить объем обрабатываемых данных. Во-
вторых, если потребность в каких-то данных отпала до окончания программы, то занятую ими память
можно освободить для другой информации. В-третьих, использование динамической памяти позволяет
создавать структуры данных переменного размера.

Динамическая переменная это блок оперативной памяти который выделяется во время выполнения
программы. Так как динамическая переменная создается во время выполнения программы у нее нет
собственного идентификатора. Работа с динамическими переменными ведется косвенно через указатели,
которые в свою очередь являются статическими переменными.

Создание динамических переменных заключается в выделении блока памяти с помощью специальных


функций.

Эта функция возвращает адрес начала выделения блока памяти который присваивается указателю. Процесс
доступа к памяти через указатель называется разыменованием. После окончания работы с динамической
переменной выделенную память необходимо освободить.
20 билет. Функции динамического выделения и освобождения
памяти.
void * malloc(size_t size);

Функция выделяет size байтов памяти и возвращает указатель на неё. Если память выделить не удалось, то
функция возвращает NULL.

void* calloc(size_t num, size_t size);

Функция выделяет num объектов размером size и заполняет их нулями. Обычно она используется для
выделения памяти под массивы.

void* calloc(size_t num, size_t size);

Поскольку обе функции в качестве возвращаемого значения имеют типизированный указатель требуется
явное приведение типа возвращаемого значения. Для определения размера массива в байтах нужно
умножить кол-во элементов на размер 1 элемента. Поскольку размер типов данных является
платформозависимым и также элементы массива могут быть сложного типа для определения размера
элемента используется sizeof

Освобождение памяти.

Память выделенная динамически с помощью рассмотренных выше функций может быть освобождена с
помощью функции free.

Free(void*p);

Функция освобождает раннее выделенный блок дин.памяти на который указывает указатель p.


21 билет. Динамическое выделение памяти для 1мерных и
2мерных массивов.
1мерный:

#include<stdlib.h>

Int main(){

Int*a;

Unsigned n;

Printf(«Введите N»);

Scanf(«%u»,&n);

a=(int*)malloc(n*sizeof(int));

for(int i=0;i<n;i++){

printf(«a[%d]=»,i);

scanf(«%d»,&a[i]);

For(int i=0;i<n;i++){

Printf(«%d»,a[i]);

Free(a);

Getchar();

Return 0;

2мерный:

Пусть требуется разместить в динамической памяти матрицу содержащую Nстрок и M столбцов. Двумерная
матрица будет располагаться в оперативной памяти в форме ленты, состоящей из элементов строк. При этом
индекс любого элемента двумерной матрицы можно получить по формулеindex = i*m+j;где i - номер
текущей строки; j - номер текущего столбца.

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

intmain()

int *a;

inti, j, n, m;

printf("Введите количество строк: ");

scanf("%d", &n);

printf("Введите количество столбцов: ");

scanf("%d", &m);
a = (int*)malloc(n*m * sizeof(int));

for (i = 0; i<n; i++) {

for (j = 0; j<m; j++) {

printf("a[%d][%d] = ", i, j);

scanf("%d", (a + i*m + j));

for (i = 0; i<n; i++) {

for (j = 0; j<m; j++) {

printf("%5d ", *(a + i*m + j));

printf("\n");

free(a);

getchar(); getchar();

return 0;

}
22 билет. Динамического выделения памяти под многомерный
массив с использованием массива указателей
2 способ динамического выделения памяти под многомерный массив - с использованием массива
указателей. Для этого необходимо:

1)выделить блок оперативной памяти под массив указателей;

2)выделить блоки оперативной памяти под одномерные массивы, представляющие собой строки искомой
матрицы;

3)записать адреса строк в массив указателей.

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

int main()

inti, j, n, m;

printf("Введите количество строк: ");

scanf("%d", &n);

printf("Введите количество столбцов: ");

scanf("%d", &m);

Int**a = (int**)malloc(n * sizeof(int*));

for (i = 0; i<n; i++) {

a[i] = (int*)malloc(m * sizeof(int));

for (j = 0; j<m; j++) {

printf("a[%d][%d] = ", i, j);

scanf("%d", &a[i][j]);

for (i = 0; i< n; i++) {

for (j = 0; j < m; j++) {

printf("%5d ", a[i][j]);

printf("\n");

for (i = 0; i< n; i++)

free(a[i]);

free(a);

getchar(); getchar();
return 0;

Использование подобного подхода позволяет:

1)создавать память под дин.массив произвольного размера.

2)создавать массив неправильной формы

3)работать с каждой строкой по отдельности


23 билет. Перераспределение динамической памяти.
Void*realloc(void*ptr, size_t size);

Функция realloc выполняет перераспределение блоков памяти. Размер блока памяти, на который ссылается
параметр ptr изменяется на size байтов. Блок памяти может уменьшаться или увеличиваться в размере.
Содержание блока памяти сохраняется даже если новый блок имеет меньший размер, чем старый.
Отбрасываются только те данные, которые не вместились в новый блок. При увеличении размера
содержимое вновь выделенной памяти будет неопределенным. В случае, если ptr равен NULL, функция
ведет себя именно так, как функция malloc, т. е. выделяет память и возвращает указатель на этот участок
памяти. В случае, если size равен 0, ранее выделенная память будет освобождена, как если бы была вызвана
функция free, и возвращается нулевой указатель. Функция всегда возвращает Указатель на
перераспределенный блок памяти, который может быть либо таким же, как аргумент ptr или ссылаться на
новое место.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

main(){

char *ptr;

ptr = realloc(NULL, 20*sizeof(char));

strcpy(ptr, "Это первая часть, ");

ptr = realloc(ptr, 100*sizeof(char));

strcat(ptr, "Это вторая часть);

printf("%s\n", ptr);

realloc(ptr, 0);

}
25 билет. Ошибки работы с динамической памятью: ошибки
выделения памяти, изменения указателя на область памяти,
освобождение освобожденной памяти.
1) Бывает ситуация, при которой память не может быть выделена. В этом случае функция malloc (и
calloc) возвращает NULL. Поэтому, перед выделением памяти необходимо обнулить указатель, а
после выделения проверить, не равен ли он NULL.

Int*array;

If(!(array=(int*)malloc(100*sizeof(int))){

//действия когда память не выделена

//действия когда все ОК

2) Изменение указателя, который хранит адрес выделенной области памяти. Как уже упоминалось
выше, в выделенной области хранятся данные об объекте - его размер. При удалении free получает
эту информацию. Однако, если мы изменили указатель, то удаление приведёт к ошибке.

int *p = (int*) malloc(100 * sizeof(int))))

p++;

free(p);

Таким образом, если указатель хранит адрес, то его не нужно изменять. Для работы лучше создать
дополнительную переменную указатель, с которой работать дальше.

3) Освобождение освобожденной памяти.

#include <conio.h>

#include <stdio.h>

void main() {

int *a, *b;

a = (int*) malloc(sizeof(int));

free(a);

b = (int*) malloc(sizeof(int));

free(a);

free(b);

Решение здесь такое же как и раньше - обнулить указатель явно после удаления:

#include <conio.h>

#include <stdio.h>

void main() {

int *a, *b;

a = (int*) malloc(sizeof(int));

free(a);
a = NULL;

b = (int*) malloc(sizeof(int));

free(a);

free(b);

b = NULL;

}
25 билет. Ошибки работы с динамической памятью: изменения
указателя на область памяти, использование освобожденной
памяти, одновременная работа с 2 указателями на одну область
памяти.
1) Изменение указателя, который хранит адрес выделенной области памяти. Как уже упоминалось
выше, в выделенной области хранятся данные об объекте - его размер. При удалении free получает
эту информацию. Однако, если мы изменили указатель, то удаление приведёт к ошибке.

int *p = (int*) malloc(100 * sizeof(int))))

p++;

free(p);

Таким образом, если указатель хранит адрес, то его не нужно изменять. Для работы лучше создать
дополнительную переменную указатель, с которой работать дальше.

2) Использование освобождённой памяти. Эта ошибка выливается в другую – так называемые висячие
указатели. Вы удаляете объект, но при этом забываете изменить значение указателя на NULL. В
итоге, он хранит адрес области памяти, которой уже нельзя воспользоваться, при этом проверить,
валидная эта область или нет, у нас нет возможности.

Int*array=(int*)malloc(5*sizeof(int));

For(int i=0;i<5;i++) array[i]=i;

For(int j=0;j<5;j++) printf(«%d»,array[i]); //01234

Free(array);

For(int i=0;i<5;i++)printf(«%d»,array[i]); //бред

3) Одновременная работа с 2 указателями на одну область памяти.

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#define SIZE 10

void main() {

int *p1 = NULL;

int *p2 = NULL;

size_ti;

p1 = malloc(sizeof(int) * SIZE);

p2 = p1;

for (i = 0; i< SIZE; i++) {

p1[i] = i;

p2 = realloc(p1, SIZE * 5000 * sizeof(int));

for (i = 0; i< SIZE; i++) {


printf("%d ", p1[i]);

printf("\n");

for (i = 0; i< SIZE; i++) {

printf("%d ", p2[i]);

Пусть, например, у нас два указателя p1 и p2. После вызова функции realloc указатель p1 не изменится и
указывает на ту же но недоступную область памяти. Указатель p2 изменился и указывает на новый адрес
памяти, который вернула функция realloc.
26 билет. Динамические структуры данных. Связные списки.
Отличия от массивов.
Динамические структуры данных, которые представляют собой отдельные элементы, связанные с помощью
ссылок.

Каждый элемент (узел) состоит из двух областей памяти: поля данных и ссылок. Ссылки – это адреса других
узлов этого же типа, с которыми данный элемент логически связан. В языке Си для организации ссылок
используются переменные указатели.

При добавлении нового узла в такую структуру выделяется новый блок памяти и (с помощью ссылок)
устанавливаются связи этого элемента с уже существующими. Для обозначения конечного элемента в цепи
используются нулевые ссылки (NULL).

Связный список является простейшим типом данных динамической структуры, состоящей из элементов
(узлов). Каждый узел включает в себя в классическом варианте два поля:

-данные (в качестве данных может выступать переменная, объект класса или структуры и т. д.)

-указатель на следующий узел в списке.

Элементы связанного списка можно помещать и исключать произвольным образом. Доступ к списку
осуществляется через указатель, который содержит адрес первого элемента списка, называемый корнем
списка.

Сравнение массивов и связных списков

Массив Список
Выделение памяти осуществляется единовременно под
Выделение памяти осуществляется по мере ввода новых элементов
весь массив до начала его использования

При удалении/добавлении элемента требуется


Удаление/добавление элемента осуществляется переустановкой
копирование всех последующих элементов для
указателей, при этом сами данные не копируются
осуществления их сдвига

Для хранения элемента требуется объем памяти, Для хранения элемента требуется объем памяти, достаточный для
необходимый только для хранения данных этого хранения данных этого элемента и указателей (1 или 2) на другие
элемента элементы списка

Доступ к элементам может осуществляться в


Возможен только последовательный доступ к элементам
произвольном порядке

27 билет. Связные списки. Классификация и виды. Отличия от


массивов.
Связный список является простейшим типом данных динамической структуры, состоящей из элементов
(узлов). Каждый узел включает в себя в классическом варианте два поля:

-данные (в качестве данных может выступать переменная, объект класса или структуры и т. д.)

-указатель на следующий узел в списке.

По количеству полей указателей различают однонаправленный (односвязный) и двунаправленный


(двусвязный) списки.

Связный список, содержащий только один указатель на следующий элемент, называется односвязным.

Связный список, содержащий два поля указателя – на следующий элемент и на предыдущий, называется
двусвязным.

По способу связи элементов различают линейные и циклические списки.

Связный список, в котором, последний элемент указывает на NULL, называется линейным.

Связный список, в котором последний элемент связан с первым, называется циклическим.

Виды списков

Таким образом, различают 4 основных вида списков.

Односвязный линейный список (ОЛС).

Каждый узел ОЛС содержит 1 поле указателя на следующий узел. Поле указателя последнего узла содержит
нулевое значение (указывает на NULL).

Односвязный циклический список (ОЦС).

Каждый узел ОЦС содержит 1 поле указателя на следующий узел. Поле указателя последнего узла содержит
адрес первого узла (корня списка).

Двусвязный линейный список (ДЛС).

Каждый узел ДЛС содержит два поля указателей: на следующий и на предыдущий узел. Поле указателя на
следующий узел последнего узла содержит нулевое значение (указывает на NULL). Поле указателя на
предыдущий узел первого узла (корня списка) также содержит нулевое значение (указывает на NULL).

Двусвязный циклический список (ДЦС).

Каждый узел ДЦС содержит два поля указателей: на следующий и на предыдущий узел. Поле указателя на
следующий узел последнего узла содержит адрес первого узла (корня списка). Поле указателя на
предыдущий узел первого узла (корня списка) содержит адрес последнего узла.

Сравнение массивов и связных списков

Массив Список
Выделение памяти осуществляется единовременно под
Выделение памяти осуществляется по мере ввода новых элементов
весь массив до начала его использования

При удалении/добавлении элемента требуется


Удаление/добавление элемента осуществляется переустановкой
копирование всех последующих элементов для
указателей, при этом сами данные не копируются
осуществления их сдвига

Для хранения элемента требуется объем памяти, Для хранения элемента требуется объем памяти, достаточный для
необходимый только для хранения данных этого хранения данных этого элемента и указателей (1 или 2) на другие
элемента элементы списка

Доступ к элементам может осуществляться в


Возможен только последовательный доступ к элементам
произвольном порядке
28 билет. Реализация односвязного линейного списка.
Добавление в начало, поиск, удаление, печать, очистка,
примеры вызова.
Struct node{

Int data;

Node*next;

}
Добавление в начало:

Void**add_first(node**head, int data){

Node*temp=(node*)malloc(sizeof(node));

Temp->data=data;

Temp->next=head;

Return temp;
}

Поиск:

void poisk(node*head)

node *tmp;

int el;

printf("введите искомый элемент\n");

scanf(«%d»,el);

tmp=head;

while(tmp->next){

if(tmp->data=el) {

printf("Искомый элемент есть в списке \n");

break;

else

tmp=tmp->next;

Удаление:

int delete(node*del, node**head) {

node*t = (*head)->next;

node*buf;

if ( *head&& *head!= del ) {

while ( t&& t->next != del ) t = t->next;

if( t ) { // Если он существует, то удаляем

buf = t->next;

t->next = t->next->next;

free(buf);

return 0;

}
else

return -1;

else

if( *head != del ) {

buf = del;

*head = del->next;

free(buf);

return 0;

else

return -1;

Печать:

void print_list(node*head) {

while (head) {

printf("[%d]->", head->data);

head = head->next;

printf("NULL\n");

Очистка:

void clean(node**head) {

node*tmp;

while (*head) {

tmp = *head;

*head = (*head)->next;

free(tmp);

Пример:

Int main(){

Node*head=NULL;

Add_first(&head, 25);

Add_first(&head, 30);

Print_list(head);
Clean(&head);

29 билет. Реализация двусвязного линейного списка.


Добавление в начало, поиск, удаление, печать, очистка,
примеры вызова.
structnode{

intdata; //

node *next;

node *prev;
}

void add_first(node *head, node*tail, int data) {

node* tmp = NULL;

tmp = (node*)malloc(sizeof(node));

if (tmp == NULL) {

exit(1);

tmp->data=data;

tmp->next = head;

tmp->prev = NULL;

if (*head) {

*head->prev = tmp;

*head=tmp;

If(!(*tail)) *tail=tmp;

Поиск:

Node*find(node*head, int match){

While(head&&(head->data!=match)){

Head=head->next;
}

Return(head);

Удаление:

Void del(node**head, node**tail, node*deleted){

If(deleted){

If(*head){

If(*head) (*head)->prev=NULL;

Else *tail=NULL;

Free(deleted);

Else{

Deleted->prev->next=deleted->next;

If(deleted->next) deleted->next->prev=deleted->prev;

Else *tail=deleted->prev;

Free(deleted);
}
}
}

Печать:

Void print(node*head){

While(head){

printf("[%d]->", head->data);

head = head->next;

printf("NULL\n");

Очистка:

void delete_list(node *head) {

node *tmp = NULL;

while (head != tail)

tmp = tail->prev;

free(tail);

tail = tmp;

free(head);

head = NULL;

tail = NULL;

Пример:

Node*h=NULL;

Node*t=NULL;

Add_first(&h,&t,8);

Add_first(&h,&t,4);

Print(h);

Node*tmp=find(h,8);

Del=(&h,&t, tmp);
30 билет. Деревья. Способы обхода. Двоичное дерево поиска.
Дерево – структура данных, представляющая собой древовидную структуру в виде набора связанных узлов.

Бинарное дерево — это конечное множество элементов, которое либо пусто, либо содержит элемент
(корень), связанный с двумя различными бинарными деревьями, называемыми левым и правым
поддеревьями. Каждый элемент бинарного дерева называется узлом. Связи между узлами дерева
называются его ветвями.

Для решения многих задач требуется организовать обход дерева- просмотр всех его узлов в определенном
порядке. Различают 2 алгоритма обхода:
1)прямой. Сначала просматривается сам узел, а затем все его поддеревья.

2)обратный. Сначала просматриваются все поддеревья а затем сам узел.

Для бинарных деревьев также применим симметричный обход при котором сначала обрабатывается левое
поддерево затем узел после него правое.

Двоичное дерево поиска.

Двоичное дерево поиска - это двоичное дерево, для которого выполняются следующие дополнительные
условия (свойства дерева поиска):

-Оба поддерева — левое и правое — являются двоичными деревьями поиска.

-У всех узлов левого поддерева произвольного узла X значения ключей данных меньше, нежели значение
ключа данных самого узла X.

-У всех узлов правого поддерева произвольного узла X значения ключей данных больше либо равны,
нежели значение ключа данных самого узла X.

Очевидно, данные в каждом узле должны обладать ключами, на которых определена операция сравнения
меньше.

31 билет. Реализация двоичного дерева поиска. Добавление,


поиск, печать, очистка, примеры вызова.
Struct trode{

Int data;

Trode*left,*right;

}
Добавление:

Void add(trode**root, int data){

If(!(*root)){

Trode*tmp=(trode*)malloc(sizeof(trode));

Tmp->data=data;

Tmp->left=NULL;

Tmp->right=NULL;

*root=tmp;

Else{

If(data<(*root)->data) add((*root)->left,data);

Else add((*root)->right,data)

}
}

Поиск:

Trode*find(trode*root, int match){

If(!root) return NULL;

Else{

If(root->data==match) return root;

Else{

If(root->data>match) return find(root->left, match);

Else return find(root-right, match);

Печать:

Void print_preorder(trode*root){

If(root){

Printf(«%d», root->data);

Print_preorder(root->left);

Print_preorder(root->right);

Void print_inorder(trode*root){

If(root){

Print_inorder(root->left);
Printf(«%d», root->data);

Print_inorder(root->right);

}
}

Void print_postorder(trode*root){

If(root){

Print_postorder(root->left);

Print_postorder(root->right);

Printf(«%d», root->data);

Пример:

Int mail(){

Trode*root=NULL;

Add(&root, 9);

Add(&root, 44);

Print_inorder(root);

Trode*tmp=NULL;

Tmp=find(root, 9);

Tmp? Printf(«9 found»):

Printf(«9 not found»);

Getchar();

Return 0;

32 вопрос. Рекурсия. Определения. Выполнение рекурсивный


программ.
Рекурсия это такой способ определения объекта, при котором объект получает аналогичный объект в виде
составной части. Другими словами объект частично определяется через самого себя. В рекурсивном
определении должно присутствовать ограничение, граничное условие, при выходе на которое дальнейшая
инициация рекурсивных обращений прекращается.

n!=1*2*3*...*n. С другой стороны,


Рекурсия — это такой способ организации подпрограммы, при котором эта подпрограмма в ходе
выполнения ее операторов обращается сама к себе.

Unsigned long f_n(unsigned char x){

If(x<=1) return 1;

Else return x*f_n(x-1);

Обращение к рекурсивной подпрограмме ничем не отличается от вызова любой другой подпрограммы. При
этом при каждом новом рекурсивном обращении в памяти создаётся новая копия подпрограммы со всеми
локальными переменными. Такие копии будут порождаться до выхода на граничное условие. Очевидно, в
случае отсутствия граничного условия, неограниченный рост числа таких копий приведёт к аварийному
завершению программы за счёт переполнения стека.

Порождение все новых копий рекурсивной подпрограммы до выхода на граничное условие называется
рекурсивным спуском. Максимальное количество копий рекурсивной подпрограммы, которое
одновременно может находиться в памяти компьютера, называется глубиной рекурсии. Завершение работы
рекурсивных подпрограмм, вплоть до самой первой, инициировавшей рекурсивные вызовы, называется
рекурсивным подъёмом.

1) Рекурсивный спуск

Void F(){

Операторы;

F();

2) Рекурсивный подъем

Void F(){

F();

Операторы;

3) И рекурсивный спуск и подьем

Void F(){

Операторы;

F();

Операторы;

33 билет. Рекурсия. Способы организации. Виды.


Порождение все новых копий рекурсивной подпрограммы до выхода на граничное условие называется
рекурсивным спуском. Максимальное количество копий рекурсивной подпрограммы, которое
одновременно может находиться в памяти компьютера, называется глубиной рекурсии. Завершение работы
рекурсивных подпрограмм, вплоть до самой первой, инициировавшей рекурсивные вызовы, называется
рекурсивным подъёмом.

1) Рекурсивный спуск
Void F(){

Операторы;

F();

2) Рекурсивный подъем

Void F(){

F();

Операторы;

3) И рекурсивный спуск и подьем

Void F(){

Операторы;

F();

Операторы;

Следует отметить что рекурсии бывают простой(непосредственной) при которой вызов подпрограммой
самой себя осуществляется в теле этой же подпрограммы. И сложной(косвенной) при которой вызов
подпрограммой самой себя осуществляется через цепочки вызова других функций.

Простая: void F(){

F();

}
Сложная:

Void A(){

B();

Void B(){

A():

}
34 билет. Система ввода и вывода в си. Потоки. Текстовые,
стандартные и двоичные потоки.
В системе ввода\вывода поддерживается единый интерфейс, независимо от того, к какому устройству
осуществляется доступ – поток. Потоки бывают текстовые и двоичные.
Текстовый поток - это последовательность символов. Текстовый поток организован в виде строк. В
текстовом потоке может не быть однозначного соответствия между символами, которые пишутся или
читаются с теми, которые хранятся во внешнем устройстве.

Двоичный поток - это последовательность байтов, которые взаимо однозначно соответствует хранимым
байтам на внешнем устройстве. Количество записанных байтов равно количеству хранимых байтов.

Стандартные потоки – потоки, открываемые при выполнении программы автоматически.

 Stdin – ввод
 Stdout – вывод
 Stderr – ошибки

По умолчанию связываются с консолью.

35 билет. Стандартный ввод и вывод. Перенаправление


стандартных потоков.
1. Int putchar (int c); - выводит символ «c» в стандартный поток stdout. Возвращает код напечатанного
символа. В случае ошибки EOF (end of file).
2. Int puts (const char*s); - выводит строку s до \0 в stdout. В конце выводит перевод строки, возвращает
не отрицательное значение, если ошибка – EOF.
3. Int printf (const char*format,…) – Выводит строку в формат stdOut. Если строка содержит
спецификатор формата, то дополнительные аргументы преобразовывается к строковому виду и
замещают в результирующей строке соответствующие спецификаторы. Возвращает число
выведенных символов, в случае ошибки возвращает отрицательное число. 
4. Int getchar (void); - Читает символ из stdin возвращает код прочитанного символа в случае ошибки
возвращает EOF. 
5. Char* gets (char*s); - Читать символы из stdin и помещает их в массив символов. Символы
считываются до тех пор, пока в потоке не встретится символ новой строки и не будет достигнут
конец файла. Символ новой строки в результирующую строку не включается, а замещается на ноль
- символ завершающей строку.
6. Int scanf (const char* format, …); - Читает данные из stdin, выполняет их преобразование и
сохранение в переменной в соответствии с параметром в «format». Возвращает число успешно
считанных значений, в случае ошибки возвращает константу EOF.
7. Void perror (const char*s); - Интерпретирует значение глобальной переменной в строку и записывает
эту строку в стандартный поток ошибок после указанной строки s.

При запуске программы можно указать, что ввод\вывод можно указать из файла в файл. Для
перенаправления используют следующую строку запуска:

Program.exe <in.txt

Program.exe >out.txt //данные будут записываться в файл.

Program.exe >>out.txt

Int main()

Char str [255];

Gets (str);

Puts (str);

Return 0;

36 билет. Файлы и их связь с потоками. Указатель файла.


Файл – это логическая концепция, применяемая ко всему начиная от дисковых файлов до
консольных. Поток связывает с конкретным файлом операцией открытия. После открытия
возможен обмен между файлом и программой посредством потока. Если файл поддерживает
произвольный доступ, то при открытии создаётся индикатор позиции файла на его начало. По мере
чтения или записи данных из файла в файл, этот индикатор увеличивается. Связь потока с файлом
уничтожается операцией закрытия или когда заканчивается работа программы (корректно).

Это указатель на структуру типа FILE, который содержит следующее:

 дескриптор файла для обращения системам вызовом


 указатель, на буфер, ассоциированный с этим файлом
 указатель на текущую позицию в буфере, откуда можно считывать текущую позицию
 счётчик оставшихся символов либо оставшегося в буфере места
 решение открытия файла (1 – чтение, 2 – запись, 3 – чтение + запись)
 текущее состояние файла (достигнут ли конец файла).

Любые действия с файлами происходят через указатели FILE *fp;


37 билет. Открытие, закрытие, и перенаправление
потоков.
Для открытия файлов используется функция fopen. Синтаксис:
FILE *fopen (const char*filename, const char* mode);
Функция возвращает указатель на структуру файла который используется для идентификации
потока и выполняет операции с файлом. В случае ошибки возвращает нулевой указатель.

 FILE name – строка, содержащая открываемый файл.


 Mode – строка, содержащая режим открытия файла.

Режимы:

1. «r» - открыть текстовый файл для чтения (файл должен существовать)


2. «w» - создать текстовый файл для записи, если он существует, то файл перезаписывается.
3. «a» - открыть текстовый файл для добавления. Если он не существует, то он создается.
4. «rb» - открыть бинарный файл для чтения (файл должен существовать).
5. «wb» - создать бинарный файл для записи, если он существует, то файл перезаписывается.
6. «ab» - открыть бинарный файл для добавления. Если он не существует, то он создается.
7. «r+» - открыть текстовый файл для чтения и записи (файл должен существовать)
8. «w+» - создать текстовый файл для чтения и записи, если он существует, то файл перезаписывается.
9. «a+» - открыть текстовый файл для добавления, чтения и записи. Если он не существует, то он
создается.
10. «rb+» - открыть бинарный файл для чтения и записи.
11. «wb+» - создать бинарный файл для чтения и записи.
12. «ab+» - открыть бинарный файл для добавления, чтения и записи.

«r» «rb» «r+» «rb+» - открывается существующий файл. Если файл не существует, то возвращает 0.

«w» «wb» «w+» «wb+» - создается новый файл. Если файл же существует, то его содержимое будет удалено.
После открытия файла указатель идет в начало.
«a» «ab» «a+» «ab+» - создается новый файл, если он существует, то он открывается и указатель идет в
конец файла.

Максимальное количество, которое можно открыть одновременно, задается глобальной переменной :


FOPEN_MAX

Максимальная длина имени файла задается: FILENAME_MAX

Для закрытия файла используют: int fclose (FILE*stream); - функция закрывает файл с потоком stream.
Функция возвращает 0, если операция прошла успешно и const EOF в случае ошибки.

Для перенаправления потоков используется функция: FILE *freopen (const char *filename, const char*mode,
FILE *stream); - закрывает файл с потоком stream, открывает файл, имя которого открыто в параметре
filename и связывает его с этим потоком. Функция возвращает указатель на структуру, в случае ошибки – 0.

Пример:

#include <stdio.h>

Int main () {

FILE *fp = fopen («test.txt», «r» );

Fclose (fr);

Return 0;}
38 билет. Символьный ввод/вывод.
Int fputc (int c, FILE*stream); - записывает символы в поток Stream, и продвигает индикатор
позиции на следующий символ, в случае успеха вызывает установки символа, в случае неудачи
EOF и устанавливает индикатор ошибки
Int putc (int c, FILE*stream); - Тоже самое, но в виде макрокоманды и работает быстрее
Int fgets (FILE* stream); - Читает символ из потока *stream и продвигает индикатор на следующий
символ. В случае успеха возвращает прочитанные символы, в случае достижения конца файла
возвращает EOF и устанавливает конец файла, в случае ошибки возвращает EOF и устанавливает
индикатор ошибки.
Int gets (FILE* stream); - Тоже самое но в виде макрокоманды и работает быстрее, не может быть
параметром функции.
Int ungetc (int c, FILE*stream); - Возвращает символ с в поток stream и перемещает индикатор
позиции на предыдущий символ таким образом символ с будет возвращен при следующей
операции чтения из этого потока. В случае успеха, возвращает код этого элемента, в случае
ошибки EOF, и поток не изменён. Функция влияет только на поток, только на одну следующую
операцию чтения. Содержание файла связано с потоком, никак не меняется.
Char* fgets (char* str, int n, FILE*stream); - Читает строку из потока stream в строку str, до тех пор
пока не прочитано N -1 символов или не встретился символ новой строки, или не достигнут конец
файла. После прочитанных символов в строку автоматически добавляется нулевой указатель. В
случае успеха функции возвращает указатель на строку STR, в случае ошибки возвращает нулевой
указатель. Несмотря на то, что символ новой строки завершает чтение строки, он считается
результирующим и добавляется в результирующую строку
Int fgets (const char* str, FILE* stream); - Записывает строку STR в поток stream, не включающий
завершающий нулевой символ. В случае успеха возвращает ненулевое число, в случае ошибки eof.
Int fprintf (FILE*stream, const char* format…); - Функция записывает строку формат в поток stream,
если строка содержит спецификаторы формата, то доп. аргументы преобразуется к строковому
виду и заменяют в результирующей строке соответствующие спецификаторы. Возвращает число
выведенных символов, в случае ошибки отрицательные значения.
Int fscanf (FILE*stream, const char* format…); - Читает данные из потока stream, а также
преобразует и сохраняет их в соответствующие переменные в соответствии параметрам формата.
Возвращает число успешно считанных значений, в случае ошибки eof.
39 билет. Работа с индикаторами ошибки, позиции и конца
файла.
С каждым потоком связан индикатор ошибок, который находится в установленном порядке.
Для работы с индикатором ошибок используют int ferror (FILE*stream). Функция возвращает не ноль, если
индикатор ошибок в установленном порядке, в противном случае 0 .
Работа с индикатором конца файлов. Структура FILE содержит индикатор конца файла, который установлен
ненулевым значением функции чтения из файлов, при достижении этой функции конца файла, сост. индикатор
конца файла можно считать ,исп int feof(FILE*stream), которая возвращает ненулевое значение, если индикатор
конца файла установлен.
Работа с индикатором позиции.
Для каждого файла после его открытия определяется индикатор позиции, который указывает на смещение от
начала в байтах. Для работы с индикатором позиций используются следующие функции:
-void frewind(FILE*stream)
Установлен индикатор позиции на начало файла, связанный с потоком stream, при этом сбрасывается
индикатор ошибки индикатор конца файла.
-int fseek(FILE*stream,long offset,int mode)
Сдвиг индикатора позиции файла на offset байтов. В случаи успешного завершения возвращается 0, в
противном ненулевое значение. Параметр mode указывает на режим сдвига и может принимать следующие
значения:
1)SEEK_SET смещение от начала
2) SEEK_CUR смещение от текущего
3)SEEK_END смещение от конца
-long ftell(FILE*stream)
В случае успешного завершения возвращает текущую позициб в поток stream , в случае неудачи возвращает -1.
Для бинарного потока позиция равна смещению байтов от начала файла.
40 билет. Блочный ввод вывод.
Блоком называется область оп, содержимое которого записывается или считывается с файла. Блочный ввод,
вывод используется совместно с двоичными потоками.

 функция чтения
size_t fread (const void *ptr,size_t size,size_t count,FILE*stream)
Считывает массив, состоящий из count элементов, каждый из из которых имеет размер size
байт, из текущей позиции потока stream в блок памяти, на который указывает ptr, при этом
индекс потока увеличивается на общее число считываемых байт. Функция возвращает
число считываемых элементов массива. Если возр значение отличается от count, значит
произошла ошибка, либо был достигнут конец файла.
 Функция записи.
size_t fwrite(const void*ptr,size_t size,size_t count,FILE *stream)
Записывает массив, состоящий из count элементов, каждый из которых имеет размер size
байт из блока памяти, на который указывает ptr в текущей позиции потока stream.
Индикатор положения потока увеличивается на общее число записанных байт. Функция
возвращает число записанных элементов массива.
41 билет. Служебные функции работы с файлами.
1. Int remove (const char*filename); - функция удаляет filename (Успешно – 0, неудача – отличное от 0
значение).
2. Int rename (const char*oldfilename, const char*newfilename); - Изменяет имя файла или каталога
указанного параметры OldFilename на новое имя NewFilename (Успешно – 0, неудача – отличное от
0 значение). Если в Old и New указан один и тот же файл, но с разным путём, то файл будет
перемещен, если это поддерживается операционной системой. Если файл New уже существует это
может привести как к ошибке, так и к перезаписи текущего файла.
3. FILE *tmpFile(void); - создает временный файл в режиме wb+. После закрытия потока файл
удаляется. (Успешно – возвращает указатель на файл, неудача – нулевой указатель)
4. Char *tmp (char *str); - Генерирует уникальное имя файла и применяется для безопасного создания
временных файлов. Без риска перезаписи уже существующих файлов. Максимальная длина равна
L_tmpnam. Если значение str = nam, то функция возвращает указатель на внутренний статический
массив символов, содержимое которого хранится до следующего вызова функции (ошибка –
возвращает нулевой указатель)
42 билет. Генерация псевдослучайных чисел.
stdlib.h
1. void srand(unsigned seed);
Выполняет инициализацию псевдослучайных чисел значение аргумента
seed,которое называют зерном.
2. int rand(void);
Возвращает значение целого типа, не имеет параметра. 
В диапазоне [0;RAND_MAX]
RAND_MAX- макроопределение, значение которого может меняться в
зависимости от реализации, но как правило оно равно 32767.
Число генерируется алгоритмом, который возвращает последовательность
псевдослучайных чисел. Этот алгоритм использует зерно для создания серии
случайных чисел. Для одного и того же значения зерна последовательность
чисел меняться не будет, поэтому для инициализации генератора случайных
чисел удобно использовать значение, которое будет различаться при каждом
запуске. Поэтому для инициализации хорошо подходит значение системного
t.
3. time_t time(time_t*timeptr);
<time.h>
Возвращает значение календарного t в секундах.
Если функция не удалось, то -1. Отчёты от 1 января 1970 года. Для
масштабирования получения псевдослучайных чисел [а;b) применяют
следующие преобразования:
x'=a+x(b-a)/(RAND_MAX+1)
43 билет. Преобразование строковых представлений чисел.
Целые значения.
1. int atoi(const char*string);
Преобразовывает строку string в целое значение типа int. 
Сначала отбрасываются пробелы до первого значащего символа, затем возможен знак,
потом цифры.
После чего последнее десятичное значение интерпретируется в числовое значение. Строка
может содержать другие символы, после считывания числа эти символы игнорируются и
никак не используются. В случае успеха функция возвращает целое число, если в строке не
было обнаружено целого числа, функция возвращает нулевое значение.
2. long int atol(const char*string);
3. long int strtol(const char*string,char**endptr,int base);
Функция преобразовывает строку в целое значение long int в соответствии с основной
системы счисления, указанной в параметре base. 
Сначала отбрасываются все символы пробела до тех пор, пока не найдётся значищий
символ. Затем функция принимает символы, удовлетворяющие синтаксису целого числа в
соответствии со значением основной системы счисления и интерпретирует их как
численное значение. 
Если значение base находится между 2-36, то ожидается формат числа- целое число,
представленые последовательностью цифр в указанной системе счисления. Также может
быть "+" И "-". В случае успеха- целое число типа longint, если в строке не было целого
числа то 0, если преобразованное значение выходит за диапазон, функция возвращает
значение LONG_MIN/LONG_MAX и глобальной переменной errno присваивается значение
ERANGE.
4. unsigned long strtol(const char*string,char**endptr,int base);
44 билет. Преобразование строковых представлений чисел.
Вещественные значения.
1. double atof(const char*string);
Преобразование строки string в вещественное значение типа Double. Сначала отображаются
все пробелы, начиная со значещего символа, функция принимает все символы,
удовлетворяющие записи вещественного числа, преобразовывая к числовому
представлению.
+/-,0...9, . , е/Е - для показателей степени
Если преобразование выполнено успешно, то функция возвращает числовое представление
к типу Double,в противном случае 0.
2. double strtol(const char*string, char**endptr);
Преобразование string в вещественное значение типа Double.
В случае успеха- вещественное число типа Double, если в строке не было найдено
вещественное число, то функция возвращает 0. Если выходит за пределы типа Double
возвращается +-HUGE_VAL и глобальной переменной errno присваивается ERANGE
45 билет. Часто используемые математические функции.
<math.h>

 Округление
-double floor(double x); - округлить значение в меньшую сторону. Возвращает значение, имеющие
тип double
-double ceil(double x); - Округлить в большую сторону. Возвращает значение типа Double
 Модуль
-int abs(int x);
-double fabs(double x);
 double fmad(double num,double denom); - Вычисляет вещественный остаток от целочисленного
деления числителя num на знаменатель denom
fmod=num-n*denim
Результирующее знак, как у аргумента num и значение<denim. Если denom равен нулю, то главной
переменной errno присваивается значение EDOM.
 double modf(doublex,double *intpart); - Разделяет вещественное значение X на дробную и целую
часть. Дробная часть возвращается в функцию, а целая сохраняется по указателю.
 double sqrt(double x);
 double pow(double base,double exp);
Если происходит переполнение, то HUGE_VAL и переменной errno присвается ERANGE.
Если base отрицательный, а exp-не целое,или base=0,а exp-отрицательный, то переменной errno
присваивается значение EDOM.
46 билет. Директивы препроцессора (include, define, undef).
Оператор typedef.
Они представляют собой инструкции в тексте программы и выполняются до ее трансляции. Позволяют
выполнять некоторые текстовые замены, вставлять текст из другого файла ,запретить трансляцию части
текста программы.

Все директивы начинаются с "#" и с новой строки,(;) в конце не ставится.

1) #include

Включает в текст программы содержимое указанного файла, имеет две синтаксические формы:

#include "имя_файла"

Поиск файла осуществляется в соответствие с заданным путем.

#include <имя_файла>

Если указано в <>,то поиск соответствующего файла производится в стандартных директориях


опер.системы, определяемый переменной среды PATH .

Применяется для включения в программу заголовочных файлов.

2)#define

Служит для замены часто использующихся констант,ключевых слов или выражений некоторыми
идентификаторами. Идентификаторы, заменяющие текстовые или числовые константы, называют
именованными константами,а идентификаторы ,заменяющие фрагменты программы называют
макроопределеными,также они могут иметь аргументы.

#define идентификатор текста

#define идентификатор( список параметров)

Эта директива заменяет все последующие вхождения идентификатора на текст-этот процесс называется
макроподстановкой.

3)#undef

#undef идентификатор используется для отмены действий директивы #define

4) ключевое слово typedef

typedef тип имя

определяет имя уже существующего типа данных


47 билет. Модульная организация программы.
 Иерархия программных единиц. 
1 уровень: Функция - это автономная синтаксическая единица языка. 
2 уровень: Функции, объединённые общим описанием обрабатываемых структур данных,
составляют библиотеку функций. В физическом представлении ей соответствует модуль
или файл исходного кода. Особенности модульного программирования состоит в том, что
отдельные модули могут разрабатываться, транслироваться и отлаживается отдельно друг
от друга. Но для этого им может понадобиться описание интерфейсов взаимодействия -
заголовочные файлы
3 уровень: Вся программа в целом образует проект. Проект состоит из файлов исходного
кода, модулей и вспомогательных файлов.
 Трансляция - преобразование программы, представленной на одном из языков в другой.
Выделяют два вида:
1) компиляция - преобразования исходного кода с какого-либо языка программного в
машинный язык.
2) интерпретация- анализ исходного кода на каком-либо языке программирования с
одновременным исполнением

Подготовка программы начинается с редактирования файла исходного кода. Затем выполняется его
трансляция, которая включает в себя несколько фаз: препроцессор, синтаксический, семантический,
лингвистический анализ текста, генерация кода и его оптимизация. В результате получим объектный
модуль. 

 Препроцессор - предварительная фаза трансляции, которая позволяет заменить одни части текста
программы другими.
 Анализ текста:
1. Лексика языков программирования - это правило правописания слов программы таких как
идентификаторы, константы, служебные слова. Лексический анализ проверяет лексемы на этом
уровне.
2. Синтаксис языков программирования - это правило составления предложений из отдельных слов,
такими предложениями являются: операции, выражения, определение функций и переменных.
3. Семантика языков программирования - это смысл который закладывается в каждую
конструкцию языка. Семантический анализ - это смысловая корректность.
 Генерация и оптимизация кода.
Генерация - это преобразование элементарных действий полученных в результате анализа текста в
некоторое внутреннее представление, состоящих из кодов машинных программ, адресов из
содержимых ячеек памяти. В процессе генерации кода происходит его оптимизация. Наиболее
распространенные цели оптимизации: сокращение программируемого кода, экономия памяти,
сокращение операции ввода памяти.
 Компоновка.
При независимой трансляции модулей получается объектный модуль.
Компоновка - процесс оборки программы из объектных модулей и библиотек, который включает в
себя оборку кода и данных каждого объектного модуля в едином адресном пространстве
исполняемого модуля. Вычисление заполнение перекрестных ссылок между модулями, а также
связь с библиотеками.
Функции высшего порядка
Фу́нкция вы́сшего поря́дка — в программировании функция,
принимающая в качестве аргументов другие функции или возвращающая
другую функцию в качестве результата. Основная идея состоит в том, что
функции имеют тот же статус, что и другие объекты данных. Использование
функций высшего порядка приводит к абстрактным и компактным
программам, принимая во внимание сложность производимых ими
вычислений.