19-C++基础-char类型:字符与小整数

本文介绍了C++中的char类型,主要用于存储字符,它可以表示ASCII或更广泛的字符集,如Unicode。通过示例展示了cin和cout如何处理字符输入和输出,以及如何使用put()函数。此外,文章还讨论了char的有符号和无符号变体,以及宽字符类型wchar_t,以及C++11引入的char16_t和char32_t。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2.1.8 char 类型:字符和小整数

下面介绍最后一种整型:char类型。顾名思义,char类型是专门存储字符(如字母和数字)而设计的。现在,存储数字对于计算机来说算不了什么,但存储字母则是另一回事。编程语言通过使用字母的数值编码解决了这个问题。因此,char类型是另一种整型。它足够长,能够表示目标计算机系统中所有基本符号——所有字母、数字、标点符号等。实际上,很多系统支持的字符都不超过128个,因此用一个字节就可以表示所有的符号。因此,虽然char最常被用来处理字符,但也可以用它用作比short更小的整型。

在美国,最常用的符号集是ASCII字符集。字符集中的字符用数值编码(ASCII码)表示。

例如,字符A的编码为65,字母M的编码为77。为方便起见,我们在示例中使用的是ASCII码。然而,C++实现使用的是其主机系统的编码——例如,IBM大型机使用EBCDIC编码。ASCIIEBCDIC都不能很好的满足国际需要,C++支持的宽字符类型可以存储更多的值,如国际Unicode字符集使用的值。本章稍后将介绍wchar_t类型。

程序3.5使用了char类型。

程序3.5 chartype.cpp

  1. //chartype.cpp——the char type  
  2. #include<iostream>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.       
  7.     char ch;   
  8.       
  9.     cout<<"Enter a character:"<<endl;   
  10.     cin>>ch;  
  11.     cout<<"Hola ! ";  
  12.     cout<<"Thank you for the "<<ch<<" character."<<endl;  
  13.       
  14.     return 0;  
  15. }   
//chartype.cpp——the char type
#include<iostream>
using namespace std;
int main()
{
	
	char ch; 
	
	cout<<"Enter a character:"<<endl; 
	cin>>ch;
	cout<<"Hola ! ";
	cout<<"Thank you for the "<<ch<<" character."<<endl;
	
	return 0;
} 

同样,\nC++中表示换行符。下面是该程序的输出:

有趣的是,程序中输入的是M,而不是对应的字符编码77。另外,程序将打印M,而不是77。通过查看内存可以知道,77是内存在变量ch中的值,这种神奇的力量不是来自char类型,而是来自cincout这些工具为您完成了转换工作,输入是,cin将键盘输入的M转换为77;输出时,cout将值77转换为所显示的字符Mcincout的行为都是由变量类型引导的。如果将77存储在int变量中,则cout将把它显示为77(也就是说,cout显示两个字符7)。程序3.6说明了这一点,该程序还演示了如何在C++中书写字符字面值;将字符用单引号括起,如M(注意,示例中没有使用双引号。C++对字符用单引号,对字符串使用双引号。cout对象能够处理这两种情况,但正如第四章将讨论的,这两者有天壤之别)。最好,程序引入了cout的一项特性——cout.put()函数,该函数显示一个字符。

程序3.6 morechar.cpp

  1. //morechat.cpp——the char type and int type contrasted  
  2. #include<iostream>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.       
  7.     char ch='M';  
  8.     int i=ch;  
  9.     cout<<"The ASCII code for "<<ch<<" is"<<i<<endl;   
  10.       
  11.     cout<<"Add one to the character code:"<<endl;   
  12.     ch=ch+1;  
  13.     i=ch;  
  14.     cout<<"The ASCII code for "<<ch<<" is"<<i<<endl;  
  15.       
  16.     cout<<"Displaying char ch using cout.put(ch)";  
  17.     cout.put(ch);  
  18.       
  19.     cout.put('!');  
  20.     cout<<endl<<"Done"<<endl;    
  21.       
  22.     return 0;  
  23. }   
