【问题标题】:Why doesn't the C program stack grow downward?为什么C程序栈不向下增长?
【发布时间】:2012-06-25 23:49:34
【问题描述】:

在 gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 和 Intel(R) Core(TM)2 Duo CPU 上运行以下程序,我想验证 c 程序堆栈向下增长,我编写以下代码:

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>

    static int a = 1;
    static int b;
    int c = 2;
    int d;

    void foo(void)
    {
        int *p1;
        int *p2;
        int *p3;
        int *p4;

        printf("&p1\t%p\n", &p1);
        printf("&p2\t%p\n", &p2);
        printf("&p3\t%p\n", &p3);
        printf("&p4\t%p\n", &p4);
    }

    int main()
    {
        static int e = 3;
        static int f;
        int g = 4;
        int h;

        char *str1 = "abc";
        char *str2 = "abc";
        char *str3;
        char *str4;

        printf("&\"abc\"\t%p\n", &"abc");
        printf("&str1\t%p\n", &str1);
        printf("&str2\t%p\n", &str2);
        printf("str1\t%p\n", str1);
        printf("str2\t%p\n", str2);

        printf("&str3\t%p\n", &str3);
        printf("str3\t%p\n", str3);

        str4 = (char *)malloc(strlen("abc")*sizeof(char));
        printf("&str4\t%p\n", &str4);
        printf("str4\t%p\n", str4);

        printf("&g\t%p\n", &g);
        printf("&h\t%p\n", &h);

        foo();

        return 0;
    }

我得到这个结果:

    &"abc"  0x8048680
    &str1   0xbff1be20
    &str2   0xbff1be24
    str1    0x8048680
    str2    0x8048680
    &str3   0xbff1be28
    str3    0x8048599
    &str4   0xbff1be2c
    str4    0x950f008
    &g  0xbff1be18
    &h  0xbff1be1c
    &p1 0xbff1bde0
    &p2 0xbff1bde4
    &p3 0xbff1bde8
    &p4 0xbff1bdec

我发现str1、str2、str3、str4的地址向上增长,p1、p2、p3、p4的地址也向上增长,而不是向下增长,为什么?

【问题讨论】:

  • 请搜索 [c] 堆栈方向
  • 上升或下降是如此相对:对你来说“上升”的东西对澳大利亚人来说很可能是“下降”:)
  • 列出编译器和平台以完善您的问题可能会有所帮助,但这里有一个很好的链接:stackoverflow.com/questions/3572610/stack-growth-direction
  • @VictorS 我的意思是 StackOverflow,你发帖的地方。
  • @octopusgrabbus 我使用 gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 和 Intel(R) Core(TM)2 Duo CPU

标签: c stack


【解决方案1】:

C 标准没有说明堆栈,更不用说它的增长方向了。

您观察到的任何行为都完全取决于您的特定编译器(这反过来又会受到您所运行的特定平台的影响。)

【讨论】:

  • 我正在使用 gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
【解决方案2】:

你的程序没有测试栈的方向。

    int *p1;
    int *p2;
    int *p3;
    int *p4;

编译器可以按照它们在程序中出现的相反顺序推送自动对象。

测试堆栈方向(在某些平台上确实向上)的一个很好的检查是检查两个不同函数中自动对象的地址,一个函数调用另一个函数。

void f(void)
{
    int a = 0;
    g(&a);
}

void g(int *p)
{
    int a = 0;

    if (p - &a > 0) printf("stack goes upward\n");
    else  printf("stack goes downard\n");
}

【讨论】:

    【解决方案3】:

    实际上,它确实在您的平台上向下增长,我猜是 Linux + 32 位 x86。在 x86 平台上有 2 个寄存器用于寻址堆栈,基指针 (BP) 和堆栈指针 (SP)。 SP 在值被推送时自动递增,并在它们被弹出时递减。在调用函数之前,被调用者 以相反的顺序将函数压入堆栈,第一个参数是堆栈上最顶部的参数。

    但是,在函数体中,编译器发出的代码将原始 SP 存储在 BP 中,然后将 SP 递增到足以覆盖所有局部变量;这些通常以递增方向分配,并通过函数体内的 BP 指针寻址。值得注意的是,在您的案例中,局部变量没有被“推送”到堆栈上,因为它们未初始化。

    【讨论】:

      【解决方案4】:

      显然main 中的局部变量在堆栈中的地址比foo 中的更高,因此您的堆栈向下增长 - 无需担心 :)

      为了支持编译器相关变量放置的情况,请考虑在使用 GCC-LLVM 4.2.1 在 Mac OS X 上运行代码时的结果:

      &p1     0x7fff63a5dbf8  ^
      &p2     0x7fff63a5dbf0  |
      &p3     0x7fff63a5dbe8  |
      &p4     0x7fff63a5dbe0  |
      

      在带有 GCC 4.2.4-1ubuntu4 的 Linux 上:

      &p1     0x7fffa1e6d7b8  ^
      &p2     0x7fffa1e6d7b0  |
      &p3     0x7fffa1e6d7a8  |
      &p4     0x7fffa1e6d7a0  |
      

      使用 GCC 4.4.3-4ubuntu5.1 观察到相同的行为。但使用 Intel C Compiler v11.0 时,事情就发生了逆转:

      &p1     0x7fff59a84c20  |
      &p2     0x7fff59a84c28  |
      &p3     0x7fff59a84c30  |
      &p4     0x7fff59a84c38  v
      

      两种编译器生成的汇编代码中的差异清晰可见。 GCC 使用基于基指针 (EBP/RBP) 的负偏移寻址(即相对于堆栈帧顶部的寻址),而 ICC 使用基于堆栈指针 (ESP/RSP) 的正偏移寻址(即相对于堆栈底部的寻址)堆栈帧)。在这两种情况下,p1 的绝对偏移量最小,p2 的绝对偏移量次之,以此类推。

      如果提供-fomit-frame-pointer 选项,GCC 也可以使用基于堆栈指针的寻址(自动启用更高的优化级别),但至少 4.4.3 之前的 GCC 仍保留旧的变量布局,即 p1 现在有最高偏移量,p2 具有次高偏移量,依此类推。可能这在较新的 GCC 版本中有所改变。

      【讨论】:

        猜你喜欢
        • 2012-07-30
        • 1970-01-01
        • 2023-03-13
        • 2014-06-24
        • 2015-02-21
        相关资源
        最近更新 更多