em có thể lên https://godbolt.org/ xem nó biên dịch ra mã máy thế nào
int function()
{
int i = 1;
return ++i
+ ++i
+ i;
}
x86-64 GCC -O0 nó biên dịch ra
function:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
add DWORD PTR [rbp-4], 1
add DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4]
lea edx, [rax+rax]
mov eax, DWORD PTR [rbp-4]
add eax, edx
pop rbp
ret
-
mov DWORD PTR [rbp-4], 1
nghĩa là gán i = 1
2 dòng add tiếp theo nghĩa là tăng giá trị i lên 1, 2 lần.
-
lea edx, [rax+rax]
chả hiểu nó làm cái gì @_@ nhưng có lẽ là tính phép cộng đầu tiên, rồi gán vào edx
-
mov eax, DWORD PTR [rbp-4]
nó lại vác i lên eax
- rồi sau cùng tính edx + eax
==> tăng i thêm 1 đơn vị 2 lần, i = 3, rồi tính edx = 3 + 3 = 6, rồi lấy edx + (eax = i), vậy giá trị trả về là 9.
vì side effect của ++i
và ++i
ko được sequence, nên nó thích tính ở đâu thì tính, ở đây nó phang 2 cái ++ trước khi lấy giá trị của i
cuối cùng.
so với Clang:
function: # @function
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], 1
mov eax, dword ptr [rbp - 4]
add eax, 1
mov dword ptr [rbp - 4], eax
mov ecx, dword ptr [rbp - 4]
add ecx, 1
mov dword ptr [rbp - 4], ecx
add eax, ecx
add eax, dword ptr [rbp - 4]
pop rbp
ret
ở đây Clang có cảnh báo -Wunsequenced
, to mồm hơn và cũng tốt bụng hơn GCC
Clang lại đánh giá kiểu khác:
- gán i = 1
- vác i lên eax, tăng eax lên 1 đơn vị (eax = 2), vác eax xuống i lại (i = 2)
- vác i lên ecx, tăng ecx lên 1 đơn vị (ecx = 3), vác ecx xuống i lại (i = 3)
- cộng ecx vào eax: eax += 3 (eax = 5)
- cuối cùng là cộng i vào eax: eax += 3 (eax = 8)
kết quả trả về lại là 8. Vì side effects của ++i ko được sequence nên nó thích đánh giá ở đâu thì nó đánh, Clang đánh theo thứ tự trái sang phải, còn ông GCC thì vác lên phía đầu tiên
(++i) thứ nhất gồm 2 operation là (a1 = gán i=i+1, a2 = trả về giá trị của i), a1 đi trước a2
(++i) thứ hai gồm 2 operation là (b1 = gán i=i+1, b2 = trả về giá trị của i), b1 đi trước b2
(i) gồm 1 operation là (c = trả về giá trị của i)
vì chúng nó ko sequence, GCC được phép đánh giá đan xen là a1, b1, a2, b2, c (a1 vẫn đứng trước a2, b1 vẫn đứng trước b2) => 3 + 3 + 3. Clang cũng được phép đánh giá đan xen nhưng anh chơi “đẹp” đánh giá theo thứ tự trái sang phải a1, a2, b1, b2, c => 2 + 3 + 3.