3

以下代码是否在数组 [10] = 22 或数组 [9999] = 22 处出现段错误?
我只是想弄清楚整个代码是否会在出现段错误之前执行。(在 C 语言中)。

#include <stdio.h>
int main(){

int array[10];
int i;
for(i=0; i<9999; ++i){
    array[i] = 22;
}    
return 0;    
}
4

8 回答 8

13

这取决于......如果数组 [9] 之后的内存是干净的,那么什么都不会发生,直到一个人到达被占用的内存段。

试用代码并添加:

 printf("%d\n",i);

在循环中,您将看到它何时崩溃和燃烧。我得到了各种结果,范围从 596 到 2380。

于 2009-06-01T16:02:46.280 回答
9

使用调试器

$ gcc -g seg.c -o so_segfault
$ gdb so_segfault
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run
Starting program: /.../so_segfault 
Program received signal SIGSEGV, Segmentation fault.
0x080483b1 in main () at seg.c:7
7       array[i] = 22;
(gdb) print i
$1 = 2406
(gdb) 

事实上,如果你再次运行它,你会发现对于相同的 i 值,段错误不会总是发生。可以肯定的是,当 i>=10 时会发生这种情况,但是无法确定 i 的值会导致崩溃,因为这不是确定性的:它取决于内存的分配方式。如果内存在 array[222] 之前是空闲的(也就是没有其他程序使用它),它会一直持续到 i=222,但是对于 i>=10 的任何其他值,它也可能会崩溃。

于 2009-06-01T16:08:11.663 回答
8

答案是也许。C 语言没有说明在这种情况下应该发生什么这是未定义的行为。编译器不需要检测问题、做任何事情来处理问题、终止程序或其他任何事情。所以它什么也不做。

您写入不属于您的内存,实际上可能会发生以下三种情况之一:

  • 你可能很幸运,只是得到一个段错误。如果您点击了未分配给您的进程的地址,则会发生这种情况。操作系统会检测到这一点,并向您抛出错误。
  • 您可能会碰到真正未使用的内存,在这种情况下不会立即发生错误。但是如果内存被分配并稍后使用,它会覆盖你的数据,如果你希望它仍然在那里,你会得到一些很好的延迟操作错误。
  • 您可能会遇到已经实际用于其他用途的数据。你覆盖它,很快,当需要原始数据时,它会读取你的数据,然后会出现不可预测的错误。

越界写作:只是不要这样做。当它发生时,C 语言不会做任何事情来告诉你,所以你必须自己留意它。

于 2009-06-01T16:54:33.507 回答
4

您的代码何时以及是否崩溃是不确定的。这取决于您在哪个平台上运行代码。

array是一个堆栈变量,因此您的编译器将为它保留10 * sizeof(int)堆栈上的字节。根据编译器如何安排其他局部变量以及堆栈增长的方式,i可能会紧随array. 如果您按照 Daniel 的建议将printf语句放入其中,您可能会注意到一个有趣的效果。在我的平台上,when i = 10array[10] = 22clobbersi和下一个任务是 to array[23]

当用户代码试图触摸它无权访问的页面时,就会发生分段冲突。在这种情况下,如果您的堆栈足够小以至于 9999 次迭代用完堆栈,您将得到一个。

如果您已经array在堆上分配(通过使用malloc()),那么当您跑出页面边界的末尾时,您将获得一个 SIGSEGV。即使是 10 字节的分配也会返回一整页。页面大小因平台而异。请注意,一些 malloc 调试器可以尝试标记数组越界情况,但除非您在页面末尾运行时涉及硬件,否则您不会获得 SIGSEGV。

于 2009-06-01T16:16:10.757 回答
3

您的代码将在哪里出现段错误取决于您使用的编译器、运气以及相关程序的其他链接细节。您很可能不会i == 10. 即使那在您的数组之外,您几乎可以肯定仍然会在该位置为您的进程分配内存。但是,当您不断超出数组边界时,您最终会留下分配给进程的内存,然后出现段错误。

但是,如果您写入超出数组边界,您可能会覆盖同一堆栈帧中的其他自动变量。如果这些变量中的任何一个是指针(或稍后使用的数组索引),那么当您引用这些现已损坏的值时,您可能会出现段错误。(取决于损坏的确切值以及您现在是否要引用未分配给您的进程的内存。)

这不是非常确定的。

于 2009-06-01T16:09:20.833 回答
2

访问进程外的专用内存时会发生分段错误,这不容易预测。当 i == 10 它在数组之外..但可能仍然在进程的内存中。这取决于进程内存是如何分配的,这是无法(通常)知道的(取决于操作系统的内存管理器)。所以段错误可能发生在 i = 10 - 9999 中的任何一个,或者根本不发生。

于 2009-06-01T16:16:24.330 回答
0

我建议使用 GDB 来调查此类问题:http ://www.gnu.org/software/gdb/documentation/

于 2009-06-01T16:04:33.813 回答
0

更一般地说,您可以通过使用调试器找出 Linux 系统中发生分段错误的位置。例如,要使用 gdb,请使用 -g 标志编译带有调试符号的程序,例如:

gcc -g segfault.c -o segfault

然后,使用您的程序和使用 --args 标志的任何参数调用 gdb,.g.:

gdb --args ./segault myarg1 myarg2 ...

然后,当调试器启动时,键入run,您的程序应该会一直运行,直到它收到 SIGSEGV,并且应该告诉您它在收到该信号时在源代码中的位置。

于 2009-06-01T16:15:26.467 回答