什么是分段错误?

Raj*_*pal 530 c c++ segmentation-fault

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

zou*_*oul 621

分段错误是由访问"不属于您"的内存引起的一种特定错误.它是一种帮助机制,可以防止破坏内存并引入难以调试的内存错误.每当你得到一个段错误时,你就知道你正在做一些错误的内存 - 访问已经被释放的变量,写入内存的只读部分等.在大多数语言中,分段错误本质上是相同的,让你搞砸了在内存管理方面,C和C++中的段错误没有主要区别.

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

int *p = NULL;
*p = 1;
Run Code Online (Sandbox Code Playgroud)

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

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

悬空指针指向一个不再存在的东西,就像这里:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling
Run Code Online (Sandbox Code Playgroud)

指针p悬空,因为它指向c块结束后不再存在的字符变量.当你试图取消引用悬空指针(如*p='A')时,你可能会得到一个段错误.

  • 当我构建时,最后一个例子特别令人讨厌:int main(){char*p = 0; {char c ='x'; p =&c; } printf("%c \n",*p); 返回0; 使用gcc或其他几个编译器,它"似乎"可行.编译时没有警告.没有段错误.这是因为'}'超出范围,实际上并没有删除数据,只是将其标记为可以再次使用.代码可以在生产系统上运行多年,你可以改变代码的另一部分,更改编译器或其他东西和BOOOOOM! (144认同)
  • 对不起,但只是一个侧面说明...你的例子都没有必然导致段错误,事实上它只是未定义的行为;-) (31认同)
  • @oldrinb:编写*必然*导致段错误的代码是不可能的.尤其是因为有些系统在没有内存保护的情况下运行,因此无法判断一块内存是否真正"属于你",因此**不知道**段错误,只有未定义的行为......(经典的AmigaOS) , 例如) (17认同)
  • @ChrisHuang-Leaver,你需要明白`c`是本地的,这意味着它在`{`之后被推到了堆栈上并且在`}之后弹出了它.悬空指针只是对偏移量的引用,该偏移量现在不在堆栈中.这就是为什么在一个简单的程序中修改它永远不会触发任何段错误.另一方面,它可能导致更复杂的用例中的段错误,其中其他函数调用可能导致堆栈增长并包含悬空指针指向的数据.写入该数据(本地变量)将导致未定义的行为(segfault&Co) (6认同)
  • @ ChrisHuang-Leaver,通常当你超出范围时,编译器必须恢复一些堆栈空间以释放未使用的堆栈空间,但这并不总是发生(gcc是这个编译器之一).此外,分配的堆栈空间通常会再次被重用,所以我听说没有操作系统将未使用的堆栈页面返回给系统,使得该空间成为`SIGSEGV`,所以我不希望这样的信号来自于堆栈. (3认同)
  • 为了进一步阅读,将“static”添加到“char c”将解决该问题。因为在函数内添加“static”会告诉变量进入“.data”部分,而不是堆栈中。 (2认同)

kon*_*ski 108

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

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

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

  • @paulm:是的,我知道。这就是我在“像 shmat ;) - 这些是我所认为的‘间接’访问”中想到的。 (3认同)

Ign*_*ams 34

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

悬空指针是一个指针,可能指向或可能不指向有效页面,但确实指向"意外"的内存段.

  • 这是事实,但如果您已经不知道分段错误是什么,它真的会对您有所帮助吗? (9认同)

Com*_* 10 28

老实说,正如其他海报所提到的,维基百科有一篇非常好的文章,所以看看那里.此类错误非常常见,通常称为"访问冲突"或"常规保护错误"等其他内容.

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

  1. 在正确初始化之前使用
  2. 在他们指向的内存被重新分配或删除后使用.
  3. 用于索引位于数组边界之外的索引数组中.这通常只在您对传统数组或c字符串进行指针数学处理时,而不是基于STL/Boost的集合(在C++中).


Orh*_*nar 15

根据维基百科:

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


Ale*_*din 12

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

在这种情况下的解决方案,更改RAM.

编辑:

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

  • 对错误的RAM进行快速而肮脏的测试是在循环中反复运行崩溃程序.如果程序没有内部非确定性 - 也就是说,它总是为同一个输入产生相同的输出,或者至少它应该 - 但是,对于某些特定的输入,它会崩溃_sometimes_,并不总是但不是永远不会:那么你应该开始担心糟糕的RAM. (3认同)

Soh*_*N3N 7

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

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here
Run Code Online (Sandbox Code Playgroud)

  • 删除数组的正确方法是delete [] arr; (4认同)

Fil*_*tto 7

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


Roy*_*Roy 5

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

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

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

  • 解除引用NULL指针 - 这是内存管理硬件特有的
  • 试图访问不存在的内存地址(进程外的地址空间)
  • 试图访问内存程序没有权限(例如进程上下文中的内核结构)
  • 尝试写入只读内存(例如代码段)

反过来,这通常是由导致无效内存访问的编程错误引起的:

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

  • 取消引用或分配释放的指针(悬空指针,指向已释放/取消分配/删除的内存)

  • 缓冲区溢出.

  • 堆栈溢出.

  • 试图执行无法正确编译的程序.(尽管存在编译时错误,一些编译器仍将输出可执行文件.)