众所周知,在C++中字符串文字是不可变的,修改字符串文字的结果是未定义的。例如:
char * str = "Hello!";
str[1] = 'a';
这将导致未定义的行为。
此外,字符串字面量存储在静态内存中。因此它们在整个程序执行期间存在。我想知道为什么字符串字面量具有这样的属性。
众所周知,在C++中字符串文字是不可变的,修改字符串文字的结果是未定义的。例如:
char * str = "Hello!";
str[1] = 'a';
这将导致未定义的行为。
此外,字符串字面量存储在静态内存中。因此它们在整个程序执行期间存在。我想知道为什么字符串字面量具有这样的属性。
有几个不同的原因。
其中一个是为了允许将字符串字面量存储在只读内存中(正如其他人已经提到的那样)。
另一个是允许合并字符串字面量。如果一个程序在多个不同的地方使用相同的字符串字面量,允许(但不一定要求)编译器将它们合并起来很好,这样你就可以得到指向同一片内存的多个指针,而不是每个占用单独的内存块。当两个字符串字面量不一定相同但具有相同的结尾时,也可以应用此功能:
char *foo = "long string";
char *bar = "string";
在这种情况下,如果我数对了的话,bar
可能是foo+5
。
在这两种情况中,如果允许修改字符串常量,它可能会修改具有相同内容的其他字符串常量。与此同时,实际上没有必要强制执行任何一种方式--拥有足够多的字符串常量可以重叠的情况非常罕见,大多数人可能希望编译器运行得更慢,仅仅为了节省(也许)几十个字节的内存。
在第一个标准编写时,已经有编译器使用了这三种技术(可能还有其他几种)。由于无法描述通过修改字符串常量获得的一种行为,而且显然没有人认为这是一个需要支持的重要功能,他们做出了明智的决定:即试图这样做将导致未定义的行为。
修改字面量是未定义行为,因为标准规定了这一点。标准之所以这样规定是为了允许编译器将字面量放入只读内存中。它之所以这样做有多种原因,其中之一是允许编译器进行优化,即在源代码中重复多次的字面量只存储一个实例。
char *str="Hello";
.../* some code, but str and str[...] are not modified */
printf("%s world\n", str);
很自然地,你会认为你 知道 将要被打印的内容,
因为在初始化和使用之间,str
(以及其内容)没有在特定位置被修改。
然而,如果字符串字面值是可写的,你就不再 知道了: 在这段代码或深度嵌套的函数调用中,str[0] 可能会在稍后被覆盖, 并且当代码再次运行时,
char *str="Hello";
str
内容的任何内容。如我们所预期的那样,此初始化被实现为将链接时间中已知的地址移动到str
的位置。它不检查str
是否包含“Hello”,也不分配新副本。但是,我们理解这段代码是将str
重置为“Hello”。很难克服这种自然理解,并且很难推断代码在没有保证的情况下。当您看到像x + 14
这样的表达式时,如果您必须考虑14可能在其他代码中被覆盖,从而变为42会怎么样?字符串也有同样的问题。
这就是禁止修改字符串文字的原因,无论是在标准中(没有要求早期检测故障)还是在实际目标平台中(提供检测潜在错误的奖励)。
我相信许多试图解释这个问题的尝试都受到了最糟糕的循环推理的影响。标准禁止对字面量进行编写,因为编译器可以合并字符串,或者它们可以被放置在只读内存中。它们被放置在只读内存中是为了捕捉标准的违规行为。合并字面量是有效的,因为标准禁止...这是你要求的一种解释吗?C++
,我不确定它在非常量char*
的隐式转换方面的当前状态:如果是一种转换,它是否已过时?我希望其他回答能够完全解决这个问题。因为我们在谈论其他语言,在这里让我提到纯C。在这里,字符串字面值不是const,一个等效的问题是为什么我不能修改字符串字面值(有更多经验的人会问如果我不能修改它们,为什么字符串字面值不是const?)。然而,尽管存在这种差异,上述推理对C也是完全适用的。int arr[] = {1, 2, 3};
? - AlwaysLearningconst
的,因为你不允许修改它们。在标准C中,它们也应该是const
的,但是当const
被引入到C中时,有很多像char* p = "somethin";
这样的代码,如果使它们成为常量,就会出现问题,这被认为是不可接受的。(C++委员会选择了不同的解决方案,使用了一个弃用的隐式转换来允许上述情况。)void
mutate(char* p)
{
static char c = 'a';
*p = a ++;
}
mutate( "hello" ); // Can't trust what is written, can you.
F(4)
可能会使用几乎任何整数值调用F
。就像C委员会修复C中的字符串字面量一样,Fortran委员会也进行了修复。char * str = "Hello!";
如果标准委员会将文本字面值设为const,所有这些程序都将无法编译。所以他们做了一个妥协。文本字面值是官方的const char[]
,但它们具有静默的隐式转换到char*
。