什么是分段错误?

Posted

技术标签:

【中文标题】什么是分段错误?【英文标题】:What is a segmentation fault? 【发布时间】:2011-01-21 18:05:18 【问题描述】:

C和C++有区别吗?分段错误和悬空指针有什么关系?

【问题讨论】:

出现问题时只是内存转储! 通常通过尝试取消引用空指针来调用,因此分段错误通常类似于 Java NullPointerException Segmentation 来自Memory Segmentation。您正在访问不属于您的内存。 【参考方案1】:

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

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

#include <stdio.h>

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

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

将此string 或字符链存储为单个char 试图将方形钉插入圆孔中。

因信号终止:SEGMENTATION FAULT (11)

Segm 是一种更日常实用的理解方式。错误就像试图在水中呼吸一样,你的肺不是为此而生的。为整数保留内存然后尝试操作是浮点数或字符串不会简单地工作。

【讨论】:

【参考方案2】:

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

    printf 中的参数类型不匹配时,您可能会在以下情况下遇到分段错误:
#include <stdio.h>
int main()   
  int a = 5;
  printf("%s",a);
  return 0;

输出:Segmentation Fault (SIGSEGV)

    当您忘记为指针分配内存,但尝试使用它时。
#include <stdio.h> 
typedef struct
  int a;
 myStruct;   
int main()
  myStruct *s;
  /* few lines of code */
  s->a = 5;
  return 0;

输出:Segmentation Fault (SIGSEGV)

【讨论】:

【参考方案3】:

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

【讨论】:

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

根据Wikipedia:

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

【讨论】:

【参考方案5】:

考虑以下代码的sn-ps,

片段 1

int *number = NULL;
*number = 1;

片段 2

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

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

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

原因如下。

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

重点是您不能将水放入尚未购买的碗或已购买但未经您授权使用的碗中。 当您尝试这样做时,计算机会收到警报并引发 SegFault 错误。

您应该只在使用接近低级语言(如 C/C++)时遇到此错误。其他高级语言中有一个抽象可以确保您不会犯此错误。

了解 Segmentation Fault 不是特定于语言的,这一点也很重要。

【讨论】:

【参考方案6】:

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

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

以错误的方式使用scanf()(忘记输入&amp;)。
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()'时使用错误的格式说明符

【讨论】:

【参考方案7】:

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

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

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

【讨论】:

【参考方案8】:

答案中有几个关于“Segmentation fault”的很好的解释,但是由于segmentation fault通常会有内存内容的转储,我想分享Segmentation中“core dumped”部分之间的关​​系故障(核心转储)和内存来自:

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

取自here。

【讨论】:

【参考方案9】:

简单来说:segmentation fault就是操作系统向程序发送信号 说它已经检测到非法内存访问并提前终止程序以防止 内存被破坏。

【讨论】:

【参考方案10】:

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

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

【讨论】:

首先,seg fault与地址是否存在无关。这是关于您在不允许这样做的地方访问它。在您的特殊示例中,标准甚至可以保证该位置存在。因为标准说在数组的情况下,必须给出指针指向的有效地址在其边界内的对齐数组上 AND 1 后面 它也和地址有关,如果你没有地址,如果你尝试访问这个地址,也有seg。过错。在我的例子中,这只是为了理解观点。【参考方案11】:

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

这种情况下的解决办法,换内存。

编辑:

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

【讨论】:

对有故障的 RAM 的快速而简单的测试是在循环中一遍又一遍地运行崩溃的程序。如果程序没有内部不确定性——也就是说,它总是为相同的输入产生相同的输出,或者至少它应该——但是,对于某些特定的输入,它有时会崩溃,并非总是如此,而是也不是永远不会:那么你应该开始担心内存坏了。【参考方案12】:

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

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

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

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

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

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

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

缓冲区溢出。

堆栈溢出。

试图执行编译不正确的程序。 (尽管存在编译时错误,一些编译器仍会输出可执行文件。)

【讨论】:

【参考方案13】:

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

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

【讨论】:

删除数组的正确方法是delete [] arr;【参考方案14】:

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

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

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

【讨论】:

使用共享内存/内存映射文件,其他人可能会弄乱您的内存。在 WIN32 中也有像“WriteProcessMemory”这样讨厌的 API! @paulm:是的,我知道。这就是我在“还有 shmat 之类的东西 ;) 中的想法——这些都是我认为的‘间接’访问。” 在虚拟内存操作系统中,一个进程无法访问另一个进程的虚拟内存,而不是某种允许您访问的内存附加系统调用。虚拟内存地址通常意味着不同的东西,具体取决于所考虑的进程。【参考方案15】:

老实说,正如其他发帖者所提到的,Wikipedia 上有一篇关于 so have a look there. 的非常好的文章。这种类型的错误非常常见,通常被称为其他东西,例如访问冲突或一般保护错误。

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

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

【讨论】:

【参考方案16】:

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

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

【讨论】:

这是真的,但是如果您已经不知道分段错误是什么,它真的对您有帮助吗?

以上是关于什么是分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这段代码在 leetcode 运行良好,但在 geeksforgeeks 出现分段错误?

分段错误错误;绝对值表

为啥调用 glCreateShader 时这个 OpenGL 着色器分段错误?

C++:当我添加看似无关的代码行时,分段错误消失了

为啥我的字符串分配会导致分段错误?

带有 std::promise 的 C++11 分段错误