第3章
C编程基础知识 
本章内容提要
(1)常量和变量。
(2)基本数据类型。
(3)基本运算符和表达式。
(4)数据的类型转换。
本章介绍的是编程用的基础知识,为后面的编程套路(如选择结构、循环结构等)做准
备,就如同学习武术,必须把基本功练好了,才能学套路。
本章主要内容有常量和变量,C语言的基本数据类型,算术运算符、赋值运算符、自增
自减运算符、逗号运算符以及由这些运算符所构成的表达式,数据的类型转换等。
3.1 常量和变量
C程序中,可以使用的数据分为两类:常量和变量。
3.1.1 常量
有些数据是“死数”,不可能变化,例如2,它在任何时候都是2;再如3.14,永远都是
3.14。类似这样的不可能发生变化的数据称为常量。C语言中的常量并非仅限于数值型
的“常数”,还包括字符、字符串、符号常量和常变量等。本书在3.2节和3.3节中会分别讲
述这些常量。
3.1.2 变量
变量的概念特别重要,因为几乎每个程序都要用到变量。能否正确地理解变量将直
接决定能否学好C语言。
1.什么是变量
程序之所以需要变量,是因为在程序运行的过程中有些数据需要记住。变量就是用
来记住这些数据的。
常量和变量

36 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
生活中,人们在求解一个问题时,通常都要记住一些数据,例如,要计算表达式1+ 
2+…+100的值,在求解过程中,有两个数据必须记住:一是已经求得的和是多少;二是
下一次该加的数是几。
用计算机解题也是如此,也需要记住这些数据。人类用大脑或纸来记录数据,而计算
机则是用内存:在内存中开辟一部分空间,把数据化成二进制存放进去,这部分存储数据
的空间便是变量,即变量是内存中的一段存储区域。至于为什么叫变量,是因为这一段存
储空间所存的数据可以改变。例如下面的代码: 
short a; //定义变量a,a 的值不确定
a=2; //执行后,a 的值是2 
a=3; //执行后,a 的值是3 
变量a刚分配空间时,其内容是不确定的,执行“a=2;”后,a中的值变成2;执行“a= 
3;”后,a中的值变为3。其变化过程如图3-1所示。
图3-1 变量的变化过程
图3-1中灰色的2字节是系统分配给a用来存整数的,因为其中所存的数可以改变, 
所以把这2字节称为变量a。
说明:本书表示内存的图形,一律以图形上方为内存的低地址,下方为高地址, 
以后不再说明。
说明:几乎所有微机的CPU 在存数据时都采用小端模式,即先存低字节,再存
高字节。
2.变量的类型
C语言中的数据类型有很多种,如整型、实型和字符型等。通常,每种类型的数据都
要用与它同类型的变量来存储,故变量的类型也分很多种。
表3-1是常用的变量类型以及它们在内存空间中所占用的字节数。

第3章 C编程基础知识 37 
表3-1 常用的变量类型及相关数据
类 型类型表示
VisualC++6.0中TurboC2.0中
字节数表示范围字节数表示范围
存储方式
字符型 char 1 0~(28-1) 1 0~255 ASCII码
短整型 short 2 -215~(215-1) 2 -32768~32767 补码
整型 int 4 -231~(231-1) 2 -32768~32767 补码
长整型 long 4 -231~(231-1) 4 -231~(231-1) 补码
无符号短整型unsignedshort 2 0~(216-1) 2 0~65535 补码
无符号整型 unsignedint 4 0~(232-1) 2 0~65535 补码
无符号长整型unsignedlong 4 0~(232-1) 4 0~(232-1) 补码
浮点型 float 4 -3.4×1038~ 
3.4×1038 4 -3.4×1038~ 
3.4×1038 IEEE754 
双精度型 double 8 -1.7×10308~ 
1.7×10308 8 -1.7×10308~ 
1.7×10308 IEEE754 
说明: 
(1)ASCII码是为了在计算机中用二进制存储字符而制定的一种编码。本书附录D 
中列有常用字符及其ASCII码值。
(2)unsigned是无符号的意思。C语言中有些数据不可能是负数,没有必要用最高位
表示正负,故unsigned类型的数据,其最高位不再表示负号,而是跟后面的位一样代表大小。
(3)float和double型变量的存储方式遵循IEEE754标准,详见3.2.2节。
(4)有些编译器还支持longlong、longdouble等类型,需要时请自行学习。
除了上面给出的类型,C语言中还有一种指针类型的变量,这种变量用来存储某种实
体的地址,这里所说的实体包括变量、数组和函数等。
存储地址的变量称为指针变量。
指针变量在内存中所分配的字节数一般与int型变量相同。
C语言中,可以用运算符sizeof()求得某种类型或某项数据在内存中占用多少字节, 
使用时要在括号中写上类型名或变量名或表达式。例如: 
char c; 
float x; 
printf("%d,%d\n",sizeof(char),sizeof(c)); 
printf("%d,%d\n",sizeof(float), sizeof(x)); 
运行结果: 
试一试:自己编写程序验证一下表3-1中每种类型的变量需要多少字节的内存
空间。

