在执行以下程序期间,堆栈中一次绑定的最大变量数是多少:
int x, y, z;
int g(int a, int b) {
int c = 5 * a + b;
return c;
}
int f(int a, int b) {
a = g(a, 5);
return g(b, a);
}
int main() {
int a, b, c;
x = y = z = 0;
a = 5; b = 6;
c = f(a, b);
printf("%d", c);
}
如果有人知道如何找到它,请。你能解释一下该怎么做才能在可能给出的每个代码中找到它吗? 没有任何优化。
再比如:
int x,y;
int f(int a){
if (a!=5)
return f(--a);
else
return a;
}
int main(){
int a,b;
a=8;b=6;
x=f(a);
y=f(b);
printf("%d", x+y);
return 0;
}
这是答案6以上吗?因为第一次返回,返回变量 3 次。第二次返回返回一次数字,我们在 main 中有两个变量,所以 6?
您可以在头脑中(或在调试器中(逐步运行程序,并且可以计算调用堆栈的每一帧中的所有活动变量(然后获得它们的最大总和(。
在您的特定示例中,假设没有优化,最深的调用堆栈发生在g
语句return c;
由main
调用f
调用。所以我们有 3 个调用堆栈帧,其中 3 个变量用于 g
(a
、b
、c
;我假设形式就像局部变量,这在实践中并不总是正确的!(,2 个变量用于f
(a
,b
(,3 个变量用于main
。
您的老师可能希望您将调用堆栈绘制到最深的状态。我把这个留给你。
在实践中,几个变量不占用任何堆栈空间,例如,因为它们位于寄存器中,或者与另一个变量共享一个堆栈槽。这是编译器和 ABI 以及特定于处理器架构和操作系统的。
然而,正如Ilya所回答的那样,一个好的编译器会为了优化目的而转换你的程序,所以在实践中答案可能会有所不同。
如果使用GCC,您可以尝试查看生成的汇编代码(使用gcc -fverbose-asm -S
(,您将看到结果和所用变量的数量取决于优化标志(即 -O1
、-O2
等...或缺乏它们(。您也可以使用 GCC 特定的 -fstack-usage 标志。您甚至可以尝试使用 -fdump-tree-all
标志来gcc
它提供了数百个转储文件,详细解释了编译器中程序的各种中间表示(Gimple、SSA 等(。
另请阅读有关延续(也可能是call/cc(,tail call,recursion和内联扩展的wiki页面。
顺便说一句,调用堆栈的存在并不是 C99 标准要求的严格意义上的(但我知道没有不使用任何调用堆栈的 C 实现(。如果你很好奇,你应该阅读 A.Appel 的旧纸质垃圾收集可以比堆栈分配更快(这解释了不使用任何调用堆栈的 SML 实现,因为它在垃圾收集堆中分配每个"调用帧",又名"延续帧"(。
我还建议编译您的示例(使用 gcc -Wall -g
(,然后在gdb
调试器中逐步运行它们。经常使用gdb
的display
、step
、backtrace
、frame
命令。
这取决于编译器和优化设置。好的优化算法将生成如下内容:
int main() {
printf("%d", 155);
}
其他算法将生成其他内容。因此,尝试使用反汇编器来获取编译结果。