You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言将链表结构体封装到另一结构体的方法及段错误排查

段错误(SIGSEGV)原因分析与修复

想要实现一个隐藏底层链表细节的抽象数据类型HiddenHeaders,配套IsEmpty和Enqueue函数,让main函数的用户无需知晓底层struct listStructure的存在。但程序调用函数时直接触发SIGSEGV段错误,原始代码如下:

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

struct listStructure {
    int data;
    struct listStructure *nextPtr;
};

typedef struct listStructure ListStructure;

struct hiddenHeaders {
    ListStructure *ptr_to_list;
};

typedef struct hiddenHeaders HiddenHeaders ;

HiddenHeaders * Enqueue (HiddenHeaders *abstract, int user_input);
bool IsEmpty (HiddenHeaders *abstract);

int main () {
    HiddenHeaders *my_list;
    int input;

    my_list = NULL;
    printf ("Enter data item value to add  ");
    scanf ("%d", &input);
    my_list = Enqueue (my_list, input);

return 0;
}

bool IsEmpty(HiddenHeaders *abstract) {
    ListStructure *adt_el = abstract->ptr_to_list;
    bool isEmpty;
    if (adt_el == NULL) {
        return isEmpty=true; }
    else
        return isEmpty=false;
}

HiddenHeaders * Enqueue (HiddenHeaders *abstract, int user_input) {
    ListStructure *adt_el = abstract->ptr_to_list;
    HiddenHeaders *abstract_Ptr = abstract;

      if (!IsEmpty(abstract)) {
        while ((adt_el -> nextPtr) == NULL) {
            adt_el = adt_el -> nextPtr; }

        adt_el -> nextPtr = (ListStructure  *) malloc (sizeof (ListStructure));
        adt_el = adt_el -> nextPtr;
        adt_el -> nextPtr = NULL;
        adt_el -> data = user_input;

        return abstract_Ptr;
    }
    else {
        adt_el = (ListStructure  *) malloc (sizeof (ListStructure));
        adt_el -> nextPtr = NULL;
        adt_el -> data = user_input;

        return abstract;
    }
}

核心错误点

  • 空指针解引用:main中my_list初始化为NULL,调用Enqueue和IsEmpty时,函数直接尝试访问abstract->ptr_to_list——对NULL指针进行解引用,这是触发段错误的直接原因。
  • 未初始化HiddenHeaders实例:当传入的HiddenHeaders指针为NULL时,没有先创建HiddenHeaders结构体实例,直接操作其内部的链表指针,导致非法内存访问。
  • Enqueue逻辑错误:
    • else分支中,创建了链表节点但未将其关联到HiddenHeaders的ptr_to_list,新节点完全游离。
    • 寻找链表尾的循环条件写反:while ((adt_el -> nextPtr) == NULL)只会在当前节点是尾节点时循环一次(且循环体里adt_el = adt_el->nextPtr会让adt_el变成NULL,后续操作再次触发错误),正确的条件应该是while (adt_el->nextPtr != NULL)来遍历到真正的尾节点。

修复后的完整代码

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

// 底层链表结构体,对用户透明
struct listStructure {
    int data;
    struct listStructure *nextPtr;
};
typedef struct listStructure ListStructure;

// 抽象数据类型,用户仅需操作该类型
struct hiddenHeaders {
    ListStructure *ptr_to_list;
};
typedef struct hiddenHeaders HiddenHeaders;

HiddenHeaders *Enqueue(HiddenHeaders *abstract, int user_input);
bool IsEmpty(HiddenHeaders *abstract);

int main() {
    HiddenHeaders *my_list = NULL;
    int input;

    printf("Enter data item value to add: ");
    scanf("%d", &input);
    my_list = Enqueue(my_list, input);

    // 测试IsEmpty功能
    printf("Is list empty? %s\n", IsEmpty(my_list) ? "Yes" : "No");

    return 0;
}

bool IsEmpty(HiddenHeaders *abstract) {
    // 先判断抽象对象是否为空,为空则链表一定为空
    if (abstract == NULL) {
        return true;
    }
    // 再判断底层链表指针是否为空
    return abstract->ptr_to_list == NULL;
}

HiddenHeaders *Enqueue(HiddenHeaders *abstract, int user_input) {
    // 如果抽象对象未初始化,先创建并初始化
    if (abstract == NULL) {
        abstract = (HiddenHeaders*)malloc(sizeof(HiddenHeaders));
        abstract->ptr_to_list = NULL;
    }

    // 创建新的链表节点
    ListStructure *new_node = (ListStructure*)malloc(sizeof(ListStructure));
    new_node->data = user_input;
    new_node->nextPtr = NULL;

    // 如果链表为空,直接作为头节点
    if (IsEmpty(abstract)) {
        abstract->ptr_to_list = new_node;
    } else {
        // 遍历到链表尾节点
        ListStructure *current = abstract->ptr_to_list;
        while (current->nextPtr != NULL) {
            current = current->nextPtr;
        }
        // 将新节点挂载到尾节点后
        current->nextPtr = new_node;
    }

    return abstract;
}

关键修改说明

  • 空指针防护:IsEmpty和Enqueue先判断传入的abstract是否为NULL,避免非法内存访问。
  • 初始化抽象对象:当abstract为NULL时,先分配HiddenHeaders内存并初始化其链表指针为NULL,确保后续操作的合法性。
  • 修正链表遍历逻辑:将寻找尾节点的循环条件改为current->nextPtr != NULL,正确遍历到链表末尾。
  • 关联新节点:在空链表时将新节点赋值给abstract->ptr_to_list,非空时挂载到尾节点的nextPtr,确保节点被正确加入链表。

内容的提问来源于stack exchange,提问作者AnnaKarin47

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 23:35:24