38 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
3.变量的定义
1)变量定义的格式
普通变量的定义格式:用类型名开头,加一个空格之后再写变量名(由程序员命名)。
若定义多个变量,变量之间用逗号隔开。例如: 
int a; //定义一个变量
float x,y,z; //定义多个变量,变量之间用逗号隔开
指针变量的定义格式与普通变量定义格式类似,区别是:指针变量名字前面要多写
一个*,例如: 
char *p; //定义了一个指针变量p,用来存char 型变量的地址
int *p1,*p2; //定义两个指针变量p1 和p2,都用来存int 型变量的地址
float x,*p3,y,*p4; //定义了两个普通变量x、y 和两个指针变量p3、p4 
变量必须先定义,然后才能使用。
定义变量的目的:一是给变量起一个名字,以便在程序中分辨它;二是把变量的类型
告诉计算机,以便让计算机给变量分配空间。因为有了类型,计算机才能知道该给变量分
配多少字节,才能知道变量的值用什么方式存储。例如,若是字符变量则分配1字节,变
量的值用ASCII码存储;若是短整型变量则分配2字节,用补码存储…… 
变量定义的位置应该在同级别的执行语句之前(C99取消了这条规定)。
例如: 
int main() 
{ 
int a,b; //变量定义 
float x; //变量定义 
a=1; //第一条执行语句 
. 
if(a>b) 
{ 
int s; //花括号内变量的定义 
s=a+b; //花括号内第一条执行语句 
… 
} 
. 
} 
2)变量的命名
不管是变量,还是今后要学到的数组、函数和结构体等,每样东西都应该有一个名字
作为标识,其名字即为标识符。
C语言对标识符有如下要求。
(1)标识符只能由英文字母、数字和下画线组成,但不能以数字开头。
(2)C语言是区分大小写的,即大小写被认为是两个不同的字符。例如,name和
Name是两个不同的标识符。

第3章 C编程基础知识 39 
(3)不允许用关键字作为标识符。关键字是指已经赋予一定含义的字符序列,如
int、float、for、if、return等。C语言有32个关键字,见附录C。
(4)标识符有长度限制,超过限制时,后面的字符不起作用。C89限制的标识符长度
是不超过31个字符。
注意:C语言中,变量名不能与函数名相同。例如,已经有函数max(),则变量名
便不能用max,反之亦然。
4.变量的属性
每个变量都有值和地址两个属性。
变量的值指的是变量在内存中所存的内容。变量的地址指的是变量在内存中所处的
位置,其起始地址称为变量的地址。
设有代码“shorta=5;”,则程序运行时需要在内存中分配2字节作为变量a的存储
区域,并且将5存放进去。设系统给a分配的空间是内存中1027和1028两个单元,如
图3-2 变量的两个属性
图3-2所示,则变量的值是5,变量的地址是1027。
把内存的哪2字节分配给变量是不可预知的,但是变量分
配在什么地方,系统是知道的。每当在内存中给一个变量分配
了空间,系统都会把变量名和它的地址、类型等信息记录下来, 
以便将来找到它、存取它。
因此,在变量获取空间之后,其地址是可以被知道的,用取
地址运算符& 便可以获取变量的地址。
下面的程序可以输出整型变量a的两个属性。 
#include <stdio.h> 
int main() 
{ 
int a=5; 
printf("%d,%p\n",a,&a); //%p 表示用十六进制数输出地址 
return 0; 
}
运行结果: 
试一试:把代码中的“=5”去掉,运行一遍程序,看a还有没有值。
5.变量的赋值和赋初值
如前所述,定义变量的目的是为了存储数据,而赋值或赋初值都可以实现这一目的。
1)赋值
在给变量分配空间的任务完成之后,再给变量存放数据,称为赋值。例如,给普通变
量赋值: 
int a; //在内存中给a 分配空间

40 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
a=5*2; //向a 中存放数据,即赋值
其中,=是赋值号,不是数学里的等于号,赋值就是存储:把赋值号右边表达式的值计算
出来然后存储到左边变量的内存空间中。赋值后a的值是10。
说明:C语言中,=是赋值号,==才是等于号。
又如,给指针变量赋值: 
int a,*p; //在内存中给a 和p 分配空间
p=&a; //把a 的地址存到p 中,即给p 赋值
2)赋初值
在给变量分配空间的时候就向其中存放数据,称为赋初值。例如: 
int a=10; 
赋值和赋初值的区别:开辟空间和存放数据这两件事情若是分两次完成的,是赋值; 
开辟空间和存放数据这两件事情是一次就完成的,是赋初值。
定义若干变量时,可以只对一个或一部分变量赋初值,例如: 
int a,b=1,c,d=3,e,f; 
也可以给全部变量都赋初值,例如: 
int a=5,b=5,c=5; 
注意:变量初值相同时,不可以写成: 
int a=b=c=5; 
3.2 基本数据类型
C语言的数据类型分为基本类型和构造类型,基本类型指的是系统固有的类型,也是
常用的类型;构造类型指的是用户自定义出来的类型。
C语言的基本数据类型如表3-1所示。本节介绍这些基本数据类型的表示方法、数
据存储方式以及输出方法。
3.2.1 整型数据
本节所说的整型数据包括short、int、long、unsignedshort、unsigned、unsignedlong 
等所有整数。
1.整型常量的表示
程序中用到整型常量时,可以用3种进制表示:十进制、八进制和十六进制。例如: 
int a,b,c,d,e; 
基本数据
类型

