【问题标题】:2 apparently identical codes! One compiles, one throws up seg fault2个明显相同的代码!一个编译,一个抛出 seg fault
【发布时间】:2013-10-31 15:49:03
【问题描述】:

我是一名初学C语言程序员,刚刚编写了一个涉及队列操作的练习题,在调试时,我遇到了以下场景:

代码示例 1

int dequeue (struct queue_node * Q) {

    struct queue_node * curr = Q->next;
    if(!Q->next)
            return -2;
    else
    {
            int s = Q->next->v_no;
            Q->next = curr->next;
            free(curr);
            return s;
    }

}

代码示例2(相同的函数,稍作修改):

int dequeue (struct queue_node * Q) {

    struct queue_node * curr = Q;
    if(!curr->next)
            return -2;
    else
    {
            int s = curr->next->v_no;
            Q->next = curr->next->next;
            free(curr->next);
            return s;
    }

}

预定义的数据结构如下

struct queue_node {

    int v_no;
    struct queue_node * next;

};

void enqueue (struct queue_node * Q , int s) {

    struct queue_node * curr = Q;
    while (curr->next)
            curr = curr->next;

    curr->next = malloc(sizeof(struct queue_node));
    if(!curr->next)
            exit(10);   //No specific reason for errno 10, just wanted to exit
    curr->next->v_no = s;
    curr->next->next = NULL;

}

问题: 场景 1 中的代码允许程序成功执行并输出预期的答案,但根据我的理解,场景 2 中的代码也尝试实现相同的目标,但给出了分段错误。有人可以指出我的理解是否有缺陷,或者我的代码本身有什么问题吗??

谢谢!

【问题讨论】:

  • 请修正代码格式。编辑器为此提供了按钮和快捷键 (Ctrl+K)。
  • 您确定要删除->next 处的项目吗?
  • 记得最终接受an的答案——不一定是我的,但应该有人从中得到一些因果报应。 :)
  • @Nikos:感谢您指出这一点

标签: c struct queue


【解决方案1】:

下线导致问题

 Q->next = curr->next->next;

因为 Q 和 curr 是同一个指针,在赋值之后,Q->next 可以为空(如果您的队列只有一个元素)。所以,当你打电话时

free(curr->next);

您正在尝试释放 null。还有逻辑错误,你永远不会释放出队的元素。

【讨论】:

  • 在这一行 curr->next 是指向队列顶部第三个元素的指针
【解决方案2】:

假设您的队列结构当前包含三个元素:

{a} -> {b} -> {c} -> NULL

Q 当前指向b

让我们来看看你的每个函数会做什么。

/* Example 1 (working) */
int dequeue (struct queue_node * Q) {

    struct queue_node * curr = Q->next;    // curr = {c}
    if(!Q->next)                           // c != NULL, so OK
            return -2;
    else
    {
            int s = Q->next->v_no;         // s = c.v_no
            Q->next = curr->next;          // Q->next = NULL
            free(curr);                    // free({c})
            return s;                      // return c.v_no
    }
}

因此,示例 1 提供了指向 Q 节点的指针,将下一个节点出列并移除,返回其 v_no

/* Example 2 (segfaults) */
int dequeue (struct queue_node * Q) {

    struct queue_node * curr = Q;          // curr now points to the same element as q
    if(!curr->next)                        // {b}->next == {c}, so OK
            return -2;          
    else 
    {
            int s = curr->next->v_no;      // s = Q
            Q->next = curr->next->next;    // Q->next = NULL ({c}->next)
            free(curr->next);              // same as free (Q->next) == free(null)
            return s;
    }

}

所以在第二个例子中,你释放了一个空指针——段错误!

【讨论】:

  • 你们都解释得很好,只需要选择最佳答案的人......毕竟业力无法摧毁,它只能转移:)。谢谢大家!
【解决方案3】:

问题在于你在哪里进行释放。这是一个有效的方法:

int s = Q->next->v_no;
Q->next = curr->next;
free(curr);

还有一个没有:

int s = curr->next->v_no;
Q->next = curr->next->next;
free(curr->next);

请记住,在第二种情况下,您有 curr = Q。所以,在第一个中,我们将Q->next 设置为Q->next->next,然后我们删除了 Q->next

在第二个中,我们将Q->next 设置为Q->next->next,但随后我们删除了当前 Q->next

【讨论】:

  • 我想我们都同时看到了它
  • 这很有趣,几秒钟内就有四个几乎相同的答案。
  • 有趣的是,我们每个人都花了那么长时间才找到答案。
  • @scott 是的,我也看了好一阵子
  • Ingo、Paul、Scott 和 Christian:非常感谢您的明确回答。真的很感激!
【解决方案4】:

在第二个代码部分,当你这样做时:

curr = Q;

然后:

Q->next = curr->next->next;

Qcurr 指向同一个东西,所以当你改变Q->next 时,你也改变了curr->next,你对free(curr->next) 的调用出错了。

在第一个代码部分,curr 开头设置为Q->next,而不是Q,因此当您更改Q->next 时,curr 仍然不受影响,因此您的free() 正确指针。

【讨论】:

  • 我想我们都同时看到了它
【解决方案5】:

这就是场景 2 中的问题:

curr = Q;          
...  
Q->next = curr->next->next;   
free(curr->next);

由于Q == curr,如果您将Q->next 设置为另一个指针,您也会更改curr->next。因此,子序列free(curr->next) 不会释放出队元素,而是释放新分配的元素。

【讨论】:

    猜你喜欢
    • 2014-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    相关资源
    最近更新 更多