在下面定义的两个函数中,它尝试在堆栈中分配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。 如果我们没有在堆栈指针处引用任何内容,我们的程序应该有效。 的
我需要帮助理解这最后的陈述以及我之前对此的疑问。
答案 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错误的原因是程序无法在堆栈上分配所需的空间。我很高兴听到与上述不同的观察结果。