我是C ++和指针的新手,但是我似乎无法解释为什么我在两个几乎完全相同的代码之一中出现分段错误。我正在完成遍历LinkedList的琐碎任务,这是我这样做的第一种方法:

Node * curNode = head; //Head is the pointer to the head of the LinkedList
while(curNode) {
    curNode = curNode->next;
}


这将产生所需的结果,并成功遍历整个LinkedList。但是,这会引发Segmentation fault错误:

Node * curNode = head;
while(curNode->next != NULL) {
    curNode = curNode->next;
}


将条件更改为强制转换的布尔值或!= nullptr不会执行任何操作,因此条件不是在空指针上提高true的情况。但是,我似乎无法弄清为什么得到Segmentation fault的原因,似乎合适的循环条件可确保我不访问任何空指针。

最佳答案

重写:

Node * curNode = head;

// presumably, head is initialized somehow in omitted code

while(curNode->next != NULL) {
    curNode = curNode->next;
}


有两个严重的问题。一个是比较琐碎的问题,它不考虑head为null的情况。这会导致崩溃。

第二个问题是,即使第一个问题得到解决,它在算法上仍然不正确。它忽略遍历列表的最后一个节点。

如果curNode是非空指针,但curNode->next为null,则表示“我们有一个有效节点,没有后继节点:curNode是最后一个节点”。

因此,循环保护条件while (curNode->next != NULL)表示“而curNode不是最后一个节点”。它不等同于遍历整个列表(包括最后一个节点)的第一个循环。

如果遍历除最后一个节点之外的所有节点,那是正确的,并且我们可以理解为什么需要head测试:如果head为null,则列表为空,因此没有最后一个节点。由于它没有最后一个节点,因此不能将空列表分为“最后一个节点”和“其他所有”。这就是为什么这是一种特殊情况,以及为什么仅出于特殊情况而存在额外的null检查。我们可以这样写:

Node * curNode = head;

if (head) {  // list is not empty: visit all but the last node
    while (curNode->next != NULL) {
        curNode = curNode->next;
    }
} else { // special case: list has no last node
   // do nothing? Or maybe some special behavior.
}


如果在特殊情况下我们不需要任何特殊行为(可以不做任何事情),则可以这样编写:

while (head && curNode->next != NULL) { // fold the if test into the while
    curNode = curNode->next;
}


我们在循环的每次迭代上都浪费了head的测试;但是,编译器可以轻松地优化它,因为在循环体中未修改head。这也是可能的:

while (curNode && curNode->next != NULL) {
    curNode = curNode->next;
}


但是,它在循环的每次迭代中都浪费了curNode的测试。我们知道,除了第一次迭代之前,curNode永远不能为null。在随后的迭代中,curNode的值是从curNode->next获得的,并且我们知道curNode->next不为null,因为这是进入迭代的条件。也许您的编译器也可以解决这个问题,尽管与优化对head的访问相比,它需要更深入的分析。编译器必须遵循相同的数据流推理,即从curNode获得curNode->next,该值不为null,但这仅在第二次及其后的迭代中适用。

08-26 17:00