【问题标题】:c & gcc : Stack growth and alignment - for a 64 bit machinec & gcc : 堆栈增长和对齐 - 用于 64 位机器
【发布时间】:2012-06-19 02:23:16
【问题描述】:

我有以下程序。我想知道为什么它在下面的 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 应该有更高的地址,而 a 应该有更低的地址。所以结果应该是积极的。但我得到-4。谁能解释一下?

  2. 参数是两个字符,占用堆栈帧中的 2 个字节。但我看到差异为 4,而我期望为 1。即使有人说这是因为对齐,那么我想知道具有 2 个字符的结构在 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 */

【问题讨论】:

    标签: c linux gcc


    【解决方案1】:

    这是我的猜测:

    在 x64 的 Linux 上,calling convention 声明前几个参数是通过寄存器传递的。

    因此,在您的情况下,ab 都通过寄存器而不是堆栈传递。但是,由于您获取了它的地址,编译器会将其存储在堆栈中函数被调用后的某个位置。
    (在向下的顺序中没有必要。)

    函数也有可能是完全内联的。

    在任何一种情况下,编译器都会腾出临时堆栈空间来存储变量。这些可以按任何顺序排列并进行优化。因此,它们可能不会按照您所期望的任何特定顺序排列。

    【讨论】:

    【解决方案2】:

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

    gcc -S -fverbose-asm file.c
    

    给出一个看起来有点像的file.s(我已经删除了所有不相关的部分,括号中的部分是我的注释):

    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
    

    This question 很好地解释了 [re]bp[re]sp 是什么。)

    差异为负的原因是堆栈向下增长:即,如果您将两个东西压入堆栈,则您首先压入的一个将具有更大的地址,并且ab 之前被压入。

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

    (另外,查看汇编程序显示了-mpreferred-stack-boundary 的效果:它本质上意味着堆栈上的内存被分配在不同大小的块中。)

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我尝试了 -mpreferred-stack-boundary 的值 4 到 12。但结果保持不变
      猜你喜欢
      • 1970-01-01
      • 2021-12-08
      • 2017-01-04
      • 1970-01-01
      • 2015-09-18
      • 2011-03-23
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多