我读到了检测堆栈增长检测问题的不同响应,我理解,在现代架构中,堆栈可能会随机增长,可能会在堆外创建,等等。
然而,在这个经典的访谈问题中,我想了解为什么人们使用函数调用而不是在同一函数中比较2个局部变量。我认为必须有一些特殊的理由这样做,但不是一个C /低级开发人员[Java :)],我只是在猜测。
这是我尝试过的代码:
void sub (int *a) {
int b;
int c;
printf ("a:%d\n", a);
printf ("b:%d\n", &b);
printf ("c:%d\n", &c);
if (&b > a) {
printf ("Stack grows up.\n");
} else {
printf ("Stack grows down.\n");
}
}
int main (void) {
int a;
int b;
sub (&a);
printf ("\nHere we go again!!\n");
if (&b > &a) {
printf ("Stack grows up.\n");
} else {
printf ("Stack grows down.\n");
}
return 0;
}
我还发现这篇文章试图优化我不理解的解决方案:http://www.devx.com/tips/Tip/37412
P.S:从对这个和其他线索的不同反应来看,这个问题本身似乎是有缺陷/不相关的,作为一个面试问题它可能会重新执行不正确的假设,除非有人研究答案!谢谢!
答案 0 :(得分:6)
您无法完全控制编译器选择分配局部变量的顺序。但是,您可以合理地控制将要调用的函数以及按什么顺序。
答案 1 :(得分:5)
声明变量将放置在堆栈上的顺序是未定义的,但是在函数调用的函数中,内部函数调用的参数必然会在外部函数的后面推送到堆栈上。
答案 2 :(得分:4)
在单个堆栈框架内,编译器可以根据需要自由地对局部变量进行排序,因此代码:
int i;
double j;
在i
之前或之后可能有j
。只要编译生成正确的代码来访问变量,它就可以去任何地方。
实际上,除非你使用地址运算符&
(或者必须得到地址),否则变量可能永远不会在堆栈上。它可以在呼叫期间存储在寄存器中。
但是,堆栈框架本身的放置顺序是受限制的,因为如果它们发生故障,功能返回就会很好地工作(温和地说)。
当然,我应该提到堆栈增长的方向仅在非常有限数量的场景中有用。绝大多数代码都不应该关心它。如果您对不同的体系结构以及它们如何处理堆栈感兴趣,请参阅this answer。
答案 3 :(得分:4)
编译器可能会对堆栈帧中的变量进行重新排序:
#include <stdio.h>
int main ()
{
char c1;
int a;
char c2;
int b;
printf("%p %p %p %p\n", &c1, &a, &c2, &b);
return 0;
}
打印
0x7ffff62acc1f 0x7ffff62acc18 0x7ffff62acc1e 0x7ffff62acc14
这里(在64位Linux上使用gcc 4.4.3)。 c2已移至c1旁边。
答案 4 :(得分:3)
问题是当你这样做时:
void test(void)
{
int a;
int b;
if (&a < &b)
...
您获得的结果与堆栈增长方向无关。您知道堆栈增长方式的唯一方法是创建新的框架。编译器可以自由地将a
置于b
之上,或a
以下b
。不同的编译器可能会给出不同的结果。
但是,如果你调用另一个函数,那个函数的局部变量有在一个新的堆栈框架中,即从调用者变量的增长方向。