//morechat.cpp——the char type and int type contrasted
#include<iostream>
using namespace std;
int main()
{
	
	char ch='M';
	int i=ch;
	cout<<"The ASCII code for "<<ch<<" is"<<i<<endl; 
	
	cout<<"Add one to the character code:"<<endl; 
	ch=ch+1;
	i=ch;
	cout<<"The ASCII code for "<<ch<<" is"<<i<<endl;
	
	cout<<"Displaying char ch using cout.put(ch)";
	cout.put(ch);
	
	cout.put('!');
	cout<<endl<<"Done"<<endl;  
	
	return 0;
} 

下面是程序清单3.6中程序的输出:

1. 程序说明

在程序3.6中,M表示字符M的数值编码,因此将char变量ch初始化为M,将把c设置为77。然后,程序将同样的值赋给int变量i,这样chi的值都是77.接下来,coutch显示为M,而把i显示为77。如前所述,值得类型将引导cout选择如何显示值——这是智能对象的另一个例子。

由于ch实际上是一个整数,因此可以对它使用整数操作,如加1,这将把ch的值变为78。然后,程序将i重新设置为新的值(也可以将i1)。cout再次将这个值的char版本显示为字符,将int版本显示为数字。

C++将字符表示为整数提供了方便,使得操纵字符值很容易。不必使用笨重的转换函数在字符和ASCII码之间来回转换。

即使用过键盘输入的数字也被视为字符,请看下面的代码:

char ch;

cin>>ch;

如果您输入5并按回车键,上述代码将读取字符“5”,并将其对应的字符编码(ASCII编码53)存储到变量ch中。请看下面的代码:

int n;

cin>>n;

如果您也输入5并按回车键,上述代码将读取字符“5”,将其转换为相应的数字值5,并存储到变量n中国能。

最后,该程序使用函数cout.put()显示变量ch和一个字符常量。


1、成员函数cout.put()

cout.put()到底是什么东西?其名称中为何有一个句号?函数cout.put()是一个重要的C++OOP概念——成员函数——的第一个例子。类定义了如何表示和控制数据。成员函数归类所有,描述了操纵类数据的方法。例如类ostream有一个put()成员函数,用来输出字符。只能通过类的特定对象(例如这里的cout对象)来使用成员函数。要通过对象(如cout)使用成员函数,必须用句点将对象名和函数名称(put())。第十章介绍类时将更详细地介绍这一点。现在,您接触的类只是istreamostream,可以通过使用它们的成员函数来熟悉这一概念。

cout.put()成员函数提供了另一种显示字符的方式,可以替代<<运算符。现在读者可以会问,为何需要cout.put()。答案与历史有关。在C++Release 2.0之前,cout将字符变量显示为字符,而将字符常量(如MN)显示为数字。问题是,C++的早期版本与C一样,也将把字符常量存储为int 类型。也就是说,M的编码77将被存储在一个16位或32位的单元中。而char变量一般占8位。下面的语句从常量M中复制8位(左边的8位)到变量ch中:

char ch =M;

遗憾的是,这意味着对cout来说,Mch看上去有天壤之别,虽然它们存储的值相同。因此,下面的语句将打印$字符ASCII码,而不是字符$;

cout<<$;

但下面的语句将打印字符$:

cout.put($);

Release 2.0之后,C++将字符变量存储为char类型,而不是int类型。这意味着cout现在可以正确处理字符常量了。

cin对象有几种不同的方式可以读取输入的字符。通过使用一个利用循环来读取几个字符的程序,读者可以更容易地领会到这一点。因此在第五章介绍了循环后再来讨论这个主题。

2char字面值

C++中,书写字符常量的方式有多种。对于常规字符(如字母、标点符号和数字),最简单的方法是将字符用单引号括起。这种表示法代表的是字符的数值编码。例如,ASCII系统中的对应情况如下:

l A65,即字符AASCII码;

l a97,即字符aASCII码;

