/* "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]不存在,所以发生了段错误 .
Causes of segmentation fault:
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).
12 回答
根据维基百科:
分段错误是由访问“不属于您”的内存引起的一种特定错误 . 它是一种帮助机制,可以防止破坏内存并引入难以调试的内存错误 . 每当你遇到段错误时,你就知道你正在做内存错误 - 访问已经被释放的变量,写入内存的只读部分等等 . 在大多数语言中,分段错误本质上是相同的,让你搞砸了在内存管理方面,C和C中的段错误没有主要区别 .
有很多方法可以获得段错误,至少在较低级别的语言中如C() . 获取段错误的常用方法是取消引用空指针:
当您尝试写入标记为只读的内存部分时,会发生另一个段错误:
悬空指针指向一个不再存在的东西,就像这里:
指针
p
悬空,因为它指向在块结束后不再存在的字符变量c
. 当你试图取消引用悬空指针(如*p='A'
)时,你可能会得到一个段错误 .当进程(正在运行程序的实例)试图访问其他进程正在使用的只读内存地址或内存范围或访问不存在的(无效)内存地址时,会发生分段错误 . 悬空引用(指针)问题意味着尝试访问其内容已从内存中删除的对象或变量,例如:
维基百科的Segmentation_fault页面有一个非常好的描述,只是指出原因和原因 . 有关详细说明,请查看Wiki .
在计算中,分段错误(通常缩短为段错误)或访问冲突是由具有内存保护的硬件引发的错误,通知操作系统(OS)存储器访问冲突 .
以下是分段故障的一些典型原因:
解除引用NULL指针 - 这是内存管理硬件特有的
试图访问不存在的内存地址(外部进程的地址空间)
试图访问程序没有权限的内存(例如进程上下文中的内核结构)
尝试写入只读内存(例如代码段)
反过来,这通常是由导致无效内存访问的编程错误引起的:
取消引用或分配未初始化的指针(指向随机存储器地址的野指针)
解除引用或分配释放指针(悬空指针,指向已释放/取消分配/删除的内存)
缓冲区溢出 .
堆栈溢出 .
试图执行无法正确编译的程序 . (尽管存在编译时错误,一些编译器仍将输出可执行文件 . )
当程序试图访问不存在的内存位置或尝试以不允许的方式访问内存位置时,会发生分段错误或访问冲突 .
这里我[1000]不存在,所以发生了段错误 .
Causes of segmentation fault:
在答案中有"Segmentation fault"的几个很好的解释,但由于分段故障经常有内存内容的转储,我想分享分段故障(核心转储)中的"core dumped"部分与内存之间的关系来自:
取自here .
分段错误是由对进程未在其描述符表中列出的页面的请求,或对其确实已列出的页面的无效请求(例如,在只读页面上的写入请求)引起的 .
悬空指针是一个指针,可能指向或可能不指向有效页面,但确实指向“意外”的内存段 .
虽然Zoul的答案解释了分段错误是什么,但我发现这些错误特别难以捕捉,特别是如果你不熟悉C或C等低级语言 . 这里有一些常见的方法可以获得程序中的分段错误:
Improper format control string in printf or scanf statements
格式控制字符串应具有相同数量的转换说明符(
%s
,%d
等)因为printf
或scanf
具有要打印或读取的参数 . 这同样适用于fprintf
和fscanf
.Not using & on the arguments to scanf
函数
scanf
将格式控制字符串和变量的地址作为参数,它将放置它所读取的数据.&
(地址)运算符用于提供变量的地址 .Out-of-bounds array references
确保您没有违反正在使用的任何阵列的边界;即,您没有使用小于其最低元素的索引或大于其最高元素的索引的值来预订数组 .
Valgrind
可以派上用场来检测这样的引用 - 你可以使用带有--tool=exp-sgcheck
标志的valgrind
.Accessing uninitialized pointers
必须在访问之前为指针变量分配有效地址 . 确保已初始化所有指针以指向有效的内存区域 .
Incorrect use of the & (address of) and * (dereferencing) operators
使用它们时需要小心,特别是在通过引用/使用指针传递参数时 .
Shell Limits
有时,分段错误不是由程序中的错误引起的,而是由系统内存限制设置得太低引起的 . 通常,堆栈大小的限制会导致这种问题(堆栈溢出) . 要检查内存限制,请使用
bash
中的ulimit
命令 .Debugging using gdb
您可以使用调试器
gdb
查看程序转储的core
文件的回溯 . 每当程序发生段错误时,它们通常会在崩溃时将内存内容转储到core
文件中(core dumped
) . 使用-g
标志编译程序,在gdb
中运行并使用bt
(回溯) .值得注意的是,分段故障不是由直接访问另一个进程内存引起的(这是我有时听到的),因为它根本不可能 . 对于虚拟内存,每个进程都有自己的虚拟地址空间,并且无法使用任何指针值访问另一个进程 . 对此的例外可以是共享库,它们是相同的物理地址空间,映射到(可能)不同的虚拟地址和内核内存,甚至在每个进程中以相同的方式映射(以避免系统调用上的TLB刷新,我认为) . 像shmat这样的东西;) - 这些是我所谓的'间接'访问 . 但是,可以检查它们通常位于远离过程代码的位置,我们通常能够访问它们(这就是它们存在的原因,但是以不正确的方式访问它们会产生分段错误) .
但是,如果以不正确的方式访问我们自己的(进程)内存(例如尝试写入不可写空间),则可能发生分段错误 . 但最常见的原因是访问虚拟地址空间中未映射到物理地址的部分 .
所有这些都与虚拟内存系统有关 .
简单来说:分段错误是操作系统向程序发送信号,表示它已检测到非法内存访问,并过早终止程序以防止内存被破坏 .
老实说,正如其他海报所提到的,维基百科有一篇非常好的文章so have a look there.这种类型的错误很常见,通常称为访问违规或一般保护错误等其他内容 .
它们在C,C或任何其他允许指针的语言方面没有区别 . 这些类型的错误通常是由指针引起的
在正确初始化之前使用
在指向的内存被重新分配或删除后使用 .
用于索引位于数组边界之外的索引数组中 . 这通常仅在您对传统数组或c字符串进行指针数学处理时,而不是基于STL / Boost的集合(在C中) .
分段故障也是由硬件故障引起的,在这种情况下是RAM存储器 . 这是不常见的原因,但如果您在代码中没有发现错误,也许memtest可以帮助您 .
在这种情况下的解决方案,改变RAM .
编辑:
这里有一个参考:Segmentation fault by hardware