什么是分段故障?它在C和C++中不同吗?分段错误和悬挂指针是如何关联的?


当前回答

分段错误是由对进程未在其描述符表中列出的页面的请求,或对进程已列出的页面(例如,只读页面上的写入请求)的无效请求引起的。

悬空指针是指可能指向或不指向有效页,但确实指向“意外”内存段的指针。

其他回答

答案中对“分段错误”有几个很好的解释,但由于分段错误通常会导致内存内容转储,所以我想分享分段错误(核心转储)中“核心转储”部分与内存之间的关系来源:

大约从1955年到1975年,在半导体存储器之前,计算机存储器中的主导技术使用了铜线上的微小磁性甜甜圈。甜甜圈被称为“铁氧体磁芯”,主存储器因此被称为核心存储器或“核心”。

从这里拍摄。

分段错误是由于访问“不属于您的”内存而导致的一种特定错误。它是一种帮助机制,可以防止损坏内存并引入难以调试的内存错误。每当你得到一个segfault时,你就知道你在内存方面出了问题——访问一个已经被释放的变量,写入内存的只读部分,等等。在大多数语言中,segfault基本上是一样的,这会让你陷入内存管理的困境,在C和C++中segfault之间没有主要区别。

有很多方法可以获得segfault,至少在C(++)等较低级别的语言中是如此。获取segfault的常见方法是取消引用空指针:

int *p = NULL;
*p = 1;

当您尝试写入标记为只读的内存部分时,会发生另一个segfault:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

悬挂的指针指向一个不再存在的东西,如下图所示:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

指针p摆动,因为它指向在块结束后不存在的字符变量c。当您尝试取消引用悬空指针(如*p='A')时,可能会得到一个segfault。

考虑以下代码片段,

代码段1

int *number = NULL;
*number = 1;

代码段2

int *number = malloc(sizeof(int));
*number = 1;

如果你问这个问题,我假设你知道函数的含义:malloc()和sizeof()。

既然已经解决了,SNIPET 1将引发分段错误。而SNIPET 2则不会。

原因如下。

代码段1的第一行是创建一个变量(*number)来存储其他变量的地址,但在本例中,它被初始化为NULL。另一方面片段二的第二行是创建相同的变量(*number)来存储另一个的地址,在这种情况下,它被赋予了一个内存地址(因为malloc()是C/C++中的一个函数,它返回计算机的内存地址)

关键是你不能把水放在一个没有买过的碗里,或者一个已经买过但没有授权使用的碗里。当您尝试这样做时,计算机会发出警报,并抛出SegFault错误。

您应该只使用接近低级的语言(如C/C++)来面对这些错误。其他高级语言中有一个抽象,可以确保您不会犯此错误。

同样重要的是要理解分段错误不是特定于语言的。

当程序试图访问不存在的内存位置,或试图以不允许的方式访问内存位置时,会发生分段错误或访问冲突。

 /* "Array out of bounds" error 
   valid indices for array foo
   are 0, 1, ... 999 */
   int foo[1000];
   for (int i = 0; i <= 1000 ; i++) 
   foo[i] = i;

这里i[1000]不存在,因此出现segfault。

分段故障的原因:

it arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

De-referencing NULL pointers – this is special-cased by memory management hardware.

Attempting to access a nonexistent memory address (outside process’s address space).

Attempting to access memory the program does not have rights to (such as kernel structures in process context).

Attempting to write read-only memory (such as code segment).

值得注意的是,分段错误不是由直接访问另一个进程内存引起的(这是我有时听到的),因为这根本不可能。对于虚拟内存,每个进程都有自己的虚拟地址空间,无法使用任何指针值访问另一个地址空间。例外情况可能是共享库,它们是映射到(可能)不同虚拟地址的相同物理地址空间,内核内存甚至在每个进程中以相同的方式映射(我认为是为了避免系统调用时的TLB刷新)。还有像shmat这样的东西;)-这些就是我所说的“间接”访问。然而,我们可以检查它们通常位于距离过程代码很远的地方,并且我们通常能够访问它们(这就是它们存在的原因,尽管以不正确的方式访问它们会产生分段错误)。

尽管如此,如果以不正确的方式访问我们自己的(进程)内存(例如试图写入不可写空间),可能会发生分段错误。但最常见的原因是访问虚拟地址空间中根本没有映射到物理地址空间的部分。

所有这些都与虚拟内存系统有关。