const修饰数组类型时,const作用于数组对象本身而非元素,真正使元素只读需将const置于类型左侧或使用const std::array;constexpr std::array更安全高效,而const_cast修改只读数组是未定义行为。

用 const 修饰数组类型,不是修饰元素值
很多人写 const int arr[5] = {1,2,3,4,5};,以为这样就“只读”了——其实这只是让数组对象本身不可重新赋值(比如不能写 arr = another_arr;),但数组元素仍可修改。真正阻止写入的写法,是把 const 放在类型左侧: const int arr[5] = {1,2,3,4,5}; 这个写法没错,但容易误解;更清晰的是写成 int const arr[5] = {1,2,3,4,5};,强调“每个 int 都是 const”。
-
const int*是指向常量的指针(内容不可改) -
int* const是常量指针(地址不可改) -
const int* const才是既不能改内容也不能改地址
推荐用 std::array + const 避免 C 风格数组陷阱
C 风格数组退化为指针后丢失长度信息,const 也容易被绕过(比如通过指针强制转换)。用 std::array 更安全:
const std::array<int, 5> arr = {1, 2, 3, 4, 5};它天然禁止修改元素,且支持 .size()、范围 for、结构化绑定等现代特性。注意:必须指定大小模板参数,不能写 std::array<int>。
- 初始化列表长度必须与模板参数一致,否则编译失败
- 如果用
auto推导,需加const:const auto arr = std::array{1,2,3}; - 不加
const的std::array仍是可写的,和原生数组一样
全局/静态只读数组优先用 constexpr
如果数组内容编译期已知,且希望放进只读段(RODATA)、零开销、支持 switch 分支或模板参数,就该用 constexpr:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
constexpr std::array<int, 3> lookup_table = {10, 20, 30};这比 const 更强:不仅运行时不可改,连编译期也无法绕过约束。C 风格写法如 static constexpr int arr[] = {1,2,3}; 也可行,但无法获取长度(除非用 sizeof(arr)/sizeof(arr[0]) 或 C++17 的 std::size)。
-
constexpr数组必须用字面量或 constexpr 函数初始化 - 函数内定义的
constexpr数组不会分配运行时内存 - VS 和 GCC 对
constexpr数组的调试符号支持不一,某些场景下变量可能“看不到”
千万别用 const_cast 去改只读数组
哪怕你看到“只读数组地址可取”,也别动 const_cast<int*>(&arr[0]) 这种念头。对 const 对象(包括 constexpr 初始化的)进行写操作,是未定义行为(UB),编译器可能优化掉你的写入、崩溃、或看似正常但后续逻辑错乱。
- 即使当前平台没报错,换编译器或开高优化级(
-O2)就可能出问题 - 某些嵌入式平台把
const数据放在 Flash 区,写操作直接触发硬件异常 - 真需要运行时可变+初始只读,该用
std::vector初始化后再冻结语义(比如封装类)
实际项目里,90% 的只读数组需求,用 constexpr std::array 就够了;剩下那 10%,往往不是“怎么定义只读”,而是“为什么非得只读”——这时候该回头看看设计是否真需要硬性约束。

















