Итак, динамическое управление памятью – сбрасываем оковы прибитых при компиляции размеров структур!
Итак. третий тип, самый интересный в этой теме для нас – динамический тип памяти.
Как мы работали с массивами раньше? int a[BIG_SISE]
Как мы работаем сейчас?
Выделяем столько, сколько нужно:
#include <stdio.h> #include <stdlib.h> int main() { size_t size; // Создаём указатель на int // – по сути, пустой массив. int *list; scanf("%lu", &size); // Выделяем память для size элементов размером int // и наш "пустой массив" теперь ссылается на эту память. list = (int *)malloc(size * sizeof(int)); for (int i = 0; i < size; ++i) { scanf("%d", list + i); } for (int i = 0; i < size; ++i) { printf("%d", *(list + i)); } // Не забываем за собой прибраться! free(list); } //*
Помимо функции malloc
есть и другие. Прочитать про них можно
с помощью man malloc
. Все они выделяют память, но различаются
механизмы работы ОС с памятью. Например, можно повторно перевыделять
память, а не запрашивать новую у ОС.
void * malloc(size_t size);
Но в общем и целом это функция, выделяет size байт неинициализированной памяти (не нули, а мусор).
Если выделение прошло успешно, то возвращается указатель на самый первый байт выделенной памяти.
Если неуспешно – NULL. Также errno
будет равен ENOMEM
(эту замечательную переменную
мы рассмотрим позднее).
То есть правильнее было написать:
#include <stdio.h> #include <stdlib.h> int main() { size_t size; int *list; scanf("%lu", &size); list = (int *)malloc(size * sizeof(int)); if (list == NULL) { goto error; } for (int i = 0; i < size; ++i) { scanf("%d", list + i); } for (int i = 0; i < size; ++i) { printf("%d", *(list + i)); } free(list); return 0; error: return 1; } //*
Очищать NULL указатель не нужно
#include <stdlib.h> int main() { free(NULL); }
– в том же clang всё пройдёт нормально (сделает ничто), но в более экзотических случаях вполне может крэшнуть программу.
Рядом с malloc
и free
в мане можно увидеть ещё:
void * calloc (size_t count, size_t size);
Равно как и malloc
выделит память под count
объектов размером по size
байт.
Выделяемая память инициализируется нулями.
void * realloc (void *ptr, size_t size);
Перевыделяет (если может) память, на которую указывает ptr
, в размере size
байт.
Если не хватает места для увеличения выделенной памяти, на которое указывает ptr
, realloc
создает новое выделение (аллокацию),
копирует старые данные, на которые указывает ptr
,
освобождает старое выделение и возвращает указатель на выделенную память.
Если ptr
равен NULL
, realloc
идентичен вызову malloc
.
Если size
равен нулю, а ptr
не NULL
, выделяется кусок памяти
минимального размера, а исходная освобождается.
void * reallocf (void *ptr, size_t size);
Придумка из FreeBSD API. Как и realloc
, но если не сможет перевыделить, очищает
принятый указатель.
void * valloc (size_t size);
Как и malloc
, но выделенная память выравнивается по границе страницы.