第3章 C编程基础知识 41 
a=100; //用十进制表示整数
b=0144; //用八进制表示整数,必须用0 开头
c=-0144; 
d=0x64; //用十六进制表示整数,必须用0x 或0X 开头
e=-0x64; 
上面5个赋值语句执行后,5个变量的值分别是100、100、-100、100、-100。
注意:程序中不允许使用二进制。
2.整型数据的存储
所有整数在计算机中都是以补码形式存放的。下面的代码定义了5个变量,5个变
量在内存中的存储状态如图3-3所示。 
short a=5,b=-1,c=-32768; 
unsigned short d=32768; 
long e=65536; 
图3-3 整数在内存中的存储
5的补码是0000000000000101,-1的补码是1111111111111111,-32768的补码
是1000000000000000,32768的补码也是1000000000000000,65536的补码是00000000 
000000010000000000000000。
3.整型数据的输出
说明:对于数据的输出格式,本书第4章会详细介绍,这里先简单介绍一些常用
的输出格式。
(1)带符号整数输出时可以用十进制、八进制或十六进制。例如: 
int a=76; 
long b=65536; 
short c=26; 
printf("%d\n",a); // %d 表 示用十进制输出整数
printf("%o\n",a); // %o 表 示用八进制输出整数
printf("%x,%X\n",a,a); //%x 或%X 表示用十六进制输出整数
printf("%ld,%Lo,%lx\n",b,b,b); //加上L 或l 表示输出长整数
printf("%hd,%ho,%hx\n",c,c,c); //加上h 表示输出短整数
输出结果:

42 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
试一试:在TC 中输出长整数时,漏掉L 或l,会怎样? 把65536、65537、65538 
用%d格式输出看看,并解释原因。在TC中输出整数时,多加了L或l,又会怎样? 解释
一下原因。
(2)无符号整数一般用%u格式输出,表示用十进制把一个数据当无符号整数来输
出。例如: 
unsigned int a=32768; 
long b=50000; 
printf("%u,%lu\n",a,b); //%u 表示输出无符号整数
(3)带符号整数可以当成无符号整数输出,反之亦然。例如: 
short a=-1; 
unsigned short b=65535; 
printf("%hd,%hd\n",a,b); //%hd,输出带符号短整数
printf("%hu,%hu\n",a,b); //%hu,输出无符号短整数
图3-4 -1和65535的存储状态
这段代码的输出结果如下: 
想一想:为什么会出现这样的结果? 请根据
图3-4所示的存储状态解释其原因。
3.2.2 实型数据
1.实型常量 
1)实型常量的表示
带小数点的常量称为实型常量。程序中的实型常量可以用两种形式表示。
(1)小数形式。例如,3.14、-12.5、0.38、.2、-.3等。
说明:当一个数是纯小数时,小数点前面的0可以省略。
(2)指数形式。例如,1.25E-2、12.5E-3、0.0125E0等。
指数形式相当于数学中的科学记数法。C语言用1.25E-2这种形式代表数学中的
1.25×10-2。可以看出,上面所列举的3个数,其大小是相同的。由此可见,同一个数可
以有无限种表示方式。
C语言规定:用指数形式表示实数时,E前面必须有数字,E后面必须是整数。
说明:实数只能用十进制表示,上面两种表示方法都是十进制的。
2)实型常量的类型
实型常量有单精度(float)和双精度(double)两种类型,有效数字分别是7位和15

第3章C编程基础知识43
位,最后一位是近似值。
程序中表示实型常量时,可在实型常量后面加F或f,表示它是单精度型,或者加L 
或l,表示它是长双精度型。若什么都不加, 2, ouble型。

如1.则系统默认是d


编程经验:表示长双精度型时,最好用大写L而不是小写l,后者容易被看成
是1。

2.实型数据的存储
单精度和双精度型的数据,都是以浮点数的方式存储的,遵循IEEE(Instituteof 
ElectricalandElectronicsEnginers)754标准。本书只介绍float型数据的存储方式, 
double型数据的存储与float型类似。

float型的任何数据,在存储前都必须先表示为下面的格式: 
(符号)×M 
×2n 
其中,
n 
是指数,
M 
须满足条件:1.0。例如:

