将字符串文字转换为 char 数组实际上在 C++ 中如何工作?

How does conversion of string literals to char arrays actually work in C++?

我正在尝试了解指针、数组和字符串文字在 C++ 中的工作方式。

假设我们有下面一行代码:

const char* const letters[] = {"A+","A"};

如果我没理解错的话,这个声明将字母声明为指向常量字符的常量指针数组。根据我的理解,编译器实际上会将每个字符串文字转换为以空字符结尾的 char 数组,字母的每个元素实际上是指向该数组第一个元素的常量指针。

因此,例如,letters[0] 实际上是指向 "A+" 的 "A" 的指针。不过

std::cout<< letters[0];

实际上输出"A+"到标准输出。怎么会这样?特别是因为 letters[0] 是一个常量指针?

我的第二个问题与上面的声明有关:如果字符串文字实际上是 const char 数组,那么为什么下面的代码行

const char* const letters[] = {{'A','+','[=15=]'},{'A','[=15=]'}};

投掷

error: braces around scalar initializer for type ‘const char* const’ const char* const letters[] = {{'A','+','[=16=]'},{'A','[=16=]'}}; ^

谢谢!

关于你的第一个问题,letters[0]的类型是const char * const。这是一个指向字符的指针,而不是字符本身。当将指向字符的指针传递给 std::cout 时,它会将其视为以 NUL 终止的 C 字符串,并从指向的内存开始写出所有字符,直到遇到 NUL 字节。这就是输出为 A+ 的原因。您可以通过以下方式单独传递第一个字符串的第一个字符:

std::cout << letters[0][0];

指针 and/or C 字符串本身是 const 的事实在这里无关紧要,因为没有任何内容写入它们。

关于您的第二个问题,const char * const 声明了一个数组,但您在该语句的右侧提供了一个嵌套数组。如果你真的想要两个字符数组,写:

const char *const letters[] = {{'A', '+', '[=11=]'}, {'A', '[=11=]'}};

这等于你的代码形成了第一题。或者,如果您想要一个数组:

const char *const letters = {'A', '+', '[=12=]', 'A', '[=12=]'};

该行等于:

const char *const letters = "A+[=13=]A";

标准规定,就您的程序而言,字符串文字表示为静态存储持续时间为 const 个字符的数组,尾随 '[=16=]' 终止符。该标准未指定编译器如何实现此效果,仅指定您的程序可以以这种方式处理字符串文字。

因此,要么阻止修改字符串文字(例如,将字符串文字传递给期望 char * 的函数是可诊断的错误,代码将无法编译)或者 - 如果代码围绕类型系统工作修改字符串文字中的任何字符 - 涉及未定义的行为。

在您的示例中,letters[0] 的类型为 const char *,其值等于字符串文字中第一个字符的地址 "A+"

std::cout,属于 std::ostream 类型,有一个接受 const char *operator<<()。此函数由语句 std::cout << letters[0] 调用,并且该函数假定 const char * 指向 char 的零终止数组。它遍历该数组,单独输出每个字符,直到遇到尾随 '[=16=]'(不输出)。

事实是,const char * 意味着指针指向 const char,而不是指针不能更改(即 char * const)。所以可以增加指针,但不能改变它指向的值。所以,如果我们这样做

 const char *p = letters[0];

 while (*p != '[=10=]')
 {
     std::cout << *p;
     ++p;
 }

遍历字符串文字 "A+" 的字符,单独打印每个字符,并在到达 '[=16=]' 时停止(上面产生相同的可观察输出 std::cout << letters[0]) .

然而,在上面

*p = 'C';

不会编译,因为 p 的定义告诉编译器 *p 不能更改。但是,递增 p 仍然是允许的。

的原因
const char* const letters [] = {{'A','+','[=12=]'},{'A','[=12=]'}};

不编译是数组初始化程序不能用于初始化指针。例如;

const int *nums =  {1,2,3};                          // invalid
const * const int nums2 [] = {{1,2,3}, {4,5,6}};     //  invalid

都是非法的。相反,需要一个来定义数组,而不是指针。

const int nums[] = {1,2,3};
const int nums2[][3] = {{1,2,3}, {4,5,6}};

所有版本的 C 和 C++ 都禁止以这种方式初始化指针(或您的示例中的指针数组)。

从技术上讲,使用字符串文字来初始化指针的能力实际上是异常的,而不是禁止使用数组初始化指针。 C 引入字符串文字豁免的原因是历史性的(在 C 的早期,在 K&R C 之前,字符串文字也不能用于初始化指针)。