本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)
本文是对《C++语法详解》一书相关章节的第二版(增修版),《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg
有兴趣的读者可参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。
实现:通常是指按某种标准或原理而做出来的应用程序,如C++实现是指的按C++标准而制作出来的应用程序,通常是指编译器,因此,若本文中出现“依实现而定”是指的“依实现C++标准的应用程序而定,通常是指依编译器而定”
计算机只能处理二进制数字,那么怎样处理字符呢?可以想到的最简单的办法就是在字符和数字之间进行映射(编码),即,只需把字符想办法转换为二进制数字就行了,比如将字符“A”映射为10进制整数65,然后再将65直接映射为二进制数0100 0001,同理,可将“B”映射为66等,这样计算机就能处理字符A了,这里的“映射”在计算机中被称为“编码”,现在的计算机就是使用这种简单的思想处理字符的,只是其具体过程更复杂,至此,可能大家会产生出以下问题:
1、C++使用的是什么字符集是一个很重要的问题,这关系到能否正确显示、处理字符以及字符处理的方式,若两个C++系统使用了不同的字符集,有可能会出现乱码的情形。
2、C++实现(通常指编译器)有4种类型的字符集:基本源字符集、基本执行字符集、扩展源字符集、扩展执行字符集。
3、 C++具体使用的是什么字符集,要依实现而定。通常情况下,C++实现使用的字符集是主机系统的编码,如IBM大型机使用EBCDIC编码,不过,最常用的字符集是ASCII字符集,本文未作特殊说明,均表示使用的ASCII字符集。
将常规字符使用单引号括起来,如 ‘a’,‘b’,‘3’等,此种表示法代表的是字符的数值编码,这种表示方式的好处是不需要记住该字符的数值编码。单引号是C++表示字符的特定标志,比如字符a应书写为’a’,若直接写成a通常会被认为是变量名a,再如,字符2应书写为’2’,若直接写成2,则会被认为是数字2(通常被认为是整型)
直接使用数值编码,即使用整数,这种表示方法并不是在所有场合都能表示字符,需视该数值的类型而定,如
char c = 65; //C++使用char声明字符类型,详见后文 cout<< c <<endl; //输出字符A int a= 65; cout<< a <<endl; //输出整数65
使用单引号括起来的转义序列,转义序列详见后文。比如
cout<< '\x41' <<endl; //输出字符A
通用字符名(详见后文)。比如
cout<< '\u0041' <<endl; //输出字符A
宽字符(详见后文),如
wcout << L'A' << endl; //输出字符A,注:宽字符需使用wcout输出
2、字符串是用双引号括起来的,由0个或多个字符组合成,与单引号类似,双引号是C++表示字符串的特殊标志,应把字符串与字符加以区别,比如”a”表示字符串a,而’a’表示字符a。
3、除转义序列、通用字符名等特殊情形外,在单引号中只能有一个字符,若单引号中有超过1个以上的字符C++未作规定,因此不建义使用。超过1个以上的字符应使用双引号,表示这是一个字符串。比如’ab’,应写成”ab”。
1、很多字符我们无法从键盘直接输入,比如回车符、退格符等不可打印字符,还有一些字符在C++中有特殊的意义,这些字符也不能被直接显示,比如双引号、单引号等,这些字符就可以使用转义序列的形式来表示,由转义序列表示的字符通常被称为转义字符。
2、转义序列以\反斜杠符号开始,后面紧接数字或字符,同时需要使用单引号括起来,比如’\n’表示回车换行,而’\b’表示退格,’\x61’表示字符a,同理’\’表示符号\。
3、注意:转义序列表示的是一个字符,虽然他是由几部分组成的。比如’\n’,表示一个字符,而不是两个字符’\’和’n’的组合。
4、转义序列有如下几种格式:
1)、符号转义序列:即反斜杠后使用字符形式的转义序列,如\b、\n等,表2.4为C++定义的符号转义序列
2)、8进制格式为:
\ooo //其中ooo表示1~3个8进制数
3)、16进制格式为:
\xh或\xhh... //表示以x开头,后跟一个或多个16进制数
5、有关转义序列的规则
1)、8进制或16进制的数值代表的是所使用字符集里与该字符所对应的数值编码,以下示例是使用ASCII字符集所表示的转义字符
\40 (空格) \7 (响铃) \141 (字符a)
2)、8进制或16进制转义序列,其取值范围不应超过该字符类型的长度,否则有可能报错,默认情况下字符的类型为char,通常char的长度为8位,比如
cout<< ' \433'; //超出范围,可能报错
字符数值编码超过char长度时,应使用其他字符类型,C++使用特有的前缀来表示其他字符类型,详见后文
3)、8进制格式转义序列,最多只使用3位数来构成转义字符,超过3位数时就表示转义字符结束,比如,
cout << "\1413"; //输出a3,即141所对应的字符a和字符3
4)、16进制格式转义序列需要使用到后面跟着的所有数字,如
cout<<"\x1413"; //可能会因数值超出范围而报错
\x1413表示一个字符,该字符是16进制数值编码1413所对应的字符,由于大多数机器的char型只占据8位的长度,所以,该示例可能会报错。
5)、系统默认使用8进制格式的转义序列,书写时不必书写前缀符数字0,因此,需要注意的是,无法在“\”反斜杠后面使用十进制数。比如"\0141",表示两个字符,即相当于’\014’和’1’两个字符,再如 ’ \141’ 其后的数字141是8进制数,而非10进制数
6)、16进制格式转义序列的前缀x不能省略,前缀0须省略。如
cout<<'\x61'; //输出字符a cout<< '\0x61'; //不能输出字符a,相当于单引号中有多个字符的情形
1、通用字符名其格式为
\uhhhh \Uhhhhhhhh
其后是相应的16进制数,表示的是字符的ISO 10646码点,需要注意的是\u之后必须是4位数,\U之后必须是8位数。比如\u00E2、\u00F6、\U00006C49等,若系统不支持ISO 10646编码,则无法显示这些字符。注:ISO 10646是与Unicode同步的国际标准
2、若实现支持扩展字符集,则可在标识符或字符串中以“通用字符名”的形式使用一些特殊字符,比如,法文的重元音、汉字等。注意:转义字符不能用于标识符中,比如
int aaa\u6C49cccc = 33; //正确,\u6C49是Unicode码点为6C49的“汉”字 int aaa\x6C40dddd=44; //错误,转义字符\x6C49不能用于标识符中 int aaa'\x6C40'eeee=55; //错误,原因同上
3、\u开始的通用字符名的类型为char16_t,\U开始的通用字符名的类型为char32_t,其中char16_t和char32_t是C++11中新增的类型,详见后文
4、注意:经gcc和VC++测试,通用字符名的类型是不确定的,gcc当数值范围在128之内时,其类型为char,超过则为int类型。VC++2019的规则是,若该字符不能正常显示则通通为char,在ASCII范围内的字符为char,能正常显示且超过ASCII范围的字符都为int。
1、对于像ASCII码这样的字符集,在将其编码为计算机所存储的格式时(即第二次编码)只使用1个字节(8位)来存储就足够了,但对于汉字来讲,使用一个字节来存储显然是不行的,比如汉字的“汉”字,GB18030的编码为0xBABA ,很明显,即使直接存储该数值也至少需要2个字节,也就是说1字节(8位)长的char类型是无法存储汉字的。除了汉字的编码之外,各个国家都为各自的国家制定了自已的编码,这些编码有些是使用等长的字节来存储,有些使用的长度则是不等的。
2、当需要处理的字符无法用一个字节来表示时,C++有两种处理方式
3、宽字符常量
4、宽字符类型
5、宽字符应使用wcout进行输出,比如wcout<<L’a’<<endl;
6、多字节字符和宽字符简介
通过添加前缀的方式可以改变字符的默认类型,如表XXX所示,其中char16_t、char32_t是C++11新增的类型,详见后文
在C++中有如下几种字符类型
1、char所占据的长度为能够表示目标计算机系统中的所有基本符号,也就是说,char的长度可能是8位、16位等长度,需视具体情况而定。通常情况下,使用8位就可以表示所有的基本符号了。
2、注意:虽然C++标准没有对1字节是多少位作出规定,但是char的长度始终被认为是“一个字节”,也就是说,若char的长度为16位,则在C++中,一个字节的长度就是16位,而不是8位。通常情况下,一个字节都是指的8位,本文未作特殊说明,一个字节都是指的8位。
3、char占据的位数可以使用climits头文件中的符号常量CHAR_BIT或根据其最大值UCHAR_MAX来判断。
1、默认情况下C++没有规定char的符号,也就是说,默认情况下char既不是有符号的,也不是没有符号的,char的符号由实现决定,一般的编译器都把char当作signed来处理。可以使用unsigned和signed来明确指定char是否有符号,比如unsigned char表示无符号char。
2、当把char当作整数来使用时,char的符号就很重要了,因为char类型占据一个字节(一般为8位),因此signed char表示范围是-128 ~ 127,unsigned char表示的范围是0 ~ 255。
3、判断编译器中char的类型是否是有符号char的方法:
1、C++将字符表示为整数,该整数是与其字符相对应的数值编码,这样就不必使用专门的转换函数在字符和ASCII码之间转换,这在一定程度上提供了方便。因此,在C++中通常把char当作比short更小的整型来使用,或把整型当作char来使用,这意味着
可对char进行整数的相关操作,比如,进行加、减等运算,比如
char c='a'+1; cout<<c<<endl; //输出字符b
可将整数赋值给char类型变量,同理也可以将char变量赋给int变量;比如
char c=97; cout<<c<<endl; //输出字符a(数值97是ASCII字符集中的字符a的编码) int d=’a’; cout<<d<<endl; //输出97,因为字符a的ASCII编码为97。
2、为什么cout会把char类型变量输出为字符而不是整数值呢?这是因为cout将需要输出的内容进行了相应的转换,这种转换工作是由变量的类型所引导的,也就是说,变量的类型将影响cout如何显示值,因此,cout在输出char时,将其相应的值转换为需要显示的字符,同理,cout在输出int型变量时,则将其值显示为一个整数值。比如
char c=97; cout<<c; //输出a,通过查看内存可以知道,变量c的值仍然是整数97
3、注意:在C++版本2.0以前,cout将字符变量显示为字符,将字符常量显示为数字,把字符常量存储为int类型,不过现在基本上都是2.0以后的版本,C++将字符常量存储为char类型,因此不用担心以上问题。比如
char c = 'a'; //在早期版本中,表示从常量'a'(int型)中复制8位到变量c中,此步会有精度损失 cout<< 'a' <<endl; //早期版本输出整数值97,并不会输出字符a cout<< c << endl; //输出整数97,注:此例有待考证,因为第一步赋值过程可能有精度损失
1、宽字符类型wchar_t的长度和符号是不确定的,随实现而异,然而,在实际处理字符时,若使用特定长度和符号的类型,将会大有益处,为此,C++11新增了char16_t和char32_t两种类型。
2、char16_t是无符号的,长度为16位,其对应的常量使用前缀u表示,char16_t还与\u开始的通用字符名相匹配。
3、char32_t是无符号的,长度为32位,其对应的常量使用前缀U表示,char32_t还与\U开始的通用字符名相匹配。比如
char16_t c1 = u'x'; char32_t c2 = U'\u00006C49';
作者:黄邦勇帅(原名:黄勇) 2021年5月6日