Добавить узел в связанный список — почему ошибка сегментации?

Я реализую связанный список в C со структурой

struct node
{
    int data;
    struct node *next;
};

Я написал функцию добавления для добавления узла в конец связанного списка, как показано ниже, и функцию отображения для отображения всех узлов. Но я думаю, что дисплей дает ошибку сегментации из-за некоторой несогласованности в функции добавления. Что здесь может быть не так? В моей книге есть аналогичная функция для добавления с использованием malloc. Я хочу знать, что не так в моей функции.

void append(struct node **q, int d)
{
    struct node *temp;
    temp = *q;
    printf("\nBegin: Address at temp = %u", temp);
    while (temp!= NULL){
        temp = temp->next;
        printf("\nTravel: Address at temp = %u", temp);
    }

    struct node p1;
    p1.data = d;
    p1.next = NULL;
    *q=&p1;
    printf("\nEnd: Address at *q = %u\n", *q);
    printf("\n*q->data = %d next = %u", (*q)->data,(*q)->next );
} 
void display(struct node *q)
{
    printf("\n");
    while (q != NULL){
        printf(" -> %d",q->data);   
        q = q->next;        
    }
}

int main(int argc, char *argv[])
{
    struct node *p;
    p = NULL; /* empty linked list */

    printf("\nNo. of elements in the Linked List = %d", count(p));
    append(&p,1);
    display(p);
    append(&p,2);
    display(p);
    printf("\nNo. of elements in the Linked List = %d", count(p));
}

Выход:

No. of elements in the Linked List = 0
Begin: Address at temp = 0
End: Address at *q = 6684096

*q->data = 1 next = 0
-> 1Segmentation fault

Однако, когда я заменяю

struct node p1;
p1.data = d;
p1.next = NULL;
*q=&p1;

с

temp = *q;
*q = malloc(sizeof(struct node))
temp->data = d;
temp->next = NULL;

ошибка исчезла.
Кто-нибудь может объяснить причину?

Вы изменили свой вопрос соответственно после того, как получили ответ. Это не ценится и считается вопросом «движущейся мишени». Пожалуйста, избегайте этого. К счастью, я думаю, что на новый вопрос все еще отвечает мой пост. В противном случае дайте мне знать. Однако я мог бы попросить вас вернуться к вопросу, на который я ответил, а затем указать, чего не хватает в моем ответе... (Я вижу, вы приняли мой ответ сейчас. Я полагаю, что вы все еще воспринимаете его как ответ... .) Веселиться.

Yunnosch 10.12.2020 16:55

@Юннош. Извини за это. Мое редактирование и ваш ответ пришли параллельно. Я пробовал разные вещи и нашел точное утверждение, в котором я мог ошибиться, поэтому отредактировал пост, а затем увидел ваш ответ, который прояснил мое замешательство. Так что я тоже принял ваш ответ. Если это вас оскорбляет, я могу вернуть свою правку.

Monalisha Bhowmik 10.12.2020 17:23

"обидеть" - это слишком сильно. И в этом случае, я думаю, все обошлось. Нет необходимости возвращаться. С другой стороны, пожалуйста, поймите недооцененную концепцию вопроса о движущейся цели и по возможности избегайте ее. Да, я вижу, что редактирование и мой ответ (особенно версии...) имеют некоторое правдоподобное совпадение. Это нормально. Не беспокойся. (радостно играю с моим новым блестящим 25 повторением... :-))

Yunnosch 10.12.2020 17:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

*q=&p1;

Затем вы покидаете функцию.
Всякий раз, когда к нему обращаются позже, он будет обращаться к памяти, которая больше НЕ является локальной переменной.

Вам нужно выделить память для переменной. Используйте malloc() для этого.
Например.:

struct node *p1;
p1 = malloc(sizeof(*p1));
/* skipping recommended check of success/NULL */
p1->data = d;
p1->next = NULL;
*q=p1;

Есть и другие проблемы, например, вы позволяете списку начинаться с нового узла, за которым следует NULL, что приводит к потере/утечке всего вашего предыдущего списка. Но непосредственная проблема вызвана ссылкой на место в памяти ушедшей локальной переменной после того, как она существует.

по поводу петли:

while (temp!= NULL){

Это приводит к тому, что temp содержит NULL, поэтому он прошел весь связанный список и вышел за его конец.

Предлагать:

while (temp->next != NULL){

так как это перестанет проходить через связанный список, когда он указывает на последний «узел» в связанном списке.

Затем нужно использовать temp в качестве указателя на последний «узел» в связанном списке (где вы хотите добавить новый узел)

касательно:

struct node p1;
p1.data = d;
p1.next = NULL;
*q=&p1;
  1. это создает новый «узел» в стеке. Однако все в стеке «исчезает», когда функция возвращается.

  2. q — это указатель на первый «узел» в связанном списке, а не на последний «узел» в связанном списке. Предлагайте использовать temp как (после исправления 1) указывает на последний «узел» в связанном списке

  3. каждый «узел» должен быть создан в памяти «кучи» через malloc() или calloc(), чтобы он все еще существовал после выхода из функции.

касательно:

temp = *q;
*q = malloc(sizeof(struct node))
temp->data = d;
temp->next = NULL;

the error is gone.

НЕТ, ошибка НЕ ​​исчезла. Скорее, это всегда вставляет новый «узел» как второй «узел» в связанном списке. (и разрывает ссылку на следующие узлы связанного списка.)

размещенный код не может вернуть всю выделенную память в кучу (через вызовы free()). Результатом является утечка памяти для каждого вызова malloc().

Другие вопросы по теме