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


当前回答

简单地说:分段故障是操作系统向程序发送信号表示已检测到非法内存访问,并正在提前终止程序以防止防止内存损坏。

其他回答

根据维基百科:

当程序试图访问内存不允许的位置访问或尝试访问内存以不允许的方式定位(例如,尝试写入只读位置,或覆盖操作系统的一部分)。

分段错误是由于访问“不属于您的”内存而导致的一种特定错误。它是一种帮助机制,可以防止损坏内存并引入难以调试的内存错误。每当你得到一个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。

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

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

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

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

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

有足够多的分段错误的定义,我想引用几个我在编程时遇到的例子,这可能看起来像是愚蠢的错误,但会浪费很多时间。

当printf中的参数类型不匹配时,在以下情况下可能会出现分段错误:

#include <stdio.h>
int main(){   
  int a = 5;
  printf("%s",a);
  return 0;
}

输出:分段故障(SIGSEGV)

当您忘记为指针分配内存时,请尝试使用它。

#include <stdio.h> 
typedef struct{
  int a;
} myStruct;   
int main(){
  myStruct *s;
  /* few lines of code */
  s->a = 5;
  return 0;
}

输出:分段故障(SIGSEGV)