什么是分段错误?

什么是分段错误? C和C ++有什么不同? 分段错误和悬挂指针如何相关?

分段错误是由于访问“不属于你”的内存引起的一种特定types的错误。它是帮助你避免破坏内存并引入难以debugging的内存错误的辅助机制。 每当你遇到一个段错误时,你就知道你正在做的是内存错误 – 访问已经释放的variables,写入内存的只读部分等等。在大多数语言中,分段错误基本上是一样的,在内存pipe理方面,C和C ++中的段错误没有主要区别。

有很多方法可以获得段错误,至less在C(++)这样的低级语言中。 获取段错误的常用方法是取消引用空指针:

int *p = NULL; *p = 1; 

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

 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悬挂,因为它指向字符variablesc ,在块结束后不再存在。 而当你试图解引用悬挂指针(如*p='A' ),你可能会得到一个段错误。

值得注意的是,分段错误不是由于直接访问另一个进程内存(这是我有时听到的)而造成的,因为这是不可能的。 使用虚拟内存,每个进程都有自己的虚拟地址空间,无法使用任何指针值访问另一个进程。 例外可以是共享库,这是相同的物理地址空间映射到(可能)不同的虚拟地址和内核内存,甚至映射在每个进程相同的方式(以避免系统调用TLB刷新,我认为)。 而像shmat这样的东西) – 这些就是我所谓的“间接”访问。 但是,可以检查它们通常位于远离进程代码的位置,我们通常能够访问它们(这就是为什么它们在那里,但是以不正确的方式访问它们将产生分段错误)。

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

而这一切都与虚拟内存系统有关。

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

悬挂指针是一个可能指向或不指向有效页面的指针,但指向内存的“意外”段。

说实话,正如其他海报所说,维基百科有一个非常好的文章, 所以看看那里。 这种types的错误是非常常见的,通常被称为其他的东西,如访问冲突或一般保护错误。

它们在C,C ++或任何其他允许指针的语言中没有区别。 这些types的错误通常是由指针引起的

  1. 在正确初始化之前使用
  2. 在它们指向的内存被使用之后使用或被删除。
  3. 用于索引在数组边界之外的索引数组中。 这通常只在你对传统数组或Cstring进行指针运算的时候,而不是基于STL / Boost的集合(在C ++中)。

虽然Zoul的答案解释了什么是分段错误,但是我发现这些错误可能特别难以捕捉,特别是如果您是C ++或C这样的低级语言的新手。以下是一些常见的方法来获取程序中的段错误:

printfscanf语句中格式控制string不正确

格式控制string应具有相同数量的转换说明符( %s%d等),因为printfscanf具有要打印或读取的参数。 这同样适用于fprintffscanf

不使用&参数scanf

函数scanf将格式控制string和variables的地址作为自variables的参数,它将读取数据。 & (地址)运算符用于提供variables的地址。

越界数组引用

确保你没有违反你使用的任何数组的边界; 也就是说,您没有为小于或小于其最高元素的索引值的数组下标数组。 Valgrind可以派上用场来检测这种引用 – 你可以使用valgrind--tool=exp-sgcheck标志。

访问未初始化的指针

指针variables在被访问前必须被分配一个有效的地址。 确保你已经初始化所有指针指向一个有效的内存区域。

& (地址)和* (取消引用)操作符的错误使用

使用这些时需要小心,特别是在通过引用/使用指针传递参数的时候。

壳牌限制

有时,分段错误不是由程序中的错误引起的,而是由系统内存限制设置太低造成的。 通常这是堆栈大小的限制导致这种问题(堆栈溢出)。 要检查内存限制,请在bash使用ulimit命令。

使用gdbdebugging

您可以使用debugging器gdb来查看程序转储的core文件的回溯。 每当程序出现段错误,他们通常会在崩溃时将内存中的内容转储到core文件( core dumped )中。 使用-g标志编译你的程序,在gdb运行并使用bt (backtrace)。

根据维基百科:

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

分段错误也是由硬件故障引起的,在这种情况下是RAM存储器。 这是不太常见的原因,但是如果你没有在你的代码中发现错误,也许memtest可以帮助你。

在这种情况下的解决scheme,更改RAM。

编辑:

这里有一个参考: 硬件分割故障

当进程(程序的运行实例)试图访问其他进程正在使用的只读内存地址或内存范围或访问不存在的(无效的)内存地址时,会发生分段错误悬挂引用(指针)问题意味着试图访问一个对象或variables,其内容已经从内存中删除,例如:

 int *arr = new int[20]; delete arr; cout<<arr[1]; //dangling problem occurs here 

维基百科的Segmentation_fault页面有一个非常好的描述,只是指出原因和原因。 查看维基的详细说明。

在计算中,分段错误(通常缩写为段错误)或访问冲突是由具有内存保护的硬件引发的故障,通知操作系统(OS)关于内存访问冲突。

以下是分段故障的一些典型原因:

  • 取消引用NULL指针 – 这是内存pipe理硬件的特殊function
  • 试图访问一个不存在的内存地址(外部进程的地址空间)
  • 试图访问内存程序没有权限(例如在进程上下文中的内核结构)
  • 试图编写只读内存(如代码段)

这些反过来又往往是由导致无效内存访问的编程错误引起的:

  • 解引用或分配给未初始化的指针(野指针,指向随机存储器地址)

  • 解引用或分配给已释放的指针(悬挂指针,指向已释放/释放/删除的内存)

  • 缓冲区溢出。

  • 堆栈溢出。

  • 试图执行不能正确编译的程序。 (尽pipe存在编译时错误,一些编译器将输出可执行文件。)

简而言之:分段错误是操作系统向程序发送一个信号,说明它检测到非法的内存访问,并且过早地终止程序以防止内存被损坏。

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

  /* "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; 

这里我[1000]不存在,所以发生段错误。

分段故障的原因:

 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).