将字符串文字转换为 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 之前,字符串文字也不能用于初始化指针)。
我正在尝试了解指针、数组和字符串文字在 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 之前,字符串文字也不能用于初始化指针)。