char类型用于存储字符(如,字母或标点符号),但是从技术层面看,char是整数类型。因为char类型实际上存储的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定的字符。美国最常用的编码是ASCII编码,本书也使用此编码。例如,在ASCII码中,整数65代表大写字母A。因此,存储字母A实际上存储的是整数65。标准ASCII码的范围是0~127,只需7位二进制数即可表示。通常,char类型被定义为8位的存储单元,因此容纳标准ASCII码绰绰有余。一般而言,会保证char类型足够大,以存储系统(实现C语言的系统)的基本字符集。许多字符集都超过了127,甚至多于255。例如,日本汉字(kanji)字符集。商用的统一码(Unicode)创建了一个能表示世界范围内多种字符集的系统,目前包含的字符已超过110000个。国际标准化组织(ISO)和国际电工技术委员会(IEC)为字符集开发了ISO/IEC 10646标准。统一码标准也与ISO/IEC 10646标准兼容。

C语言把1字节定义为char类型占用的位(bit)数,因此无论是16位还是32位系统,都可以使用char类型。
1.声明char类型变量
char类型变量的声明方式与其他类型变量的声明方式相同。下面是一些例子:
char response
char itable
以上声明创建了3个char类型的变量:response、itable和latan。
2.字符常量和初始化
如果要把一个字符常量初始化为字母A,不必背下ASCII码,用计算机语言很容易做到。通过以下初始化把字母A赋给grade即可:
char grade = 'A';
在C语言中,用单引号括起来的单个字符被称为字符常量(characterconstant)。编译器一发现'A',就会将其转换成相应的代码值。单引号必不可少。下面还有一些其他的例子:
char broiled; /* declare a char variable */
broiled = 'T'; /* OK */
broiled = T; /* NO! Thinks T is a variable */
broiled = "T"; /* NO! Thinks "T" is a string */
如上所示,如果省略单引号,编译器认为T是一个变量名;如果把T用双引号括起来,编译器则认为"T"是一个字符串。字符串的内容将在第4章中介绍。
实际上,字符是以数值形式存储的,所以也可使用数字代码值来赋值:
在本例中,虽然65是int类型,但是它在char类型能表示的范围内,所以将其赋值给grade没问题。由于65是字母A对应的ASCII码,因此本例是把A赋给grade。注意,能这样做的前提是系统使用ASCII码。其实,用'A'代替65才是较为妥当的做法,这样在任何系统中都不会出问题。因此,最好使用字符常量,而不是数字代码值。奇怪的是,C语言将字符常量视为int类型而非char类型。例如,在int为32位、char为8位的ASCII系统中,有下面的代码:
char grade = 'B';
本来'B'对应的数值66存储在32位的存储单元中,现在却可以存储在8位的存储单元中(grade)。利用字符常量的这种特性,可以定义一个字符常量'FATE',即把4个独立的8位ASCII码存储在一个32位存储单元中。如果把这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的值是'E'。
3.非打印字符
单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些ASCII字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终端响铃或蜂鸣)。C语言提供了3种方法表示这些字符。
第1种方法前面介绍过—使用ASCII码。例如,蜂鸣字符的ASCII值是7,因此可以这样写:
char beep = 7
第2种方法是,使用特殊的符号序列表示一些特殊的字符。这些符号序列叫作转义序列(escape sequence)。表3.2列出了转义序列及其含义。把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如,假设有下面一行代码:
char nerf = 'n'
稍后打印变量nerf的效果是,在打印机或屏幕上另起一行。
Table 3.2 Escape Sequences
现在,我们来仔细分析一下转义序列。使用C90新增的警报字符(a)是否能产生听到或看到的警报,取决于计算机的硬件,蜂鸣是最常见的警报(在一些系统中,警报字符不起作用)。C标准规定警报字符不得改变活跃位置。标准中的活跃位置(active-position)指的是显示设备(屏幕、电传打字机、打印机等)中下一个字符将出现的位置。简而言之,平时常说的屏幕光标位置就是活跃位置。在程序中把警报字符输出在屏幕上的效果是,发出一声蜂鸣,但不会移动屏幕光标。接下来的转义字符b、f、n、r、t和v是常用的输出设备控制字符。了解它们最好的方式是查看它们对活跃位置的影响。换页符(f)把活跃位置移至下一页的开始处;换行符(n)把活跃位置移至下一行的开始处;回车符(r)把活跃位置移动到当前行的开始处;水平制表符(t)将活跃位置移至下一个水平制表点(通常是第1个、第9个、第17个、第25个等字符位置);垂直制表符(v)把活跃位置移至下一个垂直制表点。这些转义序列字符不一定在所有的显示设备上都起作用。例如,换页符和垂直制表符在PC屏幕上会生成奇怪的符号,光标并不会移动。只有将其输出到打印机上时才会产生前面描述的效果。
接下来的3个转义序列(\、'、")用于打印、'、"字符(由于这些字符用于定义字符常量,是printf()函数的一部分,若直接使用它们会造成混乱)。如果打印下面一行内容:
Gramps sez, "a is a backslash."
应这样编写代码:
printf("Gramps sez, "a \ is a backslash."n");
: Gramps sez, "a is a backslash."
表3.2中的最后两个转义序列( oo和\xhh)是ASCII码的特殊表示。如果要用八进制ASCII码表示一个字符,可以在编码值前面加一个反斜杠()并用单引号括起来。例如,如果编译器不识别警报字符(a),可以使用ASCII码来代替:
beep = ' 07'
可以省略前面的0,' 7'甚至'7'都可以。即使没有前缀0,编译器在处理这种写法时,仍会解释为八进制。
从C90开始,不仅可以用十进制、八进制形式表示字符常量,C语言还提供了第3种选择—用十六进制形式表示字符常量,即反斜杠后面跟一个x或X,再加上1~3位十六进制数字。例如,Ctrl+P字符的ASCII十六进制码是10(相当于十进制的16),可表示为'\x10'或'\x010'。图3.5列出了一些整数类型的不同进制形式。
Figure 3.5 Writing constants with the =int= family.
使用ASCII码时,注意数字和数字字符的区别。例如,字符4对应的ASCII码是52。'4'表示字符4,而不是数值4。关于转义序列,读者可能有下面3个问题。
- 上面最后一个例子(printf("Gramps sez, "a \ is abackslash."n"),为何没有用单引号把转义序列括起来?无论是普通字符还是转义序列,只要是双引号括起来的字符集合,就无需用单引号括起来。双引号中的字符集合叫作字符串(详见第4章)。注意,该例中的其他字符(G、r、a、m、p、s等)都没有用单引号括起来。与此类似,printf("Hello! 07n");将打印Hello!并发出一声蜂鸣,而printf("Hello!7n");则打印Hello!7。不是转义序列中的数字将作为普通字符被打印出来。
- 何时使用ASCII码?何时使用转义序列?如果要在转义序列(假设使用'f')和ASCII码(' 14')之间选择,请选择前者(即'f')。这样的写法不仅更好记,而且可移植性更高。'f'在不使用ASCII码的系统中,仍然有效。
- 如果要使用ASCII码,为何要写成' 32'而不是032?首先,' 32'能更清晰地表达使用字符编码的意图。其次,类似 32这样的转义序列可以嵌入C的字符串中,如printf("Hello! 07n");中就嵌入了 07。
printf("Hello! 62n");
: Hello!2
4.打印字符
printf()函数用%c指明待打印的字符。前面介绍过,一个字符变量实际上被存储为1字节的整数值。因此,如果用%d转换说明打印char类型变量的值,打印的是一个整数。而%c转换说明告诉printf()打印该整数值对应的字符。程序清单3.5演示了打印char类型变量的两种方式。
/* charcode.c-displays code number for a character */
#include <stdio.h>
int main(void)
{
char ch;
printf("Please enter a character.n");
scanf("%c", &ch); /* user inputs character */
printf("The code for %c is %d.n", ch, ch);
return 0;
}
# 运行该程序后,输出示例如下:
Here is a sample run:
Please enter a character.
C
The code for C is 67.
运行该程序时,在输入字母后不要忘记按下Enter或Return键。随后,scanf()函数会读取用户输入的字符,&符号表示把输入的字符赋给变量ch。接着,printf()函数打印ch的值两次,第1次打印一个字符(对应代码中的%c),第2次打印一个十进制整数值(对应代码中的%d)。注意,printf()函数中的转换说明决定了数据的显示方式,而不是数据的存储方式(见图3.6)。
Figure 3.6 Data display versus data storage.
5.有符号还是无符号
有些C编译器把char实现为有符号类型,这意味着char可表示的范围是-128~127。
而有些C编译器把char实现为无符号类型,那么char可表示的范围是0~255。请查阅相应的编译器手册,确定正在使用的编译器如何实现char类型。或者,可以查阅limits.h头文件。下一章将详细介绍头文件的内容。根据C90标准,C语言允许在关键字char前面使用signed或unsigned。这样,无论编译器默认char是什么类型,signed char表示有符号类型,而unsigned char表示无符号类型。这在用char类型处理小整数时很有用。如果只用char处理字符,那么char前面无需使用任何修饰符。
胜象大百科



