在类型限定上的不同
C 中的字符串字面量 "hello" 是数组类型 char[6](相应地,每个字符元素是无 const 限定的 char 型);作为右值使用的时候转换为指针类型 char*。
在 C++ 中 "hello" 是 char const [6] 类型(相应地,每个字符元素的类型是 char const);转换为指针使用的时候是 char const*、在特殊情况下也可以是 char*。
之所以在 C 中字符串字面量不是 const 数组(也就是说每个字符元素的类型不是 char const),是因为 C
要照顾或者考虑到标准制定之前已经存在的大量代码——那时的 C 语言还没有 const 关键字,如果硬性规定为 const 数组,则 char* p
= "hello"; 这样的初始化或者 char* q; q = "hello"; 这样的赋值就是非法的了(因为右边的类型 char
const* 不能默认转换为左边的类型 char* )。
同样,为了使上述代码能顺利通过编译过程,C++
采取了另外一种策略:它规定了字符串字面量的类型是 const 数组,同时又特别规定了字符串字面量也可以有限制地转换为指向非常量的指针(对于
"hello" 来说就是 char*),从而解决了上述代码中存在的问题。不过,转换到 char* 主要是为了兼容以前的代码,这种转换被 C++
标准标记为“Deprecated”,所以在写程序的时候不应该依赖于这种转换。
C++ 中的字符串字面量是常量,而在 C 中不是常量。
正是由于标准在类型上的不同规定造成了在 C 和 C++ 中字符串字面量常量性质上的差别。
在 C 中,除了 string literals 和 compound literals(C99 only)之外,其它的
literals 都是常量;而在 C++ 中,包括 string literals 在内的所有 literals 都是常量(注意:C++
中不存在 compound literals。)
在现实中,经常可以看到用“字符串常量”来指代“字符串字面量”的情况,其实对于 C 来说这是不正确的,因为在 C 中字符串字面量不属于常量;而对于 C++ 来说,“字符串常量”和“字符串字面量”实际上是一回事,只不过看问题的角度不同罢了。
顺便提一下:C++ 中的常量可以有对象常量(如字符串字面量、const 限定的对象)和非对象常量之分,而 C 中的常量不包含对象,它们最明显的特征就是不能进行取址运算,因此常量只能作为非左值(即右值)来使用。
语法及语义上的区别
C 中的字符串字面量不是常量,它的每个字符元素也不是常量,所以字符元素的不可变性仅仅表现在语义层面,但在语法和约束条件上没有要求。而
C++ 中字符串字面量是常量,每个字符元素也是常量,因此在语义和约束条件两方面都要求不能改变其中的每个字符元素;另外,出于兼容性考虑 C++
还存在着特殊情况下的向非 const 指针的转换。
下面用具体的代码来对以上内容进行说明。
*"hello" = 'A';
表达式 *"hello" 代表字符串字面量的第一个字符元素对象。上述语句试图通过赋值操作改变第一个元素,当然这样的行为在 C 和 C++ 中都是无定义的。除了这个相同点外,还有如下的一些细微的区别:
在 C++ 中,*"hello" 是一个 const 对象(其类型是 const char。注意:这里的 "hello" 不会转换为
char* 指针、从而 *"hello" 不会是 char
类型),所以上述赋值违反了赋值号左操作数必须是一个可被改变的左值的约束条件。在此情况下,标准要求给出诊断信息。
在 C 中,*"hello" 是一个非 const 对象(其类型是 char),是一个可被改变的左值,所以不违背赋值的约束条件。在此情况下,尽管这个赋值操作是未定义的,标准对诊断信息没有要求。
char* p = "hello";
char* q; q = "hello";
void foo( char* s ); foo( "hello" );
上面的初始化和赋值语句中 "hello" 都能转换为 char* 指针类型,所以都是合法的。在 C++ 中,尽管 "hello"
作为指针使用时是 char const * 类型,在此情况下(如果不是 char*
类型则初始化或者赋值不能成立)基于对字符串字面量的特殊规定使它可以转换为 char * 使用。
要注意 C++ 中字符串字面量转换为指向非常量的指针是有限制的,仅仅在有明确的目标类型要求的情况下才能进行这样的转换,否则是非法的。比如下面的情况:
char* p = "hello" + 1;
char* q; q = "hello" + 1;
void foo( char* s ); foo( "hello" + 1 );
上述是合法的 C 代码,但是作为 C++ 代码是非法的。非法的原因在于:"hello" 转换为 char const *
指针类型,而不能转换为 char *,因为 + 运算符对其操作数的类型没有转换为 char* 这样直接的要求(因为无论是 char const *
还是 char* 都能进行指针加法运算),所以指针加法表达式的结果仍然是 char const *
类型。这样,上面指针的初始化或赋值操作就违反了在类型上的约束条件,需要给出诊断信息。