我們或多或少都有過,或者見過將賦值表達式參與運算的情況。這通常會伴隨著一些意想不到的問題。今天我就見到了一段奇怪的代碼: 乍一看,似乎答案很明朗,按照順序運算之後,a的值是3,b的值是5.有經驗的程式員肯定會一眼看出,這裡的計算過程是一個未定義行為(Undefined behavior).在這裡簡單 ...
我們或多或少都有過,或者見過將賦值表達式參與運算的情況。這通常會伴隨著一些意想不到的問題。今天我就見到了一段奇怪的代碼:
#include<stdio.h> int main() { int a =5; int b = (a=2)+(a=3); printf("%d %d\n",a,b); return 0; }
乍一看,似乎答案很明朗,按照順序運算之後,a的值是3,b的值是5.有經驗的程式員肯定會一眼看出,這裡的計算過程是一個未定義行為(Undefined behavior).在這裡簡單來說就是:無法確定哪一個括弧里的表達式會先執行。
括弧只能改變運算符的結合律,不能改變表達式的求值順序。這個順序是取決於編譯器的。所以a的值是2還是3是不能確定的。
這段代碼在gcc(Ubuntu)下得到的結果是
3 6
而在clang(Mac)下運行的結果是
3 5
為什麼會這樣呢? 這是怎麼一回事呢?
查看它們生成的彙編代碼
gcc ... movl $5, -8(%rbp) // a=5 movl $2, -8(%rbp) // a = 2 movl $3, -8(%rbp) //a = 3 movl -8(%rbp), %eax // eax = a addl %eax, %eax //eax = eax + eax movl %eax, -4(%rbp) // b = eax ... clang ... movl $5, -8(%rbp) movl $2, -8(%rbp) // a = 2 movl $3, -8(%rbp) // a = 3 movl $5, -12(%rbp) // b = 5 ...
在gcc的理解中
a = (b=c) //會被改寫成 b=c a=b //所以對於 a = (b=c)+(d=e) //會被改寫成 b = c d = e a = b+d //當b和d為同一個值的時候,變數空間被覆用了,
在clang的理解中
a = (b=c)+(d=e) //被改寫成了 i=b=c j=d=e a=i+j //所以直接得到了賦值符號右邊表達式值之和
由此得出結論:賦值表達式的返回值為賦值符號右邊的值。
但在某些特殊情況下,使用某些編譯器可能無法得到想要的結果。所以我們應當儘量避免使用賦值表達式的值參與運算。
註意:雖然在兩個例子中,a的值都是3,但這並不意味著表達式的求值順序是從左往右的。
有關編譯器求值順序的詳細內容可以參考這篇文章