C++ 分段故障常见原因的最终列表

C++ 分段故障常见原因的最终列表,c++,c,segmentation-fault,C++,C,Segmentation Fault,注意:我们有很多segfault问题,基本上是相同的 答案,所以我试着把它们压缩成一个标准问题,比如 我们有时间 虽然我们有一个问题,它涵盖了什么,但没有列出很多原因。最上面的答案是“有很多原因”,只列出了一个,而其他大多数答案没有列出任何原因 总而言之,我认为我们需要一个组织良好的社区wiki,它列出了所有导致错误的常见原因(还有一些)。正如答案的免责声明中提到的,其目的是帮助调试 我知道分段错误是什么,但如果不知道它们通常是什么样子,就很难在代码中发现它们。虽然有很多人无法详尽地列出, C+

注意:我们有很多segfault问题,基本上是相同的 答案,所以我试着把它们压缩成一个标准问题,比如 我们有时间

虽然我们有一个问题,它涵盖了什么,但没有列出很多原因。最上面的答案是“有很多原因”,只列出了一个,而其他大多数答案没有列出任何原因

总而言之,我认为我们需要一个组织良好的社区wiki,它列出了所有导致错误的常见原因(还有一些)。正如答案的免责声明中提到的,其目的是帮助调试

我知道分段错误是什么,但如果不知道它们通常是什么样子,就很难在代码中发现它们。虽然有很多人无法详尽地列出, C++和C++中的分割错误是最常见的原因是什么? 警告 以下是分段故障的潜在原因几乎不可能列出所有原因。此列表的目的是帮助诊断现有SEG故障

分段错误和未定义行为之间的关系怎么强调都不够!以下所有可能导致分段错误的情况在技术上都是未定义的行为。这意味着它们可以做任何事情,而不仅仅是分段错误——正如有人在USENET上所说的那样,“”。当您有未定义的行为时,不要指望segfault会发生。您应该了解C和/或C++中存在哪些未定义的行为,避免编写具有它们的代码!p> 有关未定义行为的更多信息:


什么是断层? 简言之,当代码试图访问其无权访问的内存时,会导致分段错误。每个程序都有一块内存(RAM)可供使用,出于安全原因,它只允许访问该块中的内存

有关分段错误的更全面的技术解释,请参阅。

以下是导致分段错误的最常见原因。同样,这些应用于诊断现有segfault。要学习如何避免它们,请学习您的语言未定义的行为

此列表也不能代替您自己的调试工作。(请参阅答案底部的那一部分。)这些是您可以查找的内容,但调试工具是解决问题的唯一可靠方法


访问空指针或未初始化指针 如果您的指针为空(
ptr=0
)或完全未初始化(尚未设置为任何值),则尝试使用该指针进行访问或修改将具有未定义的行为

int* ptr = 0;
*ptr += 5;
由于失败的分配(例如使用
malloc
new
)将返回空指针,因此在使用指针之前,应始终检查指针是否为空

还要注意的是,即使读取未初始化指针(以及通常的变量)的值(不取消引用),也是未定义的行为

int* ptr = 0;
*ptr += 5;
有时,对未定义指针的这种访问可能非常微妙,例如在试图将此类指针解释为C print语句中的字符串时

char* ptr;
sprintf(id, "%s", ptr);
另见:


访问悬空指针 如果您使用
malloc
new
分配内存,然后稍后通过指针
free
delete
该内存,则该指针现在被视为悬挂指针。取消对它的引用(以及简单地读取它的值-假定您没有为它指定一些新值,例如NULL)是未定义的行为,并且可能导致分段错误

Something* ptr = new Something(123, 456);
delete ptr;
std::cout << ptr->foo << std::endl;
堆栈溢出的另一个原因是一次有太多(非动态分配的)变量

int stupidArray[600851475143];
有一种情况是,为了防止函数中出现无限递归,在条件语句中省略了
return
语句。这个故事的寓意是,始终确保错误检查有效

另见:


野指针 创建指向内存中某个随机位置的指针就像用代码玩俄罗斯轮盘赌——您很容易会错过并创建指向您没有访问权限的位置的指针

int n = 123;
int* ptr = (&n + 0xDEADBEEF); //This is just stupid, people.
一般来说,不要创建指向文字内存位置的指针。即使他们工作过一次,下一次也可能不工作。您无法预测在任何给定执行时程序内存的位置

另见:


试图读取超过数组结尾的内容 数组是一个连续的内存区域,其中每个连续元素位于内存中的下一个地址。然而,大多数数组对它们有多大或最后一个元素是什么没有天生的感觉。因此,很容易吹过数组的末尾而永远不知道它,特别是在使用指针算法的情况下

如果读取超过数组的末尾,则可能会进入未初始化的内存或属于其他内容的内存。这在技术上是未定义的行为。segfault只是众多潜在的未定义行为之一。[坦率地说,如果你在这里遇到了一个SEGFULT,你就很幸运了。其他的更难诊断。]

// like most UB, this code is a total crapshoot.
int arr[3] {5, 151, 478};
int i = 0;
while(arr[i] != 16)
{
   std::cout << arr[i] << std::endl;
   i++;
}
甚至是一个不走运的打字错误,它编译得很好(如图所示),只分配了1个元素,并用
dim
而不是
dim
元素初始化

int* my_array = new int(dim);
此外,应该注意的是,甚至不允许创建(更不用说取消引用)指向数组外部的指针(只有当指针指向数组中的元素,或者指向数组末尾的元素时,才可以创建这样的指针)。否则,你就是
int* my_array = new int(dim);
char str[3] = {'f', 'o', 'o'};
int i = 0;
while(str[i] != '\0')
{
   std::cout << str[i] << std::endl;
   i++;
}
char* foo = "Hello, world!"
foo[7] = 'W';