0≤
M 
<2.30.875×2

0,要先表示为+1.4 
-0.要先表示为-1.-2

3925, 57×2
这之后,计算机用4字节,分成三部分分别存储符号、指数部分和小数部分。三部分
的位置及所占空间大小如表3-2所示。

表3-
2 
float型数据存储空间的分配

符号位(0或1) 指数部分(n+127) 小数部分(M-1) 
占1位[第31位] 占8位[第30位~第23位] 占23位[第22位~第00位] 
注:表中最右边是第0位,最左边是第31位。


注意:指数部分存储的是n+127而不是n,小数部分存储的是
M 
-1而不是
M 
。
下面分别说明这三部分怎样存储。
(1)符号位。占1位,用0表示正,用1表示负。
(2)指数部分。指数用8位存储,本来也有正负的,但是考虑到前面已经有一个正负
号了,再设一个符号位不合适,所以IEEE754标准规定:将指数部分加上127后再存储, 
例如,若实际指数
n 
为-2,则存储为125;若实际指数
n 
为4,则存储为131 。这样规定的
目的是,指数加上127后不会是负数,故不必设指数的符号位。
(3)小数部分。用23位存储
M 
-1。按照规定,0≤
M 
<2.这样,
M 
的小
M 
满足1.0, 
数点前面将肯定有一个1,因此存储时就可以不存1(将1默认了),而只存小数点后面的
纯小数
M 
-1,例如, 875,只存0.

对于1.875 。这样做可以多存几位小数以提高数据精度。
综上所述,对于30.4,三部分的数据分别如下。

0=+1.

(1)符号位:0(表示正)。
875×2
(2)指数部分:10000011(其值为131,表示实际指数是4)。
(3)小数部分:11100000000000000000000(0.875 )。
表示实际小数是1.
故浮点数30.0的实际存储状态如图3-5所示。
875,



44 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
图3-5 浮点数30.0的存储状态
特别注意:实数的存储不都是精确的。
例如,对实数1.2,其纯小数部分0.2化为二进制是一个无限循环小数:00110011 
00110011…要想存储这个值,需要无限多的内存单元,而实际上存储纯小数部分的空间只
有23位,后面部分只能截掉或进位(0舍1入),所以实际存储的数据是近似值。
1.2的实际存储状态是00111111100110011001100110011010(有进位),它表示的小
数是1.2000000476837158203125,比1.2稍大。
由于有些实数存储不精确,所以在程序中应尽量避免比较两个实型数据是否相等。
下面的程序段的输出结果是“不相等”。 
float x=0.2; 
if(x==0.2) 
printf("相等\n"); 
else 
printf("不相等\n"); 
原因:x是float型变量,存储的是0.2的近似值,有7位有效数字,而常数0.2默认是
一个double型数据,有15位有效数字,它们的大小必然不同。
试一试:运行下面的程序,看运行结果是什么。请解释为什么会出现这样的结果。 
#include <stdio.h> 
int main() 
{ 
float x=-789.124; 
printf("%f\n",x); 
return 0; 
} 
说明:上面介绍的是float型数据的存储方式,double型数据的存储方式与此类
似,只不过指数部分和小数部分的位数更多,分别是11位和52位。
3.实型数据的输出
单精度和双精度型数据都可以用%f或%e(%E)格式输出,%f格式是用小数形式输
出,%e(%E)格式是用指数形式输出。例如: 
float x=12345678,y=0.00314; 
double z=123.456789123456789; 
printf("%f, %f, %f\n",x,y,z); //%f: 小数形式输出
printf("%e, %e, %e\n",x,y,z); //%e: 指数形式输出
printf("%E, %E, %E\n",x,y,z); //%E: 指数形式输出,结果中E 大写

