Непосредственные проблемы:
1 / При добавлении вы не устанавливаете следующий указатель текущего хвоста в качестве нового узла.
2 / При вставке вы не проверяете, что вы вышли за пределы своего списка.
3 / Аналогично, при удалении вы не проверяете, пытаетесь ли вы выйти за пределы своего списка.
4 / При удалении вы не проверяете крайние случаи. Например, удаление текущей головки приведет к тому, что вы попытаетесь установить (current->prev)->next
на что-то, даже если current->prev
равно NULL.
5 / Цикл в main
, наряду с пунктом 1 выше, является причиной вашего segfault. Это потому, что head->next
никогда не устанавливается на хвост, поэтому, даже если вы создаете предметы, они не тот список, о котором head
знает.
На самом деле это странный подход, поскольку обычно вы делаете удаление на основе значения, а не индекса. Если вы собираетесь удалить (или распечатать или обработать любым способом) узел на основе индекса, вам следует обработать случай, когда указанный индекс выходит за пределы списка.
Я часто обнаруживал, что полезно составить текущий список с помощью бумаги и карандаша, а затем выполнить пробный запуск кода, чтобы убедиться, что он работает должным образом. Прекрасный пример этого см. здесь .
Объясняя, что не так с вашим кодом append
(где ?
представляет неизвестное значение, а !
означает неизвестный указатель, прямые ссылки вверху, обратные ссылки внизу):
head
\
NULL
\
tail
Append (7):
# head = (node*) malloc(sizeof(node));
head !
\ /
? NULL
/ \
! tail
# head->prev = NULL;
head !
\ /
?
/
NULL NULL
\
tail
# head->data = item;
head !
\ /
7
/
NULL NULL
\
tail
# head->next = NULL;
head NULL
\ /
7
/
NULL NULL
\
tail
# tail = head;
head NULL
\ /
7
/ \
NULL tail
Теперь, когда все выглядит хорошо. Вы можете пройти это вперед и назад без проблем. Теперь давайте добавим еще одно значение.
# Initial state.
head NULL
\ /
7
/ \
NULL tail
Append (9):
# node *current = (node*) malloc(sizeof(node));
head NULL !
\ / /
7 ? <- curr
/ \ /
NULL tail !
# current->prev = tail;
head NULL !
\ / /
7 ? <- curr
/ \___________/
NULL tail
# current->data = item;
head NULL !
\ / /
7 9 <- curr
/ \___________/
NULL tail
# current->next = NULL;
head NULL NULL
\ / /
7 9 <- curr
/ \___________/
NULL tail
# tail = current;
head NULL NULL
\ / /
7 9 <- curr
/ \___________/ \
NULL tail
Теперь вы должны легко видеть, что там отсутствует. Нет ссылки от head
на добавляемый нами узел.
Это означает, что любой цикл, начинающийся с заголовка, будет только когда-либо видеть один элемент в списке. Это также означает, что если вы попытаетесь разыменовать head->next
(например, используя 10-итерационный цикл for
), вы получите неопределенное поведение, которое и вызывает вашу ошибку.
Чтобы исправить это, сделайте резервную копию на один шаг и вставьте tail->next = current;
перед tail = current;
:
# current->next = NULL; // Back up a bit.
head NULL NULL
\ / /
7 9 <- curr
/ \___________/
NULL tail
# tail->next = current; // This is the step we're adding.
head ___________ NULL
\ / \ /
7 9 <- curr
/ \___________/
NULL tail
# tail = current;
head ___________ NULL
\ / \ /
7 9 <- curr
/ \___________/ \
NULL tail
И вот наш новый список, который можно перемещать в обоих направлениях. Вы можете добавить еще один, используя тот же метод, чтобы проверить правильность поведения.