Основные функции для работы со строками
С символьными строками нельзя проводить операции присваивания или арифметические операции (их имена являются указателями). Для этого необходимо пользоваться функциями из библиотеки <string.h>.
1. Char* strcpy(char *s1, const char *s2); – переписываетстрокуs2 встрокуs1 и возвращает строку s1.
2. Char* strcat(char *s1, const char *s2); – добавляетстрокуs2 вконецстрокиs1 и возвращает изменённую строку s1.
3. Char* strncat(char *s1, const char *s2, int n); –добавляетn символовизстрокиs2 кстрокеs1.
4. Int strcmp(const char *s1, const char *s2); – сравниваетстрокипосимвольно (код символов). Регистр букв учитывается. Возвращает 0, если строки равны, -1, если вторая строка больше, и 1, если первая строка больше.
5. Int strncmp(const char *s1, const char *s2); –сравниваетn буквсначаластроки.
6. Int strcmpi(const char *s1, const char *s2); – сравниваетстроки, игнорируярегистр.
7. Int strlen(const char *s); – определяет и возвращает фактическую длину строки (без символа «\0»).
8. Char* strstr(const char *s1, const char *s2);–возвращаетадресначаластрокиs2 встрокеs1. Если в строкеs1 нет строки s2, функция возвращает 0.
Массив строк. Сортировка строк в алфавитном порядке
Char str[size1][size2];
Size1 – количество строк.
Size2 – размер каждой строки.
Сортировка строк в алфавитном порядке:
int i, j, k;
char x[N][100], buf[100];
if(strcmp(x[j], x[k])<0)
k=j;
strcpy(buf, x[i]);
strcpy(x[i], x[k]);
strcpy(x[k], buf);
Оператор sizeof
Оператор sizeofопределяет размер данной переменной или любого типа данных в байтах.
Динамическое выделение памяти
Библиотека <stdlib.h>.
1. Функции выделения динамической памяти
a. Void* malloc(int);– функция как параметр получает количество байт, которое требуется выделить. Возвращает указатель на начало выделенной памяти. Если память выделить не удалось, возвращает 0. Выделенная память автоматически заполняется мусором.
b. Void* calloc(int, int);Первый параметр – количество ячеек памяти, которое необходимо выделить. Второй параметр – размер типа данных, для которых тебуется выделить ячейки памяти. Возвращаемые значения аналогичны nalloc. Выделенная память автоматически обнуляется.
2. Функция переопределения памяти
void* realloc(void*, int);Первый параметр – указатель на динамически выделенный участок памяти, который необходимо переопределить. Второй –новый размер выделяемой памяти. Функция возвращает адрес нового участка памяти. Если переопределение выполнить не удалось, возвращает 0 и старый участок памяти не изменяется.
3. Функция освобождения памяти
void free(void*);–освобождает динамически выделенную память. В случае передачи в функцию указателя на нединамически выделенную память происходит аварийное прерывание работы. После освобождения памяти указателю следует присваивоить нулевое значение. Нельзя дважды освободить одну и ту же область памяти.
Порядок работы с динамически выделенной памятью:
1. Взять указатель на соответствующий тип
2. Динамически выделить память
3. Проверить на выделение или переопределение памяти
4. Работа с памятью (перенапрявление указателя на переопределённую память, если таковая есть)
5. Освободить динамически выделенную память
Перечисляемый тип
Позволяет определить несколько именованных констант, которые могут принимать различные значения. Все они являются целочисленными и могут быть проинициализированы обычным способом.
Если инициализация отсутствует, первой константе присваивается значение 0, а каждой последующей – на единицу больше предыдущей.
Enum [ID_type] {ID_const1, ID_const2, …, ID_constN} [ID_variable];
ID_type – идентификатор, который представляет новый тип перечисления. Если он отсутствует, обязательно должы присутствовать имена переменных, которые могут принимать значения именованных констант. Именованные константы перечисляются через запятую в фигурных скобках.
Переменные типа enumмогут использоваться, как и обычные переменные, в индексах, арифметических выражениях и т.д.
Правила определения перечисляемого типа:
1. Идентификаторы именованных констант в списке перечисления не должны совпадать друг с другом.
2. Имя самого типа также должно быть уникальным.
3. Именованные константы могут принимать одинаковые значения.
Структура
Структура –структура, в отличие от массива, где все элементы оимеют один тип, может содержать элементы с различными типами данных.
Определение структуры состоит из двух этапов:
1. Объявление структуры (шаблон). Память не выделяется.
2. Определение переменных типа структура, выделение памяти под них.
Struct ID_struct
{
type1 val1;
type2 val2;
…
typeN valN;
};
ID_struct – имя нового типа данных.
Val1,val2, …,valN; – поля структуры. Могут быть любого типа (как простые, так и составные, ранее определённые пользователем), кроме типа этой же структуры, однако могут являться указателем на структуру. Поля структуры не могут быть проинициализированы сразу при её объявлении.
Объявление структуры предоставляет компилятору необходимую информацию о полях структуры для резервирования памяти и организации доступа к полям при определении структурной переменной. Память под переменную структурного типа выделяется большая или равная сумме размеров всех полей, входящих в структуру.
Структурная переменная можеть быть определена как глобальная сразу при определении структуры:
StructID_struct
{
type1 val1;
type2 val2;
…
typeNvalN;
} x, y, z;
Инициализация структурных переменных может происходить следующим образом:
ID_struct ID_object={val1, val2, …, valN};
Правила инициализации структурных переменных:
1. Присваиваемые значения должны совпадать по типу с соответствующими полями структуры.
2. Если количество перечисленных значений меньше, чем полей структуры, остальные поля обнуляются.
3. Список инициализации последовательно присваивает значения полям структуры, вложенным структурам и массивам.
Размещение структурных переменных в памяти
Число байт, выделенных под структурную переменную, не всегда равно сумме размеров отдельных её полей. Это происходит из-за особенности работы процессора и называется выравниванием – размещением полей с чётного адреса. Для этого компилятор встявляет пустые байты между полями структуры и массивами объектов
Правила размещения объектов в памяти компьютера:
1. Структурные переменные или объекты массива всегда начинаются с чётного адреса.
2. Любое поле, кроме полей типа char, также начинается с чётного адреса и имеет чётное смещение от начала структурной переменной.
3. При небходимости в конце структуры также добавляются неиспользуемые байты, чтобы их общее число было чётным.
Обращение к полям структуры
Осуществляется с помощью операции выбора:
1. «.» – прямой доступ к полям, осуществляется с помощью объекта структуры.
2. «->» – косвенный доступ, осуществляется через указатели.
Присваивание структур
Для переменных одного структурного типа определена операция присваивания – побитового (поверхностного) копирования полей структуры бит за битом.
Если в определении структуры присутствуют поля-указатели, необходимо вручную переопределять операцию присваивания, т.к. при поверхностном копировании в полях-указателях будут храниться адреса, а содержимое указателей скопировано не будет.
Передача объектов структуры в функцию
1. По значению: void f(ID_struct);неэффективно, загружает память и требует много времени для копирования/удаления копии объекта. Вместо этого следует передавать объекты с помощью константной ссылки: void f(const ID_struct&);
2. По адресу
a. С помощью указателей: void f(ID_struct*);
b. С помошью ссылок: void f(ID_struct&);
3. Массив объектов структуры: void f(ID_struct*, int);
Битовые поля структуры
В языке Си имеется встроенная поддержка битовых полей, которая даёт возможность получать доступ к единичному биту. Битовые поля целесообразно использовать, если:
1. Ограничена память
2. Есть необходимость получения информации, закодированноы в одном байте
3. Проводится процедура шифрования
Битовое поле может быть определено либо в структуре, либо в объединении.
Общий вид определения битового поля:
type [ID]: ширина;
Тип поля может быть любым, который может рассматриваться как целочисленный (int, char, enum).
Ширина – количество бит для данного поля. Задаётся по следующим правилам:
1. Ширина имеет целое неотрицательное значение
2. Ширина не может превышать количество битов, предназначенных для данного типа
3. При определении знакового поля шириной в 1 бит может хранить два значения: -1 и 0.
ID – идентификатор, с помошью которого можно обратиться к данному полю. Если имя поля отсутствует, то количество бит будет отведено, но доступа к ним не будет. Если у поля с отсутствующим именем ширина равна 0, то следующее поле структуры обязательно будет начинаться с границы целого.
Битовое поле размещается в следующем байте, если:
1. Битов в текущей ячейке недостаточно.
2. Предыдущий элемент структуры не был битовым полем.
3. Предыдущий элемент – битовое поле без имени с шириной 0.
Доступ к битовым полям осуществляется с помощью операций выбора («.» или «->»).
Использование битовых полей приводит к ограничениям:
1. Нельзя получить адрес битового поля и направит на него указатель.
2. Нельза определить массив битовых полей.
3. Битовое поле не может являться возвращаемым значением функции.
4. Возникает проблема при переносе кода на другие компьютеры.
5. Проинициализировать битовые поля можно только с помощью операций присваивания.
Объединения
Объединения – частный случай структуры. Они используются для экономии памяти, когда известно, что объединённые элементы логически существуют в разный момент времени.
Определение объединения состоит из двух частей: объявление объединения и определение переменной типа объединение.
UnionID_union
{
type1 val1;
type2 val2;
…
typeNvalN;
};
Все поля объединения располагаются по одному адресу. В каждый момент временив этой переменной хранится только одно значение.
Размер объекта типа union равен максимальному размеру поля, входящего в объединение.
При инициализации переменной типа union указывается только первое поле объединения.
Переименованиетипов
Typedef type_name new_name;
Typedef unsigned int UINT;
Typedef char[100] Msg;
Msg str[10];//==str[10][100];
Typedef struct {
int a;
double b;
} complex;//complex – новыйтипданных
Файлы и потоки
Файл – именованная информация на внешнем носителе, конечное число последовательных байтов.такие устройства как дисплей, клавиатура, принтер и т.д. также можно рассматривать как частный случай файла.
Файлы не имеют фиксированной длины, она ограничивается лишь ёмкостью носителя.
Передача данных со внешних устройств в оперативную память компьютера называется чтением (вводом), а обратный процесс – записью (выводом).
При выполнении любой программы на языке Си автоматически открываются 3 файла и связанные с ними потоки:
1. Стандартный поток ввода – stdin
2. Стандартный поток вывода – stdout
3. Стандартный поток ошибок – stderr
Для повышения скорости передачи данних обмен ими производится через специальную область памяти – буфер, который выделяется для каждого открытого файла.
Различают два вида файлов:
1. Текстовый – последовательность символов; может быть просмотрен в любом текстовом редакторе.
2. Бинарный (двоичный) – набор двоичной информации. Каждая программа сама определяет структуру двоичного файла. Двоичный файл можно открыть только с помощью кода программы.
Каждому файлу присваивается логическое имя, которое используется в программе при обращении к файлу. Логическое имя представляет собой указатель на файл (на область памяти, в которй содержится вся необходимая информация о файле):
FILE *k;
FILE – идентификатор структурного типа,описан в библиотеке <stdio.h>.
Инициализация файла
Для работы с файлом его необходимо проинициализировать – открыть для доступа. Для этого используется следующая функция:
FILE* fopen(const char*, const char*);
Функция возвращает указатель на файл.
Первый параметр функции – физическое имя файла (имя и путь к нему), которое состоит из двух частей (имя.расширение). Расширение бинарного файла может быть любым, кроме тех, что определены как текстовые (txt, docx, …).
Второй параметр –режим доступа к файлу и вида файла.
Режим вида – «t»для текстового и «b»для бинарного файла.
Режим доступа:
· “w” – для записи. Если не существует, будет создан.в противном случае информация в файле теряется.
· “r” – для чтения. Обязательно должен существовать. Если не существует, функция возвращает 0.
· “a” – для добавления информации в конец файла.
· “w+” – для записи и чтения.
· “r+” – для чтения и записи в любое место.
· “a+” – для чтения и добавления информации в конец файла.
FILE* – указатель на текущую позицию файла. С его помощью осуществляется проверка открытия файла и работа с самим файлом в программе.
Ошибки при открыти файла могут возникнуть в следующих ситуациях:
1. Файл не найден (неверный путь для записи или чтения или файл не существет для чтения).
2. Носитель заполнен (режим записи).
3. Носитель защищён от записи (режим записи).
Для закрытия файла используется функция int fclose(FILE*);
Функция voidfcloseall();закрывает все файлы.