分段错误 - 为什么以及如何工作?

时间:2013-12-03 18:16:15

标签: c++ c memory-management segmentation-fault

在下面定义的两个函数中,它尝试在堆栈中分配10M的内存。但是分段错误仅在第二种情况下发生,而不是第一种情况,我试图理解为什么如此。

功能定义1:

a(int *i)
{
    char iptr[50000000]; 
    *i = 1;
}

功能定义2:

a()
{
    char c;
    char iptr[5000000];

    printf("&c     = 0x%lx, iptr = 0x%x  ...  ", &c, iptr);
    fflush(stdout); 
    c = iptr[0]; 
    printf("ok\n");
}

根据我的理解,如果局部变量没有分配动态存储器,则存储在程序的堆栈部分。所以我想,在编译期间,编译器会检查变量是否适合堆栈。

因此,如果上述情况属实,则在两种情况下都应发生分段错误(即情况也是如此)。

我选择此网站的网站(http://web.eecs.utk.edu/courses/spring2012/cs360/360/notes/Memory/lecture.html)指出,当代码尝试在堆栈上为exef调用推送iptr时,在 a 中的函数2中发生了段错误。这是因为堆栈指针指向void。 如果我们没有在堆栈指针处引用任何内容,我们的程序应该有效。

我需要帮助理解这最后的陈述以及我之前对此的疑问。

4 个答案:

答案 0 :(得分:4)

  

所以我想,在编译期间,编译器会检查变量是否适合堆栈。

不,那是不可能做到的。在编译函数时,编译器不知道调用函数时调用堆栈是什么,因此它会假设您知道自己在做什么(可能是或不是这种情况)。另请注意,堆栈空间量可能会受到编译时和运行时限制的影响(在Linux中,您可以在启动进程的shell上使用ulimit设置堆栈大小)。

  

我需要帮助理解这最后的陈述以及我之前对此的疑问。

我不会试图过多地看待那个陈述,它不是标准,而是基于特定实现的知识,甚至没有在那里描述,因此建立在一些假设之上不一定是真的。

它假设分配数组的行为不会“触摸”分配的内存(在某些实现中的某些调试版本中为false),因此如果数据未被触及,您是否尝试分配1个字节或100M您的程序分配 - 这不一定是这种情况。

它还假设函数printf的参数在堆栈中传递(由于函数的可变参数性质,这实际上是我所知道的所有实现中的情况)。根据先前的假设,数组将溢出堆栈(假设堆栈为<10M),但不会因为未访问内存而崩溃,但是为了能够调用printf参数的值将是被推到阵列之外的堆栈。这将写入到内存中,并且写入将超出堆栈和崩溃的分配空间。

同样,所有这些都是实现,不是由语言定义的。

答案 1 :(得分:0)

以下代码抛出了代码中的错误:

; Find next lower page and probe
cs20:
        sub     eax, _PAGESIZE_         ; decrease by PAGESIZE
        test    dword ptr [eax],eax     ; probe page. "**This line throws the error**"
        jmp     short cs10

_chkstk endp

        end

从chkstk.asm文件中,提供对过程条目的堆栈检查。此文件明确定义:

_PAGESIZE_      equ     1000h

现在,作为对您的问题的解释This Question告诉您需要的所有内容:Shafik Yaghmour

答案 2 :(得分:0)

您的printf格式字符串假定指针,整数(%x)和长整数(%lx)的大小相同;这在您的平台上可能是错误的,导致未定义的行为。请改用%p。我打算将此作为评论,但还不能。

答案 3 :(得分:0)

我很惊讶没有人注意到第一个函数分配的空间是第二个函数的10倍。在第一个函数中5之后有7个零,而第5个函数之后第二个函数有6个零: - )

我用gcc-4.6.3编译它并在第一个函数上得到分段错误但在第二个函数上没有。在第一个函数中删除了额外的零后,seg错误就消失了。在第二个函数中添加零引入了seg错误。所以至少在我的情况下,这个seg错误的原因是程序无法在堆栈上分配所需的空间。我很高兴听到与上述不同的观察结果。