一个抽象的问题:假设我有一个节点的单链列表:
#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
,因为它是按值传递的。