In order to deepen the impression about how the " (*p)++ " works, I wrote some test codes like:
为了加深对“(*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);
}
OUTPUT IS: a = 5, b = 3, c = 5, d = 4, p = 0xc6dc3490, q = 0xc6dc348c
输出为:a = 5, b = 3, c = 5, d = 4, p = 0xc6dc3490, q = 0xc6dc348c。
But my question is about the assembly (codes are in orders and not off and on):
但我的问题是关于程序集(代码是在订单中,而不是断断续续的):
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)
Please pay attention to the "Flagx" lines which make me confused.
From above , we know that
when pointer: int *q = p++ :
请注意那些让我感到困惑的“Flagx”线。从上面,我们知道指针:int *q = p++:
lea rdx, [rax+4] ;Flag3
Here, 'lea' seems to read the addr value store in 'rax' and +4. then pass to 'rdx'.
while: int c = a++ or int d = c++ :
在这里,“lea”似乎在“rax”和+4中读取addr值存储。然后通过“黑索今”。while: int c = a++或int d = c++:
lea edx, [rax+1] ;Flag4/Flag5
Here, 'lea' seems to read the content of the addr value store in 'rax' (which is 3 here), and +1, come to 4 and pass to 'edx'.
But! the point is that 'rax' in these two statments are the same one. They're all from
在这里,“lea”似乎阅读了“rax”(这里是3)的addr值存储的内容,然后+1,到4,然后传递到“edx”。但是!关键是这两个状态中的“rax”是同一个。他们都从
mov rax, QWORD PTR [rbp-8] ;Flag2
As we can see, they ( Flag3 and Flag4/Flag5 ) look very similar, but they work very differently base on a same 'rax', how come ? Can the 'lea' instruction distinguish between 'rdx' and 'edx / ecx' and turn out different results ?
Thank you very much.
正如我们所看到的,它们(Flag3和Flag4/Flag5)看起来非常相似,但它们在同一个“rax”上的工作方式非常不同,为什么呢?“lea”指令能区分“rdx”和“edx / ecx”,结果会有不同吗?非常感谢。
3 个解决方案
#1
5
Here, 'lea' seems to read the content of the addr value store in 'rax' (which is 3 here), and +1, come to 4 and pass to 'edx'.
在这里,“lea”似乎阅读了“rax”(这里是3)的addr值存储的内容,然后+1,到4,然后传递到“edx”。
No, you are mistaken. lea edx, [rax+1]
doesn't change rax
. rax
is already 3
before the lea
instruction is evaluated.
不,你是错误的。lea edx, [rax+1]不会改变rax。rax在lea指令被评估之前已经是3了。
But! the point is that 'rax' in these two statments are the same one. They're all from
mov rax, QWORD PTR [rbp-8]
但是!关键是这两个状态中的“rax”是同一个。它们都来自mov rax, QWORD PTR [rbp-8]
No, you are mistaken. rax
is being set by mov eax, DWORD PTR [rbp-36]
.
不,你是错误的。rax是由mov eax, DWORD PTR [rbp-36]设置的。
Different parts of the general purpose registers can be referenced using different names.
通用寄存器的不同部分可以使用不同的名称引用。
64 32 16 8 0
| | | | |
v v v v v
+----+----+----+----+----+----+----+----+
| | | | | | | | |
+----+----+----+----+----+----+----+----+
|<------------------------------------->| rax
|<----------------->| eax
|<------->| ax
|<-->| ah
|<-->| al
This means that when you write to eax
, you are also writing to the bottom half of rax
(and the top half gets zeroed).
这意味着,当你给eax写信的时候,你也在写到rax的下半部分(上半部分得到了0)。
So,
所以,
; 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 |
; +----+----+----+----+ +----+----+----+----+
#2
1
p
and q
are pointers to int
and the size of an int
is 4 on your platform. So incrementing p
actually increments its value by 4.
p和q是指向int的指针,而int的大小在您的平台上是4。所以递增p实际上是增加了4。
int *q = p++;
mov rax, QWORD PTR [rbp-8] ; rax = p
lea rdx, [rax+4] ; same as rdx = rax + 4
mov QWORD PTR [rbp-8], rdx ; p = rdx
mov QWORD PTR [rbp-24], rax ; q = rax
c
is an int
. So incrementing a
actually just increments its value by 1.
c是一个整数,所以递增a的值是1。
c = a++;
mov eax, DWORD PTR [rbp-40] ; rax = a (yes modifying eax actually modifies rax)
lea edx, [rax+1] ; same as edx = rax + 1
mov DWORD PTR [rbp-40], edx ; a = edx
mov DWORD PTR [rbp-28], eax ; c = eax (eax still contains the inital value of a)
More details about the LEA
instructions here.
这里是关于LEA指令的更多细节。
#3
-1
In line int *q = p++
the address pointer is getting incremented. As you know the size of int is 4 bytes and size of int is size of pointer variable, so in assembly code you can see lea rdx, [rax+4]
.
But in line int c = a++
the value of variable a
is getting incremented. So in assembly code you can see lea edx, [rax+1]
.
在line int *q = p++中,地址指针不断增加。您知道int的大小是4字节,int的大小是指针变量的大小,所以在汇编代码中您可以看到lea rdx, [rax+4]。但是,在直线int c = a++中,变量a的值不断增加。在汇编代码中,可以看到lea edx, [rax+1]。
Note: Size of int
may vary from compiler to compiler. But as per GCC
based compilers and in your case int
is 4 byte long
注意:int的大小可能因编译器而异。但是根据GCC的编译器,在你的情况下,int是4字节长。
#1
5
Here, 'lea' seems to read the content of the addr value store in 'rax' (which is 3 here), and +1, come to 4 and pass to 'edx'.
在这里,“lea”似乎阅读了“rax”(这里是3)的addr值存储的内容,然后+1,到4,然后传递到“edx”。
No, you are mistaken. lea edx, [rax+1]
doesn't change rax
. rax
is already 3
before the lea
instruction is evaluated.
不,你是错误的。lea edx, [rax+1]不会改变rax。rax在lea指令被评估之前已经是3了。
But! the point is that 'rax' in these two statments are the same one. They're all from
mov rax, QWORD PTR [rbp-8]
但是!关键是这两个状态中的“rax”是同一个。它们都来自mov rax, QWORD PTR [rbp-8]
No, you are mistaken. rax
is being set by mov eax, DWORD PTR [rbp-36]
.
不,你是错误的。rax是由mov eax, DWORD PTR [rbp-36]设置的。
Different parts of the general purpose registers can be referenced using different names.
通用寄存器的不同部分可以使用不同的名称引用。
64 32 16 8 0
| | | | |
v v v v v
+----+----+----+----+----+----+----+----+
| | | | | | | | |
+----+----+----+----+----+----+----+----+
|<------------------------------------->| rax
|<----------------->| eax
|<------->| ax
|<-->| ah
|<-->| al
This means that when you write to eax
, you are also writing to the bottom half of rax
(and the top half gets zeroed).
这意味着,当你给eax写信的时候,你也在写到rax的下半部分(上半部分得到了0)。
So,
所以,
; 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 |
; +----+----+----+----+ +----+----+----+----+
#2
1
p
and q
are pointers to int
and the size of an int
is 4 on your platform. So incrementing p
actually increments its value by 4.
p和q是指向int的指针,而int的大小在您的平台上是4。所以递增p实际上是增加了4。
int *q = p++;
mov rax, QWORD PTR [rbp-8] ; rax = p
lea rdx, [rax+4] ; same as rdx = rax + 4
mov QWORD PTR [rbp-8], rdx ; p = rdx
mov QWORD PTR [rbp-24], rax ; q = rax
c
is an int
. So incrementing a
actually just increments its value by 1.
c是一个整数,所以递增a的值是1。
c = a++;
mov eax, DWORD PTR [rbp-40] ; rax = a (yes modifying eax actually modifies rax)
lea edx, [rax+1] ; same as edx = rax + 1
mov DWORD PTR [rbp-40], edx ; a = edx
mov DWORD PTR [rbp-28], eax ; c = eax (eax still contains the inital value of a)
More details about the LEA
instructions here.
这里是关于LEA指令的更多细节。
#3
-1
In line int *q = p++
the address pointer is getting incremented. As you know the size of int is 4 bytes and size of int is size of pointer variable, so in assembly code you can see lea rdx, [rax+4]
.
But in line int c = a++
the value of variable a
is getting incremented. So in assembly code you can see lea edx, [rax+1]
.
在line int *q = p++中,地址指针不断增加。您知道int的大小是4字节,int的大小是指针变量的大小,所以在汇编代码中您可以看到lea rdx, [rax+4]。但是,在直线int c = a++中,变量a的值不断增加。在汇编代码中,可以看到lea edx, [rax+1]。
Note: Size of int
may vary from compiler to compiler. But as per GCC
based compilers and in your case int
is 4 byte long
注意:int的大小可能因编译器而异。但是根据GCC的编译器,在你的情况下,int是4字节长。