c&gcc:堆栈增长和alignment – 对于64位机器

我有以下程序。 我想知道为什么它在以下64位机上输出-4? 我的哪个假设出错了?

[Linux ubuntu 3.2.0-23-generic#36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux]

  1. 在上面的机器和gcc编译器中,默认情况下b应该是第一个和第二个。 堆栈向下生长。 所以b应该有更高的地址和更低的地址。 所以结果应该是积极的。 但我得到了-4。 有人可以解释吗?

  2. 参数是在堆栈帧中占用2个字节的两个字符。 但我看到了不同的地方,因为我期待1。即使有人说,这是因为alignment,然后我想知道2字符的结构是不alignment在4个字节。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> void CompareAddress(char a, char b) { printf("Differs=%ld\n", (intptr_t )&b - (intptr_t )&a); } int main() { CompareAddress('a','b'); return 0; } /* Differs= -4 */ 

回答这些问题(关于特定平台上特定编译器的行为)的最好方法是查看汇编器。 你可以让gcc通过传递-S标志来转储它的汇编程序(而且-fverbose-asm标志也很好)。 运行

 gcc -S -fverbose-asm file.c 

给出一个看起来有点像的文件(我已经删除了所有不相关的位,括号中的位是我的注释):

 CompareAddress: # ("allocate" memory on the stack for local variables) subq $16, %rsp # (put a and b onto the stack) movl %edi, %edx # a, tmp62 movl %esi, %eax # b, tmp63 movb %dl, -4(%rbp) # tmp62, a movb %al, -8(%rbp) # tmp63, b # (get their addresses) leaq -8(%rbp), %rdx #, b.0 leaq -4(%rbp), %rax #, a.1 subq %rax, %rdx # a.1, D.4597 (&b - &a) # (set up the parameters for the printf call) movl $.LC0, %eax #, D.4598 movq %rdx, %rsi # D.4597, movq %rax, %rdi # D.4598, movl $0, %eax #, call printf # main: # (put 'a' and 'b' into the registers for the function call) movl $98, %esi #, movl $97, %edi #, call CompareAddress 

( 这个问题很好地解释了[re]bp[re]sp是什么)。

差异为负的原因是堆栈向下增长:即,如果将两个东西压入堆栈,则先推送的地址将会有更大的地址,并且将a推到b之前。

其原因是-4而不是-1是因为编译器决定将参数对齐到4个字节的边界是“更好的”,可能是因为32位/ 64位CPU处理4个字节的时间比处理单个字节更好。

(另外,查看汇编器显示了-mpreferred-stack-boundary具有的效果:它基本上意味着堆栈上的内存以不同大小的块分配。

这是我的猜测:

在x64的Linux上, 调用约定规定前几个参数是通过寄存器传递的。

所以在你的情况下, ab都是通过寄存器而不是堆栈来传递的。 然而,由于你的地址,编译器会在函数调用后将其存储在堆栈的某个地方。
(不需要在下面的顺序。)

函数也可能是直接内联的。

无论哪种情况,编译器都会使用临时堆栈空间来存储变量。 这些可以按任何顺序进行优化。 所以他们可能没有任何特定的顺序,你可能会期望。

我认为程序给出的答案是正确的,GCC的默认首选堆栈边界是4,你可以设置-mpreferred-stack-boundary=num到GCC选项来改变堆栈,然后程序会给你不同的根据你的答案回答。