((*strDest++ = *strSrc++) != '\0');
這行經典字符串拷貝循環條件,蘊含了C語言表達式求值的精髓與陷阱。網上眾說紛紛:有稱其「緊湊高效」,有嘆「晦澀難維護」,更有警告「存在未定義行為」。本文憑藉優先級與結合性兩大核心規則,配合表達式值 vs 變量值的關鍵區分,為你徹底拆解其執行步驟,並建立分析任意複雜表達式的通用方法論。
優先級高的運算符先結合,其操作數優先求值。如 * (解引用) > ++ (後綴) > = (賦值) > != (比較)。
同優先級運算符,左結合 (如 + - * /) 先算左邊,右結合 (如 = += -=) 先算右邊。
i++ 的表達式值是 i 的舊值;i 的變量值隨後變為 i+1。a = b 的表達式值是 a 的新值 (即 b 的值)。;、&& || ? :、函數調用參數求值完畢、函數返回前等。((*strDest++ = *strSrc++) != '\0')!= 比較() 最高優先級,整個表達式結構為:(左邊子表達式) != '\0'。任務:先求左邊值,再與 '\0' 比較,產生布爾結果 (0/1)。
*strDest++ = *strSrc++= 優先級低,結構為 (*strDest++) = (*strSrc++)。賦值右結合,故先求右邊 *strSrc++,再求左邊 *strDest++,最後執行賦值。
*strSrc++++ (後綴) 優先級 > * (解引用) → 等價 *(strSrc++)strSrc++:表達式值 = strSrc舊值,副作用:strSrc 指向下一字符 (何時生效?下一序列點前)* 解引用:取 strSrc舊值 地址處的字符值*strDest++同理:*(strDest++) → 表達式值為 strDest舊值,副作用 strDest 後移,解引用得目標地址。
將右邊字符值寫入左邊地址。賦值表達式值 = 左邊新值 = 寫入的字符值。
!= 比較用賦值表達式值 (剛寫入的字符) 與 '\0' 比較,得布爾結果。
*strDest = *strSrcstrDest++, strSrc++'\0'結論:該表達式行為良定 (無未定義行為),因修改不同對象 (strDest/strSrc/目標內存),且讀取與修改同一對象 (strSrc++ 讀舊值寫新值) 有序列點隔離 (賦值運算符兩側求值無序,但各自內部有序)。
| 代碼模式 | 問題 | 標準分類 |
|---|---|---|
i = i++; | 同一變量 i 被修改兩次 (賦值、後綴++),無序列點隔離 | 未定義行為 (UB) |
a[i] = i++; | 修改 i 同時讀取 i 計算下標,無序列點 | 未定義行為 |
printf("%d %d", i++, i++); | 參數求值順序未規定,兩次修改 i 相互干擾 | 未指定行為 (值不定,但無UB) |
*p++ = *q++; | 修改 p/q、讀寫不同內存,互不干擾 | 良定行為 ✅ |
strcpy 實現);業務邏輯建議展開。gcc -Wall -Wextra -Wsequence-point 可捕獲部分序列點違規。掌握 優先級 + 結合性 + 序列點 + 表達式值/變量值區分,即可破解任意C表達式求值謎題。((*strDest++ = *strSrc++) != '\0') 是良定、高效、地道的C語言慣用語,理解它,你就理解了C表達式求值的核心機制。