【发布时间】:2019-01-16 05:18:45
【问题描述】:
我无法理解为什么编译器选择以我编写的代码的方式偏移堆栈空间。
我正在玩弄 Godbolt 的 Compiler Explorer 以研究 C 调用约定,这时我想出了一个简单的代码,让我对它的选择感到困惑。
代码位于this link。我选择了 GCC 8.2 x86-64,但我的目标是 x86 处理器,这很重要。 Bellow 是 C 代码的转录和 Compiler Explorer 报告的生成程序集。
// C code
int testing(char a, int b, char c) {
return 42;
}
int main() {
int x = testing('0', 0, '7');
return 0;
}
; Generated assembly
testing(char, int, char):
push ebp
mov ebp, esp
sub esp, 8
mov edx, DWORD PTR [ebp+8]
mov eax, DWORD PTR [ebp+16]
mov BYTE PTR [ebp-4], dl
mov BYTE PTR [ebp-8], al
mov eax, 42
leave
ret
main:
push ebp
mov ebp, esp
sub esp, 16
push 55
push 0
push 48
call testing(char, int, char)
add esp, 12
mov DWORD PTR [ebp-4], eax
mov eax, 0
leave
ret
从现在开始看汇编列,据我了解,第15行负责为局部变量保留堆栈中的空间。问题是我只有一个本地 int 并且偏移量是 16 个字节而不是 4 个。这感觉像是在浪费空间。
这是否与单词对齐有关?但即使是这样,如果通用寄存器的大小是4字节,那么这种对齐不应该是4字节吗?
我看到的另一件奇怪的事情是关于testing 函数的本地chars 的位置。它们似乎在堆栈中每个占用 4 个字节,如第 7-8 行所示,但只有较低的字节被操作。为什么不每个只使用 1 个字节?
这些选择可能是出于好意,我真的很想了解它们的目的(或者是否没有目的)。或者,也许我只是感到困惑,并没有完全理解。
【问题讨论】:
-
现在想起来,我相信
chars的位置确实是因为字对齐。是吗? -
为什么不构建优化?
-
有趣。即使您启用优化并将 args 更改为
volatile char a等等 (godbolt.org/z/ul--bv),它们仍然会被复制到堆栈上的不同位置,而不是使用 arg 传递槽作为它们的永久位置。而且它们仍然保存在不同的双字中。此外,只有char参数被复制,除非您b++int。这看起来像是 gcc 错过了优化。标题问题是一个简单的重复问题,已被多次询问(i386 System V ABI 要求在call之前进行 16 字节堆栈对齐),但char打包有点有趣。跨度> -
是什么让你认为编译器不会浪费空间和时间,如果你不允许它优化? (
-O0)。实际上,即使您允许它进行优化,是什么让您认为编译器将有足够的时间来计算完美的解决方案? (让我们忽略对于任何中等大小的源代码,甚至可能没有单一的“完美”解决方案)编译器的时间量非常短(秒/分钟),所以它只会划伤可能结果的总量,并且感谢编译器设计,即使是微小的划痕通常也会表现出色,但有点“浪费”...... -
请将代码放入您的问题中。 Stack Overflow 希望您的所有问题都是独立的,以防外部网站出现故障。
标签: c assembly x86 calling-convention gcc8