什么是分段故障?它在C和C++中不同吗?分段错误和悬挂指针是如何关联的?


当前回答

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

其他回答

根据维基百科:

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

有足够多的分段错误的定义,我想引用几个我在编程时遇到的例子,这可能看起来像是愚蠢的错误,但会浪费很多时间。

当printf中的参数类型不匹配时,在以下情况下可能会出现分段错误:

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

输出:分段故障(SIGSEGV)

当您忘记为指针分配内存时,请尝试使用它。

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

输出:分段故障(SIGSEGV)

当您的程序试图访问不允许访问的内存时,就会发生分段错误(有时称为segfault)。换句话说,当程序试图访问超出操作系统为程序设置的边界的内存时。这是导致程序崩溃的常见情况;它经常与名为core的文件相关。

程序内存分为不同的部分:

程序指令的文本段编译时定义的变量和数组的数据段子程序和函数中定义的临时(或自动)变量的堆栈段在运行时由函数分配的变量的堆段,如malloc(在C中)和allocate(在Fortran中)。

当对变量的引用超出该变量所在的段时,或者当试图写入只读段中的某个位置时,会发生segfault。实际上,segfaults几乎通常是由于尝试读取或写入不存在的数组成员、在使用指针之前未能正确定义指针,或者(在C应用程序中)无意中将变量的值用作地址(参见下面的扫描示例)。

*例如,调用memset()会导致程序segfault:

memset((char *)0x0, 1, 100);

*以下三个示例显示了最常见的与阵列相关的分段故障:

案例A

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

案例B

/* Illegal memory access if value of n is not in the range 0, 1, ... 999 */ 
int n; int foo[1000]; for (int i = 0; i < n ; i++) foo[i] = i;

案例C

/* Illegal memory access because no memory is allocated for foo2 */
float *foo, *foo2; foo = (float*)malloc(1000); foo2[0] = 1.0;

在情况A中,数组foo被定义为索引=0,1,2。。。999.然而,在for循环的最后一次迭代中,程序尝试访问foo[1000]。如果内存位置位于foo所在的内存段之外,这将导致segfault。即使它没有引起segfault,它仍然是一个bug。在情况B中,整数n可以是任意随机值。与情况A一样,如果它不在0、1、…范围内。。。999,它可能会导致分段故障。不管它有没有,它肯定是一个bug。在案例C中,忽略了变量foo2的内存分配,因此foo2将指向内存中的一个随机位置。访问foo2[0]可能会导致segfault。

*导致segfault的另一个典型编程问题是未能正确使用指针。例如,C函数scanf()要求变量的地址作为其第二个参数;因此,以下情况肯定会导致程序因segfault而失败:

int foo = 0; scanf("%d", foo); 
/* Note missing & sign ; correct usage would have been &foo */

尽管变量foo可以在存储器位置1000处创建,但是前面的函数调用将尝试根据foo的定义将整数值读入存储器位置0。

当软件试图以未经授权的方式对内存区域进行操作时(例如,尝试写入只读位置将导致segfault),就会发生segfault。当应用程序耗尽堆栈空间时,可能会发生segffault。这可能是由于shell将堆栈大小限制设置得太低,而不是软件中的错误。

悬挂的指针指向的东西已经不存在了。悬空指针就是一个例子。

char *ptr = NULL;
{
char c;
ptr = &c; //After the block is over, ptr will be a dangling pointer.
}

当块结束时,变量c的作用域终止。因为它现在指向的是不存在的东西,“ptr”将变成一个悬空指针。

但是,当您试图访问不属于您的内存或试图写入只读区域时,会出现分段错误。

char *str ="Testing Seg fault.";
*str= "I hate Seg fault :( ";

编译器会将“r”设置为常量。当您试图更新值时,您正在更改只读部分,从而导致分段错误。因此,分段错误和悬挂指针之间有明显的区别。

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

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

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

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