l 553,即字符5ASCII码;

l  32,即空格字符的ASCII码;

l 33,即惊叹号的ASCII码;

这种表示法优于数值编码,它更加清晰,而不需要知道编码方式。如果系统使用的是EBCDIC,则A的编码将不是65,但是A表示的仍然是字符A

有些字符不能直接通过键盘输入到程序中。例如,按回车键并不能使字符串包含一个换行符:相反,程序编译器将把这种键击解释为在源代码中开始新的一行。其他一些字符也无法从键盘输入,因此C++语言赋予了它们特殊的含义。例如,双引号字符用来分割字符串字面值,因此不能把双引号放在字符串字面值中。对于这些字符,C++提供了一种特殊的表示方法——转义序列,如表3.2所示。例如,\a表示振铃字符,它可以是终端扬声器振铃。转义序列\n表示换行符,\将双引号作为常规字符,而不是字符串分割符。可以在字符串或字符常量中使用这些方法,如下列所示:

char alarm=\a;

cout<<alarm<<Dont do that again!\a\n;

cout<<Ben \Buggsie\Hacker\nwas here!\n

最后一行的输出如下:

Ben Buggsie Hacker

was here!

3.2        C++转义序列的编码

字符名称

ASCII符号

C++代码

十进制ASCII

十六进制ASCII

换行符

NL(LF)

\n

10

0xA

水平制表符

HT

\t

9

0x9

垂直制表符

VT

\v

11

0xB

退格

BS

\b

8

0x8

回车

CR

\r

13

0xD

振铃

BEL

\a

7

0x7

反斜杠

\

\\

92

0x5C

问号

?

\?

63

0x3F

单引号

\

39

0x27

双引号

\

34

0x22

注意,应该像处理常规字符(如Q)那样处理转义序列(如\n)。也就是说,将它们作为字符常量是,应用单引号括起;将它们放在字符串中时,不要使用单引号。

转义序列的概念可追溯到使用电传打字机与计算机通信的时代,现代系统并非都支持所有大的转义序列,例如,输入振铃字符时,有些系统保持沉默。

换行符可替代endl,用于在输出中重起一行,可以以字符常量表示法(\n)或字符串方式(n)使用换行符。下面三行代码都将光标移到下一行开头:

cout<<endl;

cout<<\n;

cout<<\n;

可以将换行符嵌入到较长的字符串中,这通常比使用endl方便,例如,下面两条cout语句的输出相同:

cout<<endl<<endl<<What next?<<endl<<Enter a number:<<endl;

cout<<\n\nWhat next?\nEnter a number:\n;

显示数字时,使用endl比输入\n\n更容易些,但显示字符串时,在字符串末尾添加一个换行符所需的输入了要少些:

cout<<x<<endl;

cout<<Dr.  X.\n;

最后,可以基于字符的八进制和十六进制编码来使用转义序列。例如,Ctr+ZASCII码为26,对应的八进制编码为032,十六进制编码为0x1a。可以用下面的转义序列来表示该字符:\032\x1a。将这些编码用单引号括起,可以得到相应的字符常量,如\032,也可以将它们放在字符串中,如hl\x la there

提示:在可以使用数字转义序列或符号转义序列(如\0x8\b)时,应使用符号序列。数字表示与特定的编码方式(如ASCII码)相关,而字符表示适用于任何编码方式,其可读性也更强。

程序3.7演示了一些转义序列。它使用振铃字符来提请注意,使用换行符是光标前进,使用退格字符使光标向左退一个(Houdini曾经在只使用转义序列的情况下,回值了一副哈德逊河图画;他无疑是一位转义序列艺术大师)。

程序3.7     hondini.cpp

  1. //bondini.cpp——using escape sequences  
  2. #include<iostream>  
  3.   
  4. int main()  
  5. {  
  6.     using namespace std;  
  7.     cout<<"\aOperation \"HyperHype\" is now activated!\n";  
  8.     cout<<"Enter your agent code:______\b\b\b\b\b\b\b\b";   
  9.     long code;  
  10.     cin>>code;  
  11.     cout<<"\aYou entered "<<code<<"....\n";  
  12.     cout<<"\aCode verified!Proceed with plan Z3!\n";  
  13.     return 0;  
  14. }   
//bondini.cpp——using escape sequences
#include<iostream>

int main()
{
	using namespace std;
	cout<<"\aOperation \"HyperHype\" is now activated!\n";
	cout<<"Enter your agent code:______\b\b\b\b\b\b\b\b"; 
	long code;
	cin>>code;
	cout<<"\aYou entered "<<code<<"....\n";
	cout<<"\aCode verified!Proceed with plan Z3!\n";
	return 0;
} 

注意:有些基于ANSI C之前的编译器的C++系统不能识别\a。对于使用ASCII字符集的系统,可以用\007替换\a。有些系统的行为可能有所不同,例如可能将\b显示为一个小矩形,而不是退格,或者在退格时删除,还可能忽略\a

运行程序清单3.7中的程序时,将在屏幕上显示下面的文本:

打印下划线字符后,程序使用退格字符,程序使用退格字符将光标退到第一个下划线处。读者可以输入自己的密码,并继续。下面是完整的运行情况:

4、通用字符名

c++实现支持一个基本的源字符集,即可以用来编写源代码的字符集。它由标准美国键盘上的字符(大写和小写)和数字、C语言中使用的符号(如{=})以及其他一些字符(如换行符和空格)组成。还有一个基本的执行字符集,它包括在程序执行期间可处理的字符(可从文件中读取或显示到屏幕上的字符)。它增加了一些字符,如退格和振铃。C++标准还允许实现提供扩展源字集。另外,那些被作为字母的额外字符也可用于标识符名称中。也就是说,德国实现可能允许使用日耳曼语的元音变音,而法国实现则允许使用重元音。C++有一种表示这种特殊字符的机制,它独立于任何特定的键盘,使用的是通用字符名。

通用字符名的用法类似于转义序列。通用字符名可以以\u或\U打头。\u后面是8个十六进制位,\U后面则是十六进制位。这些位表示的是字符的ISO 10646码点(ISO 10646是一种正在制定的国际标准,为大量的字符提供了数值编码,后面会给大家总结的)。

如果使用的实现支持扩展字符,则可以在标识符(如字符常量)和字符串中使用通用字符名。例如,请看下面的代码:

int k\u00F6rper;

cout<<Let them eat g\u00E2teau.\n;(楼楼的电脑不支持,这里就不做演示了)

ö的ISO 10646 码点为00F6,而â的码点为00E2。因此,上述C++代码将变量名设置为körper,并显示下面的输出:

Let them eat gâteau.

如果系统不支持ISO 10646,它将显示其他字符或g\u00E2teau,而不是â。

实际上,从易读性的角度来看,在变量名中使用\u00F6没有多大意义,但如果实现的扩展源字符集包含ö,它可能允许您从键盘输入该字符。

请注意,C++使用术语“通用编码名”,而不是“通用编码”,这是因为应将\u00F6解释为“Unicode码点为U-00F6的字符”。支持Unicode的编译器知道,这表示字符ö,但无需使用内部编码00F6。无论计算机使用的是ASCII还是其他编码系统,都可在内部表示字符T;同样,在不同的系统中,将使用不同的编码来表示字符ö。在源代码中,可使用适用于所有系统的通用编码名,而编译器将根据当前系统使用合适的内部编码来表示它。

Unicode和ISO 10646

Unicode提供了一种表示各种字符集的解决方案——为大量字符和符号提供标准数值编码,并根据类型将它们分组。例如,ASCII码为Unicode的子集,因此在这两种系统中,美国的拉丁字符(如A和Z)的表示相同。然而Unicode还包含其他拉丁字符,如欧洲语言使用的拉丁字符、来自其他语言(如希腊语、西里尔语、希伯来语、切罗基语、阿拉伯语、泰语、孟加拉语)中的字符以及象形文字(如中国和日本的文字)。到目前为止,Unicode可以表示109000多种符号和90多个手写符号,它还在不断发展中。

