Итак. третий тип, самый интересный в этой теме для нас – динамический тип памяти.
Как мы работали с массивами раньше? 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
, но выделенная память выравнивается по границе страницы.