C++ 函數參數聲明為數組類型時,**編譯器隱式轉換為指針類型**。以下三個聲明**完全等價**:
void func(int* arr); // 指針
void func(int arr[]); // 數組 (無長度)
void func(int arr[10]); // 數組 (有長度,但被忽略!)
本質:調用時數組表達式退化為指向首元素的指針 (&arr[0])。**長度信息在函數簽名中完全丟失**。
void modify(int arr[]) { arr[0] = 99; } // 修改原數組首元素
int main() {
int a[3] = {1,2,3};
modify(a); // a[0] 變為 99
}
對策:若不想修改,參數聲明 const int arr[] 或 const int* arr。
void unsafe(int arr[10]) {
// 編譯器不檢查實參長度!
arr[9] = 0; // 若實參只有 3 元素 → 越界寫入,未定義行為
}
int main() {
int small[3] = {0};
unsafe(small); // 編譯通過,運行崩潰/靜默破壞
int* ptr = new int[5];
unsafe(ptr); // 合法但危險
delete[] ptr;
}
| 機制 | 代碼 | 優點 | 缺點 |
|---|---|---|---|
| 額外長度參數 | void func(int arr[], size_t n) | 簡單、C兼容 | 易參數不匹配、無類型安全 |
| 哨兵值 (C字符串) | void func(const char* str) (以 \0 結尾) | 無需長度參數 | 僅適用字符串、不可含嵌入空字符 |
// 參數為「10個int的數組的引用」
void func(int (&arr)[10]) {
// 編譯器強制實參必須是 int[10]!
static_assert(std::extent_v == 10); // 編譯期獲取長度
}
int main() {
int a[10] = {};
func(a); // OK
int b[5] = {};
// func(b); // 編譯錯誤:類型不匹配 (int[5] vs int[10])
int* p = a;
// func(p); // 編譯錯誤:指針非數組
}
優勢:編譯期強制長度匹配、函數內可用 sizeof(arr) 獲取完整大小、std::size(arr) (C++17) 獲取長度。
#include
#include
void func(std::array& arr) {
std::sort(arr.begin(), arr.end()); // 迭代器、算法全支持
std::cout << arr.size(); // 10
}
int main() {
std::array arr = {1,2,3,4,5,6,7,8,9,0};
func(arr); // 傳引用,零開銷
// std::array arr2; func(arr2); // 編譯錯誤:類型不同
}
特點:值語義 (可拷貝/賦值)、棧分配、長度是類型一部分、標準容器接口。
#include
#include // C++20
// 接受任意連續序列 (array, vector, C數組, new[])
void modern_func(std::span s) {
std::cout << "Size: " << s.size() << std::endl;
for (int v : s) std::cout << v << ' ';
}
int main() {
int carr[10] = {};
std::vector vec = {1,2,3};
std::array arr = {};
modern_func(carr); // OK,自動推導長度 10
modern_func(vec); // OK,長度 3
modern_func(arr); // OK,長度 5
modern_func({1,2,3,4}); // OK,臨時初始化列表 (C++20)
}
std::span 是現代 C++ 接受連續序列的統一標準參數類型:無所有權、輕量視圖、保留長度、兼容所有連續容器。
void f(int arr[]) 等同 void f(int*),長度丟失、不安全。std::array (有所有權) 或 T (&arr)[N] (引用現有數組)。std::span (C++20, 首選視圖) 或 std::vector& (需所有權/擴容)。span 構造 {ptr, len},內部用 span。std::ranges (C++20)。