Объяснение кода (связанный список 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 ;
   }
  • head = NULL → почему для головы задано значение NULL? Я знаю, что вы должны (я делаю это по привычке), но я действительно не знаю, почему.

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

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

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

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

Ответ 1

# 1: head = NULL

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

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

# 2: curr->next = head

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

# 3: head = curr

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

Ниже иллюстраций визуализируются шаги № 2 и № 3:

firstsecondthirdforth and final

# 4: curr = head

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

before

after

# 5: while(curr)

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

Ответ 2

  • head указывает на заголовок списка. Поскольку список в настоящее время пуст, он устанавливает значение null
  • Когда вы добавляете в список node, вы устанавливаете "следующий" указатель на текущий заголовок списка. Поместите новый node во главе списка.
  • Вы установите "head" в "curr" , чтобы сделать новую node главу списка.
  • По завершении цикла вы повторно используете переменную "curr" для перемещения по списку.
  • Вы переходите по списку "curr" к каждому node по очереди, пока не выйдете из нижней части списка (где curr- > next is null)

Ответ 3

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

Ответ 6

Мы ничего не начинаем. Что то, что

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;

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

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

Ответ 7

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