Общий анализ кода
Давайте рассмотрим tail_insertion(...)
и то, что вы хотите, чтобы он делал:
- Вы создаете новый узел;
- Вы даете ему правильные значения;
- Вы запускаете цикл
for
с намерением создать больше узлов на основе определенных условий; - Вы печатаете весь список, чтобы проверить, работал ли;
Теперь посмотрите на код для tail_insertion(...)
функции ( комментариев удалено ):
Node *pNew = (Node*) malloc(sizeof(Node));
pNew->info=0;
pNew->pNext= NULL;
for(int i=0; i<3;i++){
if(pFirst == NULL){
pNew->info++;
pFirst = pNew;
pLast = pNew;
printf("Ok, the list was empty.\n");
} else {
pNew->info++;
pLast-> pNext= pNew;
pLast= pNew;
printf("Ok, the list wasn't empty.\n");
}
}
print_list(pFirst);
Видите еще проблемы?Нет?Что ж, давайте продолжим.
Отладка
Мы собираемся пошагово отладить эту функцию .Помимо исправлений программы, это важно и для процесса обучения:
- Создание узла :
- Вы создаете
pNEW
.Это пусто; - Давайте дадим ему адрес
0x01
(выдуманный); - A
0
назначен на pNew->info
; - A
NULL
назначен на pNew->pNext
;
- Начало цикла
for
- попытка создать 3 узла для списка: i = 0
: - Вы проверяете, пустой ли список.Это:
- Вы добавляете
1
к pNew->info
значению.Адрес pNew
равен 0x01
; pFirst
назначен для точки по адресу 0x01
; pLast
назначен для точки по адресу 0x01
поскольку список был пуст; - Вы печатаете сообщение о том, что список пуст;
- В конце
i = 0
список выглядит следующим образом: pFirst
точек по адресу 0x01
; pLast
точек по адресу 0x01
; 0x01->info
равно 1
;
i = 1
- Вы проверяете, пустой ли список.Это не:
- Вы добавляете
1
к pNew->info
значению.Адрес pNew
- 0x01
.Создание другого узла никогда не было; pLast->pNext
назначено для указания на адрес 0x01
; pLast
назначено для указания на адрес 0x01
; - Вы печатаете сообщение о том, что список не был пустым;
- В конце
i = 1
список выглядит следующим образом: pFirst
указывает по адресу 0x01
; pLast
указывает по адресу 0x01
; pLast->pNext
указывает по адресу 0x01
; 0x01->info
is 2
;
i = 2
- Вы проверяете, пустой ли список.Это не:
- Вы добавляете
1
к pNew->info
значению.Адрес pNew
- 0x01
.Никогда не было создания другого узла; pLast->pNext
назначен для указания по адресу 0x01
; pLast
назначен для указания по адресу 0x01
; - Вы печатаете сообщение о том, что список не был пустым;
- В конце
i = 2
список выглядит следующим образом: pFirst
точек по адресу 0x01
; pLast
точек по адресу 0x01
; pLast->pNext
точек по адресу 0x01
; pLast->pNext->pNext
указывает на адрес 0x01
; 0x01->info
is 3
;
- Конец цикла
for
;
- Печать полученного списка :
Заключение
На данный момент вы, вероятно, видели , что не так : вы не создали новый узел внутри цикла for
.Вы создали это вне цикла.Это приводит к повторяющемуся процессу , добавляющему тот же самый узел с тем же адресом в список .
Также у вас возникают проблемы со значением info
.Но это благодаря использованию того же узла, как указано выше.Потребуется лишь некоторая корректировка в этой инкрементальной логике, которая устанавливает значение info
, чтобы приспособить изменения, чтобы исправить использование одного и того же узла и заставить все работать.
Исправление кода
Использование вассобственный код, вы должны исправить tail_insertion(...)
, чтобы быть похожим на код ниже.Есть комментарии, которые помогут понять изменения.
Node *pNew; /* Declaring pNew *without* assignment. */
for(int i = 0; i < 3; i++){
pNew = (Node*) malloc(sizeof(Node)); /* Allocation of new memory address. */
pNew->info = i; /* pNew->info works like an ID in you current code. */
pNew->pNext = NULL; /* It is proper to assign NULL */
if(pFirst == NULL){
pNew->info++;
pFirst = pNew;
pLast = pNew;
printf("The list was empty.\n");
} else {
pNew->info++;
pLast->pNext = pNew;
pLast = pNew;
printf("The list wasn't empty.\n");
}
}
print_list(pFirst);
Обратите внимание, что я удалил функцию return(...)
, так как в этом не было необходимости.В этом нет необходимости, поскольку tail_insertion(...)
имеет тип void
.
Кроме того, pNew->info
сохраняет идентификатор узла на момент моего чтения.Поэтому не должно быть проблем с присвоением i
.При необходимости внесите необходимые изменения.
Заключительные замечания
Есть комментарии о том, что вам не нужно pLast
.Хотя это действительно так, вам необходимо учитывать:
- Если вы используете только
pFirst
, вам нужно убедиться, что после вставки нового узла в pFirst->pNext
вы выполните pFirst->pNext->pNext = NULL
.Таким образом, вы можете узнать, когда достигли конца списка, сравнивая с NULL
; - Если вы продолжаете использовать
pLast
, вы можете сравнить с pLast
, чтобы узнать, является ли адреспоследний адрес в списке.Очевидно, что вы также можете сравнить с NULL после моего исправления; - Учитывая хвостовые вставки , наличие
pLast
значительно облегчает работу и снижает нагрузку на процессор в долгосрочной перспективе;
Вы, наверное, знаете, но вы должны поместить print_list(pFirst);
в функцию main(...)
, чтобы сделать вещи более организованными.Кроме того, это заставляет мой «код OCD» достигать новых уровней отчаяния!; -)