普通数组退化 vs. 字符串字面量退化#
1. 本质上的统一:都是“数组退化” (Array Decay)#
在 C++ 编译器眼中,无论是你定义的数组还是手写的字符串,它们的原始身份都是固定长度的数组:
int arr[5]的原始类型是:int[5]"abc"的原始类型是:const char[4](包含隐藏的\0)
退化规则:当它们作为参数传递给接收指针的函数时,会执行相同的操作——丢失长度信息,仅留下首元素的内存地址。
2. 它们的三大核心区别#
虽然底层逻辑相同,但在内存布局和使用习惯上,它们有显著差异:
| 维度 | 普通数组 (如 int[]) | 字符串字面量 (如 "abc") |
|---|---|---|
| 结束标志 | 无。必须额外传递 size 参数。 | 有。自带“哨兵” \0 (Null Terminator)。 |
| 内存区域 | 通常在 栈 (Stack),可读可写。 | 在 只读数据区 (Static Data),不可修改。 |
| 退化后的类型 | int[5] $\rightarrow$ int* | const char[4] $\rightarrow$ const char* |
3. 详细拆解#
区别 A:哨兵机制 (The Terminator)#
- 普通数组:函数拿到
int*后,完全不知道哪里是数组的终点。如果你不传size,函数极易越界。 - 字符串:虽然也退化成了
const char*,但函数可以通过不断向后寻找\0来确定长度(这就是strlen的工作原理)。
区别 B:内存安全性#
- 普通数组:
int arr[] = {1, 2, 3};
arr[0] = 9; // 完全合法,你拥有这块内存的写权限
- 字符串字面量:通常在只读数据区(Static/Data Segment)。它虽然退化成了
const char*,但你绝对不能尝试通过指针去修改它,否则程序会直接崩溃。
区别 C:退化后的类型#
int a[5]$\rightarrow$ 退化为int*"abc"(即const char[4]) $\rightarrow$ 退化为const char*(注意多了一个 const,因为字面量不可修改)。
string及string引用传参对比#
void test1(const std::string& s ) {}
void test2(const std::string s ) {}
int main()
{
test1("abc");
test2("abc");
}| 特性 | const std::string& s | const std::string s |
|---|---|---|
| 内存身份 | 函数内部的 s 只是外部对象的别名 | 函数内部的 s 是一个全新的副本 |
| 拷贝次数 | 仅隐式转换产生的 1 次临时构造 | 转换构造 + 传参拷贝(共 1-2 次) |
| 推荐程度 | 极高(行业标准) | 不推荐(除非你打算在函数里修改它) |
| 安全性 | 保证不会在函数内产生冗余副本 | 容易引发不必要的性能损耗 |