История вопроса:
Механизмы dynamic memory allocation появились в С вместе с функциями стандартной библиотеки malloc/free в <stdlib.h>. Они позволили реализовать структуры переменного размера, сложные коллекции и объекты, что дало мощный толчок к развитию языка и его применению в большом программировании.
Проблема:
Работа с динамической памятью требует от программиста полного контроля над жизненным циклом объектов. Ошибки (утечки памяти, двойное освобождение, неправильное использование указателей) могут привести к сбоям или уязвимостям (например, к эксплойтам через ошибку use-after-free).
Решение:
— Всегда проверять возвращаемые значения malloc/calloc/realloc. Если выделение не удалось — они возвращают NULL. — При освобождении памяти указатель направлять на NULL во избежание использования освобожденного блока. — Не использовать указатель после free. — Соблюдать корректное соответствие malloc/free и calloc/free.
Пример кода:
#include <stdio.h> #include <stdlib.h> int main() { int *arr = malloc(5 * sizeof(int)); if (!arr) { perror("Не удалось выделить память"); return 1; } for (int i = 0; i < 5; ++i) arr[i] = i * i; for (int i = 0; i < 5; ++i) printf("%d ", arr[i]); printf(" "); free(arr); arr = NULL; return 0; }
Ключевые особенности:
Что произойдет при освобождении памяти, выделенной через malloc, с помощью оператора delete или наоборот (в C++)?
Нельзя смешивать механизмы выделения и освобождения памяти между языками (C/C++). В C — только malloc/free, в C++ — new/delete.
Что происходит при попытке вызвать free(NULL)?
free(NULL) — безопасно (это гарантировано стандартом C). Такой вызов ничего не делает.
Можно ли использовать realloc для увеличения или уменьшения блока памяти и что происходит с исходным указателем?
realloc может переместить блок памяти, и если это произошло, старый указатель становится невалидным. Всегда присваивайте новый указатель:
ptr = realloc(ptr, new_size);
Выделяли память под массив в цикле, но забыли освобождать в конце итерации. За ночь на сервере программа "съела" всю RAM.
Плюсы:
Минусы:
В цикле память всегда освобождали после использования, все проверки на NULL делались сразу после malloc, использовали отладочные средства для контроля за утечками.
Плюсы:
Минусы: