++i 與 i++ 是 C/C++ 程序員每天打交道的運算符,但其語義差異、底層實現、重載規則及性能影響常被忽視。本文從汇編生成、運算符重載語法、標準庫迭代器慣例、現代編譯器優化四個維度全景解析。
| 運算符 | 副作用(修改變量) | 表達式值 | 口訣 |
|---|---|---|---|
| ++i (前置) | 先執行 | 新值 | 先加,後用 |
| i++ (後置) | 後執行(序列點前) | 舊值 | 先用,後加 |
int i = 5; int a = ++i; // i=6, a=6 int b = i++; // i=7, b=6
// 前置 ++i mov eax, DWORD PTR [rbp-4] ; 載入 i add eax, 1 ; 加 1 mov DWORD PTR [rbp-4], eax ; 存回 i ; 表達式值在 eax 中,即新值 // 後置 i++ mov eax, DWORD PTR [rbp-4] ; 載入 i (舊值) mov edx, eax ; 保存舊值到 edx (作為表達式值) add eax, 1 ; 加 1 mov DWORD PTR [rbp-4], eax ; 存回 i ; 表達式值在 edx 中,即舊值
結論:後置多一條 mov 指令保存舊值。對基本類型,現代編譯器 -O2 會在無副作用上下文(如單獨語句 i++;)將兩者優化為相同代碼。
class Counter {
int value;
public:
// 前置:返回引用,支援鏈式 ++(++c)
Counter& operator++() {
++value;
return *this;
}
// 後置:int 佔位參數區分,返回舊值副本(值傳遞)
Counter operator++(int) {
Counter old = *this; // 複製構造
++value; // 復用前置邏輯
return old; // 返回舊值副本
}
};
++(++c)。Google C++ Style Guide:對於迭代器和模板對象,優先使用前置
++it。理由:前置避免臨時對象拷貝,效率更高。
類型 前置 後置 差異 int / pointer 1 指令 1 指令 無(優化消除臨時值) class (trivial copy) 內聯無拷貝 內聯+移動構造 可忽略 class (heavy copy) 無拷貝 深拷貝/分配內存 顯著 std::vector::iterator 指針遞增 構造臨時迭代器 微小但可測 六、最佳實踐清單
- 獨立語句:
++i;與i++;無差異,但統一用++i培養肌肉記憶。- 表達式中:必須 根據語義選擇。需要舊值用
i++,需要新值用++i。- 迭代器/智能指針/自定義類型:堅決使用前置,除非確確實實需要舊值副本。
- 重載實現:後置內部調用前置
++(*this),保證邏輯一致,減少維護負擔。- 模板代碼:始終寫
++it,因類型未知,後置可能極其昂貴。七、經典面試陷阱