Распределение динамической памяти для узла в связанном списке

Aug 20 2020

Почему, когда мне нужно объявить указатель на узел (голову), мне также нужно выделить память для него 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 или как?

Ответы

2 VladfromMoscow Aug 19 2020 at 23:35

Этот фрагмент кода

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.

2 rici Aug 19 2020 at 23:43

В 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укажите на одно и то же воспоминание.

Если вас это сбивает с толку, вероятно, это связано с тем, что вы ожидаете, что указатель будет отличаться от простого значения. Однако это простые ценности, и вам нужна ментальная модель, основанная на этой простой реальности.