0

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

int adad=12345;
char character;

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

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

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

4

2 回答 2

3

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

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

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

  return 0;
}

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

_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)

是的movl初始化adad。然后,如您所见,它读出 的第 3 个字节adad,并将其存储回内存中(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. 将 2 添加到该地址
  4. 通过取消引用新地址来加载一个字节
  5. 将一个字节存入character

各种优化会改变输出......如果您出于某种原因确实需要一些特定的行为/寻址模式,您可能必须自己编写程序集。

于 2010-08-07T15:02:06.410 回答
2

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

[1] 请注意,在不知道我们在谈论什么 CPU 架构以及编译器如何使用它的情况下,我们甚至不能说“从基址寄存器加载一个固定偏移量的单词”是否是在指令(正如人们所希望的那样,许多流行的架构肯定支持;-)或需要在辅助寄存器中进行单独的地址算术。

IOW,无论这是否是一个好主意,绝对有可能定义一个 CPU 架构,它不能加载/存储寄存器,除了其他寄存器或由其他寄存器或常量定义的内存地址之外,并且存在一些这样的架构(尽管它们可能不是全部)在这个时候流行;-)。

于 2010-08-07T15:01:33.210 回答