第3章 C编程基础知识 45 
该代码段的输出如下: 
用%f时,默认输出6位小数(不管有多少位有效数字,总是输出6位小数)。
说明:用%f格式输出时程序员可以用诸如%.3f这样的方式指定小数位数,参
见第4章printf()函数的介绍。
用%e(%E)时,按标准格式输出,即小数点前有且仅有1位非0的有效数字。而小数
点后面有几位小数以及e(E)后面的指数部分占几位,取决于编译器。
3.2.3 字符型数据
数据不仅仅指数值,还包括字符,字符也是C程序中经常要处理的数据。C语言可以
处理的字符有英文字母(大小写)、数字、标点、空格及其他一些符号,见附录D。
1.字符常量
单个的字符是字符常量。程序中要表示一个字符常量,不能直接写字符名,因为会引
起二义性。例如: 
int a=1; 
char c=a; //此处的a 是变量a 还是字符a? 
为了区分变量和字符常量,C语言规定:字符必须放在一对单引号之中。例如: 
char c1='a',c2='A',c3=' '; / /正确
这样,C语言中,单引号就被赋予了一个含义,即它是定界符,用来表示一个字符的前
界和后界。也就是说,在C语言中,单引号(’)已经不是单引号了,而是定界符。
说明:程序中的单引号是不分左右的。
那么,C语言中若用到单引号,怎么写呢? 显然不能写成''',因为这样写编译器会把它
们都认作定界符。为了表示中间的单引号不是定界符,而是单引号,C语言又做了规定: 
在中间的单引号前面加上一条反斜线(\),表示它不是定界符,而是单引号,所以,程序中
单引号应该表示成\' '',例如: 
char ch='\''; //给变量ch 存入一个单引号
printf("%c",ch); //%c 表示要输出一个字符
输出结果如下: 
反斜线(\)的作用是把后面字符的本来含义转为另外的含义。例如,\' n',若不加反斜
线代表字符n本身,加上反斜线后就变成了换行符。
这种用\开头的字符,称为转义字符。C语言中的转义字符很多,表3-3列出的是常

46 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
用的一些转义字符及作用。
表3-3 常用的转义字符及作用
转义字符代表的含义输出该字符的结果
\' '' 一个单引号(') 输出:' 
\' "' 一个双引号(") 输出:" 
\' \' 一条反斜线(\) 输出:\ 
\' b' 退格键(Backspace) 光标向前移动一格(退回一格) 
\' n' 换行符光标移动到下行开头
\' r' 回车键(CR) 光标移动到本行开头
\' t' 水平制表符(Tab) 光标移动到下一个Tab位置
\' ooo' 
如\' 101' 
一个字符,该字符的ASCII码值用八进制
表示是ooo 
输出该字符。注:ooo代表八进制数, 
最多3位
\' xhh' 
如\' x41' 
一个字符,该字符的ASCII码值用十六进
制表示是hh 
输出该字符。注:hh代表十六进制
数,最多2位 
考考你:到目前为止,要表示字符'A' ,共有3种方法,你知道是哪3种吗? 
2.字符数据的存储
计算机中只能存储0和1,任何数据都必须先化成0和1才能存储,字符也不例外。
多数计算机都是用“存字符的ASCII码值”的方法来存储字符。
基本ASCII码表(见附录D)中只有128个字符,加上后来扩充的128个,不过才256 
个,所以C语言规定:字符用1字节存储。
图3-6 字符数据的存储
设有如下代码: 
char c1='A',c2='1'; 
则c1和c2两个变量在内存中的存储状态如图3-6所示。
想一想:整数1在内存中怎样存储,与字符'1'是
一样的吗? 它们的十进制数分别是多少? 
3.字符数据的大小
由于字符在计算机中实际存储的是其ASCII码值,是个整数,所以,C语言认为字符
也有大小,其ASCII码值就是它的大小。
因此,字符数据既可以用作字符,也可以用作整数。给字符变量赋值时,既可以赋字
符,也可以赋整数。例如: 
char c1='A',c2=65,c3=' ',c4='1'; 
printf("%c,%c,%c,%c\n",c1,c2,c3,c4); //%c 表示要输出一个字符
printf("%d,%d,%d,%d\n",c1,c2,c3,c4); //字符可当作整数输出
printf("%d,%d,%d\n",c1+c2,c1+1,'A'+1); //字符可参与运算

第3章 C编程基础知识 47 
运行结果如下: 
把整数当成字符输出也可以,只要不超过255。例如,printf("%c",65),结果是A。
4.字符数据的输出
如前面代码所示,输出字符型数据,可以用printf()函数(%c或%d格式)。除了
printf()函数之外,还可以使用putchar()函数,putchar()函数的使用方法将在第4章中
介绍。
3.2.4 字符串
程序中有时候需要用到一串字符,即字符串,而不是一个字符。
1.字符串的表示
C语言规定,字符串必须用一对双引号括起来,如"abc"、"12"、""、"A"。双引号中
可以没有字符,如"",表示一个空串。
说明:程序中的双引号也是不分左右的。
C语言中只有字符串常量,没有字符串类型的变量。
2.字符串的存储
虽然没有字符串变量,但字符串在处理时,仍要在内存中找空间存储。
图3-7 字符串的存储
存储字符串时,总是先把双引号中的每个字符按顺序存储到
内存中(连续存放),然后再在后面多存一个空字符('\0')。例如, 
"AB"在内存中占3字节,存储状态如图3-7所示。
空字符的ASCII码是0,表示为\' 0'。
之所以最后要多存一个空字符,是为了给字符串加一个结束
标志,不然,将来使用字符串时,不知道字符串是到哪结束的。
注意:空字符和空格并不是同一个字符,空字符的ASCII 
码是0,空格的ASCII码是32,它们的存储状态不同,数值大小不同,作用也不同。
考考你:"A"和'A'是否相同? 若不同,区别是什么? 
3.字符串的输出
输出字符串时,printf()函数中要使用%s格式。例如: 
printf("%s%s%s%s%s\n","ABCD"," ","123","","xyz"); 
输出结果如下:

48 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
除了printf()函数外,还可以使用puts()函数输出字符串,puts()函数的使用方法将
在10.3节中介绍。
3.3 符号常量和常变量
除了前面已经讲述过的几种常量外,C语言中还有两种常量:符号常量和常变量。
3.3.1 符号常量
为方便编程,增加程序的可读性,程序中经常需要定义符号常量。例如: 
#define PI 3.141593 
其中的PI称为符号常量,它代表后面的3.141593。
说明:其实这是一条编译预处理命令,称为宏定义,参见第9章。
注意:符号常量的定义是一条命令,不是语句,故后面不需要有分号。
定义符号常量之后,程序中用到圆周率时,既可以写3.141593,也可以写PI,如“s= 
PI*r*r;”。显然,使用后者更方便,这便是定义符号常量的第一个好处。
定义符号常量的第二个好处是便于修改程序。例如: 
#define NUM 60 
用NUM 代表人数60。假设程序中很多地方都用到这个NUM,当人数发生变化时,例如
少了一个人,只需要把上面代码中的60改为59即可。若不用符号常量,程序中都写成了
60,当人数减少时,需要修改多处源代码。
定义符号常量还有一个好处:增加程序的可读性。若写成60,阅读程序的人看到60并
不一定把它当成人数,还可能把它当做年龄、体重、分数等,写成NUM 则不易引起误解。
符号常量只是个符号,它不是变量,内存中没有它的空间,所以不能赋值,也不能这样
定义: 
#define PI=3.141593 //错误的符号常量定义
符号常量名通常使用大写字母。
3.3.2 常变量
有些C编译器中允许定义常变量(有些书上称为常量),常变量的定义方法如下: 
const int n=60,m=50; //定义两个常变量并初始化
const float x=3.14; //定义一个常变量并初始化
常变量定义要用const开头,后面部分与变量的定义类似,只不过要初始化。
注意:常变量在定义时必须初始化。

第3章 C编程基础知识 49 
说明:常变量的定义是一条语句,后面有分号。
常变量其实也是变量,也在内存中分配空间(同时要初始化),但初始化后就不允许再
变了。因为不能变,具有常量的特点,故称为常变量。
常变量只可以赋初值,不能赋值,因为赋值就等于是改写。
下面的代码有两处语法错误: 
const float x; //错误,常变量定义未赋初值
const int a=1; 
a=1; //错误,常变量不允许赋值
定义常变量之后,程序中可以随时使用它,但不能改变它。
3.4 运算符和表达式
本节介绍C语言中最基本的运算符和表达式。
3.4.1 算术运算符
算术运算是最常用的运算,C语言中的算术运算与数学中的算术运算不尽相同。
1.算术运算符
算术运算符有7个:+(正号)、-(负号)、*(乘)、/(除)、%(求余)、+(加)、-(减)。
说明:C语言中,乘号用*表示,且不可省略。例如,a*b不能写成ab。
%是求余运算符,它用来求出两个整数相除之后的余数。例如,8%3的值是2,20%7 
的值是6。
说明:求余运算的结果,其符号应与%前面那个数的符号相同。例如,-5%3的
结果是-2,而5%-3的结果是+2。
求余运算符要求参与运算的两个量必须都是整数。其余运算符对此没有要求,参与
运算的数可以是整数,也可以是其他类型的数据。
2.算术运算符的目数
7个算术运算符中,+(正号)、-(负号)都是单目运算符,剩下5个都是双目运算符。
单目运算符是指它只需要一个运算量(即操作数),例如-5,只需要在负号后写一个数,前
边不需要,故它是单目运算符。双目运算符则需要两个运算量,例如,a+b,加号前后各需
要一个运算量。
3.算术运算符的优先级
算术运算符中,+(正号)、-(负号)的优先级别最高,*、/、%的优先级别次之,+(加
号)、-(减号)的优先级别最低(参看附录E)。
运算符和
表达式以
及类型转
换

50 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
根据优先级可知,表达式-5*-2+2%3与((-5)*(-2))+(2%3)运算次序
相同。算
术运算时,当两个运算符的优先级别不同时,先运算哪个取决于优先级;当两个运
算符优先级别相同时,先运算哪个取决于它们的结合性。
4.算术运算符的结合性
算术运算符中,两个单目运算符的结合性都是自右至左,简称右结合性。其余运算符
的结合性都是自左至右,称为左结合性。
左结合性是指:当两个运算符优先级别相同时,要先算左边的。例如,20*3%7,应
先算左边的乘法,相当于是(20*3)%7,而不是20*(3%7),两者结果不同。右结合性是
指:当两个运算符优先级别相同时,要先算右边的。例如,-+2,相当于是-(+2)。
考考你:既然+和*都是左结合性,那为什么a+b*c要先算b*c? 
关于算术运算,需要特别注意的是,两个整数运算,最终结果还是整数。例如,9/5的
结果是1,1/2的结果是0,-5/3的结果是-1。结果为负时,多数机器采用向零取整的方
法将小数截掉,不四舍五入。
说明:向零取整是指,尽量使取整后的结果绝对值更小,离0更近。比如-9/5, 
可以是-1,也可以是-2,但-1的绝对值更小,故取整后值为-1。
试一试:已知华氏温度到摄氏温度的转换公式是c=5/9*(f-32)。下面程序段
用来计算摄氏温度,其中华氏温度f是由键盘输入的,请写出完整的程序并运行之,看结
果是否正确。若不正确,找出原因。 
int f,c; 
scanf("%d",&f); //输入整数作为华氏温度
c=5/9*(f-32); 
printf("%d\n",c); 
提示:上面程序中,若想让5/9的结果是实数,应该写成5./9(或5/9.),让其中
一个数变成实数,其结果就是实数(带“.”的数,系统默认是double型)。
3.4.2 赋值运算符和赋值表达式
1.赋值运算符 
赋值运算符就是=,表示“存储”,即把赋值号右边表达式的值存给左边的变量,3.1 
节已做过一些介绍,这里再补充三点。
(1)左值的概念。
可以出现在赋值号左边的式子,称为左值(lvalue,即leftvalue)。左值必须有内存空
间且允许赋值。常用的左值是变量,但常变量不是左值。例如: 
int a=1;

第3章 C编程基础知识 51 
const int b=2; 
a=2; //变量作为左值,正确
b=20; //语法错误: 常变量不是左值
(2)C语言中还有一些复合的赋值运算符,表3-4列出的是5个与算术运算有关的, 
还有5个与位运算有关的,将在第15章中介绍。
表3-4 复合的赋值运算符及含义
运算符举 例相当于
+= a+=2 a=a+2 
-= a-=b a=a-b 
*= a*=b+c a=a*(b+c) 
/= a/=b+c a=a/(b+c) 
%= a%=5 a=a%5 
(3)赋值运算符的结合性是自右至左。若有两个赋值号,要先执行右边的。
例如,a=b=2相当于是a=(b=2)。
2.赋值表达式
若一个表达式最后进行的是赋值运算,则该表达式是赋值表达式。
说明:C语言中,一个表达式最后执行的是“什么”运算,就把该表达式称为“什
么”表达式。例如,若最后执行的是算术运算,则它是算术表达式;若最后执行的是逻辑运
算,则它是逻辑表达式…… 
C语言中的所有表达式都是有值的。例如,算术表达式5-2*1的值是3,关系表达
式5>3的值是1(即“真”)等。
赋值表达式也有值。C语言规定:一个赋值表达式的值,等于赋值后左边变量的值。
例如,若赋值表达式是a=3*2,则表达式的值就是赋值后a的值,即6。
赋值表达式的值可以参与运算,如b=5+(a=3*2),执行后b的值是11。
上面表达式的运算过程是:先执行a=3*2,即把6赋给a,再计算5+(赋值表达式
的值),即5+6,得11,最后再把11存入变量b。
赋值表达式的值可以赋给另一个变量,如a=(b=2)。因赋值号是右结合性,故可以
省略括号,写成a=b=2。
还可以再复杂一点:a=b=c=2,它同a=(b=(c=2))等价。
考考你:上面a=b=c=2运算时,a、b、c3个变量哪个最先得到2? 哪个最后得
到? 若赋值表达式没有大小,还能不能像上面这样连等? 
3.4.3 自增自减运算符
自增运算符是++,自减运算符是--。两个运算符都是单目运算符,都是右结合
性,运算优先级与正负号相同,参见附录E。

52 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
自增运算符用于给变量增加一个1,自减运算符用于给变量减少一个1。
自增和自减运算符都有两种用法,本书主要以++为例介绍两种用法的作用及区别。
1.自增运算符
自增运算符++分为前++和后++,写在变量前面的是前++,写在变量后面的是
后++,两者作用不同。例如下面的两段程序代码: 
int i=1,m; 
m=++i; //++写在变量前,是前++ 
printf("%d,%d\n",m,i); 
int i=1,m; 
m=i++; //++写在变量后,是后++ 
printf("%d,%d\n",m,i); 
运行结果分别是: 
之所以出现不同的结果,是因为++i和i++的求解过程不同。
首先需要强调的是,++i和i+ + 都是表达式,且两个表达式的值都是i。只不过, 
两个i的值并不相同。
对于左侧代码中的++i来说,由于++写在前面,所以要先给i加1(变成2),再取i 
的值(即2)作为表达式(++i)的值。
对于右侧代码中的i++来说,由于i写在前面,++写在后面,所以先取i的值(即
1)作为表达式(i++)的值,然后再给i加1,使i变成2。
因此,左侧的“m=++i;”相当于是以下两行代码: 
i=i+1; //先给i 加1 
m=i; //表达式(++i)的值赋给m 
而右侧的“m=i++;”相当于如下两行代码: 
m=i; //表达式(i++)的值赋给m 
i=i+1 //给i 加1 
一句话:表达式++i的值是加1之后的i,而表达式i++的值是加1之前的i。无
论是i++还是++i,求解过程中都给i加了1。
若表达式i++和++i都不参与运算,则它们的作用相同,下面两条语句等价: 
(1)i++; 
(2)++i; 
它们都相当于是“i=i+1;”。
说明:设开始时i=1,则上面两条语句分别相当于: 
(1)1; //这是表达式i++的值与分号构成的语句,此后要给i加1。
(2)2; //这是表达式++i的值与分号构成的语句,此前已给i加1。
由于求解表达式过程中都给i加了1,只不过一个在求值后,一个在求值前,因此,实
际上前面两条语句分别等价于:

第3章 C编程基础知识 53 
(1)1; 
i= i+ 1; //后加
(2)i= i+ 1; //先加
2; 
其中,“1;”和“2;”两条语句没有任何实际意义,可以去掉,所以,最初的两种写法就都
成了:
(1)i=i+1; 
(2)i=i+1; 
因此说,“i++;”和“++i;”的作用完全相同。
2.自减运算符
自减运算符--的用法与++用法类似,不再赘述。
需要指出的是,自增和自减运算都相当于赋值运算,因此它们只能作用于变量,不能
对表达式和常量进行自增或自减。下面写法都是错误的: 
(a+b)++; //相当于写成a+b=a+b+1; 语法错误: a+b 不是左值
2--; //相当于写成2=2-1; 语法错误: 2 不是左值
3.4.4 逗号运算符和逗号表达式
1.逗号运算符 
C语言中,“,”也是一个运算符,称为逗号运算符。
逗号运算符是一个双目运算符,其结合性自左至右,其优先级在C语言的所有运算
符中最低。
2.逗号表达式
1)逗号表达式的格式 
表达式1, 表达式2 
即用逗号把两个式子连接起来。如“2,3”、“a=2,a*3”都是逗号表达式。
2)逗号表达式的求值方法
C语言中的表达式都是有值的。逗号表达式的值等于逗号后面那个式子的值,即表
达式2的值。例如,表达式“2,3”的值是3,表达式“a=2,a*3”的值是a*3的值,即6。
但是,要想计算a*3,必须先执行a=2。
所以,逗号表达式的求值方法是先求解表达式1,再求解表达式2,表达式2的值就是
逗号表达式的值。
3)多重逗号表达式
一个逗号表达式,可以作为另一个逗号表达式中的“表达式1”,例如: 
(a=1,b=2),a+b

54 C语言可以这样学(第2版·MOOC版·题库版·OnlineJudge版) 
上式也是一个逗号表达式,只不过其中内嵌了一个逗号表达式。由于逗号表达式的
结合性是自左至右,上面的表达式可以去掉括号直接写成: 
a=1,b=2,a+b 
它的求值顺序是先执行a=1,再执行b=2,最后求解a+b的值作为整个表达式
的值。还
可以继续不断嵌套,使逗号表达式成为如下模样: 
式子1,式子2,式子3,式子4,… 
其求值方法:自左至右按顺序求解每个式子,最后一个式子的值是整个表达式的值。
编程经验:其实,很多情况下使用逗号表达式的目的并不是求整个表达式的值, 
而是让计算机按顺序去求解每个表达式以完成一个个操作,例如,a=1,b=2,c=3。这种
情况通常发生在需要用一条语句完成几条语句的功能时,但一般情况下尽量不要这样用。
考考你:m=1,2,3,4是逗号表达式还是赋值表达式? 整个表达式的值是多
少? 表达式求解之后,m 的值是多少? 
3.4.5 类型转换运算符
C语言中,有时候需要人为地把某种类型的数据转换为程序需要的类型,这时候就需
要用类型转换运算符。例如,a、b是任意整数,求(a-b)/(a+b)的值。程序代码如下: 
int a,b; 
float result; 
scanf("%d%d",&a,&b); 
result=(a-b)/(a+b); 
printf("%f\n",result); 
运行上面程序,从键盘输入6和4,输出为0,结果不正确。问题出在(a-b)/(a+b) 
这个表达式上,因为分子和分母都是整型数据,相除后结果必然还是整数(0)。
要想得到实数,至少应该把分子和分母中的一个变成实数。其方法是,使用类型转换
运算符把表达式写成(float)(a-b)/(a+b)或(a-b)/(float)(a+b)。
类型转换运算符的格式如下: 
(类型名) (表达式) 
作用是把表达式的值强制转换为指定的类型。
需要指出的是,在类型转换前,系统要先求解表达式的值,然后将该值在运算器中进
行处理、转换,被处理的是运算器中的结果,而不是表达式本身。表达式的类型和数值都
不发生改变。例如: 
float x=3.14; 
int m;