我的C代码存在堆栈消耗问题,我无法弄清楚究竟发生了什么。问题已经开始触发堆栈溢出崩溃(与我的程序中没有使用的递归无关)。我怀疑是因为:
- 我的所有功能至少需要一个自制的调试接口(我用来简化调试的结构),它非常大(~1或2kB)
- 我的一些功能(例如,为某些硬件实现初始化序列的功能)太长(即进行过多的子功能调用)
注意:我的C代码用于多个应用程序,一些使用gcc编译,一些使用visual studio 2010,一些使用C ++顶层。所有人都遇到这种高堆栈使用问题。
我已经制作了以下简单的测试代码以证明问题:
typedef struct tDbgIf2 {
int verbose ; // verbose level (0:no verbose)
int callLevel ; // context depth in function call
char lhd[1024] ; // configurable line header for log formating
} tDbgIf; // function debug interface
//----------------------------------------------------------------------------------------------
tDbgIf mfunc(tDbgIf i) {
//----------------------------------------------------------------------------------------------
tDbgIf v = i;
v.verbose++;
return v;
}
//----------------------------------------------------------------------------------------------
tDbgIf test1call(tDbgIf i) {
//----------------------------------------------------------------------------------------------
tDbgIf v = i;
v = mfunc(v);
return v;
}
//----------------------------------------------------------------------------------------------
tDbgIf test2call(tDbgIf i) {
//----------------------------------------------------------------------------------------------
tDbgIf v = i;
v = mfunc(v);
v = mfunc(v);
return v;
}
//----------------------------------------------------------------------------------------------
tDbgIf test(tDbgIf i) {
//----------------------------------------------------------------------------------------------
tDbgIf v = i;
v = test1call(v);
v = test2call(v);
return v;
}
//----------------------------------------------------------------------------------------------
int main(int argc, char *argv[]) {
//----------------------------------------------------------------------------------------------
tDbgIf v;
v = test(v);
return 0;
}
使用gcc 4.7.2(XP上的MinGW)和选项-fstack-usage会产生以下奇怪的结果:
cmd=gcc -fstack-usage -S test.c -o test.exe
test.c:8:8:mfunc 1060 static
test.c:18:8:test1call 2100 static
test.c:28:8:test2call 3164 static
test.c:39:8:test 3168 static
test.c:50:5:main 2128 static
如果我理解正确:
- 主堆栈使用情况<测试堆栈使用=>统计数据不累计
- gcc认为test2call需要比test1call更多的堆栈,尽管这两个函数具有相同的输入/输出参数+局部变量
我无法理解为什么会这样?这表明:(1)函数的堆栈使用量与其产生的子调用次数成正比 (1)对我来说似乎很奇怪,因为它意味着在现实生活中,函数大小(从它产生多少次调用的意义上)将受到堆栈可用性的限制。我从来没有听说过这样的限制(不像例如在互联网上有很好解释的递归深度限制)。更重要的是,我一直认为应该在所有(子)呼叫返回时恢复堆栈状态。
生成的程序集如下所示:
...
_test: - start of test
LFB3: -
.cfi_startproc -
pushl %ebp - return context saved
.cfi_def_cfa_offset 8 -
.cfi_offset 5, -8 -
movl %esp, %ebp - new context activated
.cfi_def_cfa_register 5 -
pushl %edi -
pushl %esi -
pushl %ebx -
subl $3148, %esp - stack static alloc (for test)
.cfi_offset 7, -12 -
.cfi_offset 6, -16 -
.cfi_offset 3, -20 -
leal -1056(%ebp), %edx - v <- i (not sure)
leal 12(%ebp), %ebx - v <- i (not sure)
movl $258, %eax - v <- i (not sure)
movl %edx, %edi - v <- i (not sure)
movl %ebx, %esi - v <- i (not sure)
movl %eax, %ecx - v <- i (not sure)
rep movsl - ???
leal -1056(%ebp), %eax - ???
movl %eax, -2108(%ebp) - ???
leal 4(%esp), %edx - ???
leal -1056(%ebp), %ebx - ???
movl $258, %eax - ???
movl %edx, %edi - ???
movl %ebx, %esi - ???
movl %eax, %ecx - ???
rep movsl -
movl -2108(%ebp), %eax -
movl %eax, (%esp) -
call _test1call - sub call
leal -2104(%ebp), %eax - v <- ans (not sure)
movl %eax, -2112(%ebp) - v <- ans (not sure)
leal 4(%esp), %edx - v <- ans (not sure)
leal -1056(%ebp), %ebx - v <- ans (not sure)
movl $258, %eax - v <- ans (not sure)
movl %edx, %edi - v <- ans (not sure)
movl %ebx, %esi - v <- ans (not sure)
movl %eax, %ecx - v <- ans (not sure)
rep movsl -
movl -2112(%ebp), %eax -
movl %eax, (%esp) -
call _test2call - sub call
leal -1056(%ebp), %edx - v <- ans (not sure)
leal -2104(%ebp), %ebx - v <- ans (not sure)
movl $258, %eax - v <- ans (not sure)
movl %edx, %edi - v <- ans (not sure)
movl %ebx, %esi - v <- ans (not sure)
movl %eax, %ecx - v <- ans (not sure)
rep movsl - ans <- v (not sure)
movl 8(%ebp), %eax - ans <- v (not sure)
movl %eax, %edx - ans <- v (not sure)
leal -1056(%ebp), %ebx - ans <- v (not sure)
movl $258, %eax - ans <- v (not sure)
movl %edx, %edi - ans <- v (not sure)
movl %ebx, %esi - ans <- v (not sure)
movl %eax, %ecx - ans <- v (not sure)
rep movsl -
movl 8(%ebp), %eax -
addl $3148, %esp - stack released
popl %ebx -
.cfi_restore 3 -
popl %esi -
.cfi_restore 6 -
popl %edi -
.cfi_restore 7 -
popl %ebp - context restored
.cfi_restore 5 -
.cfi_def_cfa 4, 4 -
ret -
.cfi_endproc -
LFE3:
.def ___main; .scl 2; .type 32; .endef
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
...
如果我理解正确:
- 在子呼叫返回后,堆栈不会立即清除
- (1)为真(至少使用默认的gcc / visual studio otpimization选项)
有人可以确认吗?
答案 0 :(得分:0)
由于空间不足需要切换回答。除了-O0(XP gcc 4.8.2上的cygwin)之外,我无法重现你所描述的行为:
-O0
sc.c:8:8:mfunc 24 static
sc.c:18:8:test1call 40 static
sc.c:28:8:test2call 64 static
sc.c:39:5:main 80 dynamic,bounded
-O1
sc.c:8:8:mfunc 4 static
sc.c:18:8:test1call 4 static
sc.c:28:8:test2call 4 static
sc.c:39:5:main 80 dynamic,bounded
-O2
sc.c:8:8:mfunc 4 static
sc.c:18:8:test1call 4 static
sc.c:28:8:test2call 4 static
sc.c:39:5:main 16 static
-O3
sc.c:8:8:mfunc 4 static
sc.c:18:8:test1call 4 static
sc.c:28:8:test2call 4 static
sc.c:39:5:main 16 static
关于'(1):函数的堆栈使用与它所做的子调用的数量成正比'。如果那是真的,我会感到震惊。嵌套调用foo(x){f(g(h(x)));}增加级别(在激活树中),因此堆栈必须(好,几乎为真)增长,但是在顺序调用中foo(x){f (X); G(X); H(X);堆栈被重用。标准激活/返回序列(Aho,Lam,Sethi,Ullman:Compilers Chp 7.2)实际上非常小,如果在非递归调用中耗尽1M堆栈,则必须具有(a)大量局部变量,或者(b)极长的参数列表,或者(c)您正在使用在堆栈上分配临时对象的C ++编译器。
'相反,我一直认为堆栈状态应该在所有(子)调用返回时恢复。在C中,调用者清理堆栈。
我猜(1)你要么是某些奇怪的(对我而言)架构,要么是(2)你有一些不寻常的编译器选项(CFLAGS?)。
你最好的选择是'gcc -S'并检查装配输出。