【问题标题】:Why is this segfaulting whilst building a binary tree?为什么在构建二叉树时会出现段错误?
【发布时间】:2015-12-11 16:04:36
【问题描述】:

我通过 gdb 发现这段代码中的这一行是段错误。但是我似乎不明白为什么?在发生段故障之前,它将运行 6/7 次。 Temp是链表中的一个节点,它包含一个频率(int),我用它来查找升序链表中的位置以插入一个新节点。

while (ind == 0 && temp != NULL)
{
    temp = temp -> next;
    if (temp -> frequency > parent_node -> frequency) /*<--- SEG FAULT HERE */
    {
        parent_node -> next = temp -> next; /* parent points at higher freq node */ 
        temp -> next = parent_node; /* parent node is temp next */
        ind = 1;
    }
    if (temp -> next == NULL)
    {
        temp -> next = parent_node; 
        ind = 1;          
    }
}

【问题讨论】:

  • temp = temp -&gt; next; : temp 变为 NULL
  • 您的代码试图做什么并不完全清楚(尽管我们知道这是错误的)。 tempparent_node 在进入循环时指向什么,您希望它们在退出循环时指向什么?这两行parent_node-&gt;next = temp-&gt;next;temp-&gt;next = parent_node; 似乎正在创建一个带有两个节点的循环列表。这就是你想要的吗?
  • 在我的脑海中parent_node-&gt;next = temp-&gt;next(父节点是要插入的节点),将指向 temp -> next(更大的值),temp-&gt;next = parent_node 将前一个节点指向父节点,从而将父节点插入到列表中
  • 请注意,二进制点 . 和箭头 -&gt; 运算符确实绑定得非常紧密,并且在正统 C 中不应有空格。

标签: c linked-list segmentation-fault binary-tree


【解决方案1】:

您确实是temp = temp-&gt;next,但temp-&gt;next 可能是nullptr。在尝试访问它的成员之前,您必须检查其不为空。

【讨论】:

  • 我以为我已经在while ind == 0 &amp;&amp; temp != NULL中完成了这一切
  • @Finlandia_C:该条件保护第 3 行的取消引用,但不保护下一行 if (temp -&gt; frequency ...) 及以后的正确。
  • temp != NULL 是其中之一,但您还需要temp-&gt;next != NULL,因为您可以访问temp-&gt;next 的成员。
  • 所以...if (temp != NULL &amp;&amp; temp -&gt; next != NULL &amp;&amp; temp -&gt; frequency &gt; parent_node -&gt; frequency)?
  • 这样的,是的。通常,在执行取消引用 (foo-&gt;bar) 之前,您必须以某种方式确保 foo 不为 NULL。
【解决方案2】:

这里有一些代码会将newnode 指向的新节点插入到链表中,其中liststart 指向链表中的第一个节点,或者是NULL 的空链表:

if (liststart == NULL) {
    liststart = newnode;
    newnode->next = NULL;
} else {
    struct mylist *prev = liststart;

    while (prev->next != NULL && prev->next->frequency <= newnode->frequency) {
        prev = prev->next;
    }
    if (prev->next == NULL) {
        prev->next == newnode;
        newnode->next = NULL;
    } else {
        newnode->next = prev->next->next;
        prev->next = newnode;
    }
}

这是一个使用指向指针的替代版本:

struct mylist **pprev;

pprev = &liststart;
while (*pprev != NULL && (*pprev)->frequency <= newnode->frequency) {
    pprev = &(*pprev)->next;
}
newnode->next = *pprev;
*pprev = newnode;

【讨论】:

    【解决方案3】:

    您的问题是您检查temp 不为空,然后立即更新temp,因此它可能再次为空。

    (我不喜欢 temp 这个名字,所以在我的例子中我把它改成了 node

    分解最清楚,所以:

    while(ind == 0 && node != null) {
         node = node -> next;  // now node might be null
         doStuffWith(node);
    }
    

    相反,您可以将分配移到循环的末尾。这意味着,当然,第一次,任务还没有发生。所以你可能需要在循环外调用一次:

    node = node -> next; // or some other initialisation of node
    while(ind == 0 && node != null) {
         doStuffWith(node);
         node = node -> next;
    }
    

    也许您已经听说过 DRY 原则:“不要重复自己”,因此您可能会对重复 node = node -&gt; next 感到不舒服。然而,这是一个非常常见的模式示例,例如:

    int chars = stream.read(buffer);
    while(chars != -1) {
         doSomethingWith(buffer);
         chars = stream.read(buffer);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-05
      • 2019-09-23
      • 1970-01-01
      • 1970-01-01
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 2014-12-16
      相关资源
      最近更新 更多