什么是分段错误?

新手上路,请多包涵

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

原文由 Rajendra Uppal 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 854
2 个回答

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

原文由 zoul 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

程序存储器分为不同的段:

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

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

* 例如,调用 memset() 会导致程序出现段错误:

 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 被定义为 index = 0, 1, 2, … 999 。但是,在 for 循环的最后一次迭代中,程序尝试访问 foo[1000] 。如果该内存位置位于 foo 所在的内存段之外,这将导致段错误。即使它不会导致段错误,它仍然是一个错误。
  • 在情况 B 中,整数 n 可以是任何随机值。与案例 A 一样,如果它不在 0, 1, … 999 范围内,则可能会导致段错误。不管有没有,这肯定是一个错误。
  • 在案例 C 中,变量 foo2 的内存分配被忽略了,所以 foo2 将指向内存中的一个随机位置。访问 foo2[0] 可能会导致段错误。

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

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

虽然变量 foo 可以在内存位置 1000 创建,但前面的函数调用会尝试根据 foo 的定义将整数值读入内存位置 0。

当软件试图以未经授权的方式对内存区域进行操作时,就会发生段错误(例如,尝试写入只读位置会导致段错误)。当您的应用程序用完堆栈空间时,可能会发生段错误。这可能是由于您的 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 :( ";

‘str’ 将被编译器设为常量。当您尝试更新值时,您正在更改只读部分,从而导致分段错误。因此,分段错误和悬空指针之间有明显的区别。

原文由 Dinindu Gunathilaka 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