Расшифровка кода (связанный список C)

Это не мой код. Я удалил этот код с этого сайта:

http://www.macs.hw.ac.uk/~rjp/Coursewww/Cwww/linklist.html

Я использую справочный материал о том, как создать связанный список. Я немного запутался в том, что происходит. Может кто-нибудь объяснить мне, что происходит. Отмечу, что меня смущает, цифрами 1-5.

#include<stdlib.h>
#include<stdio.h>

struct list_el {
   int val;
   struct list_el * next;
};

typedef struct list_el item;

void main() {
   item * curr, * head;
   int i;

   head = NULL;   //1

   for(i=1;i<=10;i++) {
      curr = (item *)malloc(sizeof(item));
      curr->val = i;
      curr->next  = head; //2
      head = curr; //3
   }

   curr = head; // 4

   while(curr) {  //5
      printf("%d\n", curr->val);
      curr = curr->next ;
   }
  1. head = NULL → почему для head установлено значение NULL? Я знаю, что вы должны (я делаю это по привычке), но не знаю почему.

  2. curr-> next = head → Я тоже этого не понимал. Может быть, мое определение «головы» неверно, но в обычном связном списке это начальный узел или последний узел в списке? Я всегда предполагал, что это начальный узел, но в этой строке похоже, что это последний узел.

  3. head = curr → Почему мы устанавливаем его равным curr?

  4. curr = head →, а затем установите curr = head после завершения цикла.

  5. while (curr) → Просто чтобы убедиться, что это проходит по списку, и это эквивалентно while (curr! = NULL), верно?


person ShadyBears    schedule 15.03.2013    source источник
comment
Список строится путем присоединения узлов к внешнему интерфейсу и настройки заголовка, чтобы он указывал на новый узел.   -  person uncleO    schedule 16.03.2013
comment
Этот код создает связанный список, поэтому он может помочь получить четкое представление о том, что такое связанный список: ru .wikipedia.org / wiki / Linked_list   -  person The Jonas Persson    schedule 16.03.2013


Ответы (7)


#1: head = NULL

Инициализация указателя. Обычно рекомендуется инициализировать указатель NULL либо (1) при объявлении, либо (2) сразу после объявления. Если программисты по ошибке разыменовывают неинициализированные указатели, возвращаются мусорные значения. Часто это чрезвычайно сложно отладить, если ваш статический анализатор и компилятор не отображают предупреждения или сообщения об ошибках для неинициализированных указателей.

Для получения дополнительной информации см. Код Стива МакКоннелла: завершен: Практическое руководство по созданию программного обеспечения или страница Википедии на странице Защитное программирование .

#2: curr->next = head

Создание связного списка. Узел curr «связывается» с ранее созданным узлом в последовательности.

#3: head = curr

Обновление указателя заголовка. Указатель head обновляется и указывает на последний malloc отмеченный узел.

На иллюстрациях ниже показаны шаги №2 и №3:

firstвторой третий четвертый и последний

#4: curr = head

Повторная инициализация указателя. Этот шаг аналогичен шагу № 2: curr->next = head. Устанавливая узел curr на head, curr становится "готовым" для обхода связанного списка в цикле while. По аналогии, это похоже на инициализацию переменной итерации до 0 в начале цикла (т.е. i = 0). Чтобы наглядно представить этот шаг, обратитесь к приведенным ниже иллюстрациям, показывающим до / после выполнения этого оператора:

до

после

#5: while(curr)

Перемещение по списку. Учитывая, что curr указывает на первый узел (с шага № 4), этот while цикл просматривает список до тех пор, пока curr->next не вернет NULL. В менее абстрактной форме мы можем переписать это утверждение как while(curr != NULL).

person Community    schedule 15.03.2013
comment
Поистине отличный ответ. Очень объяснительно и легко для понимания. Борьба со связанными списками банкомата, и на этом разобрались. Спасибо. - person Finlandia_C; 10.11.2015

  1. голова указывает на начало списка. Поскольку список в настоящее время пуст, для него установлено значение null
  2. Когда вы добавляете узел в список, вы устанавливаете «следующий» указатель на текущий заголовок списка. Новый узел помещается в начало списка.
  3. Вы устанавливаете "head" на "curr", чтобы новый узел стал главой списка.
  4. После завершения цикла вы повторно используете переменную curr для обхода списка.
  5. Вы просматриваете список, устанавливающий "curr" для каждого узла по очереди, пока не выйдете за нижнюю часть списка (где curr-> next имеет значение null)
person Paul Tomblin    schedule 15.03.2013

(1). Вам нужно установить его на что-то, а использование NULL - это способ сказать, что он ни на что не указывает. Обычно NULL - это то же самое, что и 0. В некоторых языках вам не нужно инициализировать переменную, потому что она автоматически установит ее в nil. Но C этого не делает, поэтому вы должны делать это сами.

(2). head указывает на первый узел списка. Сначала он равен NULL, что означает, что список пуст и, следовательно, head ни на что не указывает. cur - это новый узел, который нужно вставить в список. curr->next хочет указать на первый узел существующего списка, поэтому для curr->next установлено значение head.

(3). В этот момент head больше не указывает на первый узел. При первом прохождении цикла это выглядит так:

curr-->node1-->NULL
         head--^

Но в целом это выглядело бы так

curr-->node3-->node2-->node1-->NULL
          head--^

Итак, нам нужно обновить head, чтобы он указывал на первый узел. Поскольку curr указывает на только что созданный узел, который помещается впереди, мы просто устанавливаем head, чтобы он указывал на тот же узел, что и curr.

(4). Первая часть программы сделана. curr больше не нужен, потому что он использовался для отслеживания созданного нами нового узла. Это была временная переменная. Эта строка curr = head означает, что мы собираемся инициализировать curr в начало списка. Мы могли бы использовать другую переменную, чтобы сделать ее более читаемой, но обычно вы видите повторное использование временных переменных.

(5). Верно. Вы, вероятно, увидите NULL, определенный как (void*)0, так что это то же самое, что и 0. Вы, вероятно, никогда не увидите другого значения, кроме 0, за исключением действительно старых машин 60-х или 70-х годов. Таким образом, логически это эквивалентно: while (curr != 0), что совпадает с while (curr).

person ckim    schedule 15.03.2013

1. head = NULL → почему для параметра head установлено значение NULL?
Рекомендуется инициализировать переменные. В некоторых системах объявленные переменные имеют то, что случайно оказалось в памяти при захвате адресного пространства.

2. curr-> next = head → Я тоже этого не понимал. Может быть, мое определение «головы» неверно, но в обычном связном списке это начальный узел или последний узел в списке? Я всегда предполагал, что это начальный узел, но в этой строке похоже, что это последний узел.
Да, начальный узел - это голова.

3. head = curr → Почему мы устанавливаем его равным curr?
Этот цикл добавляет новые узлы в качестве головы. Как стек. Другие способы сделать это - добавить новые узлы на хвосте. Оба варианта остаются «связными списками».

4. curr = head →, а затем установите curr = head после завершения цикла.
curr действует как индекс, рабочая переменная, поэтому вы не нарушаете структуру данных. Он сбрасывает его после того, как закончил. Если хотите, "перемотайте ленту".

5. while (curr) → Просто чтобы убедиться, что это перемещение по списку, и это эквивалентно while (curr! = NULL), верно?
Да, это одна из тех подразумеваемых вещей, которые вы найдете в C. Все, что само по себе в цикле while, неявно while(whatnot != 0) и null == 0.

person Philip    schedule 15.03.2013
comment
Хорошо, вот где я запутался .. en.wikipedia.org/wiki/Linked_list В раздел односвязного списка .. квадрат с 'x' в нем - это голова или хвост? - person ShadyBears; 16.03.2013
comment
@juice: Поле с "X" внутри - это NULL-адрес. Последний элемент указывает на него, чтобы показать: «Эй, я последний, позади меня никого нет». Цикл по этому списку увидит этот NULL-указатель в последнем элементе, а затем выйдет из цикла. - person flyingOwl; 16.03.2013
comment
Да, в этом примере # 12 - это значение в начале, а # 37 - это значение в хвосте. - person Philip; 16.03.2013

Во-первых, вы можете найти ответ на вопрос, почему head всегда NULL в Связанный список Head Is Всегда нулевой и простой связанный список C ++. Учебное пособие для новичков можно найти в односвязном списке в c. Оператор head = curr привязывает значение указателя head, которое было NULL, к значению текущего указателя, который получил ненулевое значение путем выделения памяти. while (curr) - это цикл, который выполняется до тех пор, пока curr отличается от NULL, NULL как связанное с макросом нулевое значение для указывающего адреса.

person Mihai8    schedule 15.03.2013

Мы начинаем с нуля. Это то что

head = NULL;

говорит нам. У нас еще нет списка, поэтому мы не можем получить к нему доступ.

Теперь мы перебираем от 1 до 10. Мы строим список от начала до конца. HEAD имеет значение NULL, поэтому «последний» (созданный первым) указывает на NULL:

curr->next  = head; // head is NULL in the first loop

HEAD теперь установлен на этот новый элемент:

head = curr;

При втором прохождении этого цикла head сохраняет указатель на последний созданный элемент. Затем вновь созданный будет указывать на него. Мы устанавливаем этот новый элемент перед последним элементом.

Параметр

head = curr;

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

// 4 на самом деле не нужно.

Последняя операция до этого была:

head = curr;

So

curr = head;

бессмысленно.

А 5-й выполняет итерацию по списку. "curr" указывает на первый элемент (с ненулевым адресом) и устанавливается в curr-> next в каждом цикле. Как только curr становится NULL (в последнем элементе), утверждение перестает быть верным.

person flyingOwl    schedule 15.03.2013

В четвертой задаче я не думаю, что curr=head необходим, потому что, когда цикл закончился, curr и head имели указатель на один и тот же узел (узел i = 10). Но это хорошая привычка.

person Chandler's Sexyface    schedule 06.03.2014