Объяснение кода (связанный список 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("%dn", 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 != Нуль) верно?

7 ответов


#1: head = NULL

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

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

#2: curr->next = head

построение связанного списка. The curr узел "связан" с ранее созданным узлом в последовательности.

#3: head = curr

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

ниже иллюстрации визуализировать шаги #2 и #3:

firstsecondthirdforth and final

#4: curr = head

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

before

after

#5: while(curr)

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


  1. head указывает на начало списка. Поскольку список в настоящее время пуст, он имеет значение null
  2. когда вы добавляете узел в список, вы устанавливаете указатель "next" на текущий заголовок списка. Ставит новый узел во главе списка.
  3. вы устанавливаете "head" в "curr", чтобы сделать новый узел главой списка.
  4. после завершения цикла вы повторно используете переменную "curr" для перемещения по списку.
  5. вы просматриваете список установка "curr" для каждого узла по очереди, пока вы не уйдете в нижнюю часть списка (где curr->next равно null)

(1). Вам нужно установить его на что-то, и использование NULL-это способ сказать, что он ни на что не указывает. Обычно значение NULL равно 0. В некоторых языках вам не нужно инициализировать переменную, потому что она автоматически установит ее в ноль. Но 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).


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

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

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

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

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


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


мы начинаем с нуля. Вот что!--7-->

head = NULL;

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

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

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

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

head = curr;

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

задание

head = curr;

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

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

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

head = curr;

так

curr = head;

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

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


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