你也许会惊讶,字符型,以及我们将在下两小节讲述到的宽字符型和布尔型,在C++标准中都被归纳为整型。我把他们分开讲述是因为他们或多或少有一些和上一节提到的整型不太一样的性质。C++的最基本字符型是char(源自character)。但是char不像int是有符号的,char可能是有符号的,也可能是无符号的,是
编译器自定义行为。如果想让程序不依赖于不同的编译器,C++提供了signed char和unsigned char这两种类型。signed char是有符号的,unsigned char是无符号的。
和short int以及unsigned short int不同,虽然字符类型占用的空间小于int,但C++定义了字符字面值常量。字符字面值常量和普通的整数字面值常量表达方式不同,一般来讲,字符字面值常量是由两个单引号引起来的字符。例如,
\begin{Verbatim}
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , "
\end{Verbatim}
注意单引号 ' 和反斜线 \ 这两个字符并不在上列。字符字面值常量是由单引号括起来的,所以字符字面值常量中无法直接表达单引号,单引号要由反斜线加上单引号来表示:'\''。虽然被单引号括起来的有两个字符:反斜线和单引号,但是这里只表示单引号一个字符。这种字符叫做转义字符(escape scequence)。因为反斜线表示转义字符的开始,所以也无法直接表示,必须用两个反斜线来表示:'\\'。
小知识:在C++中,换行是由换行符 '\n' 来表示的。但是要注意的是,不同的操作系统换行符并不一致,例如Windows操作系统会自动把 '\n' 变成两个字符: '\r' 和 '\n',而Unix操作系统就直接用 '\n'。所以,当一些Unix下面的文件在Windows里打开的时候,字都连成一行了。这是因为Windows并不把 '\n'当作换行符,Windows只把两个连在一起的 '\r','\n'当作换行符。水平制表符就是键盘上的 ``Tab'' 键。纵向制表符,退格符,进纸符是历史的遗留物,现在很少用到。报警符会让电脑滴的响一声达到报警效果,现在也很少用到。
有些转义字符像单引号和双引号并不是所有情况下都需要的。例如在字符字面值常量中,单引号转义字符是必须的,但双引号转义字符就不是必须的,例如 '"' 和 '\"'都可以;但是在字符串字面常量中,双引号转义字符就是必须的(因为字符串是用双引号括起来的),而单引号转义字符就不是必须的。
你也许会怀疑为什么问号也是转义字符,这是因为以前很多键盘上没有像#这样的字符,所以C语言为了表示#,必须用键盘上有的字符表示。例如#就用??=来表示,等等。为了和这些极为特殊的符号区分,问号有时也必须用反斜线来转义。但在正常情况下问号是不需要转义的。虽然这些特殊符号写在C++的标准里面,但是很多编译器例如Visual C++并不支持,所以在这些编译器里,问号转义字符是完全多余的。
小知识:你也许已经注意到了,我们目前为止只用过std::endl表示换到新的一行。std::endl和 '\n'的区别就在于std::endl会清空缓冲区,而 '\n'不会。当C++程序进行输出的时候,并不是每一次都真正将数据输出,而是会暂时把数据放到缓冲区,等存够足够数量之后一起输出,提高程序效率。std::endl不但换行,而且会强制把目前缓冲区的数据全部输出出去。
前面提到,字符字面值常量也是一种整数类型。所有的字符例如'a','b'等都和一个0到255(或者-127到127)之间的整数在数值上等同。字符和整数之间的这种映射关系叫做编码。现代几乎所有的编译器都采用或者兼容ASCII编码。在ASCII码中,'A'和65这个整数是等值的,'a'和97这个整数是等值的,等等。在C++中,你也可以用和一个字符等价的整数值(这个整数必须由八进制或者十六进制表示)来表示一个字符字面值常量,这种表示方法用到了八进制转义字符和十六进制转义字符。八进制转义字符用反线 \ 加上最多三个八进制数字来表示,十六进制转义字符是用反斜线 \ 加上x(或者X),然后再加上若干个十六进制数字来表示。例如'A'可以表示成 '\x41'。
要注意的是signed char,unsigned char和char是三个完全不同的类型。就像int和unsigned int一样,C++规定这几种类型的底层存储形式必须一样。这几种字符类型总结如下表,可以看到标准规定他们都必须占用1个字节(注脚:即标准规定sizeof(char),sizeof(signed char)和sizeof(unsigned char)都为1。),最少8位的内存空间(注脚:C++标准并没有规定1个字节必须是8位,不同的编译器实现或者不同的硬件构架可能规定1个字节是9位甚至16位,32位。)。但实际上对现代几乎所有的编译器和硬件构架来讲,char,signed char和unsigned char都是8位,最多表示256种字符。
字符类型 | 占用的内存空间 | 所能表达的最少数据范围 |
signed char | 1字节,>=8位 | -127 到 127 |
unsigned char | 1字节,>=8位 | 0 到 255 |
char | 1字节,>=8位 | -127 到 127 或 0 到 255 |
和short int以及unsigned short int不同,虽然字符类型占用的空间小于int,但C++定义了字符字面值常量。字符字面值常量和普通的整数字面值常量表达方式不同,一般来讲,字符字面值常量是由两个单引号引起来的字符。例如,
#include <iostream>
int main()
{
std::cout << 'I' << ' ' << 'a' << 'm' << ' ' << 'a' << ' ' <<
'C' << '+' << '+' << ' ' << 'p' << 'r' << 'o' << 'g' <<
'r' << 'a' << 'm' << 'm' << 'e' << 'r' << std::endl;
return 0;
}
\begin{Verbatim}
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , "
\end{Verbatim}
注意单引号 ' 和反斜线 \ 这两个字符并不在上列。字符字面值常量是由单引号括起来的,所以字符字面值常量中无法直接表达单引号,单引号要由反斜线加上单引号来表示:'\''。虽然被单引号括起来的有两个字符:反斜线和单引号,但是这里只表示单引号一个字符。这种字符叫做转义字符(escape scequence)。因为反斜线表示转义字符的开始,所以也无法直接表示,必须用两个反斜线来表示:'\\'。
当然除了最常用的字符外,还有一些字符是屏幕上无法显示的字符和其他特殊字符,例如换行符等。这些字符也是通过转义字符来表示。如下表,
换行符(new line) 水平制表符(horizontal tab) 纵向制表符(vertical tab) 退格符(backspace) 回车符(carriage return) 进纸符(form feed) 报警符(alert) 反斜线(backslash) 疑问号(question mark) 单引号(single quote) 双引号(double quote) | \n \t \v \b \r \f \a \\ \? \' \" |
小知识:在C++中,换行是由换行符 '\n' 来表示的。但是要注意的是,不同的操作系统换行符并不一致,例如Windows操作系统会自动把 '\n' 变成两个字符: '\r' 和 '\n',而Unix操作系统就直接用 '\n'。所以,当一些Unix下面的文件在Windows里打开的时候,字都连成一行了。这是因为Windows并不把 '\n'当作换行符,Windows只把两个连在一起的 '\r','\n'当作换行符。水平制表符就是键盘上的 ``Tab'' 键。纵向制表符,退格符,进纸符是历史的遗留物,现在很少用到。报警符会让电脑滴的响一声达到报警效果,现在也很少用到。
有些转义字符像单引号和双引号并不是所有情况下都需要的。例如在字符字面值常量中,单引号转义字符是必须的,但双引号转义字符就不是必须的,例如 '"' 和 '\"'都可以;但是在字符串字面常量中,双引号转义字符就是必须的(因为字符串是用双引号括起来的),而单引号转义字符就不是必须的。
你也许会怀疑为什么问号也是转义字符,这是因为以前很多键盘上没有像#这样的字符,所以C语言为了表示#,必须用键盘上有的字符表示。例如#就用??=来表示,等等。为了和这些极为特殊的符号区分,问号有时也必须用反斜线来转义。但在正常情况下问号是不需要转义的。虽然这些特殊符号写在C++的标准里面,但是很多编译器例如Visual C++并不支持,所以在这些编译器里,问号转义字符是完全多余的。
小知识:你也许已经注意到了,我们目前为止只用过std::endl表示换到新的一行。std::endl和 '\n'的区别就在于std::endl会清空缓冲区,而 '\n'不会。当C++程序进行输出的时候,并不是每一次都真正将数据输出,而是会暂时把数据放到缓冲区,等存够足够数量之后一起输出,提高程序效率。std::endl不但换行,而且会强制把目前缓冲区的数据全部输出出去。
前面提到,字符字面值常量也是一种整数类型。所有的字符例如'a','b'等都和一个0到255(或者-127到127)之间的整数在数值上等同。字符和整数之间的这种映射关系叫做编码。现代几乎所有的编译器都采用或者兼容ASCII编码。在ASCII码中,'A'和65这个整数是等值的,'a'和97这个整数是等值的,等等。在C++中,你也可以用和一个字符等价的整数值(这个整数必须由八进制或者十六进制表示)来表示一个字符字面值常量,这种表示方法用到了八进制转义字符和十六进制转义字符。八进制转义字符用反线 \ 加上最多三个八进制数字来表示,十六进制转义字符是用反斜线 \ 加上x(或者X),然后再加上若干个十六进制数字来表示。例如'A'可以表示成 '\x41'。
练习:
#include <iostream>
int main()
{
std::cout << '\x41' << '\x42' << '\t' << 'C' << 'D' << '\n';
return 0;
}
练习:
'\n',std::endl 有什么区别?讨论什么情况下用 '\n' 或者std::endl更好。