1

在 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的地址也在向上增长,而不是向下增长,为什么?

4

4 回答 4

5

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

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

于 2012-06-24T11:52:08.440 回答
3

您的程序不测试堆栈的方向。

    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");
}
于 2012-06-24T11:57:29.487 回答
2

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

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

于 2012-06-24T12:03:22.263 回答
0

显然,局部变量在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具有次低的绝对偏移量,依此类推。

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

于 2012-06-24T13:13:49.503 回答