Unicode给每个字符指定一个编号——码点。Unicode码点通常类似于下面这样:U-222B。其中U表示这是一个Unicode字符,而22B是该字符(积分正弦符号)的十六进制编号。

国际标准化组织(ISO)建议了一个工作组,专门开发ISO 10646——这是一个对多种语言文本进行编码的标准。ISO 10646小组和Unicode小组和Unicode小组从1991年开始合作,以确保他们的标准同步。

5、signed char 和 unsigned char

与int不同的是,char在默认情况下既不是没有符号,也不是有符号。是否有符号由C++实现决定,这样编译器开发人员可以最大限度地将这种类型与硬件属性匹配起来。如果char有某种特定的行为对您来说非常重要,则可以显式地将类型设置为signed char 或unsigned char:

char fodo;

unsigned char bar;

signed char snark;

如果将char用作数值类型,而unsigned char 和signed char之间的差异将非常重要。Unsigned char类型的表示范围通常为0~225,而signed char的表示范围为-128到127.例如,假设要使用一个char变量来存储像200这样大的值,则在某些系统上可以,而在另一些系统上可能不可以。但使用unsigned char 可以在任何系统上达到这种目的。另一方面,如果使用char变量来存储标准ASCII字符,则char有没有符号都没关系,在这种情况下,可以使用char。

6、wcha_t

程序需要处理的字符集可能无法用一个8位的字节表示,如日文汉字系统。对于这种情况,C++的处理方式有两种。首先,如果大型字符集是实现的基本字符集,则编译器厂商可以将char定义为一个16位的字节或更长的字节。其次,一种实现可以同时支持一个小型基本字符集和一个较大的扩展字符集。8位char可以表示基本字符集,另一种类型wchar_t(宽字符类型)可以表示扩展字符集。wchar_t类型是一种整数类型,它有足够的空间,可以表示系统使用的最大扩展集。这种类型与另一种整型(底层类型)的长度和符号属性相同。对底层类型的选择取决于实现,因此在一个系统中,它可能是unsigned short ,而在另一个系统中,则可能是int。

cin和cout将输入和输出看作是char流,因此不适于用来处理wchar_t类型。iostream头文件的最新版本提供了作用相似的工具——wcin和wcout,可用于处理wchar_t流。另外,可以通过加上前缀L来指示款字符常量和宽字符串。下面的代码将字母P的wchar_t版本存储到变量bob中,并显示单词tall和wchar_t版本:

wchar_t bob = Lp;

wcout << Ltall<<endl;

在支持双字节wchar_t的系统中,上述代码将把每个字符存储在一个两个字节的内存单元中。以后的代码我们不使用宽字符类型,但这里想介绍一下。尤其是在进行国际编程或使用

6、C++11x新增的类型:char16_t和char32_t

随着编程人员日益熟悉Unicode,类型wchar_t显然不再能满足需求。事实上,在计算机系统上进行字符和字符串编码时,仅使用Unicode码点并不够。具体地说,进行字符串编码时,如果有特定长度和符号特征的类型,将很有帮助,而类型wchar_t的长度和符号特征随实现而已。因此,C++11新增了类型char16_t和char32_t,其中前者是无符号的,长16位,而后者也是无符号的,但长32位。C++11使用前缀u表示char16_t字符常量和字符串常量,如uC和ube good;并使用前缀U表示char32_t常量,如UR和Udirtyrat。类型char16_t与u/00F6形式的通用字符名匹配,而类型char32_t与/U0000222B形式的通用字符名匹配。前缀u和U分别指出字符字面值的类型为char16_t和char32_t:

char16_t ch1=uq;

char32_t ch2=U\U0000222B;

与wchar_t一样,char16_t和char32_t也都有底层类型——一种内置的整型,但底层类型可能随系统而已。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值