C将链表结构传递给函数

C passing a linked list struct to a function

我用 C 实现了链表

问题是,每当我尝试使用函数 print_list(struct linked_list *list) 打印节点数据时,我都会遇到分段错误。

我不确定是什么原因造成的,因为当我尝试 print(struct linked_list *list) 时,它工作正常。

而且,当我尝试动态分配内存时,它也工作正常。但是我很好奇这样的代码有什么问题?为什么使用 print 不会导致相同的错误?

#include <stdio.h>
#include <stdlib.h>

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

struct linked_list{
    struct node *head;
};

void concat(struct linked_list* list1, struct linked_list* list2)
{
    struct node* tmp = list1->head;
    
    while(tmp->next != NULL)
        tmp = tmp->next;
    tmp->next = list2->head;
}

void print_list(struct linked_list *list)
{
    struct node* tmp = list->head;
    while(tmp != NULL){
        printf("%c - ", tmp->data);
    tmp = tmp->next;}
    printf("\n");
}

void print(struct linked_list *list)
{
    struct node* tmp = list->head;
    printf("%c\n", tmp->data);
    tmp = tmp->next;
    printf("%c\n", tmp->data);
    tmp = tmp->next;
    printf("%c\n", tmp->data); 
}

int main()
{
    struct linked_list list1,list2;
    struct node n1,n2,n3,n4,n5;
    n1.data = 'A';
    n2.data = 'B';
    n3.data = 'C';
    n4.data = 'D';
    n5.data = 'E';
    n1.next = &n2;
    n2.next = &n3;
    
    n4.next = &n5;
    list1.head = &n1;
    list2.head = &n4;
    printf("List 1 containes :\n");
    print_list(&list1);
    concat(&list1,&list2);
    printf("List 1 after concat: \n" );
    print_list(&list1);

    return 0;
}

这里:

struct node n1,n2,n3,n4,n5;

您创建了五个节点但没有对其进行初始化。 C 不会将局部变量初始化为 null 或零,因此节点的字段具有不确定的(“垃圾”)值。稍后,您初始化了一些字段,但没有初始化列表中最后一个节点的 next 字段。

有几种解决方法,例如:

(1) 显式初始化最后一个节点的next字段:

n1.next = &n2;
n2.next = &n3;
n3.next = NULL;

n4.next = &n5;
n5.next = NULL;

(2) 用数据初始化节点,然后设置链接:

struct node n1 = {'A'};
struct node n2 = {'B'};
struct node n3 = {'C'};
struct node n4 = {'D'};
struct node n5 = {'E'};

n1.next = &n2;
n2.next = &n3;

n4.next = &n5;

一旦你初始化一个结构,所有字段都将被初始化,即使没有明确给出值(比如 next)。根据类型,这些字段被隐式初始化为零或空值。现在您在设置链接之前拥有有效(但未连接)的节点。

(3) 通过初始化定义一切:

struct node n5 = {'E', NULL};
struct node n4 = {'D', &n5};
struct linked_list list2 = {&n4};

struct node n3 = {'C', NULL};
struct node n2 = {'B', &n3};
struct node n1 = {'A', &n2};
struct linked_list list1 = {&n1};

现在您已经准备好列表,但是您必须向后定义它,以便在您引用它时知道 next 节点。

还有其他可能性可以在不在堆上分配内存的情况下设置链表,例如节点数组,但我想您明白了。

首先尝试了解什么是 segmentation fault

根据Wikipedia

In computing, a segmentation fault (often shortened to segfault) or access violation is a fault, or failure condition, raised by hardware with memory protection, notifying an operating system (OS) the software has attempted to access a restricted area of memory (a memory access violation).

所以发生这种情况是因为您的程序正在访问一个它不应该访问的受限位置。但为什么?因为在 while(tmp->next != NULL) 中,程序在遍历所有元素后不一定会找到 NULL。因此,在那种情况下,循环不会中断,它允许循环进一步继续,最终程序会尝试访问受限位置。

因此,为了解决这个问题,请在 node 结构的定义中初始化 node* Next = NULL。喜欢:

struct node{
    char data;
    struct node* next = NULL;
};

现在 next 的默认值明确设置为 NULL。因此,除非您将其更改为指向另一个节点,否则它仍将保持 NULL。而且问题应该解决了。