普通数组退化 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& sconst std::string s
内存身份函数内部的 s 只是外部对象的别名函数内部的 s 是一个全新的副本
拷贝次数仅隐式转换产生的 1 次临时构造转换构造 + 传参拷贝(共 1-2 次)
推荐程度极高(行业标准)不推荐(除非你打算在函数里修改它)
安全性保证不会在函数内产生冗余副本容易引发不必要的性能损耗