我是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,但这仅在第二次及其后的迭代中适用。