ЗАМЕЧАНИЕ. Блок памяти, возвращенный malloc()
, не инициализирован. Он может содержать любой случайный мусор. Необходимо сразу же инициализировать память нужными значениями или хотя бы нулями. В последнем случае используйте функцию memset()
(которая обсуждается в разделе 12.2 «Низкоуровневая память, функции memXXX()
):
memset(coordinates, '\0', amount);
Другой возможностью является использование calloc()
, которая вскоре будет описана.
Джефф Колье (Geoff Collyer) рекомендует следующую методику для выделения памяти:
some_type *pointer;
pointer = malloc(count * sizeof(*pointer));
Этот подход гарантирует, что malloc()
выделит правильное количество памяти без необходимости смотреть объявление pointer. Если тип pointer
впоследствии изменится, оператор sizeof
автоматически гарантирует, что выделяемое число байтов остается правильным. (Методика Джеффа опускает приведение типов, которое мы только что обсуждали. Наличие там приведения типов также гарантирует диагностику, если тип pointer
изменится, а вызов malloc()
не будет обновлен.)
3.2.1.3. Освобождение памяти: free()
Когда вы завершили использование памяти, «верните ее обратно», используя функцию free()
. Единственный аргумент является указателем, предварительно полученным с использованием другой функции выделения. Можно (хотя это бесполезно) передать функции free()
пустой указатель:
free(coordinates);
coordinates = NULL; /* не требуется, но хорошая мысль */
После вызова free(coordinates)
доступ к памяти, на которую указывает coordinates
, free()
:
Если она не была освобождена, переменная coordinates
продолжает указывать на блок памяти, который больше не принадлежит приложению. Это называется NULL
. Если затем вы случайно попытаетесь получить доступ к освобожденной памяти, программа немедленно завершится с ошибкой нарушения сегментации (надеемся, до того, как вы успели вашу программу выпустить в свет).
Это создает «неопределенное поведение». После передачи блока памяти обратно выделяющим процедурам они могут объединить освобожденный блок с другой свободной памятью, которая есть в их распоряжении. Освобождение чего-то уже освобожденного ведет к неразберихе и в лучшем случае к крушению; известно, что так называемые двойные освобождения приводили к проблемам безопасности.
Это кажется очевидным, но тем не менее важно. Плоха даже передача указателя на адрес где-то в середине динамически выделенной памяти:
free(coordinates + 10);
/* Освободить все кроме первых 10 элементов */
Этот вызов не будет работать и, возможно, приведет к пагубным последствиям, таким как крушение. (Это происходит потому, что во многих реализациях malloc()
«учетная» информация хранится перед возвращенными данными. Когда free()
пытается использовать эту информацию, она обнаружит там недействительные данные. В других реализациях, где учетная информация хранится в конце выделенного блока; возникают те же проблемы.)