【问题标题】:Segmentation Fault in a struct with a pointer to another struct具有指向另一个结构的指针的结构中的分段错误
【发布时间】:2011-11-11 00:09:21
【问题描述】:

我的链接列表有一些问题。我将struct dListNode 用作列表的节点,并带有指向struct data 的指针,用作数据存储。

struct data{
    int payload;
};

struct dListNode{
    struct dListNode *next;
    struct dListNode *prev;
    struct data *val;
}*dHead, *dTail;

我的程序可以正常编译,但在下面指示的行出现分段错误。怎么回事?

newDNode = (struct dListNode *)malloc(sizeof(struct dListNode)+sizeof(struct data));
printf("newnode created\n"); // this prints
newDNode->val->payload = rand() % 1000; //error here?
printf("newnode payload: %i\n", newDNode->val->payload); //seg fault before this is printed

另外,我已经在程序中运行了这一行:srand((unsigned)time(NULL))

【问题讨论】:

    标签: c linked-list segmentation-fault


    【解决方案1】:

    NewDNode 没有关联的内存分配。所以当你这样做时

    newDNode = (struct dListNode *)malloc(sizeof(struct dListNode)+sizeof(struct data));
    

    这只是将内存分配给newDnode,而不是newDnode->val。由于newDNode->val 仅包含该位置内存中剩余的任何内容(或者甚至可能为 0(空指针)),并且您尝试为既不在堆栈上也不在堆上的内存位置分配一个值,程序抱怨是因为您试图访问未分配的内存部分。

    这是你应该做的:

    newDNode = malloc(sizeof(struct dListNode));
    newDnode->val = malloc(sizeof(struct data));
    printf("newnode created\n");
    newDNode->val->payload = rand() % 1000;
    printf("newnode payload: %i\n", newDNode->val->payload);
    

    作为提示,请始终尽量不要转换 malloc(或任何其他内存分配函数)返回的结果。它被认为是不好的做法。

    【讨论】:

    • 哦,有道理。谢谢您的帮助。我的印象是不投射 malloc 的结果是不好的......
    • 基本上,投射非常非常强大......但只有在必要时才使用它。有时,强制转换(一般来说,强制转换 malloc 几乎总是不好的)非常有用且有意义,但大多数时候,它被滥用并导致阅读代码的人感到困惑。请记住,“代码读得比写得更多”。
    【解决方案2】:

    你的问题是你从来没有初始化指针val

    newDNode->val->payload = rand() % 1000;
    

    newDNode 已分配,但未初始化任何字段,因此取消引用 val 可能会导致分段错误。

    所以你需要在访问之前为val 分配一些东西。

    newDNode = malloc(sizeof(struct dListNode));   //  Allocate "dListNode"
    newDNode->val = malloc(sizeof(struct data));   //  Allocate "data"
    newDNode->val->payload = rand() % 1000;
    

    您对分配的工作原理有一点误解。您需要分别分配每个指针。

    编辑:另一种方法是首先不使用val的指针:

    //  Declare struct as:
    struct dListNode{
        struct dListNode *next;
        struct dListNode *prev;
        struct data val;
    }*dHead, *dTail;
    
    
    //  Build object like this:
    newDNode = malloc(sizeof(struct dListNode));
    newDNode->val.payload = rand() % 1000;
    

    【讨论】:

    • 这个newDNode = malloc(sizeof(struct dListNode)+sizeof(struct data)); 是错误的。您不需要额外的空间,这是由于 OP 对如何分配结构的误解。
    • @EdS.:我正在修复你评论的问题。不过感谢您的指出。
    【解决方案3】:

    val 未指向有效的data 结构。当然,你malloc 足够大,但这并不意味着val 现在突然成为一个有效指针。您应该只用 dListNode 的大小初始化 newDNode,然后分别初始化 newDNode->val 以指向足够大的有效内存块以容纳 data 结构。

    附带说明,您不需要转换malloc 的返回值。那是 C++ 的事情;在 C 中,void* 可以隐式转换为任何其他指针类型。

    其次,如果您使用typedef 结构类型,则在使用它们时不必到处写struct

    【讨论】:

    • 但是除非有意义,否则过多地使用void *s 并不是一个好习惯,对吧?
    • @mtahmed:你没有使用void*'s; malloc 的返回值是 void*,它被隐式转换为 dListNode*。您应该在需要时使用void*,但这里不需要演员表。
    【解决方案4】:

    你永远不会设置newDNode->val 指向任何东西。因此,当您尝试设置newDNode->val->payload 时,您正在取消引用空指针或某个随机地址(我忘记了)。这两种情况都不是你想要的。

    我不太喜欢在同一个语句中同时分配两个结构的想法。但是如果你坚持这样做,你需要做类似的事情

    newDNode->val = (struct data*)((char*) newDNode + sizeof(struct dListNode));
    

    不过,更好的方法是更改​​结构,使val 成为一个结构,而不仅仅是指向一个结构的指针。然后sizeof(struct dListNode) 包含struct data 的大小,您可以像newDListNode->val.payload 一样访问它,而无需单独malloc 数据部分或进行任何棘手的指针数学运算。 (缺点是,您必须复制结构以将它们存储在数组中,交换它们等)

    【讨论】:

    • 我没有看到任何优势(甚至是理由)去做 OP 可能试图做的事情......
    • 好点。我在指出如何可以通过最小的更改来完成,但最好的解决方案(如果他想假设数据结构将立即存在)是将数据结构直接放入节点结构而不是而不是使用指针。
    • 是的,这将是最好的解决方案,因为从外观上看,他总是拥有data
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-25
    相关资源
    最近更新 更多