C - 链表未定义的行为



一个抽象的问题:假设我有一个节点的单链列表:

#node 1      #node 2
root->[data|next]->[data|next]->NULL

在 C 中,根声明为:

struct Node *root = NULL;

其中 *root 是节点指针"持有"地址"NULL"。

现在,假设我想删除链表中的最后一个节点,以下代码将允许计算机执行此操作:

//pop: pop removes last/latter node in linked list and sets new last node to NULL
void pop(struct Node *root){
struct Node *last;
struct Node *current = root;    //current point to same node as root
while(current->next != NULL){
if(current->next == NULL) {break;}    //premature break in traversing of linked list; will stop at last node
last = current;    //last points to same node as current
current = current->next;    //move current to next node
}
last->next = NULL;    //point second-to-last node to NULL, making it defacto last node in list
free(current);    //free last node to heap
}//end pop

调用 pop 并将 root 传递给函数后,新的链表如下所示:

#node 1
root->[data|next]->NULL

如果程序再次调用 pop,我们应该期望链表看起来像这样:

root->NULL

但是,事实并非如此!在整数元素按顺序排列的链表的情况下,我们将调用 pop,直到我们观察到奇怪的行为:

List: 1 2 3
Call pop
List: 1 2
Call pop
List: 1
Call pop
List 1980765

以上是由悬空指针引起的未定义行为的示例。现在的问题是:程序如何避免这种行为并产生接近root->NULL的副作用,从链表中弹出所有节点,直到所述列表为空?

首先:

这个条件永远不可能是真的:

if(current->next == NULL) {break;}

如果这是真的,我们就不会到达那条线,而是脱离while循环。

第二:

如果未至少执行一次循环主体,则指针last将保持未初始化状态。因此

last->next = NULL;

将随机内存位置设置为NULL

第三:

当您尝试删除最后一个剩余元素时,您需要free(root)。但是不能将root设置为NULL,因为它是按值传递的。

相关内容

  • 没有找到相关文章