同一序列點內,對同一標量對象多次修改,或修改後讀取值(非為了確定新值),行為未定義(UB)。
序列點:完整表達式末尾、`&&`/`||`/`?:`/`,`(逗號運算符)、函數調用前後。
#define BAND1(x) (((x) >= 5) && ((x) <= 10) ? (x) : 0)
#define BAND2(x) (((x) >= 5) && ((x) <= 5) ? (x) : 0)
int i = 5;
BAND1(++i); // 宏展開:((++i)>=5) && ((++i)<=10) ? (++i) : 0
// i 被修改 3 次!結果:GCC 下返回 8,i 變 8
// BAND2(++i):條件 (x>=5)&&(x<=5) 只在 x==5 真,但 x 被修改多次 → UB
教訓:宏參數若帶副作用,展開後可能被求值多次。絕不在宏參數中使用 `++`/`--`/賦值/函數調用。
`&&` `||` 引入序列點:左側求值完成(含副作用)→ 序列點 → 右側求值。
int i = 4, a;
a = (i++ >= 5); // i=4 -> 假(0),然後 i=5
a = (i++ >= 5) + (i++ >= 5);
// 第一項:i=5 -> 假(0),i=6;第二項:i=6 -> 真(1),i=7;a=0+1=1
a = (i++ >= 4) && (i++ >= 5);
// 第一項:i=7 -> 真(1),i=8;短路?否,繼續;第二項:i=8 -> 真(1),i=9;a=1
| 表達式 | 副作用順序 | 原因 |
|---|---|---|
| `(i++) + (i++)` | 未指定順序,UB | `+` 無序列點,兩側求值順序未定 |
| `(i++) && (i++)` | 左→右,有序列點 | `&&` 保證左側完整求值後再右側 |
後綴 `++`、前綴 `++`、解引用 `*` 同優先級,**右結合**。
char *p = "hello";
char c;
c = *p++; // 等價 *(p++):取 *p('h'),p 指向 'e'
c = *++p; // 等價 *(++p):p 指向 'l',取 *p('l')
c = ++*p; // 等價 ++(*p):*p('l') -> 'm',取值 'm'
| 表達式 | 分組 | 操作順序 | 結果 |
|---|---|---|---|
| `*p++` | `*(p++)` | 1.p++ 2.*old_p | 舊值,p 後移 |
| `*++p` | `*(++p)` | 1.++p 2.*new_p | 新值,p 先移 |
| `++*p` | `++(*p)` | 1.*p 2.++value | 修改所指值,p 不動 |
| `(*p)++` | `(*p)++` | 1.*p 2.++value | 同上,返回舊值 |
int i = 1, a;
a = (i--) && (i); // i=1->真,i=0;右側 i=0->假;a=0,i=0
a = (i) && (i--); // i=0->假,短路,i 不遞減;a=0,i=0
a = (i) && (i--) && (i); // i=0->假,短路;a=0
a = (i--) + (i); // UB:+ 無序列點,但 GCC:取舊值 0+0=0,i=-1?實測 i=0