Недавно выделенная память содержит мусор, и чтение указателя из неинициализированной памяти является ошибкой.
Если вместо этого вы используете calloc( DEFAULT_SIZE, sizeof(Node*) )
, содержимое массива будет определено: все биты будут установлены в ноль. Во многих реализациях это указатель NULL
, хотя стандарт не гарантирует этого. Технически, это мог бы быть стандартный компилятор, который приводит к аварийному завершению программы, если вы пытаетесь прочитать указатель со всеми битами, установленными в ноль.
(Об этом должны беспокоиться только языковые юристы. На практике даже пятидесятилетние мэйнфреймы приводят в качестве примера машины, где NULL
не был двоичным 0, обновлял свой компилятор C для распознавания 0 как указатель NULL
, потому что это сломало слишком много кода.)
Безопасный, переносимый способ сделать то, что вы хотите, это инициализировать каждый указатель в массиве на NULL
:
struct Node** const array = malloc(sizeof(Node**) * DEFAULT_SIZE);
// Check for out-of-memory error if you really want to.
for ( ptrdiff_t i = 0; i < DEFAULT_SIZE; ++i )
array[i] = NULL;
После выполнения цикла каждый указатель в массиве равен NULL
, и оператор !
возвращает для него 1, пока он не будет установлен в другое значение.
Вызов realloc()
ошибочен. Если вы хотите сделать это таким образом, аргумент size должен быть новым числом элементов, умноженным на размер элемента. Этот код с радостью сделает его четверть или восьмой желаемого размера. Даже без этой ошибки, связанной с повреждением памяти, вы будете слишком часто перераспределяться, что может потребовать копирования всего массива в новое место в памяти.
Классическим решением для этого является создание связанного списка страниц массива, но если вы собираетесь realloc()
, было бы лучше каждый раз умножать размер массива на константу.
Точно так же, когда вы создаете каждый Node
, вы хотите инициализировать его поля указателя, если вы заботитесь о переносимости. Ни один компилятор в этом веке не будет генерировать менее эффективный код, если вы это сделаете.
Если вы размещаете узлы только в последовательном порядке, альтернативой является создание массива Node
вместо Node*
и ведение счетчика количества используемых узлов. Современная настольная ОС будет отображать только столько страниц физической памяти для массива, сколько записывает ваш процесс, поэтому простое выделение и не инициализация большого динамического массива не приводит к потере реальных ресурсов в большинстве сред.
Еще одна ошибка, которая, вероятно, является доброкачественной: элементы вашего массива имеют тип struct Node*
, но вы выделяете sizeof(Node**)
, а не sizeof(Node*)
байт для каждого. Однако компилятор этого не проверяет, и я не знаю ни одного компилятора, где размеры этих двух типов указателей на объекты могли бы быть разными.