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


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

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


根据维基百科:

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


分段错误是由于访问“不属于您的”内存而导致的一种特定错误。它是一种帮助机制,可以防止损坏内存并引入难以调试的内存错误。每当你得到一个segfault时,你就知道你在内存方面出了问题——访问一个已经被释放的变量,写入内存的只读部分,等等。在大多数语言中,segfault基本上是一样的,这会让你陷入内存管理的困境,在C和C++中segfault之间没有主要区别。

有很多方法可以获得segfault,至少在C(++)等较低级别的语言中是如此。获取segfault的常见方法是取消引用空指针:

int *p = NULL;
*p = 1;

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

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')时,可能会得到一个segfault。


老实说,正如其他海报所提到的那样,维基百科在这方面有一篇很好的文章,所以看看吧。这种类型的错误非常常见,通常称为其他错误,如访问违规或一般保护故障。

它们在C、C++或任何其他允许指针的语言中都没有区别。这些类型的错误通常由以下指针引起:

在正确初始化之前使用在它们指向的内存被重新定位或删除后使用。在索引超出数组边界的索引数组中使用。这通常只在对传统数组或c字符串进行指针计算时发生,而不是基于STL/Boost的集合(在c++中)


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

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

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


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

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

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

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

编辑:

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


维基百科的Segmentation_fault页面对其进行了很好的描述,只是指出了原因和原因。查看wiki以获得详细描述。

在计算中,分段故障(通常简称为segfault)或访问违规是由具有内存保护的硬件引起的故障,通知操作系统(OS)内存访问违规。

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

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

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

取消引用或分配给未初始化的指针(指向随机内存地址的通配符指针)取消引用或分配给已释放指针(悬空指针,指向已释放/解除分配/删除的内存)缓冲区溢出。堆栈溢出。试图执行未正确编译的程序。(尽管存在编译时错误,某些编译器仍会输出可执行文件。)


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

 /* "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]不存在,因此出现segfault。

分段故障的原因:

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

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


答案中对“分段错误”有几个很好的解释,但由于分段错误通常会导致内存内容转储,所以我想分享分段错误(核心转储)中“核心转储”部分与内存之间的关系来源:

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

从这里拍摄。


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

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

此外,由于要向printf传递一个浮点值(顺便说一句,在传递到printf时,它会转换为双精度),因此应该使用%f格式说明符。%s格式说明符用于字符串(以“\0”结尾的字符数组)。


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

当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)


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

下面我提到了程序员所犯的导致分段错误的常见错误。

以错误的方式使用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()时使用错误的格式说明符'


考虑以下代码片段,

代码段1

int *number = NULL;
*number = 1;

代码段2

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

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

既然已经解决了,SNIPET 1将引发分段错误。而SNIPET 2则不会。

原因如下。

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

关键是你不能把水放在一个没有买过的碗里,或者一个已经买过但没有授权使用的碗里。当您尝试这样做时,计算机会发出警报,并抛出SegFault错误。

您应该只使用接近低级的语言(如C/C++)来面对这些错误。其他高级语言中有一个抽象,可以确保您不会犯此错误。

同样重要的是要理解分段错误不是特定于语言的。


在计算中,分段故障或访问违规是由具有存储器保护的硬件引起的故障或故障状况,通知操作系统该软件已尝试访问内存的受限区域-维基百科

您可能正在使用错误的数据类型访问计算机内存。您的案例可能如下所示:

#include <stdio.h>

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

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

将这个字符串或字符链存储为单个字符是为了在圆孔中插入一个方形钉。

由于信号:分段故障(11)而终止

塞格姆。错误与尝试在水下呼吸是一样的,你的肺不适合这样。为一个整数保留内存,然后尝试将其作为另一种数据类型进行操作,这根本不起作用。


当您的程序试图访问不允许访问的内存时,就会发生分段错误(有时称为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”设置为常量。当您试图更新值时,您正在更改只读部分,从而导致分段错误。因此,分段错误和悬挂指针之间有明显的区别。


当一个进程(正在运行的程序实例)试图访问另一个进程正在使用的只读内存地址或内存范围或访问不存在的内存地址时,会发生分段错误。

seg错误,当类型不匹配时