为了加深对"(*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);
}
Run Code Online (Sandbox Code Playgroud)
输出为: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)
Run Code Online (Sandbox Code Playgroud)
请注意"Flagx"线让我感到困惑.
从上面,我们知道
当指针: int*q = p ++:
lea rdx, [rax+4] ;Flag3
Run Code Online (Sandbox Code Playgroud)
在这里,'lea'似乎在'rax'和+4中读取了addr值存储.然后转到'rdx'.
while: int c = a ++或int d = c ++:
lea edx, [rax+1] ;Flag4/Flag5
Run Code Online (Sandbox Code Playgroud)
在这里,'lea'似乎在'rax'(这里是3)中读取addr值存储的内容,而+1,来到4并传递给'edx'.
但!关键是这两个陈述中的' rax '是相同的.他们全都来自
mov rax, QWORD PTR [rbp-8] ;Flag2
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,它们(Flag3和Flag4/Flag5)看起来非常相似,但基于相同的'rax'它们的工作方式却截然不同,怎么样?'lea'指令可以区分'rdx'和'edx/ecx'并得出不同的结果吗?
非常感谢你.
在这里,'lea'似乎在'rax'(这里是3)中读取addr值存储的内容,而+1,来到4并传递给'edx'.
不,你错了.lea edx, [rax+1]不会改变rax.rax已经3在lea评估指令之前.
但!关键是这两个陈述中的'rax'是相同的.他们全都来自
mov rax, QWORD PTR [rbp-8]
不,你错了.rax正在设定mov eax, DWORD PTR [rbp-36].
可以使用不同的名称来引用通用寄存器的不同部分.
64 32 16 8 0
| | | | |
v v v v v
+----+----+----+----+----+----+----+----+
| | | | | | | | |
+----+----+----+----+----+----+----+----+
|<------------------------------------->| rax
|<----------------->| eax
|<------->| ax
|<-->| ah
|<-->| al
Run Code Online (Sandbox Code Playgroud)
这意味着当你写信时eax,你也写到了下半部分rax(上半部分被归零).
所以,
; rax eax rdx edx
; q = p++ ; +----+----+----+----+ +----+----+----+----+
A1 mov rax, QWORD PTR [rbp-8] ; | p | | ??? |
A2 lea rdx, [rax+4] ; | p | | p+4 |
A3 mov QWORD PTR [rbp-8], rdx ; | p | | p+4 |
A4 mov QWORD PTR [rbp-24], rax ; | p | | p+4 |
; c = a++ ; | p | | p+4 |
B1 mov eax, DWORD PTR [rbp-40] ; | 0 | a | | p+4 |
B2 lea edx, [rax+1] ; | 0 | a | | 0 | a+1 |
B3 mov DWORD PTR [rbp-40], edx ; | 0 | a | | 0 | a+1 |
B4 mov DWORD PTR [rbp-28], eax ; | 0 | a | | 0 | a+1 |
; +----+----+----+----+ +----+----+----+----+
Run Code Online (Sandbox Code Playgroud)
cse*_*cse -2
int *q = p++地址指针依次递增。如您所知,int 的大小是 4 个字节,而 int 的大小是指针变量的大小,因此在汇编代码中您可以看到lea rdx, [rax+4]。
但int c = a++变量的值a会逐渐增加。所以在汇编代码中你可以看到lea edx, [rax+1].
注意:的大小int可能因编译器而异。但根据GCC基于编译器和你的情况int是 4 字节长