【发布时间】:2017-11-30 02:47:44
【问题描述】:
为了加深对“ (*p)++ ”工作原理的印象,我写了一些测试代码,例如:
int main()
{
int a = 3;
int *p = &a;
int b = (*p)++;
int *q = p++;
int c = a++;
int d = c++;
printf("a = %d, b = %d, c = %d, d = %d, p = %#x, q = %#x\n",a, b, c, d, p, q);
}
输出为:a = 5,b = 3,c = 5,d = 4,p = 0xc6dc3490,q = 0xc6dc348c
但我的问题是关于程序集的(代码是按顺序排列的,而不是断断续续的):
main:
push rbp
mov rbp, rsp
sub rsp, 48
;int a = 3 :
mov DWORD PTR [rbp-36], 3
;int *p = &a :
lea rax, [rbp-36]
mov QWORD PTR [rbp-8], rax
;int b = (*p)++ :
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax]
lea ecx, [rax+1] ;Flag1
mov rdx, QWORD PTR [rbp-8]
mov DWORD PTR [rdx], ecx
mov DWORD PTR [rbp-12], eax
;int *q = p++ :
mov rax, QWORD PTR [rbp-8] ;Flag2
lea rdx, [rax+4] ;Flag3
mov QWORD PTR [rbp-8], rdx
mov QWORD PTR [rbp-24], rax
;int c = a++;
mov eax, DWORD PTR [rbp-36]
lea edx, [rax+1] ;Flag4
mov DWORD PTR [rbp-36], edx
mov DWORD PTR [rbp-28], eax
;int d = c++;
mov eax, DWORD PTR [rbp-28]
lea edx, [rax+1] ;Flag5
mov DWORD PTR [rbp-28], edx
mov DWORD PTR [rbp-32], eax
... ... (ignore some)
请注意让我感到困惑的“Flagx”行。
从上面我们知道
当指针:int *q = p++ :
lea rdx, [rax+4] ;Flag3
这里,'lea' 似乎读取了 'rax' 和 +4 中的 addr 值存储。然后传递给“rdx”。
而:int c = a++ 或 int d = c++:
lea edx, [rax+1] ;Flag4/Flag5
这里,'lea'似乎读取了'rax'(这里是3)中的addr值存储的内容,并且+1,来到4并传递给'edx'。
但是!关键是这两个语句中的“rax”是同一个。他们都来自
mov rax, QWORD PTR [rbp-8] ;Flag2
正如我们所看到的,它们(Flag3 和 Flag4/Flag5)看起来非常相似,但是它们基于相同的“rax”工作方式却大不相同,为什么? 'lea' 指令能否区分 'rdx' 和 'edx / ecx' 并得出不同的结果?
非常感谢。
【问题讨论】:
-
谢谢,但我想我可能没有清楚地描述我的问题。我想知道为什么在 "lea rdx, [rax+4]" 之后,'rdx' 存储 'rax' 值(这意味着一个地址,如:0xeafffac0)+4,在 "lea edx, [rax+1]" 之后, 'edx' 存储 'rax' 地址值的内容(这里是:3,不再是地址)+1。
-
@ProbHunter 请随时edit 澄清您的问题。
-
@Stargateur 谢谢你,我已经对我的帖子做了一些修改,希望这次能让我的问题更清楚。这是我第一次在stackoverflow上发布问题,我的英语不够好,谢谢你的理解..
-
如果您在启用优化的情况下进行编译,要查看的指令会少很多。所有不必要的负载/存储都消失了。例如
int foo(int a) { return a+1; }will compile tolea eax, [rdi+1]/ret。 (或者使用 Windows ABI,其中第一个整数 arg 进入 RCX,lea eax, [rcx+1]。)