【发布时间】:2021-06-21 21:07:42
【问题描述】:
存在一些关于堆栈上变量的 GCC 排序的问题。但是,这些通常涉及混合变量和数组,而事实并非如此。我正在使用 GCC 9.2.0 64 位版本,没有特殊标志。如果我这样做:
#include <iostream>
int main() {
int a = 15, b = 30, c = 45, d = 60;
// std::cout << &a << std::endl;
return 0;
}
那么内存布局看这里的反汇编:
0x000000000040156d <+13>: mov DWORD PTR [rbp-0x4],0xf
0x0000000000401574 <+20>: mov DWORD PTR [rbp-0x8],0x1e
0x000000000040157b <+27>: mov DWORD PTR [rbp-0xc],0x2d
0x0000000000401582 <+34>: mov DWORD PTR [rbp-0x10],0x3c
所以:四个变量在 RBP 的偏移量 0x04、0x08、0x0C、0x10 处按顺序排列;也就是说,按照它们被声明的相同顺序进行排序。这是一致的和确定的;我可以重新编译,添加其他代码行(随机打印语句,其他后期变量等)并且布局保持不变。
但是,只要我添加了一条触及地址或指针的行,布局就会发生变化。例如,这个:
#include <iostream>
int main() {
int a = 15, b = 30, c = 45, d = 60;
std::cout << &a << std::endl;
return 0;
}
产生这个:
0x000000000040156d <+13>: mov DWORD PTR [rbp-0x10],0xf
0x0000000000401574 <+20>: mov DWORD PTR [rbp-0x4],0x1e
0x000000000040157b <+27>: mov DWORD PTR [rbp-0x8],0x2d
0x0000000000401582 <+34>: mov DWORD PTR [rbp-0xc],0x3c
所以:一个加扰的布局,其中变量的偏移量现在分别位于 0x10、0x04、0x08、0x0C。同样,这与任何重新编译、我认为要添加的大多数随机代码等都是一致的。
但是,如果我只是像这样触摸不同的地址:
#include <iostream>
int main() {
int a = 15, b = 30, c = 45, d = 60;
std::cout << &b << std::endl;
return 0;
}
然后变量按如下顺序排列:
0x000000000040156d <+13>: mov DWORD PTR [rbp-0x4],0xf
0x0000000000401574 <+20>: mov DWORD PTR [rbp-0x10],0x1e
0x000000000040157b <+27>: mov DWORD PTR [rbp-0x8],0x2d
0x0000000000401582 <+34>: mov DWORD PTR [rbp-0xc],0x3c
也就是说,偏移量 0x04、0x10、0x08、0x0C 处的不同序列。再一次,据我所知,这与重新编译和代码更改是一致的,除非我在代码中引用了其他地址。
如果我不知道更好,看起来整数变量是按声明顺序放置的,除非代码对寻址进行任何操作,此时它开始以某种确定性的方式对它们进行加扰。
将不满足这个问题的一些回答如下:
- “行为在 C++ 标准中未定义”——我不是在询问 C++ 标准,而是专门询问这个 GCC 编译器如何决定布局。
- “编译器可以为所欲为”——没有回答编译器在这种特定的、一致的情况下如何决定它“想要”什么。
为什么 GCC 编译器会这样布局整数变量?
什么解释了这里看到的一致的重新排序?
编辑:我想仔细观察,我触摸到的变量总是放在[rbp-0x10],然后其他的按声明顺序排列。为什么会有好处?请注意,据我所知,打印任何这些变量的 值 似乎不会触发相同的重新排序。
【问题讨论】:
-
用
-O0编译怎么样?这似乎是合理的,这只是一些优化的一部分,在这种特定情况下实际上没有任何区别。 -
@super 通过优化,编译器通常不会分配未使用的变量:godbolt.org/z/dPq5Ks5Wd.
-
我假设 gcc 将使用的变量放在最对齐的地址上,这可以提高访问速度、缓存使用或类似的东西。
-
为什么调用框架中局部变量的顺序对您很重要? C 标准n3337 没有提到它们,你也不应该在意!任何编译器都可以将 CPU 寄存器用于变量!你的问题是XY problem,你应该用书面英语解释你为什么关心变量顺序和布局
-
这并不能解释“为什么局部变量的顺序对你很重要”。例如,您的代码可能由带有插件的 GCC 编译,或者由另一个版本的 GCC 或 Clang 编译,它们以不同的方式对局部变量进行排序。你可能有理由问你的问题,值得解释一下这个原因