- •Функции.
- •Вызов функции с переменным числом параметров
- •Функция main и её параметры.
- •Директивы препроцессора (прекомпилера).
- •Объявление указателей.
- •Модификатор const.
- •Операции.
- •Указатели на различные типы.
- •Указатель на void.
- •Применение указателей для передачи данных между функциями.
- •Массивы.
- •Индексация массивов.
- •Хранение массива в памяти. Адреса элементов. Хранение массива в памяти.
- •Массивы и константные указатели.
- •Статическое и динамическое выделение памяти.
- •Функции calloc, malloc, free
- •Функция realloc
- •Передача массивов в качестве аргументов функции.
- •Указатели на функции.
- •Библиотеки функций.
- •Функции форматированного ввода-вывода.
- •Функция printf().
- •%[Флаги] [Ширина] [.Точность] [{h | l | I | i32 | i64}]тип
- •Для чего нужен форматированный вывод.
- •Функция scanf().
- •Функции sprintf() и sscanf().
- •Функции fprintf() и fscanf().
- •Функции неформатированного ввода-вывода.
- •Работа со строковыми данными (стрингами). Представление строковых данных в языке c.
- •Функции работы со строками.
- •Потоковый ввод-вывод
- •Функции форматированного ввода-вывода.
- •Функция printf().
- •%[Флаги] [Ширина] [.Точность] [{h | l | I | i32 | i64}]тип
- •Для чего нужен форматированный вывод.
- •Функция scanf().
- •Функции sprintf() и sscanf().
- •Функции fprintf() и fscanf().
- •Функции неформатированного ввода-вывода.
- •Функции работы с файлами.
- •Потоковый ввод-вывод
- •Работа с потоками
- •Курсор.
- •Ввод-вывод отдельных символов и строк.
- •Форматированный ввод-вывод информации в файл.
- •Блочный потоковый ввод-вывод
- •Смена текущей позиции в файле. Проверка конца файла.
- •Функции доступа к файлам нижнего уровня.
- •Методы сортировки данных.
- •Введение
- •Сравнение методов сортировки
- •Программная реализация алгоритмов сортировки
- •Метод пузырька.
- •Метод обмена.
- •Метод вставки.
- •Метод Шелла.
- •Метод кучи (бинарной кучи).
- •Очередь
- •Линейный список
- •Физическое (машинное) представление линейных списков
- •Программные реализации структур данных. Стек. Реализация в виде массива.
- •Стек. Связанное представление.
- •Очереди. Реализация в виде массива.
- •Дерево. Связанное представление.
- •Рекурсивный вызов функций.
- •Структуры. Объединения. Перечисления.
- •Перечисление (enum).
- •Производные типы данных.
- •Структура (struct).
- •Побитовое описание полей структуры.
- •Объявление переменных, реализующих структуру.
- •Доступ к элементам структуры.
- •Объединение (union).
- •Вложенное описание структур и объединений.
- •Описание структур и объединений в виде пользовательского типа.
- •Передача структур и объединений в виде параметров функции.
- •Инициализация структур и объединений.
- •Выгода от использования структур
Функция realloc
Функция void* realloc(void *block, size_t size); перераспределяет уже выделенный блок памяти, на который указывает параметр block, сжимая или увеличивая его размер до значения, передаваемого в параметре size.
Функция realloc пытается сжать или увеличить предварительно выделенный блок до размера в size байт. Аргумент block указывает на блок памяти, полученный при вызове функций malloc, calloc или realloc. Если block является нулевым указателем, realloc работает также как и malloc.
При уменьшении размера блока, realloc (с помощью менеджера памяти) обычно просто уменьшает его размер в таблице распределения памяти, не перемещая сам блок в памяти. При увеличении размера блока памяти, если это возможно без перемещения его в памяти, realloc просто выделяет дополнительную память сразу за блоком и присоединяет его (с помощью менеджера памяти) к этому блоку. Если же такое увеличение невозможно, то realloc выделяет новый блок памяти размером size, копирует туда содержимое старого блока, после чего освобождает старый блок.
Функция realloc возвращает адрес блока, который может отличаться от исходного. Если блок не может быть выделен или size равно 0, то realloc возвращает NULL.
Пример:
#include <alloc.h>
void main()
{
long *pl;
int i;
//Выделяем память под динамический массив.
pl=malloc(10*sizeof(long));
if(pl)
{
// память выделена, можем с ней работать
for(i=0;i<10;i++)
pl[i]=i*2;
…
// Теперь нам необходимо увеличить размер массива
pl=realloc(pl,20);
if(pl)
{
//Работаем с увеличенным массивом.
// Старые значения сохранились, дополним их новыми.
for(i=10;i<20;i++)
pl[i]=i*2+1;
…
}
else
{
// В данном случае память не освобождаем, т.к.
// она была уже освобождена в функции realloc,
// после чего realloc вышла с ошибкой, не выделив
// память заново.
return;
}
// В конце работы освобождаем память,
// на которую указывает pl
free(pl);
}
else
{
printf("Недостаточно памяти\n");
}
}
Передача аргументов в функцию с помощью указателей. Передача массива в функцию.
Передача массивов в качестве аргументов функции.
Передачу массива как параметра в какую-либо функцию можно осуществить различными способами. Но в любом случае массив будет передан в функцию в виде указателя, т.е. значения его элементов в новую область памяти копироваться не будет. Поэтому все изменения, происходящие с его элементами внутри этой функции, будут отражены на содержимом этого массива и в вызывающей функции. Например,
func1(int *ai);
func2(int ai[5]);
func3(int ai[]);
void main()
{
int ai[5]
func1(ai);
func2(ai);
func3(ai);
}
В этом примере показаны три основных формы передачи массива в функцию – в виде указателя, и в виде параметра массива. Для одномерных массивов все 3 формы записи полностью идентичны. Во всех трёх случаях в функции будет передаваться указатель на начало области памяти, где хранятся элементы массива, и внутри функций работа с ними будет происходить по всем правилам работы с указателями. При использовании формы записи с квадратными скобками, значение внутри скобок опускается.
Некоторая разница между использованием указателя и параметра-массива появляется при передаче многомерных массивов. При передаче массива в виде указателя необходимо вручную пересчитывать его индексы из двумерной системы в одномерную, т.к. внутри функции мы сможем оперировать с ним только как обычной линейно-адресуемой областью памяти:
void func(int *ai)
{
int i=2,j=5;
int element;
// нам необходимо взять элемент [i][j] из массива ai
element=ai[i*6+j];
…
}
void main()
{
int i[5][6];
func(i);
}
Эта работа может быть облегчена, если передавать двумерный массив в виде параметра-массива. В этом случае параметр всё равно представляет собой указатель (он всё так же является локальной переменной-указателем, под которую статически выделяется память), но к ней становятся применимы правила адресной арифметики массивов.
Так, если функция объявлена в виде
void func(int ai[5][6]);
то одинарная операция индексации, применённая к переменной ai, будет возвращать не элемент одномерного массива (как это было в предыдущем случае, когда передача массива осуществлялась в виде обычного указателя), а будет возвращать указатель на начало соответствующей строки двумерного массива. Поэтому для извлечения элемента из двумерного параметра-массива можно просто дважды применить операцию индексации: ai[i][j]. При этом компилятор будет автоматически перерассчитывать двумерную индексацию в одномерную по формуле из прошлого примера: i*6+j.
Как видим, в этой формуле не участвует количество элементов в самом старшем измерении, поэтому в объявлении параметров функции его можно опустить и записать:
void func(int ai[][6]);
То же самое верно и для массивов-параметров большей размерности.
Таким образом, предыдущий пример можно переписать в следующем виде:
void func(int ai[5][6])
{
int i=2,j=5;
int element;
// нам необходимо взять элемент [i][j] из массива ai
element=ai[i][j];
…
}
void main()
{
int i[5][6];
func(i);
}
В качестве небольшого вывода ещё раз обращу внимание на тот факт, что в отличие от передачи параметров простых типов, при работе с параметрами-массивами мы всегда имеем дело с указателями. Это немаловажное обстоятельство позволяет непосредственно из вызываемой функции изменять значения объектов, определённых в вызывающей функции.