0

我了解语法char * = "stringLiteral"; 已被弃用,将来甚至可能无法使用。我不明白的是WHY

我搜索了网络和堆栈,虽然有很多回声确认 char * = "stringLiteral"; 是错误的,const char * = "stringLiteral"; 是正确的,我还没有找到关于为什么说语法错误的信息。换句话说,我想知道问题到底是什么。

说明我的困惑

代码段 1 - EVIL WAY(已弃用)

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(“OK”方式)

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

也可以正常工作。没有不同。添加const有什么意义?

代码段 3

const char* const szA = "stringLiteralA";  //Works. Auto null term.
std::cout << szA << std::endl;    
szA = "stringLiteralB";           //Breaks here. Can't reasign.

我只是在这里说明,为了只读保护变量内容,您必须 const char* const szA = "something"; .

我没有看到弃用或任何问题的意义。为什么这种语法被弃用并被认为是一个问题?

4

4 回答 4

6

const char *是指向*常量 ( ) 的指针 ( const) char(指针定义很容易从右到左读取)。这里的重点是保护内容,因为正如标准所说,修改此类指针的内容会导致未定义的行为。

这源于以下事实:通常 (C/C++) 编译器将整个程序中使用的字符串分组在一个内存区域中,并且允许对在程序的不相关部分中使用的相同字符串的实例使用相同的内存位置(以最小化可执行文件大小/内存占用)。如果允许修改字符串文字,您可以通过一次更改来影响相同文字的其他不相关实例,这显然不是一个好主意。

事实上,对于大多数现代编译器(在支持内存保护的硬件上),字符串表的内存区域是只读的,因此如果您尝试修改字符串文字,您的程序就会崩溃。添加const到引用字符串文字的指针会使这些错误立即显现为编译错误而不是崩溃。

顺便说一句,请注意,字符串文字可以隐式衰减为非常量的事实char *只是对与标准前库的向后兼容性的让步(const当时还不是 C 语言的一部分),如上所述标准总是说改变字符串文字是UB。

于 2012-11-07T17:49:26.697 回答
2

"abc"是一个指向可能不可变内存的静态数组。在 C中,修改字符串文字的内容是未定义的行为 (UB)。


但是 C99 没有创建"abc"类型的对象const char [n]。事实上,这是完全相反的,为了保持与 C89(和 ANSI C)的兼容性,它指定(§3.1.4/3):

字符串文字具有静态存储持续时间和char 类型数组,并使用给定字符进行初始化。

也就是声明

char* c = "12345";

在 C中不被弃用,甚至直到 C11。

http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf,我们可以看到在 C99 中进行字符串文字修改 UB 的基本原理,同时保持类型为char [n]

字符串文字不需要是可修改的。该规范允许实现共享具有相同文本的字符串副本,将字符串文字放置在只读内存中,并执行某些优化。但是,字符串文字没有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数组”类型和静态存储持续时间 ...

并将 a 分配const char[n]给 achar*不应该编译。编译器警告“弃用”,因为当时的现有实现允许转换(因为 C 允许),所以它进入附件 D:兼容性特性:

D.4 常量字符串的隐式转换

不推荐将字符串文字 (4.2) 从 const 隐式转换为非 const 限定。

于 2012-11-07T17:44:31.620 回答
2

弃用背后的想法是帮助编译器捕获否则会在运行时导致崩溃的错误。

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.

于 2012-11-07T17:50:40.963 回答
0

语法错误,因为没有从char const *char *的隐式转换。

在 C 和 C++ 中,字符串文字的类型一直是char const *。(关于非常老的 C 可能是错误的。)

规则的更改与字符串文字的类型无关,而是与指针类型之间允许的转换有关。

转换是错误的,因为指向 const 的东西是不可变的。一个字符串字面量,它是一个已知在编译和链接时保持不变的值,可以放在只读内存段中。

于 2012-11-07T17:49:54.773 回答