什么是细分错误?


598

什么是细分错误?在C和C ++中有区别吗?分段错误和悬空指针如何关联?



22
如果是这样,为什么在我的情况下,编译器什么也没抱怨,一切都顺利进行,但是在运行时系统抛出了分段错误(核心转储)?T_T
吉姆·雷诺

3
发生问题时只是内存转储!
resultsway 2015年

7
@pinouchon:有趣,但是什么时候编译器与seg错误有关?难道不是运行时环境吗?
dhein

1
通常通过尝试取消引用空指针来调用,因此分段错误通常类似于Java NullPointerException
Raedwald

Answers:


673

分段错误是由访问“不属于您”的内存引起的特定类型的错误。它是一种帮助机制,可防止您破坏内存并引入难以调试的内存错误。每当遇到段错误时,您就知道您在内存上做错了–访问已经释放的变量,写到内存的只读部分,等等。在大多数语言中,分段错误在本质上都是相同的,这会让您陷入困境在内存管理方面,C和C ++中的段错误之间在原理上没有区别。

至少在诸如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悬空是因为它指向c在块结束后不再存在的字符变量。当您尝试取消引用悬空指针(如*p='A')时,可能会遇到段错误。


154
在构建时,最后一个示例特别讨厌:int main(){char * p = 0; {char c ='x'; p =&c; } printf(“%c \ n”,* p); 返回0; 使用gcc或其他几个编译器,它“似乎”开始工作。没有关于编译的警告。没有段错误。这是因为'}'不在范围内,实际上并不会删除数据,只是将其标记为可以再次使用。该代码可以在生产系统上正常运行数年,您可以更改代码的另一部分,更改编译器或其他操作,然后再执行!
克里斯·黄·利弗

36
抱歉,碰到了麻烦,不过只是一个旁注...您的示例都未必会引起段错误,实际上这只是未定义的行为;-)
oldrinb 2012年

18
@oldrinb:不可能编写必然导致段错误的代码。尤其是因为那里有没有内存保护功能的系统,因此无法判断内存是否确实属于您,因此不知道段错误,只有不明确的行为……(例如,经典的AmigaOS)
DevSolar 2014年

7
@ ChrisHuang-Leaver,您需要了解它c是本地的,这意味着它在之后被压入堆栈{并在之后弹出}。悬空指针只是对偏移量的引用,该偏移量现在已从堆栈中移出。这就是为什么在简单的程序中对其进行修改将永远不会触发任何段错误的原因。另一方面,在更复杂的用例中,它可能导致段错误,其中其他函数调用可能会导致堆栈增长并包含悬空指针指向的数据。写入该数据(本地变量)将导致未定义的行为(segfault&Co)
艾曼·卡穆玛

3
@ ChrisHuang-Leaver,通常当您超出范围时,编译器必须恢复一些堆栈空间以释放未使用的堆栈空间,但这并不总是发生的(gcc是其中一种编译器)。另外,分配的堆栈空间通常会再次被重用,因此我听说没有操作系统SIGSEGV会将未使用的堆栈页面返回给系统,使该空间成为的主题,因此我不会期望这样的信号来处理堆栈。
路易斯·科罗拉多州

111

值得注意的是,分段错误不是由直接访问另一个进程内存引起的(这是我有时听到的),因为这根本不可能。使用虚拟内存,每个进程都有自己的虚拟地址空间,无法使用任何指针值访问另一个进程。例外是共享库,它们是相同的物理地址空间,映射到(可能)不同的虚拟地址,内核内存甚至在每个进程中都以相同的方式映射(我认为,这是为了避免在系统调用中刷新TLB)。还有类似shmat;)的东西-这些就是我所谓的“间接”访问。但是,您可以检查它们是否通常位于距流程代码较远的位置,并且我们通常可以访问它们(这就是它们在那里的原因,

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

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


使用共享的内存/内存映射文件,其他人可能会弄乱您的内存。在WIN32中也有讨厌的API,例如“ WriteProcessMemory”!
paulm 2014年

1
@paulm:是的,我知道。这就是我在“以及类似shmat;)的想法中所想到的-这些被我视为'间接'访问。”
konrad.kruczynski 2014年

在虚拟内存操作系统中,一个进程无法访问另一个进程虚拟内存(通常,请操作系统的实现者不要为此而烦恼),而不是某种允许您执行以下操作的内存附加系统调用访问。虚拟内存地址通常取决于所考虑的进程而具有不同的含义。
路易斯科罗拉多州

38

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

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


10
的确如此,但是如果您已经不知道分段错误是什么,那对您真的有帮助吗?
zoul 2010年

29

老实说,正如其他发布者所提到的那样,Wikipedia对此有很好的文章,因此可以在那里看看。这种类型的错误非常普遍,通常称为“访问冲突”或“常规保护错误”之类的其他错误。

