看这样一段代码:
#include <iostream>
using namespace std;int main()
{const char s[] = "hello";cout << "Array content (s): " << s << endl; // 输出字符串内容cout << "Address of s (&s): " << &s << endl; // 输出整个数组的地址cout << "Address of s[0] (s): " << (void*)s << endl; // 强制转换输出地址
}
输出如下:
输出结果解释
-
Array content (s): hello
s 被解释为字符串的首地址,因此输出字符串内容。
-
Address of s (&s): 0x70fe10
&s 是整个数组的地址,和数组首地址一致,但类型不同。
-
Address of s[0] (s): 0x70fe10
s 被转换为 void*,明确输出其地址。
着重解释下为什么s 被解释为字符串的首地址,因此输出字符串内容
详细原因与机制
-
数组名的退化
-
在 C 和 C++ 中,当数组名 s 在需要指针的上下文中使用时,它会退化为一个指向数组首元素的指针(即
&s[0]
)。这种退化适用于大多数场景,例如传参或与指针运算。 -
但注意,此时数组名 s 并不是一个指针,它是一个常量,表示数组首元素的地址。
-
-
cout 的行为
-
cout 是一个流输出对象,它对不同类型的输入进行了重载:
-
如果输入的是指向 char 的指针(即
const char*
或char*
),cout 会将其解释为一个 C 风格字符串,并输出字符串内容。 -
如果输入的是其他类型的指针,例如
void*
或int*
,cout 会输出这个指针的地址。
在该代码中,s 退化为
const char*
,因此 cout 将其解释为 C 风格字符串并输出 "hello"。 -
-
字符串的存储
const char s[] = "hello";
实际上在内存中分配了一个 6 字节的数组:
地址 内容
0x70fe10 'h'
0x70fe11 'e'
0x70fe12 'l'
0x70fe13 'l'
0x70fe14 'o'
0x70fe15 '\0'
当 s 被用作 cout 的参数时,s 退化为 &s[0]
(即 0x70fe10),因此 cout 会从该地址开始,逐字节读取字符并输出,直到遇到字符串的终止符 \0。
着重解释下s和&s
-
s 是什么?
-
s 是一个字符数组,
const char s[] = "hello";
定义了一个大小为 6(包括字符串末尾的空字符 \0)的数组。 -
s 的类型是 const char[6]。
-
-
&s 是什么?
-
&s 是整个数组 s 的地址,而不是数组的第一个元素的地址(s 和 &s 在语义上不同)。
-
s 表示数组首元素(s[0])的地址,类型是
const char*
。 -
&s 表示整个数组的地址,类型是
const char (*)[6]
。
-
-
cout 如何处理 &s?
-
在 cout 中,标准输出流没有重载对
const char (*)[N]
类型的直接处理。 -
当传递 &s 时,它会退化为一个指向数组的指针。
-
通常,C++ 的 cout 会将指针解释为地址,并输出地址值(例如 0x70fe10)。
-