
1.3 数据类型、常量、变量、输入/输出与基本运算
为了提高程序的运行速度,有效地利用存储空间,并能尽早地发现错误,各种程序设计语言都将处理的数据分成不同的数据类型,并将这些数据以常量或变量的形式保存起来,本节将介绍数据类型、常量和变量等概念,以及输入/输出与基本运算的实现方式。
1.3.1 基本数据类型与数据的表示
为了提高计算机处理数据的能力,各种程序设计语言都将数据按照其特点划分成若干种基本数据类型。不同的数据类型有不同的取值范围、不同的运算以及不同的存储方式。与其他程序设计语言相比较,C语言提供的基本数据类型种类十分丰富,其中包括整型、实型和字符型等多个类别。为了便于读者理解和掌握,这里只介绍几种常用的基本数据类型,其余的数据类型将在使用时介绍。
1.整型
整型是用于描述整数的数据类型,例如,123、-89、0。在 C 语言中,常用的整型是基本整型int,用于表示一个机器字长所表示的整数。对于32位系统,int类型的数据用4字节(32 位二进制位)表示,包括 1 位符号,有效位数为 31 位,取值范围为-2 147 483 648~2 147 483 647。
2.实型
实型是指带小数点的数据类型,例如,78.34、0.0、-765.2、76.0。在C语言中,常用的实型是双精度型,用double表示。double类型的数据用8字节(64位二进制位)表示,包括1位符号,11位指数和52位尾数,取值范围为-1.797 693 134 862 32E308~1.797 693 134 862 32E308。
3.字符型
字符型是一种特殊的整型,用于表示字符,取值范围为-128~127。在 C 语言中,字符类型的名称是char,字符值用一对单引号括起来,并且每个字符对应一个ASCII编码,用1字节(8位二进制位)表示。例如,‘0’、‘B’、‘#’对应的ASCII编码分别为48、66和35。有关其他字符的ASCII编码,请参阅附录A。
1.3.2 常量
常量是指在程序运行过程中始终不发生变化的量。在C语言中,主要包括整型常量、实型常量、字符型常量、字符串型常量。
1.整型常量
在 C 语言中,整型常量常用十进制形式。例如,120、3 270、-987、-89 都是正确的十进制书写形式。在具体的书写过程中,整型常量的书写形式为:开始于“+”号或“-”号(“+”号可以省略),之后紧跟一个数字序列,其中不允许出现小数点。
2.实型常量
在C语言中提供了两种实型常量的书写形式:一种是十进制小数形式;一种是指数形式。例如,下面是几个用十进制小数形式书写的数值:123.45、509.0、-0.98、-1.0、0.0。
C语言规定:实型常量默认为double类型。通常人们认为509.0、-1.0、0.0与509、-1、0表示的数值完全一样,但在C程序中,这两种书写形式有着本质的区别。前者属于double类型,在内存中占用64位(二进制位);后者属于int类型,在内存中占用32位(二进制位)。二者的内存表示完全不同。
例如,下面是几个用指数形式书写的数值:
1.87E+10、-9.786 89E+20、1.234 5E-3,它们分别表示 1.87×1010、-9.786 89×1020 和1.234 5×10-3。
实型常量的十进制书写形式清晰、自然,符合人们的日常习惯,但在表示特别大或特别小的数值时,指数形式更为适宜。例如:
分子的质量大约为 0.000 000 000 000 000 000 000 000 000 9g,用指数形式其数值可以写成 9.0E-28g;地球与太阳之间的距离大约为 149 600 000km,用指数形式其数值可以写成1.496E8km。
3.字符常量
在C程序中,字符常量由一对单引号(')括起来,其内部存储表示是相应字符的ASCII编码。例如,'P'、'='、'@'、'9' 存储的是ASCII编码80、61、64、57。当对字符常量进行运算操作时,系统会将它们作为int类型进行处理。
这种书写字符的形式简单且清晰,适用于绝大多数的可打印字符,但像单引号(')、反斜杠(\)和ASCII 字符集中那些不可打印的字符或具有特殊功能的字符就需要使用 C 语言提供的转义符形式书写。转义符是指用一个反斜杠(\)后跟一个特定字符或一个八进制或十六进制数值表示的字符。例如,'\''、'\\'、'\101'、'\x20' 分别表示单引号(')字符、反斜杠(\)字符、'A'字符和空格符。表1-1给出了C语言提供的部分转义符。
表1-1 C语言提供的部分转义符

4.字符串常量
字符串是由若干个字符组成的字符序列。在C语言中,字符串常量常用一对双引号(")括起来。例如,"This is a C program."、"3871"和"K" 都是字符串常量。每个字符串常量占用n+1个字节的连续存储空间。这里的n是其中的字符个数,包括空格符。多余的一个字节保存一个空字节,也就是ASCII值等于0的字符'\0',用于表示字符串的结尾。
大家应该注意到,虽然0、0.0和'\0'的数值相同,但它们的内存表示完全不同。
1.3.3 变量、变量的存储与赋值
使用计算机解题与使用数学方式解题的一个重要区别就是需要使用存储器。数学运算描述数值之间的运算,而计算机程序则是面向存储器来描述各种计算。C 语言通过变量为程序设计者提供了使用存储器的手段。每个变量代表了不同的存储单元。当使用C语言描述各种计算时,可以通过变量的赋值,把计算中间结果放在变量所代表的存储单元中。当需要使用这些中间结果时,可以通过变量的引用将数据取出来。然而,由于数据的类型各不相同,每个变量在使用前需要预先进行定义,从而确定每个变量占用的存储空间大小(字节数)。
1.变量的定义
C语言规定:程序中的每一个名字(即标识符),包括变量名和函数名,都必须先定义后使用。
定义变量的语法格式为:
<数据类型> <变量名>,<变量名>,<变量名>…]];
这说明每个变量定义语句,必须指定一个数据类型和一个或若干个变量名。例如:
int count;
double value,sum;
char ch;
变量定义语句也叫变量名的声明语句,从这些变量定义语句中可以得知每个变量的名称和所属类型。变量名是引用变量的依据;变量的所属类型决定了变量的取值范围、存储方式和能够实施的操作。
C语言规定:变量名用标识符表示。标识符是由字母、数字字符和下画线(_)组成的字符序列,其中,第1个字符不能是数字字符,字母需要区分大小写。例如,s12、data_1、dist是合法的标识符;12dt是非法的标识符;count与Count是两个不同的标识符。
在上面程序示例的第 1 行中,定义了一个名为count的int类型变量。系统将为这个变量分配4字节的存储空间,如图1-18所示,可以实施算术运算、关系运算、逻辑运算等一系列操作。

图1-18 变量count占用存储空间的示意图
在第 2 行中,定义了两个名为 value 和 sum 的double类型变量。系统将为每个变量分配8字节的存储空间,可以实施取余运算(%)之外的全部算术运算、关系运算等一系列操作。另外,从这一行的定义可以看出:如果若干个变量属于同一种数据类型,利用逗号分隔,可以定义在同一行中。
第3行中定义了名为ch的char类型的变量。系统为它分配1字节的存储空间,可以表示ASCII编码表中出现的所有字符。
2.变量的赋值和引用
变量定义之后并没有一个确切的初始值,因此需要被赋值后,才能使用变量名对它进行引用。为变量赋值就是将变量所属数据类型的某个数值(介于取值范围之中)放入系统为这个变量分配的存储空间中的操作。在C程序中,通常可采用3种方式为变量赋值:
(1)在定义变量的同时为变量赋予一个初始值;
(2)通过赋值操作为变量赋值;
(3)通过某种输入流和scanf函数为变量赋值。
下面主要介绍前两种实现方式,最后一种将在后面章节给予介绍。
在定义变量的同时直接为变量赋予初始值的语法格式为:
<数据类型> <变量名> = <常量表达式>;
例如:
int data = 100;
执行这条语句之后,系统将为变量data分配4字节的存储空间,并将100置入其中。
另外,在C语言中,提供了一个专门用于为变量赋值的操作,其操作符是“=”,使用格式为:
<变量名> = <表达式>
其中,<变量名>是一个已经声明的变量名称,“=”是赋值号。它的执行过程是:首先计算赋值号右侧的表达式,然后将其结果放入赋值号左侧的变量中。例如,执行count=0 之后,将得到如图1-19所示的结果。
如果再执行count = count+1,count中的值就会变成1。它的执行过程是:引用count变量当前的值加1后再放入count变量中。
如果在赋值操作的后面加上一个分号,就变成了具有赋值功能的赋值语句。例如:
x = 64;
y = x+2;
这两条语句的操作过程是:先将 64 放入系统为变量 x 分配的存储空间中,然后引用变量x代表的存储空间中的值加上2之后再放入系统为变量y分配的存储空间中。操作结果如图1-20所示。

图1-19 赋值操作

图1-20 执行两条具有赋值功能的语句效果
为变量赋值时,需要注意赋值号右侧的表达式结果类型与赋值号左侧的变量类型是否一致。如果不一致,赋值过程将以赋值号左侧的变量类型为基准,将赋值号右侧表达式的结果转换成左侧变量的类型。如果能够成功地转换,变量将可以得到正确的数值;否则变量将不会得到正确的结果。例如,int型整数赋值给字符型变量时,将丢失高位的3个字节,仅保留低位的1个字节。
此外,读者还应该注意,赋值号左侧的变量count和右侧的变量count具有不同的意义(语义)。直接出现在赋值号左侧的变量代表系统为变量count分配的存储空间,用于指定赋值的目标;而在赋值号右侧表达式中的变量 count 代表保存在该存储空间内保存的数据。这里数据参加 count+1 运算后,其结果被赋值到 count 代表的存储空间中。为了描述这种区别,变量的这两种语义分别被称为左值和右值。
下面举一个例子,说明变量定义和变量赋值的应用。
【例1-4】 圆面积和周长的计算。
题目要求根据给定的圆半径(20毫米),计算出圆的面积和周长。
〖问题分析〗
这是一道数学计算题。可以通过圆的面积和周长的计算公式,以及已知的圆半径,计算出圆的面积和周长。然而,对于计算机来说,程序只能针对存储器中的数据进行计算,因此程序不仅需要描述计算公式,为计算所需的数据和结果准备存储空间,还要为计算所需的数据提供数据输入步骤,为结果数据提供数据输出步骤。
〖程序代码〗
#include <stdio.h>
main( )
{
double radius, area, perimeter; /* 变量定义 */
radius = 20; /* 为变量radius赋值 */
area = radius*radius*3.14159; /* 为变量area赋值 */
perimeter = 2*radius*3.14159; /* 为变量perimeter赋值*/
printf("The radius of the circle is %lf\n", radius); /* 显示radius的值 */
printf("The area of the circle is %lf\n", area); /* 显示area的值 */
printf("The perimeter of the circle is %lf\n", perimeter); /* 显示perimeter的值 */
}
在这个程序中,首先定义了3个变量radius、area和perimeter,它们分别用来保存圆的半径、面积和周长。随后的3条语句依次完成了为radius赋值、利用radius的值计算圆的面积并将结果保存在变量area中,以及计算圆的周长并将结果保存在perimeter中,最后3条printf语句将保存在3个变量中的结果输出到标准输出设备(屏幕)。
运行这个程序后,将会在屏幕上看到下面方框中显示的结果。
The radius of the circle is 20.000000
The area of the circle is 1256.636000
The perimeter of the circle is 125.663600
从上述程序中可以看出,对于计算公式已知的数学问题,C语言程序编写的步骤如下:
(1)为计算所需的数据和结果准备存储空间,也就是定义变量;
(2)通过直接赋值或scanf等函数将输入的数据(如半径)保存到变量中;
(3)采用表达式来描述数学计算公式(类似于数学表达式),并赋值给负责保存结果的变量(如面积和周长);
(4)使用C语言提供的输出函数(如printf)将计算结果显示在屏幕上。
在为变量命名的时候,不但要符合 C 语言的标识符命名规范,还做到“见名知意”,这样可以提高程序的可阅读性和可理解性,是专业人员必备的做法。
针对上述C程序设计的需求,下面逐步介绍数据的输入/输出和各种运算表达式。
1.3.4 基本的输入/输出
在程序的运行过程中,程序能够接收用户通过标准输入设备——键盘提供的数据信息;程序结束运行后,程序会将处理的结果通过标准输出设备——显示器反馈给用户,这些都是程序设计语言提供的必备功能。在C语言中,将所有的输入/输出内容视为数据流,并可通过一系列标准函数实现输入/输出。例如,scanf、getchar、printf和putchar等,这些标准函数的原型都定义在标准函数库的头文件stdio.h中,因此,在调用它们之前,需要利用编译预处理命令 include 将这个头文件嵌入到程序中。另外,根据对输入/输出格式的控制能力,这些标准函数被分成了两个类别:一类是非格式化输入/输出;另一类是格式化输入/输出。下面将分别介绍它们的使用方法。
1.非格式化输入/输出
非格式化输入/输出是指无格式控制能力的输入/输出形式。在 C 语言中,这种形式主要应用于字符或字符串的输入/输出。这里只介绍字符的非格式化输入/输出,有关字符串的输入/输出将在后面的章节中介绍。
(1)字符的非格式化输入。在C语言的标准函数库中,用于实现字符输入的函数是getchar(),其调用格式为:
getchar()
它的基本执行过程为,等待用户从标准输入设备——键盘输入一个字符。如果输入成功,函数返回这个字符的ASCII编码。例如:
char ch;
ch = getchar( );
如果在某个程序中包含上面这两条语句,则程序执行到第 2 行的语句时,就会首先调用getchar函数。此时,系统将暂时中断程序的继续执行,等待用户的键盘输入。当用户的键盘输入流中存在未读入的字符时,getchar 函数将读取输入流中第一个字符,返回该字符的 ASCII编码,然后将这个编码的整数值赋给变量ch,随后继续往下执行。此时,如果再次调用getchar函数,将获得输入流中的下一个字符。C语言的所有输入/输出都采用这种流式的工作方式。
需要注意的是:如果没有立刻将函数的返回结果保留在一个变量中,或没有立刻对返回结果进行任何处理,用户通过键盘输入的字符将无法引用,从而就会丢失。
(2)字符的非格式化输出。与getchar对应的标准输出函数是putchar,它将实现输出字符的功能。其调用格式为:
putchar(ch)
该函数要求其参数ch必须是一个字符型整数。按照C语言的规定,其他类型的数值(包括字符)都会被转换为整数,putchar按照该整数所代表的ASCII值,向标准输出流(屏幕)输出相应的字符。
下面列举一个应用非格式化输入/输出字符的例子。
【例1-5】 字符的输入/输出。
通过键盘输入两个字符,分别在两行上显示这两个字符,每行显示4次。
〖程序代码〗
#include <stdio.h>
main( )
{
char ch; /* 定义变量ch */
ch = getchar(); /* 从键盘读取一个字符 */
putchar(ch); /* 在屏幕上显示4次输入的字符 */
putchar(ch);
putchar(ch);
putchar(ch);
putchar('\n'); /* 在屏幕上显示换行 */
ch = getchar(); /* 从键盘读取下一个字符 */
putchar(ch); /* 继续在屏幕上显示4次输入的字符 */
putchar(ch);
putchar(ch);
putchar(ch);
putchar('\n'); /* 在屏幕上显示换行 */
}
运行上面这个程序后,系统将暂停执行,等待用户的输入。当用户通过键盘输入某个字符并按“Enter”键后,程序将继续向下执行。例如,用户通过键盘输入字符:
# $
然后按“Enter”键,屏幕将会显示下面方框中显示的内容。
####
$$$$
需要注意以下几点。
(1)在输入字符时,不能将字符用一对单引号括起来,这样会被认为是输入了3个字符。例如,如果希望输入大写字母 ‘M’,直接通过键盘输入M即可,千万不要输入 ‘M’。
(2)程序中第5次调用putchar函数时,给定的参数是 '\n',这是换行符的表示方式,因此,这次执行putchar函数后将会产生一个换行效果。
(3)程序运行时,用户只有输入“Enter”换行键后,程序才继续开始运行。这是因为C语言在读入输入流时,采用了行输入方式,只有单击换行键后,程序才能开始读取输入流中的字符。事实上此时标准输入流中有 3 个字符#、$和'\n'。程序读到字符$后,就继续执行后面的语句了。如果这时再次使用getchar,就可以得到下一字符'\n'。建议读者自行试试看。
2.格式化输入/输出
格式化输入/输出是指能够控制格式的输入/输出形式。在 C 语言的标准函数库中,提供的scanf和printf就是专门用于实现格式化输入/输出的两个函数。
(1)格式化输入函数scanf
调用scanf函数的格式为:
scanf(<格式控制字符串>,<变量地址>,<变量地址>,…,<变量地址> );
<格式控制字符串>是一个用双引号括起来的字符串,其中有各种格式控制说明符,依次说明每个输入数据的数据类型。常用的格式控制说明符由“%”后连接一个特定字符或字符序列组成。例如,“%d”表示这个位置应该输入一个十进制整型数值;“%c”表示这个位置应该输入一个字符;“%lf”表示这个位置应该输入一个双精度数值。
<变量地址>用于存放输入数据的变量地址,也就是该变量的存储空间的首地址,输入的数据将依次存入这些地址描述的存储空间,即各个变量中。显然,变量地址的个数和格式控制说明符的个数必须相同。在C语言中,有个专用运算符“&”用于取一个变量的地址。例如,&a、&value分别计算出变量a、value的存储地址。
假设在调用scanf函数之前已经定义了变量x、y、f1和f2:
int x, y;
double f1, f2;
此时,可以使用下面这条语句完成从键盘输入4个数据,并将它们分别保存到变量x、y、f1和f2中:
scanf("%d%d%lf%lf", &x, &y, &f1, &f2);
当程序执行到这条语句时,系统暂停继续执行,等待用户的键盘输入。此时,用户需要通过键盘输入4个数据,前两个是整型数值,后两个是双精度型实数。
C语言规定,除了字符之外,其他类型的输入数值之间可用空格或换行符分隔。例如,对于上例,可以有下面几种输入形式:
234-98 56.787 -78.43<回车>
或者
234 -98<回车>
56.787-78.43<回车>
其中,<回车>表示按“Enter”键。上面两种输入形式的效果完全一样,即将234赋给变量x;将-98赋给变量y;将56.787赋给变量f1;将-78.43赋给变量f2。读者应该注意到本例输入的234和-98之间的空格可有可无,而98和56.787之间必须有空格或换行符。同理, 56.787和-78.43之间的空格也是可有可无。这就是所谓的自然分割方式。
为了减少在输入时由于用户对输入格式缺乏了解而造成的输入错误,建议在每个输入语句之前显示一条提示信息(可使用printf)。其中包括一些关于需要用户输入的数据个数及相应的数据类型等说明性提示。
下面看一个使用格式化输入/输出的程序例子。
【例1-6】 角度到弧度的转换。
编制程序,将输入的角度转换成弧度后输出。
〖问题分析〗
本题仍然是一道数学计算题,应该包含变量定义、输入、计算和输出4个步骤。
〖程序代码〗
#include <stdio.h>
main( )
{
int degree; /* 定义存放角度的变量 */
double radian; /* 定义存放弧度的变量 */
printf("Enter degree<int>:"); /* 显示提示信息 */
scanf("%d", °ree); /* 通过键盘输入角度 */
radian = 3.14159*degree/180; /* 计算弧度 */
printf("%d degrees equal to %lf radians.\n", degree, radian); /* 显示结果 */
}
执行这个程序后,屏幕上将会显示下列提示信息:
Enter degree<int>:
看到这个提示信息后,用户就会得知应该通过键盘输入一个 int 类型的数据,它代表角度。假设通过键盘输入:
45<回车>
通过scanf函数,45就被赋给了变量degree,程序继续往下执行,最后可以看到下面方框中的显示结果。
45 degrees equal to 0.785398 radians.
在这个程序中,将存储角度的变量degree定义成int类型,将存储弧度的变量radian定义成double类型的主要原因是考虑到使用整型数据描述角度可以满足一般的需要,但0°~360 °所对应的弧度为 0~2π,所以弧度必须定义为实数类型。采用了计算公式3.141 59*degree/180,结果保存在变量radian中。
(2)使用printf函数实现格式化输出
与scanf函数相对应,printf函数的功能是实现格式化输出。调用printf函数的格式为:
printf(<格式控制字符串>,<表达式>,<表达式>,…,<表达式>);
其中,<格式控制字符串>的含义与 scanf 函数相同。但在这里,除了包含格式控制说明符外,还可以包含一些直接显示的字符串,但是格式控制说明符的个数和后面表达式的格式必须相同。函数 printf 的基本功能是将每个表达式的计算结果依次按照格式控制说明符定义的格式显示到标准输出设备——显示器上。格式控制说明符需要与将要输出的表达式依次一一对应。例如,【例1-6】的最后一条语句:
printf("%d degrees equal to %lf radians.", degree, radian);
在这条语句的格式控制字符串中,包含两个格式控制说明符%d 和%lf,分别用来控制degree和radian的输出格式,前者按照十进制整数输出,后者按照浮点数输出,其余的内容均直接输出(\n表示换行符)。前面给出的显示结果已经展示了输出内容。
在有些实际应用中,人们希望能够更加准确地控制每个数值输出时所占据的列数,为此, C语言提供了相应的功能。其格式为:
%m 和 %m.n
其中m表示数值输出时在屏幕上占据的列数,又被称为场宽,n表示输出实数时小数点后的位数。假设有下列变量定义:
int a = 365;
char c = 'Z';
double e = 7865.298;
对于下面这条语句:
printf("%6d%3c%12.6lf", a, c, e);
将在屏幕上显示下列结果:
365 Z 7865.298000
其中,整型数值365一共占6列,前3列为空格,后3列为数值;字符“Z”一共占3列,前2列为空格,后1列为字符;双精度数值7 865.298占12列,前面有1列空格,小数部分占6列。
当设定的场宽大于数值实际需要的列数时,数值将采取右对齐的方式,在前面的剩余位置补空格,这是C语言的默认处理方式。C语言提供的格式控制符功能很丰富,需要了解更多的读者,可以参考有关语言规范。
此外,由于数据的格式化和非格式化输入/输出机制的不同,程序设计者应该尽量避免在一个程序中交叉使用这两类输入/输出函数,以避免机制切换带来混乱。
1.3.5 算术运算符和算术表达式
C语言中提供的运算种类十分丰富,包括算术运算、关系运算、逻辑运算和位运算等。这里先介绍最常用、最简单的算术运算及算术表达式,在后面章节中会陆续介绍其他几种运算。
1.算术运算符
在C语言中,算术运算包含5个二元运算符,二元运算是指需要两个操作数的运算。它们分别是加(+)、减(-)、乘(*)、除(/)和取余(%)。对于加(+)、减(-)、乘(*)三种运算来说,当两个操作数的数据类型相同时,所得结果仍是这种数据类型;当两个操作数的数据类型不相同时,C语言会按照向占二进制位数较多的数据类型转换的原则,在计算之前自动地将占二进制位数较少的数据类型转换为占二进制位数较多的数据类型。例如,前面的角度转换成弧度的计算公式中:
radian = 3.14159*degree/180; /* 计算弧度 */
degree是整数,3.141 59是双精度数,因此乘法结果是双精度数;再除以整数180,结果仍然是双精度数。
C语言允许char类型的变量或常量参与各种算术运算,但在运算时,它将被视为一个整型数值,其值为字符对应的ASCII编码。例如,'A'+32等于用大写字符'A' 的ASCII编码65与32相加等于97,而97是小写字母 'a' 的ASCII编码,因此,利用这种方法可以将一个大写字母转换成小写字母。另外,'9'-'0'等于用字符'9' 的ASCII编码57与字符'0' 的ASCII编码48相减等于9,可以看出,它实现了将一个数字字符转换成相应的整型数值的功能。
对于除(/)运算,当它的两个操作数为整型时,结果也为整型。例如,48/5等于9;100/13等于 7。但当其中一个操作数为实型时,另一个操作数也将转换成实型,其结果也为实型。例如,48/5.0将把48转换成实型数值,然后再与5.0相除,最后结果等于9.6。
对于取余(%)运算,它的两个操作数必须是整型,其结果也为整型。假设a和b是两个int类型的变量,且b不等于0;则a%b的计算结果是a整除以b的余数。例如,a=20, b=3,则a%b的结果为2。
上面几个算术运算看起来很简单,但将它们组合起来却可以实现一些复杂的功能。下面进行列举说明。
【例1-7】 数字字符串到整数的转换。
将连续输入的4个数字字符拼成一个int类型的数值。例如,输入4个数字字符‘9’、‘7’、‘0’和‘2’,应该得到一个int类型的整型数值9 702。
这个程序实现的关键在于如何将数字字符变成个位的数字,以及如何将4个个位数拼接成一个整型数值。下面是解决这个问题的程序源代码。
〖程序代码〗
#include <stdio.h>
main( )
{
int d1, d2, d3, d4, value;
printf("Enter 4 characters:");
d1 = getchar()-'0'; /* 输入数字字符,并转换为个位数 */
d2 = getchar()-'0';
d3 = getchar()-'0';
d4 = getchar()-'0';
value = d1*1000 + d2*100 + d3*10 + d4; /* 将4个个位数拼成int类型的数据 */
printf("The value is %d\n", value); /* 输出结果 */
}
运行这个程序时,将会在屏幕上显示下列提示信息:
Enter 4 characters:
假设输入4个数字字符:
3408<回车>
将会在屏幕上看到下面方框中显示的结果。
The value is 3408
在这个程序中,首先定义了4个整型变量,分别用于保存每个字符对应的数字。接着,连续4次调用标准函数getchar实现接收4个数字字符,并转换为整数。按照getchar逐个输入的流方式,4个数字字符之间不能够使用分隔符(即空格符或回车符);否则它们也会作为输入数据被读入。
综上所述,字符型数据都是采用ASCII值参加运算的。由于所有数字字符的ASCII编码都是连续的,所以用数字字符减去字符‘0’得到的就是这个数字字符所对应的整型数值。这样得到的d1、d2、d3和d4分别对应千位、百位、十位和个位,于是d1×1 000 + d2×100 +d3×10 + d4就是最终结果。
本实例可以反映出计算机内部字符型数据和整型数据的异同。一方面字符采用ASCII值来表示,也是整数,因此可以参加所有整数运算;另一方面,int 型整数也可以作为字符的ASCII 值参加计算。二者作为整数除了取值范围不同,没有任何其他差别。当共同参与二元运算,或者被赋值给int型变量时,char型字符会自动转换为int型整数。但是,当int型整数被赋值给char型变量时,将会丢失高位。如果其数值大于127或小于0,这将导致数值的改变。如果输入/输出时使用%c,若两种数据的整数都是 ASCII 编码,就可以表现为字符。如果使用%d输出,可以得到整数的数值,也可以得到字符的ASCII值。
2.自增、自减运算符
除了上面介绍的5个二元运算符外,在C语言中,还提供了两个一元运算符:自增(++)、自减(--),它们可以分别实现整型变量的加1、减1操作。假设有下列变量定义:
int i=0;
int j=32760;
则执行i++之后,i的值在原值0的基础上加1变成了1;执行j++之后,j的值在原值32 760的基础上加1变成了32 761。
与其他运算符不同的是:这两个运算符既可以置于变量的前面,也可以置于变量的后面。也就是说,++i、--i、++j、--j、i++、i--、j++和j--都是正确的书写形式,它们的区别只有在表达式中才能够显现出来。如果自增(++)或自减(--)运算符位于变量前面,表达式将引用变量自加1或自减1之后的新值参与运算;反之,表达式将引用自加1或自减1之前的旧值参与运算。
假设x、y是两个int类型的变量,x=10,y=20,则x*(++y)表示用10乘以21,等于210;而x*(y++)表示用10乘以20,等于200,随后y的取值增加了1。从这里可以看出,第一个表达式用y自加1之后的新值21参与乘法运算;第二个表达式用y自加1之前的原值20参与乘法运算。这种表示方法常用于简化程序描述。
3.算术表达式
算术表达式是指仅包含算术运算符或自加(++)、自减(--)运算符的表达式。算术表达式的结果一定是数值类型的,即整型或实型。
下面是几个算术表达式:
x%100+1
(x+y)*100-60/(a-b)
(x-y)*(x+y)
x+(++x)+(++x)+(++x)
当一个表达式中包含多个运算符时,每个运算符的计算顺序将取决于运算符的优先级和结合性。C语言规定:优先级高者优先计算,优先级相同时根据结合性确定计算顺序,具有左结合性的运算符先左后右;具有右结合性的运算符先右后左。如表1-2所示为算术运算符和自增(++)、自减(--)运算符之间的优先级和结合性。
表1-2 算术运算符和自增(++)、自减(--)运算符的优先级和结合性

使用C语言书写表达式时,应该遵循下列规则。
(1)所有内容都必须写在一行上,例如,必须写成7/8。
(2)在表达式中只允许使用圆括号。为了确保表达式中的每个运算符都能按照所期望的顺序进行计算,应该适当地添加括号。
(3)表达式中的乘号(*)不允许省略,例如,(a+1)(a-1) 必须写成(a+1)*(a-2);2x 必须写成2*x。
表1-3所示为几个表达式的书写例子。
表1-3 表达式的书写范例

为了保证表达式能够得到正确的结果,必须十分清楚参与计算的每个操作数所属的数据类型,以及在运算过程中数据类型的转换规则。C语言规定,如果在运算过程中发现两个操作数的数据类型不相同,则本着由“窄位数”向“宽位数”转换的原则,将占二进制位数较少的操作数自动转换为占二进制位数较多的数据类型,然后再进行计算。
鉴于C语言提供了非常丰富的运算符,表达式的种类远远超过了数学表达式。本书将在后几章逐步介绍各种表达式和运算符,读者应注意它们的优先级和结合性,以把握正确的语义。