CSAPP 3.43题疑问:C代码对应的汇编结果为何是给定形式?
前提说明
以下解答基于《深入理解计算机系统》第3版习题3.43预设的类型内存布局规则:
union 所有成员的起始偏移均为0;内部结构体
t1中w成员的偏移量为10字节(由C语言结构体内存对齐规则决定);内部结构体t2的数组成员a元素为4字节int类型,起始偏移为0。函数第一个参数up(union指针)存在%rdi,第二个参数目标地址存在%rsi。
问题1:&up->t1.w的汇编处理前为什么要执行加10的操作?
&up->t1.w的语义是取成员w的内存地址,计算规则为union指针基址 + 成员w在结构体t1中的偏移量。
成员w在t1中的偏移量经对齐计算后为10字节,加10的操作本质就是计算目标指针值:(char *)up + 10,得到的结果就是&up->t1.w的最终值,后续再完成赋值操作即可。
问题2:为什么up->t2.a对应的汇编仅一行,不需要先访存?
前两个示例需要两行汇编的核心原因是:它们的语义是读取union指向内存中存储的成员值,所以需要先通过movq (%rdi), %rax访存拿到成员值,再写入目标地址。
而up->t2.a中的a是数组名,在作为右值赋值时会自动退化为指向数组首元素的指针:
- 数组
a的首地址与union的基址完全相同(union成员偏移为0,数组首元素偏移也为0) - 这个指针值就是
%rdi本身,不需要额外访存读取内存内容
所以直接执行movq %rdi, (%rsi)就能完成赋值,仅需要一行汇编。
问题3:up->t2.a[up->t1.u]的汇编转译逻辑是什么?
逐行解析汇编代码:
movq (%rdi), %rax movl (%rdi,%rax,4), %eax movl %eax, (%rsi)
- 第一行:
up->t1.u是结构体t1的首个成员,偏移量为0,所以读%rdi指向的内存内容,得到索引值up->t1.u存入%rax - 第二行:数组
a的首地址为%rdi,每个int元素占4字节,所以a[索引]的地址计算为%rdi + %rax * 4,读取该地址的4字节int值存入%eax - 第三行:将读取到的数组元素值写入目标地址
%rsi指向的位置,完成赋值
内容的提问来源于stack exchange,提问作者Megan Darcy
相关产品推荐
相关产品推荐

