C 和 C++ 字符串文字弃用 char * = "stringLiteral" ;
C & C++ String Literal Deprecation for char * = "stringLiteral";
我知道语法char * = "stringLiteral";已被弃用,将来甚至可能不起作用。我不明白的是为什么。
我搜索了网络和堆栈,尽管有许多回声确认char * = "stringLiteral"; 是错误的,constchar * = "stringLiteral"; 是直截了当的,但我还没有找到关于为什么所说的语法是错误的信息。换句话说,我想知道引擎盖下的问题到底是什么。
说明我的困惑
代码段 1 - 邪恶之路(已弃用)
char* szA = "stringLiteralA"; //Works fine as expected. Auto null terminated.
std::cout << szA << std::endl;
szA = "stringLiteralB"; //Works, so change by something same length OK.
std::cout << szA << std::endl;
szA = "stringLiteralC_blahblah"; //Works, so change by something longer OK also.
std::cout << szA << std::endl;
Ouput:
stringLiteralA
stringLiteralB
stringLiteralC_blahblah
那么这里到底有什么问题呢? 似乎工作得很好。
代码段 2("确定"方式)
const char* szA = "stringLiteralA"; //Works fine as expected. Auto null term.
std::cout << szA << std::endl;
szA = "stringLiteralB"; //Works, so change by something same length OK.
std::cout << szA << std::endl;
szA = "stringLiteralC_blahblah"; //Works, so change by something longer OK also.
std::cout << szA << std::endl;
Ouput:
stringLiteralA
stringLiteralB
stringLiteralC_blahblah
也可以正常工作。没有区别。添加常量有什么意义?
代码段 3
const char* const szA = "stringLiteralA"; //Works. Auto null term.
std::cout << szA << std::endl;
szA = "stringLiteralB"; //Breaks here. Can't reasign.
我在这里只是说明,为了只阅读保护变量内容,你必须 const char*constszA = "something"; .
我看不出弃用或任何问题的意义。为什么此语法被弃用并被视为问题?
const char *
是一个指向常量(const
)char
的指针(*
)(指针定义很容易从右到左读取)。这里的重点是保护内容,因为正如标准所说,修改此类指针的内容会导致未定义的行为。
这源于这样一个事实,即通常(C/C++)编译器将整个程序中使用的字符串分组到单个内存区域中,并允许对程序不相关部分中使用的相同字符串的实例使用相同的内存位置(以最小化可执行文件大小/内存占用)。如果允许修改字符串文字,则可以通过一个更改来影响同一文本的其他不相关实例,这显然不是一个好主意。
事实上,对于大多数现代编译器(在支持内存保护的硬件上),字符串表的内存区域是只读的,因此如果您尝试修改字符串文字,程序就会崩溃。向引用字符串文字的指针添加const
会使这些错误立即显现为编译错误而不是崩溃。
顺便说一下,请注意,字符串文字可以隐式衰减为非常量char *
的事实只是对向后兼容试行标准库(当const
还不是 C 语言的一部分时编写的)的让步,如上所述,标准总是说更改字符串文字是 UB。
弃用背后的想法是帮助编译器捕获错误,否则这些错误会导致运行时崩溃。
char *hello = "hello";
strcpy(hello, "world"); // Compiles but crashes
而不是
const char *hello = "hello";
strcpy(hello, "world"); // Does not compile
这是一种相对便宜的捕获整个类非常讨厌的运行时错误的方法,因此弃用转换非常符合C++为"更好的 C"的一般理念。
此外,代码段 2 不会使指针的内容受到保护的事实无效。被覆盖的是指针本身,而不是它的内容。const char *ptr
和char * const ptr
是有区别的:前者保护内容;后者保护指针本身。两者可以组合在一起以保护指针及其内容作为const char * const ptr
。
"abc"
是一个静态数组,指向可能不可变的内存。在 C 中,修改字符串文本的内容是未定义的行为 (UB)。
但是 C99 并没有使"abc"
成为const char [n]
型对象。事实上,情况恰恰相反,为了保持与 C89(和 ANSI C)的兼容性,它指定 (§3.1.4/3):
字符串文本具有静态存储持续时间和字符类型数组,并使用给定字符初始化。
即声明
char* c = "12345";
在C 中不推荐使用,甚至到 C11 也是如此。
从 http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf,我们可以看到 C99 中的基本原理,即在保持类型char [n]
的同时,使字符串文字修改 UB
字符串文本不需要可修改。 此规范允许实现共享具有相同文本的字符串副本,将字符串文本放在只读内存中,并执行 ertain 优化。 但是,字符串文字没有const char 的类型数组,以避免指针类型检查的问题,尤其是对于库函数,因为将指向 const char 的指针分配给指向 char 的纯指针是无效的。那些坚持字符串文字应该是可修改的C89委员会成员满足于将这种做法指定为共同扩展(见§J.5.5)。
其中 C99 §J.5.5 是:
J.5.5 可写字符串文字
字符串文本是可修改的(在这种情况下,相同的字符串文本应表示不同的对象)(6.4.5)。
另一方面,由于您的代码C++,这在标准C++中实际上是错误的,因为它需要 (C++03 §2.13.4/1)
。普通字符串文字具有">n
const char
数组"类型和静态存储持续时间...
并且不应编译将const char[n]
分配给char*
。编译器警告"弃用",因为当时的现有实现允许转换(因为 C 允许),所以它进入了附件 D: 兼容性功能:
D.4 从常量字符串进行隐式转换
不推荐使用字符串文本 (4.2) 从 const 限定到非 const 限定的隐式转换。
语法是错误的,因为没有从char const*到char *的隐式转换。
字符串文字的类型在 C 和 C++ 中一直是char const *。(关于非常老的C可能是错误的。
规则中的更改与字符串文本的类型无关,但与指针类型之间允许的转换有关。
转换是一个错误,因为指向常量事物的指针是该事物是不可变的。字符串文本(已知在编译和链接时常量)可以放在只读内存段中。