堆栈上的局部变量分配顺序

Dav*_*vid 26 c memory x86 stack gcc

看看这两个功能:

void function1() {
    int x;
    int y;
    int z;
    int *ret;
}

void function2() {
    char buffer1[4];
    char buffer2[4];
    char buffer3[4];
    int *ret;
}
Run Code Online (Sandbox Code Playgroud)

如果我打破function1()gdb,并打印变量的地址,我得到这个:

(gdb) p &x  
$1 = (int *) 0xbffff380
(gdb) p &y
$2 = (int *) 0xbffff384
(gdb) p &z
$3 = (int *) 0xbffff388
(gdb) p &ret
$4 = (int **) 0xbffff38c
Run Code Online (Sandbox Code Playgroud)

如果我做同样的事情function2(),我得到这个:

(gdb) p &buffer1
$1 = (char (*)[4]) 0xbffff388
(gdb) p &buffer2
$2 = (char (*)[4]) 0xbffff384
(gdb) p &buffer3
$3 = (char (*)[4]) 0xbffff380
(gdb) p &ret
$4 = (int **) 0xbffff38c
Run Code Online (Sandbox Code Playgroud)

您会注意到,在两个函数中,ret都存储在最靠近堆栈顶部的位置.在function1(),接下来是z,y最后x.在function2(),ret然后是buffer1,然后buffer2buffer3.为什么存储顺序发生了变化?我们在两种情况下使用相同数量的内存(4字节ints与4字节char数组),因此它不能成为填充问题.这种重新排序有什么原因,而且,通过查看C代码可以提前确定如何排序局部变量?

现在我知道C的ANSI规范没有说明存储局部变量的顺序,并且允许编译器选择自己的顺序,但我想编译器有关于它如何处理的规则这个,并解释为什么这些规则是这样的.

作为参考,我在Mac OS 10.5.7上使用GCC 4.0.1

Cra*_*rks 17

我不知道为什么GCC按照它的方式组织它的堆栈(虽然我猜你可以破解它的源或本文并找出),但是我可以告诉你如何保证特定堆栈变量的顺序如果由于某种原因你需要.简单地将它们放在一个结构中:

void function1() {
    struct {
        int x;
        int y;
        int z;
        int *ret;
    } locals;
}
Run Code Online (Sandbox Code Playgroud)

如果我的记忆正确地为我服务,spec保证&ret > &z > &y > &x.我离开了我的K&R工作,所以我不能引用章节和诗句.


Dav*_*vid 9

所以,我做了一些实验,这就是我发现的.它似乎是基于每个变量是否是一个数组.鉴于此输入:

void f5() {
        int w;
        int x[1];
        int *ret;
        int y;
        int z[1];
}
Run Code Online (Sandbox Code Playgroud)

我最终在gdb中得到了这个:

(gdb) p &w
$1 = (int *) 0xbffff4c4
(gdb) p &x
$2 = (int (*)[1]) 0xbffff4c0
(gdb) p &ret 
$3 = (int **) 0xbffff4c8
(gdb) p &y
$4 = (int *) 0xbffff4cc
(gdb) p &z
$5 = (int (*)[1]) 0xbffff4bc
Run Code Online (Sandbox Code Playgroud)

在这种情况下,ints和指针首先处理,最后在堆栈顶部声明,然后首先声明更接近底部.然后以相反的方向处理数组,声明越早,堆栈中的最高位.我确信这是有充分理由的.我不知道它是什么.


sig*_*ice 8

ISO C不仅没有说明堆栈上局部变量的排序,它甚至不能保证堆栈甚至存在.该标准只讨论了块内变量的范围和生命周期.

  • 有趣的是,C11 甚至没有提到“堆栈”这个词。有无堆栈计算机吗? (3认同)

lav*_*nio 5

通常它与对齐问题有关.

大多数处理器在获取非处理器字对齐的数据时速度较慢.他们必须抓住它并将它拼接在一起.

可能正在发生的事情是它将所有大于或等于处理器最佳对齐的对象放在一起,然后将更紧密的东西打包成可能不对齐的东西.只是碰巧在你的例子中你的所有char数组都是4个字节,但我敢打赌,如果你让它们成为3个字节,它们仍然会在同一个地方结束.

但是如果你有四个单字节数组,它们可能最终在一个4字节范围内,或者在四个独立的数组中对齐.

这就是处理器抓取的最简单(转换为"最快")的全部内容.