在指標陣列末尾放置 NULL(或 nullptr),遍歷時以 *ptr != NULL 為終止條件,**完全消除計數器變數、長度參數、邊界檢查**,減少堆疊使用與執行期開銷。
#include
#include
using namespace std;
void PrintStrings(char* Array[]) {
while (*Array != NULL) {
cout << *Array++ << endl;
}
}
int main() {
// 陣列以 NULL 結尾
char* Test[] = { "abc", "cde", "fgh", NULL };
PrintStrings(Test);
cin.get();
return 0;
}
abc cde fgh
| 傳統 for 迴圈 | NULL 哨兵法 |
|---|---|
需傳遞長度或計算 sizeof | 陣列自帶終止資訊,無需額外參數 |
索引變數 i 佔用暫存器/堆疊 | 無索引變數,僅指標運算 |
每次迴圈需邊界比較 i < n | 單一指標解參考比較 *p != NULL |
函數簽名需 (T* arr, size_t n) | 函數簽名簡潔 (T** arr) 或 (T* arr[]) |
| 易發生 off-by-one 錯誤 | 結構上不可能越界 |
char*[]、const char*[])Base*[]、std::unique_ptr[] )int[]、double[])— 無自然哨兵值
// C++11 範圍 for + initializer_list
void PrintStrings(initializer_list List) {
for (auto s : List) cout << s << '\n';
}
PrintStrings({"abc", "cde", "fgh"});
// C++20 std::span (非擁有視圖)
void PrintStrings(span Arr) {
for (auto s : Arr) cout << s << '\n';
}
char* Arr[] = {"abc", "cde", "fgh"};
PrintStrings(Arr); // 隱含轉換,長度自動推導
// 標準容器
vector Vec = {"abc", "cde", "fgh"};
for (auto& s : Vec) cout << s << '\n';
int main(int argc, char* argv[]) {
// argv[argc] 保證為 NULL (C++ 標準)
for (char** p = argv + 1; *p != NULL; ++p) {
ProcessArgument(*p);
}
}
Test 陣列記憶體佈局:
+--------+--------+--------+--------+
| "abc" | "cde" | "fgh" | NULL | ← 連續指標陣列
+--------+--------+--------+--------+
^ ^
Test[0] Test[3] (哨兵)
遍歷指標移動:
p = Test
while (*p) { ... ; ++p; }
NULL 哨兵是 **C 風格 API 經典慣用法**,在嵌入式、系統程式、舊代碼庫中極常見。現代 C++ 專案優先使用 std::vector/std::span/範圍 for,但在**與 C API 互操作**、**極度效能敏感**、**極簡介面**場景下,哨兵法仍具優勢。