为什么在C语言中将字符串常量转换为'char *'是有效的,但在C++中是无效的?

271

C++11 标准(ISO/IEC 14882:2011)在§ C.1.1中写道:

char* p = "abc"; // valid in C, invalid in C++

对于C++来说,指向字符串字面量的指针是有害的,因为任何修改它的尝试都会导致崩溃。但是为什么在C中是有效的?

C++11也提到:

char* p = (char*)"abc"; // OK: cast added

这意味着如果在第一条语句中添加了一个转换,它就会变得有效。

为什么在C++中进行类型转换可以使第二个语句有效?这与第一个语句有什么不同吗?难道它不仍然是有害的吗?如果是这样,为什么标准说这是可以的?


6
C++11 不允许第一个。我不知道为什么 C 一开始将字符串字面值的类型设置为 char[]。第二个是伪装成 const_cast - chris
4
如果更改这个规则,就会破坏太多的遗留C代码。 - Paul R
23
在C语言中,字符串字面量比const关键字先存在,因此它们并不是const - Casey
7
C和C++允许你将几乎任何类型转换为另一种类型,但这并不意味着这些转换是有意义和安全的。 - Siyuan Ren
4
更重要的是,这个问题必须源于一些想法,即这两种语言有更多的共同之处。但错误信息证明了这是不正确的,那么为什么你认为询问这两种语言是否有共同的子集是一个好主意呢?在该共同子集中进行编程会浪费你的时间;你将依赖两个世界中最糟糕的部分。选择其中一个语言,并在需要链接不同语言的模块时使用链接器。 - autistic
显示剩余3条评论
4个回答

328

在C++03之前,你的第一个例子是有效的,但使用了一个已弃用的隐式转换--字符串字面值应该被视为char const *类型,因为你不能修改它的内容(否则会导致未定义行为)。

从C++11开始,已经被弃用的隐式转换正式被移除,所以依赖它的代码(比如你的第一个例子)将不再编译通过。

你已经注意到了一种让代码能够编译通过的方法:虽然隐式转换已经被移除,但显式转换仍然有效,所以你可以添加一个强制类型转换。然而,我不认为这是对代码的“修复”。

真正修复代码需要将指针的类型更改为正确的类型:

char const *p = "abc"; // valid and safe in either C or C++.

关于为什么在C++中允许这一点(以及在C语言中仍然允许):简单来说,这是因为有大量现有代码依赖于隐式转换,而破坏该代码(至少没有一些官方警告)显然对标准委员会来说是个不好的主意。

9
「@rullof: 它足够危险,对于那些(至少)关心可移植性的代码来说,并没有给予任何有意义的灵活性。在现代操作系统上写入字符串字面量通常会导致程序中止,因此允许代码(尝试)在此处写入不会增加任何有意义的灵活性。」 - Jerry Coffin
4
在这个回答中给出的代码片段 char const *p = "abc"; 在 C 和 C++ 中都是“有效且安全”的,而不是只在 C 或 C++ 中的一个有效和安全。 - Daniel Le
7
这两个句子的意思相同。 - Caleth
8
哦,我的上帝![半开玩笑地插入舌头] 抱歉,但在这里“or”是正确的术语。该代码可以编译为 C 或 C++,但不能同时编译为 C 和 C++。您可以选择任意一种,但必须做出选择。您不能同时拥有两者。[恢复正常舌头操作]。 - Jerry Coffin
5
不,"both/and"在这里是最清晰和正确的措辞。 "either/or" 也恰好传达了正确的意思,但从技术上讲不够清晰。仅用"or"是无法反驳的错误("A或B"不等于"A和B")。 - Apollys supports Monica
显示剩余5条评论

29

由于历史原因,在C语言中这是有效的。传统上,C语言规定字符串字面值的类型为char *而不是const char *,尽管它还特别说明您实际上不能修改它。

当您使用强制转换时,实际上是告诉编译器您比默认的类型匹配规则更清楚,并使赋值变得可行。


5
它原本是一个 char[N],现在改成了 const char[N]。它附带有大小信息。 - chris
3
在C语言中,字符串字面值的类型是char[N]而不是char*。例如,"abc"的类型是char[4] - Grijesh Chauhan

14

你也可以使用strdup

char* p = strdup("abc");
或者
char p[] = "abc";

正如这里所指出的


5
请注意,字符串 "abc" 被复制了一次,为避免内存泄漏,需要释放指针 p - cartoonist
我最初想做一个简单的转换 char* p = (char*)"abcXXXXX"; 但是我的程序崩溃了,因为我实际上稍后修改了字符串(我使用 mkstemp 替换 X)。这个解决方案起作用了。而且我没有忘记 free(p) - user7610
1
我相信提问者知道这一点;问题在于为什么C和C++在这方面有不同的规则。 - Toby Speight
请注意,strdup是POSIX标准:https://pubs.opengroup.org/onlinepubs/009604599/functions/strdup.html然而,这会影响性能。还需要注意释放重复的char*,否则会出现内存泄漏。 - KFL

7

您可以使用以下一种方式声明:

char data[] = "Testing String";


const char* data = "Testing String";

或者
char* data = (char*) "Testing String";

5
我相信提问者知道这一点;问题在于为什么C和C++在这方面有不同的规则。 - Toby Speight

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