Распределение динамической памяти для узла в связанном списке
Почему, когда мне нужно объявить указатель на узел (голову), мне также нужно выделить память для него malloc
или calloc
для него? Я видел, что код, генерирующий список (здесь не импортированный), работает также без выделения для него памяти и простого объявления node *head
.
typedef struct str_node{
int data;
struct str_node *next;
}node;
int main(){
node *head;
head = (node*) malloc(sizeof(node));
head = NULL;
И почему, когда я выделяю память, как указано выше, я должен писать (node*)
? Разве он уже не выделен структурному узлу, поскольку я делаю это на голову? В чем именно смысл этой строки кода? Более того, когда я пишу, head = NULL
устанавливаю ли я адрес заголовка указателя на NULL или как?
Ответы
Этот фрагмент кода
node *head;
head = (node*) malloc(sizeof(node));
head = NULL;
производит утечку памяти.
Сначала node
была выделена память для объекта данного типа и ее адрес был присвоен указателюhead
head = (node*) malloc(sizeof(node));
и тут сразу значение указателя было перезаписано.
head = NULL;
В результате адрес выделенной памяти был потерян, и выделенная память не может быть освобождена.
Фрагмент кода не имеет смысла. Достаточно будет написать
node *head = NULL;
В этом случае у вас изначально будет пустой список.
И почему, когда я выделяю память, как указано выше, мне нужно писать (узел *)?
Функция malloc
возвращает указатель типа void *
. Указатель типа void *
может быть назначен указателю любого другого типа объекта. Итак, в C приведение типов избыточно.
В C ++ вы должны явно привести указатель типа void *
к типу указателя объекта, которому назначен указатель типа void *
.
Более того, когда я пишу head = NULL, я устанавливаю адрес указателя head на NULL или как?
Вы не установили адрес самого указателя. Указатель был выделен компилятором и имеет автоматическую продолжительность хранения. Вы устанавливаете значение переменной head
, имеющей тип, равным node *
NULL.
В C указатели - это значения, как и целые числа. Когда вы пишете:
int a;
a = 3;
вы сохраняете значение 3 в переменной a
.
Когда вы пишете:
int* p;
p = NULL;
вы сохраняете значение NULL
в переменной p
. В указателях нет ничего особенного. Присвоение никоим образом не зависит от значения p
, то есть от того , на что оно может указывать, а что - нет. (В данном случае это ни на что не указывает, но это не имеет значения.)
malloc
возвращает указатель на область памяти, которая, как обсуждалось выше, является значением. Указатель не имеет внутренних метаданных; malloc
не требует никакой информации, кроме размера области памяти. В частности, он не знает (или не заботится), для чего будет использоваться область памяти. Как только это значение будет произведено, вы можете использовать его по своему усмотрению, например:
int* p;
p = malloc(sizeof *p);
Поскольку p
объявлен как указатель на объект int
, ожидается, что память, на которую указывает p
объект, может содержать int
. (Пока нет, но может.) Но вы можете передавать указатель (как значение), не оказывая никакого влияния на целое число (если есть), хранящееся в памяти, на которую указывает. Например, после
int* q = p;
q
и p
укажите на одно и то же воспоминание.
Если вас это сбивает с толку, вероятно, это связано с тем, что вы ожидаете, что указатель будет отличаться от простого значения. Однако это простые ценности, и вам нужна ментальная модель, основанная на этой простой реальности.