【问题标题】:Segmentation Fault with C List when not using global List不使用全局列表时 C 列表的分段错误
【发布时间】:2020-10-30 23:42:30
【问题描述】:

我已经编写了一些代码,创建了一个单链表,然后将其转换为 C 中的动态数组。

如果我只为列表标题创建一个全局变量,代码就可以正常工作。但是,如果我想在我的 main 函数中创建列表,我总是会遇到分段错误。

只要我创建了一个全局列表变量并将列表作为参数从所有函数中删除,下面的代码就可以正常工作。

如果我想将列表作为参数传递给函数并因此能够创建多个列表,谁能告诉我为什么这不起作用?


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

typedef struct NodeStruct* Node;
typedef struct NodeStruct {
Node next;
int val;
} NodeStruct;
typedef Node List;



Node newNode(int x){
    Node n = (Node)malloc(sizeof(NodeStruct));
    if(n!=NULL){
        n->val = x;
        n->next = NULL;
        return n;
    }
    else{
        printf("ERROR: Could not allocate memory!\n");
    }
    exit(1);
}

void prepend(List l, Node node){
    if (l == NULL) l = node;
    else{
        node->next = l;
        l = node;
    }
}

void printList(List l){
    if(l!=NULL){
        Node n = l;
        while(n->next != NULL){
            printf("%d, ", n->val);
            n = n->next;
        }
        printf("%d\n", n->val);
    }
    else{
        printf("ERROR: List empty!\n");
    }
}



/*=============================*/

int* arrOf(List l){
    if(l==NULL){
        printf("ERROR: List empty\n");
        exit(1);
    }

    int size = 0;
    Node n = l;
    while(n!=NULL){
        size++; 
        n = n->next;
    }
    
    int* arr = (int*)malloc((size+1)*sizeof(int));
    n = l;
    int i = 0;
    arr[i++] = size;
    while(n != NULL){
        arr[i++] = n->val;
        n = n->next;
    }
    printf("Returning Array\n");
    return arr;
}



int main(int argc, char *argv[]){

    List l;
    
    prepend(l, newNode(5));
    prepend(l, newNode(6));
    prepend(l, newNode(7));
    prepend(l, newNode(8));
    prepend(l, newNode(9));
    prepend(l, newNode(4));
   
    printList(l);
    printf("\n===========================================\n");
    
    int* arr = arrOf(l);
    for(int i = 0; i < 10; ++i){
        printf("%d, ", arr[i]);
    }

    return 0;
}

【问题讨论】:

    标签: c struct linked-list initialization singly-linked-list


    【解决方案1】:

    这个函数

    void prepend(List l, Node node){
        if (l == NULL) l = node;
        else{
            node->next = l;
            l = node;
        }
    }
    

    处理main中声明的指针l的值的副本

    List l;
    

    而且没有初始化。

    因此在函数内更改这些语句中的副本

    if (l == NULL) l = node;
    //...
    l = node;
    

    不影响main中声明的指针的原始值。

    你必须写得至少喜欢

    void prepend(List *l, Node node){
            node->next = *l;
            *l = node;
    }
    

    主要是

    List l = NULL;
    

    函数可以这样调用

    prepend( &l, newNode(5) );
    

    即指向头节点的指针必须通过引用传递给函数。

    您还需要为列表和数组释放所有动态分配的内存。

    【讨论】:

    • 天哪,还有第二个问题。我知道我必须以某种方式将指向列表的指针作为参数传递。
    【解决方案2】:

    当您在main 中初始化List l 时,您并未分配默认值。它存储在堆栈中并且未初始化。这意味着该值未定义且不一定为 null。

    在全局创建List l时,变量存储在bss段中,并初始化为null。

    将List l 的声明更改为:

    List l = NULL;
    

    【讨论】:

    • 在现代 C++ 中,您可以使用 List l = nullptr; 或 List l{}; 而不是使用那个 C 宏。
    • 非常感谢!我仍然不得不将函数从 void 转换为最后返回 List ,但随后一切正常。现代 C++ 对我来说更容易。这是为了考 C tho
    • 没错,但 OP 使用的是 C,而不是 C++。不过,我同意您的建议对于 C++ 来说是更好的风格。 :)
    • @maxkremsner 很高兴为您提供帮助!
    • @MartinFink 抱歉,不知道为什么我在这里想到了 C++。
    猜你喜欢
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多