5
votes

In order to deepen the impression about how the " (*p)++ " works, I wrote some test codes like:

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

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++ :

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     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

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.

3
thank you , but i think that i may not have described my question clearly. i want to know why after "lea rdx, [rax+4]" , 'rdx' stores 'rax' value(which means an address like: 0xeafffac0 ) +4, and after "lea edx, [rax+1]" , 'edx' stores the content of 'rax' address value (here is : 3 , not the address any more) +1.ProbHunter
@ProbHunter Feel free to edit your question to clarify it.Stargateur
@Stargateur thank you and i have make some changes to my post, hoping to make my question more clearer this time. This's the 1st time i post a question on stackoverflow and my english's not good enough, thank you for your understanding..ProbHunter
If you compile with optimization enabled, there's a lot fewer instructions to look at. All the unnecessary loads/store go away. e.g. int foo(int a) { return a+1; } will compile to lea eax, [rdi+1] / ret. (Or with the Windows ABI where the first integer arg goes in RCX, lea eax, [rcx+1].)Peter Cordes

3 Answers

5
votes

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'.

No, you are mistaken. lea edx, [rax+1] doesn't change rax. rax is already 3 before the lea instruction is evaluated.

But! the point is that 'rax' in these two statments are the same one. They're all from mov rax, QWORD PTR [rbp-8]

No, you are mistaken. rax is being set by 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).

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 |
                                         ; +----+----+----+----+  +----+----+----+----+
1
votes

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.

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++;

   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.

-1
votes

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].

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