C-字符串数组初始化 - 是否可变?

11

可能有重复:
修改C字符串常量?
指向const char的指针 vs char数组 vs std::string

我知道这个问题可能已经被问烂了,但我有点困惑,而且我在SO或谷歌上没有找到确切的答案(我不确定是否正确 - C字符串的信息太多要筛选)。此外,我已经标记了它是C++,即使我们具体说的是C风格的字符串。

在这种情况下:

char const a*  = "hello";
char const b[] = "goodbye";
我本以为“hello”和“goodbye”都是不可变字符串,因为它们来自应该会衰减为char const* 的字符串字面值。
但我已经看到,在这种特殊情况下,假设你从b数组中去掉const修饰符的话,更改“hello”将导致未定义行为,而更改“goodbye”则可以。我认为之所以在b的情况下字符串是可变的,是因为它存储在用户定义的数组中。
在这种情况下,“hello”和“goodbye”是否不同?给定这个例子,goodbye不是一个字符串字面值吗?此外,如果goodbye不是一个字符串字面值,我可以假设它没有被保存在全局内存中,在编译后对它的唯一引用是留在用户数组单元格中的引用。

间接运算符 * 位于指针标识符之前,就像这样 *a - Salvador
我不确定仅仅因为感兴趣就打标签是一个好理由。你可能对于Arduino的C++有特别的兴趣,但这个问题并不应该打上“Arduino”的标签。 - RastaJedi
C语言(几乎)是C++的一个子集,而我当时正在使用C++进行编程,尽管问题只关注语言的C特性。我不明白我引用核心语言特性与你引用完全不属于该语言的平台有何关系。 - John Humphreys
5个回答

21
第一个代码创建了一个指针,它指向字符串常量"hello",该常量很可能存储在程序的可执行映像中的不可写内存中。即使不是这样,您也不能修改该数组的内容。
第二个代码创建了一个自动数组(在堆栈上(通常如此,但这是实现定义)),并用字符串"goodbye"进行初始化。它等同于
char const b[] = {'g', 'o', 'o', 'd', 'b', 'y', 'e', 0};

因为“goodbye”是一个字符串字面量,它是char const[8]类型的,并存储在不可写的内存中,所以它是不可变的。数组b是一个自动数组,你将其标记为const,所以它也是不可变的,但如果从变量声明中删除const,就可以使数组内容可变。你只是用数组“goodbye”的内容初始化了数组的内容。
你不能修改它们两个,因为它们都是const char[]类型的,但第二个可以改为char[]类型,以便可变,而第一个则不能。
更多信息请参见此答案: https://dev59.com/21_Va4cB1Zd3GeqPYfTc#9106798

1 如R. Martinho Fernandes在评论中指出的那样,语法T x[] = ...也可以创建一个静态数组(不是自动的,而是静态的(通常在可执行映像中,但这是实现定义的)),如果它在命名空间范围内,否则它只是一个自动数组。


第二个如果在命名空间作用域声明,就可以创建静态数组。如果在函数作用域中,它只是自动的。 - R. Martinho Fernandes
我最初误读了“删除const”为“使用const_cast删除const”,而不是“从声明中删除const”。第二个显然是可以的,而第一个是未定义的。 - Mark B
@MarkB:第一个并不是未定义的。 - Lightness Races in Orbit
1
@SethCarnegie: 没错。从有效对象中弃用const不会触发UB;尝试修改一个不可变对象(通常只能在弃用const后才能这样做)会触发UB。 - Lightness Races in Orbit
非常感谢,这是一个非常清晰的答案。@LightnessRaces inOrbit - 为什么第一个不是“未定义”? - John Humphreys
显示剩余5条评论

6
一个字符串字面量的类型是char const[N];当然,这种类型的名称可能会衰变为char const*。现在:
  1. A char array may be initialised by a string literal (8.5.2/1), and since you cannot otherwise copy or assign arrays, it follows that this initialisation implements a copy. You're free to do with the new, mutable array whatever you like.

    char str[6] = "hello";
    
  2. Conversely, when initialising a pointer, you're obtaining a pointer that's the result of the string literal's immutable array type decaying.

    char const* str = "hello";
    

    There is no new array here. Just copying a pointer to the existing, immutable data.


1
我想在#2中你的意思是“(...)字符串字面值的不可变数组(...)”,对吗? - R. Martinho Fernandes
@R.MartinhoFernandes:没错 :) - Lightness Races in Orbit

0

它们是不同的。a指向一个您无法更改的字符串文字。然而,b是一个用给定字符串初始化的字符数组。假设去掉了const,那么您可以更改b的内容。


0

是的,它们是不同的。

虽然字符串字面量本身("hello""goodbye")是不可变的,但是当您访问b时,您并没有访问原始的"goodbye"。您使用的代码声明了一个完全独立的数组b,它仅使用字符串字面量"goodbye"进行初始化。您的'b'与原始字符串字面量"goodbye"完全独立。您的b是该字符串字面量的一个副本。您的b之所以不可变是因为您在其声明中明确包含了const。删除该const,您的b将变得完全可变,就像任何普通数组一样。

至于您的'a',它是直接指向原始字符串字面量"hello"的指针。它当然是不可变的。


0

这两个选项 都是 不可变的,但并不是因为它们是从字符串字面量创建的。它们是由变量声明中的 const 关键字使其不可变。

你也可以写成 char c[] = "string_c";,这样你就会创建一个名为 c 的可变副本(命名为 string_c)。

关于去除 b 的常量性的例子,这在某些情况下可能看起来有效,但在标准方面仍然是非法的。只有真正的非常量对象才能被改变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接