这里编译器生成的汇编代码中的寻址模式是什么?

时间:2010-08-07 14:53:39

标签: c++ pointers assembly addressing-mode

假设我们有两个整数和字符变量:

int adad=12345;
char character;

假设我们正在讨论一个平台,其中,一个整数变量的长度大于或等于三个字节,我想访问这个整数的第三个字节并将它放在字符变量中,我说这样写:

character=*((char *)(&adad)+2);

考虑到代码行以及我不是编译器或汇编专家的事实,我对汇编中的模式有一些了解,我想知道第三个字节的地址(或者我想最好说第三个字节的偏移)这里是在这行代码本身生成的指令内,或者它是在一个单独的变量地址(或偏移)是否符合这些说明?

2 个答案:

答案 0 :(得分:3)

在这种情况下,最好的办法是尝试一下。这是一个示例程序:

int main(int argc, char **argv)
{
  int adad=12345;
  volatile char character;

  character=*((char *)(&adad)+2);

  return 0;
}

我添加了volatile以避免分配线被完全优化掉。现在,这是编译器提出的(对于我的Mac上的-Oz):

_main:
    pushq   %rbp
    movq    %rsp,%rbp
    movl    $0x00003039,0xf8(%rbp)
    movb    0xfa(%rbp),%al
    movb    %al,0xff(%rbp)
    xorl    %eax,%eax
    leave
    ret

我们关心的唯一三条线是:

    movl    $0x00003039,0xf8(%rbp)
    movb    0xfa(%rbp),%al
    movb    %al,0xff(%rbp)

movladad的初始化。然后,正如您所看到的,它会读出adad的第3个字节,并将其存储回内存(volatile强制该存储返回)。

我想一个很好的问题是,为什么组件生成会对您有什么影响?例如,只需将我的优化标志更改为-O0,代码中有趣部分的程序集输出就是:

    movl    $0x00003039,0xf8(%rbp)
    leaq    0xf8(%rbp),%rax
    addq    $0x02,%rax
    movzbl  (%rax),%eax
    movb    %al,0xff(%rbp)

这很简单地被视为代码的确切逻辑运算:

  1. 初始化adad
  2. adad
  3. 的地址
  4. 将2添加到该地址
  5. 通过解除引用新地址
  6. 加载一个字节
  7. 将一个字节存储到character
  8. 各种优化会改变输出......如果由于某种原因你真的需要一些特定的行为/寻址模式,你可能必须自己编写程序集。

答案 1 :(得分:2)

在不了解编译器和底层CPU架构的情况下,无法给出明确的答案。例如,并非所有的CPU架构都允许对内存中的每个任意字节进行寻址(尽管我相信所有当前流行的字节都会这样做):在字处理的CPU上,而不是字节寻址,编译器将生成的内容不可避免地会发生加载到整个单词adad的某个寄存器中(大概是从基址指针寄存器偏移,如果有问题的变量在堆栈[1]上),然后移位和屏蔽以隔离字节兴趣。

[1]请注意,在不知道我们正在讨论什么CPU架构以及编译器如何使用它的情况下,我们甚至无法说出“在基址寄存器的固定偏移处加载一个字”是否已完成指令内联(人们可能希望,许多流行的架构肯定支持;-)或需要在辅助寄存器中单独的地址算法。

IOW,无论是否是一个好主意,它肯定是可能来定义一个CPU架构,它不能加载/存储寄存器,除了从其他寄存器或其他寄存器定义的存储器地址或常量,以及一些存在这样的架构(虽然它们目前可能不是那么受欢迎; - )。