深度理解程式語言特性,不僅能精簡代碼,更能將計算前移至編譯期,消除運行期開銷。本文展示兩個經典案例:sizeof 取代 strlen、靜態初始化取代 memset。
#define MAX_STRING_TXT 255
char alarm_string[MAX_STRING_TXT];
char *tail_msg = ", List NOT Complete";
char space = MAX_STRING_TXT - strlen(tail_msg) - 1; // 每次執行都呼叫 strlen
缺點:strlen 是函數呼叫,需遍歷字串尋找 \0,且不含結束符,還需手動 -1。
#define MAX_STRING_TXT 255
char alarm_string[MAX_STRING_TXT];
static const char tail_msg[] = ", List NOT Complete"; // 靜態陣列,含 \0
char space = MAX_STRING_TXT - sizeof(tail_msg); // 編譯期常數 20 → 空間 235
優勢:
sizeof(tail_msg) 在編譯期即確定為 20(含 \0),無運行期計算。static const 使 tail_msg 落在 .rodata 段,避免棧上重複初始化。-1 易錯手動調整。
#define MAX_MSK_OCTET_LEN 64
char temp[MAX_MSK_OCTET_LEN];
memset(temp, 0, sizeof(temp)); // 每次進入函數都呼叫 memset
#define MAX_MSK_OCTET_LEN 64
char temp[MAX_MSK_OCTET_LEN] = {0}; // 編譯器生成零初始化代碼,零函數呼叫
優勢:編譯器直接生成將 64 位元組歸零的機器碼,省去 memset 函數呼叫開銷與鏈接依賴。
| 場景 | 避免寫法 | 推薦寫法 | 關鍵點 |
|---|---|---|---|
| 編譯期已知長度的字串/陣列 | strlen()、手動 -1 | sizeof(static const array) | 含結束符、編譯期常數、.rodata 段 |
| 區域陣列/結構體零初始化 | memset(ptr, 0, size) | Type var = {0}; 或 Type var{}; (C++11) | 編譯器內建零初始化、無函數呼叫 |
| 常數運算 | 運行期變數計算 | constexpr (C++11) / 宏 / enum | 前移至編譯期 |
雖然現代處理器極快,上述優化帶來的納秒級增益微乎其微,但這體現了開發者對語言機制的駕馭深度:知道何時發生什麼、記憶體佈局如何、編譯器如何優化。這正是資深工程師與初學者的分水嶺。
心法:能在編譯期解決的事,絕不拖到運行期;語言內建的機制(
sizeof、靜態初始化、constexpr),永遠優於手寫函數呼叫。