c 和 c++ 字符串字面量(string literal)既有相同之处,又有一些区别。了解这些内容对于加深字符串字面量以及相关一些概念的理解、澄清一些常见的概念误区不无助益。本文以普通字符串字面量 "hello" 为例总结说明如下。
如果你发现了本文中的错误,或者对本文有什么感想或建议,可通过 whyglinux at gmail dot com 邮箱和作者联系。
相同点:
[list][*]字符串字面量是对象
c/c++ 中的对象(object)指的是一块存储区。字符串字面量是不需要创建过程就可使用的对象,所以它既没有变量那样的声明或者定义(字符串字面量是无名对象),也不需要象动态分配的对象那样进行动态分配。由于这个原因,用来限定变量的类型限定符(如 const、volatile)以及存储类别指示符(如 extern、static、auto、register)不能用在修饰字符串字面量上。
[*] 数组类型
字符串字面量是数组类型的对象,因而具有数组的一切特点。关于这一点在下面还会进一步说明。
[*] 静态存储期
c/c++ 中对象的生存期按照其存储性质可分为三类:静态存储期(static storage duration)、自动存储期(automatic storage duration)以及动态存储期(dynamic storage duration)。相应地,对象可根据存储期性质分为静态对象、自动对象和动态对象三种。
字符串字面量是静态对象,所以在程序运行期间会一直存在。
[*] 字符串字面量是左值,而且是不可被更改的左值
例如,char s[] = "hello"; 中的 “hello” 是数组类型的左值(lvalue),用于初始化 s 数组;sizeof( "hello" ) 以及 &"hello" 中的 "hello" 也都是左值。在这些情况下,"hello" 处于左值语义上下文环境中,所以不会产生下面将要提到的数组转换为指针的现象。
另外,有些运算不但要求其操作数是左值,还要求可变。例如,对对象进行赋值、自加、自减等运算。因为数组是不可被更改的左值,所以不能对数组进行这些操作,也就是说不存在数组类型的赋值、自加、自减等运算。
[*]字符串字面量可以转换为指向其首第一个字符的指针
处于右值语义环境中的字符串字面量将被默认转换为指向第一个字符的指针。例如,char* p = "hello"; 中的 “hello” 在转换为字符指针后用于初始化指针变量 p;表达式 "hello"[0](相当于 *("hello" + 0) 或者 *"hello")中的 “hello” 也是同样转换为指针后参与下标运算,等等。
这种性质也是数组类型的特点。在右值语义环境下,一般类型的对象表示的值是由其存储内容决定的;而数组类型的对象与此不同,它代表的值不是来源于其内容,而是来源于数组对象首元素所在的地址。这是数组最为特殊的地方,也是人们容易产生误解的地方。
[*] 取址运算
字符串字面量是一个可取址的对象。例如:&"hello" 是合法的表达式。
[*] 地址常量
静态对象的地址在编译期间即可被确定,所以其地址(如 &"hello")是常量;而字符串字面量又可以从数组类型自动转换为指针(如 "hello" 转换为指针后等同于 &"hello"[0]),所以字符串字面量可以直接作为地址常量表达式来使用。
[*] 修改字符串字面量的行为是无定义的
下面的操作都试图修改字符串字面量中的第一个字符从而改变字符串字面量,所以其结果是无定义(undefined)的:
”hello”[0] = ‘a’; /* undefined */
char* p = “hello”; *p = ‘a’; /* undefined */
使用了无定义行为的程序是错误的;避免在程序中出现无定义行为是程序员的责任。
[/list]
区别点:
[list][*]在类型限定上的不同
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*),从而解决了上述代码中存在的问题