什么是分段错误?

时间:2010-02-27 09:23:15

标签: c++ c segmentation-fault

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

17 个答案:

答案 0 :(得分:593)

分段错误是一种特殊的错误,它是由访问“不属于你”的内存引起的。它是一种帮助机制,可以防止破坏内存并引入难以调试的内存错误。每当你得到一个段错误时,你就知道你正在做一些错误的内存 - 访问已经被释放的变量,写入内存的只读部分等。在大多数语言中,分段错误本质上是相同的,让你搞砸了在内存管理方面,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')时,你可能会遇到一个段错误。

答案 1 :(得分:102)

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

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

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

答案 2 :(得分:32)

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

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

答案 3 :(得分:27)

老实说,正如其他海报所提到的,维基百科有一篇非常好的文章so have a look there.这种类型的错误很常见,通常称为访问违规或一般保护错误等其他内容。

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

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

答案 4 :(得分:15)

根据维基百科:

  

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

答案 5 :(得分:11)

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

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

编辑:

这里有一个参考:Segmentation fault by hardware

答案 6 :(得分:7)

当进程(正在运行程序的实例)尝试访问其他进程正在使用的只读内存地址或内存范围或访问不存在的进程时,会发生

分段错误(无效) )内存地址。 悬空参考(指针)问题表示尝试访问其内容已从内存中删除的对象或变量,例如:

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

答案 7 :(得分:5)

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

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

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

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

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

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

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

  • 缓冲区溢出。

  • 堆栈溢出。

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

答案 8 :(得分:3)

简单来说:分段错误是操作系统向程序发送信号 说它已经检测到非法的内存访问并且过早地终止程序以防止 内存不被破坏。

答案 9 :(得分:1)

答案中对“分段错误”有一些很好的解释,但是由于分段错误经常会导致内存内容转储,因此我想分享 Segmentation中“核心转储”部分之间的关​​系故障(核心已转储),并且内存来自:

  

大约1955年至1975年-在半导体存储器出现之前-计算机存储器中的主要技术是在铜线上缠上微小的磁性圆环。甜甜圈被称为“铁氧体磁芯”,主存储器也被称为“磁芯存储器”或“磁芯”。

来自here

答案 10 :(得分:1)

<块引用>

在计算中,分段错误或访问冲突是由具有内存保护的硬件引发的错误或失败条件, 通知操作系统该软件已尝试访问 内存受限区域。 -WIKIPEDIA

您可能正在使用错误的数据类型访问计算机内存。您的情况可能类似于以下代码:

#include <stdio.h>

int main(int argc, char *argv[]) {
    
    int A = 'asd';
    puts(A);
    
    return 0;
    
}

'asd' -> 是一个字符链,而不是一个单个字符 char数据类型。因此,将其存储为字符会导致分段错误在错误的位置存储一些数据。

将此 string 或字符链存储为单个 char 是为了将方钉放入圆孔中。

因信号终止:分段错误 (11)

答案 11 :(得分:1)

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

  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)

答案 12 :(得分: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()'时使用错误的格式说明符

答案 13 :(得分:1)

请考虑以下代码段,

片段1

int *number = NULL;
*number = 1;

片段2

int *number = malloc(sizeof(int));
*number = 1;

如果您要问这个问题,我假设您知道函数的含义:malloc()sizeof()

现在解决了, SNIPPET 1将引发分段错误错误。 而SNIPPET 2则不会。

这就是为什么。

片段的第一行正在创建一个变量(* number)以存储其他变量的地址,但是在这种情况下,它被初始化为NULL。 另一方面, 片段二的第二行正在创建相同的变量(* number)以存储其他变量的地址,在这种情况下,它被赋予了一个内存地址(因为malloc()是C / C ++中的一个函数,返回的内存地址为电脑)

要点:您不能将水倒入未购买的碗中或已购买但未经您授权使用的碗中。 当您尝试执行此操作时,计算机将收到警报并抛出SegFault错误。

您应该只在接近底层语言(例如C / C ++)的语言中遇到此错误。其他高级语言中有一个抽象,可确保您不会犯此错误。

了解细分错误不是特定于语言也是至关重要的。

答案 14 :(得分:0)

将值赋给未初始化的指针可能会导致错误

int main(){
int *ptr;
*ptr=1;
return 0;
}

输出

enter image description here

基本上,分段错误是由程序试图读取或写入非法内存位置引起的。在这里我们可以看到我们将引用运算符应用于未指向任何位置的点, 它可能指向一些非法内存位置。 这意味着用户无权访问这些位置,

答案 15 :(得分:0)

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

第一个问题是您的main参数。 main函数应为int main(int argc,char * argv []),在访问argv [1]之前,应检查argc至少为2。

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

答案 16 :(得分:0)

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

 /* "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).