它们在C,C ++或任何其他允许指针的语言中没有什么不同。这些类型的错误通常是由

  1. 在正确初始化之前使用
  2. 在它们指向的内存被重新分配或删除后使用。
  3. 在索引超出数组范围的索引数组中使用。通常只有在对传统数组或C字符串而不是基于STL / Boost的集合(在C ++中)进行指针数学运算时,才可以使用。

16

根据维基百科:

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


13

分段故障也是由硬件故障(在这种情况下为RAM存储器)引起的。这是不太常见的原因,但是如果您在代码中找不到错误,也许memtest可以为您提供帮助。

在这种情况下,解决方案是更换RAM。

编辑:

这里有一个参考:硬件分割错误


3
对RAM出现故障的快速测试是在循环中反复运行崩溃的程序。如果程序没有内部不确定性,也就是说,它总是为相同的输入产生或至少应该产生相同的输出,但是对于某些特定的输入,它有时会崩溃,但并非总是崩溃,但也不是永远不会崩溃:那么您应该开始担心坏的RAM。
zwol

8

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

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

4
删除数组的正确方法是delete [] arr;
达米安

8

Wikipedia的Segmentation_fault页面对此有很好的描述,仅指出了原因和原因。查看Wiki以获取详细说明。

在计算中,分段错误(通常简称为segfault)或访问冲突是具有内存保护功能的硬件引发的错误,它会通知操作系统(OS)有关内存访问冲突的信息。

以下是分段错误的一些典型原因:

  • 解引用NULL指针–内存管理硬件对此进行了特殊处理
  • 尝试访问不存在的内存地址(进程的外部地址空间)
  • 尝试访问内存的程序无权(例如,进程上下文中的内核结构)
  • 尝试写入只读存储器(例如代码段)

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

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

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

  • 缓冲区溢出。

  • 堆栈溢出。

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


6

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


3

“分段错误”表示您试图访问您无权访问的内存。

第一个问题是您对main的争论。主要功能应为int main(int argc, char *argv[]),并且在访问argv [1]之前应检查argc至少为2。

另外,由于您要将浮点数传递给printf(顺便说一句,当传递给printf时,它会转换为double值),因此,您应该使用%f格式说明符。%s格式说明符用于字符串(以'\ 0'结尾的字符数组)。


2

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

 /* "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]不存在,因此发生段错误。

分段故障的原因:

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

2
首先,seg fault与地址是否存在无关。它是关于您正在不允许访问它的地方。在您的特殊示例中,甚至标准地保证了该位置的存在。因为该标准说的是在数组情况下,必须给出一个指针pointg的有效地址,该指针位于对齐良好的数组的范围内,后面为1
dhein

如果您没有该地址,并且您尝试访问该地址,它也会与地址相关联。故障。在我的示例中,它仅用于理解观点。
Mohit Rohilla 2015年

2

答案中有很多关于“分段错误”的很好的解释,但是由于分段错误经常存在内存内容的转储,因此我想分享分段错误中的“核心转储”部分(核心转储)与内存之间的关系。内存来自:

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

取自这里


2

分段错误有足够的定义,我想引用一些编程时遇到的例子,这些看似愚蠢的错误,但会浪费很多时间。

  1. 在以下情况下,您可能会遇到分段错误,而printf中的argumet类型不匹配

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

输出: Segmentation Fault (SIGSEGV)

  1. 当您忘记将内存分配给指针,但尝试使用它时。

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

输出: Segmentation Fault (SIGSEGV)


1

简单的含义Segmentation fault是您正在尝试访问一些不属于您的内存。Segmentation fault当我们尝试在只读内存位置读取和/或写入任务或尝试释放内存时,会发生这种情况。换句话说,我们可以将其解释为某种内存损坏。

下面我提到导致程序员犯错误的常见错误Segmentation fault

  • scanf()以错误的方式使用(忘了放&)。
int num;
scanf("%d", num);// must use &num instead of num
  • 以错误的方式使用指针。
int *num; 
printf("%d",*num); //*num should be correct as num only
//Unless You can use *num but you have to point this pointer to valid memory address before accessing it.
  • 修改字符串文字(指针尝试写入或修改只读存储器。)
char *str;  

//Stored in read only part of data segment
str = "GfG";      

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • 尝试到达已经释放的地址。
// allocating memory to num 
int* num = malloc(8); 
*num = 100; 

// de-allocated the space allocated to num 
free(num); 

// num is already freed there for it cause segmentation fault
*num = 110; 
  • 堆栈溢出-:堆栈内存不足
  • 越界访问数组'
  • 使用printf()scanf()' 时,请使用错误的格式说明符
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.