================================================== Keywords: String Literal, Object, Array, Lvalue Author: whyglinux <whyglinux AT gmail DOT com> Date: 2007-05-16 ==================================================
C 和 C++ 字符串字面量(String Literal)既有相同之处,又有一些区别。了解这些内容对于加深字符串字面量以及相关一些概念的理解、澄清一些常见的概念误区不无助益。本文以普通字符串字面量 "hello" 为例总结说明如下。
如果你发现了本文中的错误,或者对本文有什么感想或建议,可通过 whyglinux AT gmail DOT com 邮箱和作者联系。
相同点:
- 字符串字面量是对象
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 */
使用了无定义行为的程序是错误的;避免在程序中出现无定义行为是程序员的责任。
区别点:
- 在类型限定上的不同
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 * 类型。这样,上面指针的初始化或赋值操作就违反了在类型上的约束条件,需要给出诊断信息。
|
相关推荐
C 和 C++ 字符串字面量(String Literal)既有相同之处,又有一些区别。了解这些内容对于加深字符串字面量以及相关一些概念的理解、澄清一些常见的概念误区不无助益。本文以普通字符串字面量 "hello" 为例总结说明...
C 和 C++ 字符串字面量(String Literal)既有相同之处,又有一些区别。了解这些内容对于加深字符串字面量以及相关一些概念的理解、澄清一些常见的概念误区不无助益。本文以普通字符串字面量 "hello" 为例总结说明...
NULL 博文链接:https://bcfz.iteye.com/blog/2172517
5.1 一维数组 5.2 字符串 5.3 字符串库函数 5.4 二维数组 5.5 多维数组 5.6 数组初始化 5.7 字符串数组 第6章 指针 6.1 什么是指针 6.2 指针运算符 6.3 指针表达式 6.4 指针和数组 6.5 指针与字符串字面量 ...
13.1字符串字面量 13.2字符串变量 13.3字符串的读写 13.4访问字符串中的字符 13.5C语言的字符库 13.6字符串惯用法 13.7字符串数组 ......... 17指针的高级应用 17.1动态存储分配基础 17.2动态分配字符串 ...
2748.3.1 字符集、编码和Unicode 2748.3.2 C++11中的Unicode支持 2768.3.3 关于Unicode的库支持 2808.4 原生字符串字面量 2848.5 本章小结 286附录A C++11对其他标准的不兼容项目 287附录B 弃用的特性 294附录C ...
9.1.1 C字符串值和C字符串变量 243 9.1.2 cstring 库中的其他函数 247 9.1.3 C字符串的输入和输出 250 9.2 字符操作工具 252 9.2.1 字符输入/输出 252 9.2.2 成员函数get和put 252 9.2.3 成员函数putback、...
sd-s [earch]和d [isplace] sd是直观的查找和替换CLI。...字符串字面量模式非正则表达式查找和替换。 不再需要反斜杠或记住哪些字符是特殊字符并且需要转义。 易于阅读,易于编写查找和替换表达式被拆分,其中
用了anaconda,OpenCV,python3.6来实现代码的。Anaconda是一种用于数据科学和机器学习的开源...Python 3.6引入了一种新的字符串格式化语法,称为格式化字面量。它可以方便地在字符串中插入变量值,并支持各种数据类型
书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。 《你必须知道的495个C语言问题》结构...
1.3 因为C语言没有精确定义类型的大小,所以我一般都用typedef定义int16和int32。然后根据实际的机器环境把它们定义为int、short、long等类型。这样看来,所有的问题都解决了,是吗? 1.4 新的64位机上的64位类型...
4.13 HLA字符串模块和其他与字符串机关的例程 4.14 存储器内转换 4.15 字符集 4.16 在HLA中实现字符集 4.17 HLA字符集常量和字符集表达工 4.18 HLA HLL布尔表达式中的IN操作符 4.19 HLA标准库对字符集的支持 4.20 在...
08_C语言和C++语言的关系_工作经验分享 09_namespace和iotream 10_实用性加强_register增强_检测增强 11_struct关键字类型增强 12_c++类型类型检查加强 13_新增数据类型bool类型 14_中午课程回顾 15_c++中的三目...
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量,只不过常量的值在定义后不能进行修改。 整数常量 整数常量可以是十进制、八进制或十六进制的常量。前缀...
常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量,只不过常量的值在定义后不能进行修改。 整数常量 整数常量可以是十进制、八进制或十六进制的常量。前缀...
OS中的字符串 415.1 不可修改的描述符 425.2 可修改的描述符 435.3 指针描述符 445.4 基于栈的缓冲描述符 475.5 基于堆的缓冲描述符 495.6 字面描述符 515.7 小结 54第6章 良好的描述符风格 566.1...