我是一名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中的代码也尝试实现相同的目的,但是给出了分段错误。有人可以指出,如果我的理解中缺少某些内容,或者我在代码本身出错了吗?
谢谢!
答案 0 :(得分:5)
假设您的队列结构目前有三个元素:
{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;
}
}
所以在第二个例子中,你释放一个空指针 - segfault!
答案 1 :(得分:4)
问题在于你解放的地方。这是有效的:
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
。
答案 2 :(得分:3)
这是方案2中的问题:
curr = Q;
...
Q->next = curr->next->next;
free(curr->next);
由于Q == curr
如果您将Q->next
设置为另一个指针,则还会更改curr->next
。因此,子序列free(curr->next)
不释放被激活的元素,而是释放新的元素。
答案 3 :(得分:3)
在第二个代码部分中,执行:
curr = Q;
然后:
Q->next = curr->next->next;
Q
和curr
指向相同的内容,因此当您更改Q->next
时,您也会更改curr->next
,并且您对{{1}的调用也是如此出错了。
在第一个代码部分中,free(curr->next)
在开头设置为curr
,而不是Q->next
,因此当您更改Q
时,Q->next
仍然存在不受影响,所以你curr
正确的指针。
答案 4 :(得分:0)
下线导致问题
Q->next = curr->next->next;
因为Q和curr是相同的指针,所以在该赋值之后,Q-> next可以为null(如果你的队列只有一个元素)。所以,当你打电话
free(curr->next);
你正试图释放null。还有逻辑错误,你永远不会释放出队的元素。