为什么函数调用而不是变量地址用于检测堆栈增长方向?

我读到了检测堆栈增长检测问题的不同响应,我理解,在现代架构中,堆栈可能会随机增长,可能会在堆外创建,依此类推。

但是,在这个经典的访谈问题中,我想了解为什么人们使用函数调用而不是在同一函数中比较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

PS:从对这个和其他线索的不同反应来看,似乎问题本身是有缺陷/不相关的,作为面试问题它可能会重新执行不正确的假设,除非有人研究答案!

谢谢!

单个堆栈框架内,编译器可以根据需要自由地对局部变量进行排序,因此代码如下:

 int i; double j; 

j之前或之后可能有i 。 只要编译生成正确的代码来访问变量,它就可以去任何地方。

实际上,除非你使用address-of运算符& (或者必须得到地址),否则变量可能永远不会在堆栈上。 它可以在呼叫期间存储在寄存器中。

但是,堆栈帧本身的放置顺序是受限制的,因为如果它们出现故障,函数返回将无法正常工作(说得温和)。


当然,我应该提到堆栈增长的方向仅在非常有限的场景中有用。 绝大多数代码都不应该关心它。 如果您对不同的体系结构以及它们如何处理堆栈感兴趣,请参阅此答案 。

您无法完全控制编译器选择分配局部变量的顺序。但是,您可以合理地控制将要调用的函数以及按什么顺序控制。

声明变量将放置在堆栈上的顺序是未定义的,但在函数调用的函数中,内部函数调用的参数必然会在外部函数的后面推送到堆栈上。

编译器可以并且对堆栈帧中的变量进行重新排序:

 #include  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旁边。

问题是当你这样做时:

 void test(void) { int a; int b; if (&a < &b) ... 

您得到的结果与堆栈增长方向无关。 您知道堆栈增长方式的唯一方法是创建新框架 。 编译器可以自由地在其认为合适的位置放置bb以下的b 。 不同的编译器可能给出不同的结果。

但是,如果调用另一个函数,该函数的局部变量必须位于新的堆栈帧中,即从调用者变量的增长方向。