假设我们有两个整数和字符变量:
int adad=12345;
char character;
假设我们正在讨论一个平台,其中,一个整数变量的长度大于或等于三个字节,我想访问这个整数的第三个字节并将它放在字符变量中,我说这样写:
character=*((char *)(&adad)+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)
movl
是adad
的初始化。然后,正如您所看到的,它会读出adad
的第3个字节,并将其存储回内存(volatile
强制该存储返回)。
我想一个很好的问题是,为什么组件生成会对您有什么影响?例如,只需将我的优化标志更改为-O0
,代码中有趣部分的程序集输出就是:
movl $0x00003039,0xf8(%rbp)
leaq 0xf8(%rbp),%rax
addq $0x02,%rax
movzbl (%rax),%eax
movb %al,0xff(%rbp)
这很简单地被视为代码的确切逻辑运算:
adad
adad
character
各种优化会改变输出......如果由于某种原因你真的需要一些特定的行为/寻址模式,你可能必须自己编写程序集。
答案 1 :(得分:2)
在不了解编译器和底层CPU架构的情况下,无法给出明确的答案。例如,并非所有的CPU架构都允许对内存中的每个任意字节进行寻址(尽管我相信所有当前流行的字节都会这样做):在字处理的CPU上,而不是字节寻址,编译器将生成的内容不可避免地会发生加载到整个单词adad
的某个寄存器中(大概是从基址指针寄存器偏移,如果有问题的变量在堆栈[1]上),然后移位和屏蔽以隔离字节兴趣。
[1]请注意,在不知道我们正在讨论什么CPU架构以及编译器如何使用它的情况下,我们甚至无法说出“在基址寄存器的固定偏移处加载一个字”是否已完成指令内联(人们可能希望,许多流行的架构肯定支持;-)或需要在辅助寄存器中单独的地址算法。
IOW,无论是否是一个好主意,它肯定是可能来定义一个CPU架构,它不能加载/存储寄存器,除了从其他寄存器或其他寄存器定义的存储器地址或常量,以及一些存在这样的架构(虽然它们目前可能不是那么受欢迎; - )。