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

时间:2011-05-21 01:41:16

标签: c++ c stack low-level

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

然而,在这个经典的访谈问题中,我想了解为什么人们使用函数调用而不是在同一函数中比较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:从对这个和其他线索的不同反应来看,这个问题本身似乎是有缺陷/不相关的,作为一个面试问题它可能会重新执行不正确的假设,除非有人研究答案!

谢谢!

5 个答案:

答案 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。不同的编译器可能会给出不同的结果。

但是,如果你调用另一个函数,那个函数的局部变量在一个新的堆栈框架中,即从调用者变量的增长方向。