用 sizeof 计算数组长度仅在定义作用域内有效,因函数参数会退化为指针;推荐优先使用 std::size,或模板 constexpr array_size 获取编译期长度。

用 sizeof 计算数组长度时,必须确保传入的是原生数组而非指针
很多人写 sizeof(arr) / sizeof(arr[0]) 却得到错误结果,根本原因是:函数参数一旦声明为 T arr[] 或 T* arr,数组就退化为指针,sizeof 返回的是指针大小(通常是 8),而不是整个数组字节数。
正确做法是只在数组定义作用域内使用该技巧:
- ✅ 在定义处直接计算:
int arr[] = {1, 2, 3, 4};
size_t n = sizeof(arr) / sizeof(arr[0]); // 得到 4 - ❌ 不要在函数内对形参这么干:
void foo(int arr[]) {
size_t n = sizeof(arr) / sizeof(arr[0]); // 错!arr 是 int*,结果恒为 1(64 位下)
} - ⚠️ 字符串字面量要注意结尾
\0:sizeof("abc")是 4,不是 3
用模板推导数组大小,安全且泛型友好
模板可以捕获数组维度信息,绕过退化问题。核心是让编译器从实参类型中推导出长度 N:
- 基础写法:
template
调用
constexpr size_t array_size(T (&)[N]) {
return N;
}array_size(arr)就能拿到 N - 更实用的封装(C++17 起):
template
这和标准库
constexpr size_t size(const T (&)[N]) noexcept {
return N;
}std::size行为一致 - 注意不能用于动态分配内存(如
new int[5])或std::vector,它们不是数组类型
为什么 std::size 和 std::extent_v 都值得了解
std::size 是 C++17 引入的通用容器大小访问接口,对原生数组、std::array、std::vector 等都有效;而 std::extent_v 是编译期常量,专用于查询多维数组某维度大小:
立即学习“C++免费学习笔记(深入)”;
-
std::size(arr)→ 运行时可调用,类型安全,推荐日常使用 -
std::extent_v→ 得到 3;std::extent_v→ 得到 4;仅适用于已知维度的数组类型,不能用于变量 - 二者不冲突:前者面向值,后者面向类型;
std::extent_v在元编程中更底层、更早可用
容易被忽略的边界情况:多维数组、引用折叠、const/volatile 修饰
模板推导对 cv 限定符和引用很敏感,稍不注意就会匹配失败:
- 如果数组是
const int arr[5],模板参数需写成const T (&)[N]或更稳妥地用std::remove_cv_t处理 - 二维数组传入一维模板会失败:
int mat[2][3]类型是int[2][3],不是int[];需显式指定第二维或用嵌套模板 - 引用折叠可能导致意外:比如
T&&形参 + 数组左值,会触发引用折叠为T&,但原始模板仍需匹配T(&)[N]才能捕获尺寸 - 最省心的做法:优先用
std::size;若需编译期常量且确定是原生数组,再上模板 +constexpr









