新编C语言程序设计教程(本科)第3章
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第3章表达式
3.1 表达式基础3.2 算术表达式3.3 赋值表达式3.4 逻辑表达式3.5 位运算表达式3.6 其它表达式
3.1 表达式基础
3.1.1 表达式的组成
1. 运算符
运算符又称操作符, 是一个符号, 它指示在一个或多个操作数上完成某种运算操作或动作。
C语言中, 除了输入、输出及程序流程控制操作以外的所有基本操作都作为运算处理。
如, 赋值运算符“=”、逗号运算符“, ”、括号运算符“()”。
①算术运算符有+、—、*、/、%、++、--
②关系运算符有<、<=、==、! =、>、>=
③逻辑运算符有!、&&、||
④位运算符有<<、>>、~、|、^、&
⑤赋值运算符有=、+=、-=、*=、/=、%=
⑥条件运算符有?:
⑦逗号运算符有,
⑧指针运算符有*、&
⑨求字节数运算符有sizeof
10 强制类型转换运算符有(类型)
11 其它运算符有.、→、( )、[]等
2. 操作数
操作数是运算符的操作对象, 可以是常量、变量、函数与表达式。
常量、变量、函数本身就是简单表达式, 从一般意义上讲, C语言中所有操作数都是表达式。
复杂表达式由运算符连接简单表达式形成。
3.1.2 表达式的书写
C 语言的表达式虽然来源于数学表达式, 是数学表达式在计算机中的表示, 但在书写时应该注意遵循C 语言表达式书写的原则:
(1) C 语言的表达式只能采用线性的形式书写。
例如:
①应写成1/3+i+j*j*j 。
②应写成(a+b )/(c+d )*e+f 。
331j i ++f e d
c b a +*++
(2) C 语言的表达式只能使用C 语言中合法的运算符和操作数, 对有些操作必须调用库函数完成, 而且运算符不能省略。
例如:
①πr 2应写成3.14159*r*r 。
②应写成0.5*a*b*sin (alph )。
③|x -y|应写成fabs (x -y )。
④y+2X 应写成y+pow (2, x )。
a a
b sin 2
1
3.1.3 表达式的分类
C语言表达式种类很多, 有多种分类方法。
我们一般依据运算的特征将表达式分为:
①算术表达式, 如a+b*2.0-3.0/5.0。
②关系表达式, 如x>=y, 关系表达式可以认为是逻辑表达式的特殊情况。
③逻辑表达式, 如(x>=2)&&(x<=8)。
④赋值表达式, 如a=b=c=1。
⑤条件表达式, 如(a>b)?a: b。
⑥逗号表达式, 如a=2, b=a*a, c=sqrt(b)。
3.1.4 表达式的计算
1. 运算符优先级
C语言将运算符的优先级划分为15级, 初等运算( )、[]、→、.优先级最高, 单目运算!、~、++、--、-、(类型)、* 、&、sizeof优先级次高, 算术运算+、-、*、/、%再次之, 然后是关系运算<、<=、==、![KG-*4]=、>、>=, 再然后是逻辑运算&&、||, 条件运算式?: , 赋值运算=、+=、-=、*=、/=、%=, 逗号运算优先级最低, 位运算优先级比较分散。
2. 运算符的结合性
运算符的结合性是指, 优先级相同的运算从左到右进行(左结合性)还是从右至左进行(右结合性), 左结合性是人们习惯的计算顺序。
C语言中, 只有单目运算(!、~、++、--、-、*、&)、条件运算(?: )、赋值运算(=、+=、-=、*=、/=、%=)的结合性是右结合, 其余运算为左结合。
3. 类型转换
一般讲相应的运算只有相应类型的数据才能进行, 不同类型数据进行运算时, 要进行类型转换。
类型转换有自动转换(又称隐含转换)与强制转换两种方式。
(1) 自动转换。
自动转换由系统自动完成, 转换的规则如图3-1所示。
Int usigned long double
↑↑
short,char float
图 3 -1
纵向箭头方向表示必定要进行的转换, short型、char型数据必定先转换成int型, float型数据必定先转换成double型, 再进行运算。
横向箭头方向表示类型自动转换的方向。
由低向高转换, int型最低, double型最高。
int型与usingned型数据进行运算, int型转换成unsigned;int型与long型数据进行运算, int型转换成long型; int型与double型数据进行运算, int型转换成double型。
其它类推。
这种转换是一种保值映射, 在转换中数据的精度不受损失。
(2) 强制转换。
强制类型转换通过类型转换运算进行。
格式: (类型名)(表达式)
将表达式的类型强制转换成类型名指定的类型。
如: float x, y;
(int)(x+y)将表达式x+y的值强制转换成整型
强制类型转换在将高类型转换为低类型时, 数据精度受损, 是一种不安全的转换。
强制类型转换是暂时性的, 一次性的, 并不永久改变所转换表达式的类型。
如: int a;
(double)a为双精度, a依然为整型量。
例3 –1 表达式计算。
(1) float x=2.5, y=4.7;int a=7;
计算x + a%3*(int)(x+y)%2/4
⑦①④③②⑤⑥
①a%3等于1。
②x+y等于7.2。
③(int)(x+y)等于7 。
④a%3*(int)(x+y)等于7。
⑤a%3*(int)(x+y)%2等于1。
⑥a%3*(int)(x+y)%2/4等于0。
⑦x+a%3*(int)(x+y)%2/4等于2.5+0, 结果为2.5。
第二项将由整型自动转换为双精度型进行运算, 计算结果为双精度型。
(2) int a=2, b=3; float x=3.5, y=2.5;
计算(float)(a+b)/2 +(int)x%(int)y
②①③⑦④⑥⑤
①a+b等于5。
②(float)(a+b)等于5.0, 强制转换成float型。
③(float)(a+b)/2等于5.0/2.0, 结果为2.5。
④(int)x等于3, 强制转换成int型。
⑤(int)y等于2, 强制转换成int型。
⑥(int)x%(int)y等于1。
⑦(float)(a+b)/2+(int)x%(int)y等于2.5+1.0, 结果
为35(双精度型)
(3) int i=3; float f=1.0; double d=2.3;
计算10 +′a′+i*f-d
①③②④
①′a′转换成97, 10+′a′等于107。
②i, f转换成双精度型, i*f等于3.0。
③107转换成双精度型, 10+′a′+i*f等于110.0。
④10+′a′+i*f-d等于107.7。
3.2 算术表达式
1. 自增运算
自增运算符: ++
自增运算是单目运算, 操作数只能是整型变量, 有前置、后置两种方式:
++i, 在使用i 之前, 先使i 的值增加1, 俗称先增后用。
i++, 先使用i 的值, 然后使i 的值增加1, 俗称先用后增。
如:i=1999;
j=++ i; /*先将i 的值增1, 变为2000, 后使用。
j的值也为2000*/
j=i++; /*先使用, j的值为1999。
后增, 将i 的值增1, 变为2000*/ 自增运算优先级处于第2级, 结合自右向左。
2. 自减运算
自减运算符: --
自减运算与自增运算一样也是单目运算, 操作数也只能是整型变量。
同样有前置、后置两种方式:
--i, 在使用i 之前, 先使i 的值减1, 俗称先减后用。
i--, 先使用i 的值, 然后使i 的值减1, 俗称先用后减。
如:i=2000;
j=--i; /*先减, 将i 的值减1, 变为1999。
后使用, j的值也为1999*/
j=i--; /*先使用, j的值为2000。
后减, 将i 的值减1, i的值变自减算的优结合性自增算
请特别注意:
(1) 自增、自减运算只能用于整型变量, 不能用于常量或表达式。
(2) 自增、自减运算比等价的赋值语句生成的目标代码更高效。
(3) 该运算常用于循环语句中, 使循环控制变量自动加、减1, 或用于指针变量, 使指针指向下一个或上一个地址。
(4) 表达式中包含有自增、自减运算时, 容易出错, 务请小心。
如:i=1;
(++i)+(++i)+(++i)的值为多少呢?有的计算出9(=2+3+4), 其实这是错误的。
计算时先对整个表达式扫描, i 先自增3次, 由1→2→3→4, 如此计算结果为4+4+4, 等于12。
(i++)+(i++)+(i++)的值又是多少呢?类似上面分析, 应为3, 当然表达式计算完成后i 的值同样改变为4。
3. 运算符的组合问题
C语言的运算符一般为一个字符, 有的由两个字符组成, 在表达式中出现多个字符时如何组合呢?C编译系统在处理时原则上尽可能自左而右将若干字符组合成一个运算符, 标识符、关键字也按同样的原则进行处理。
如i+++j, 解释为(i++)+j, 非i+(++j)。
如要表示i+(++j), 必须加括号。
例3 –2自增自减运算。
/*程序3 -2 , 自增自减运算*/
#include ″stdio.h″
main( )
{int i, j;
i=j=5;
printf(″i++=%d, j--=%d\n″, i++, j--); printf(″++i=%d, --j=%d\n″, ++i, --j); printf(″i++=%d, j--=%d\n″, i++, j--); printf(″++i=%d, --j=%d\n″, ++i, --j); printf(″i=%d, j=%d\n″, i, j);
}
运行结果:
i++=5, j--=5 ++i=7, --j=3 i++=7, j--=3 ++i=9, --j=1 i=9, j=1
3.3 赋值表达式
3.3.1 赋值运算
赋值运算符: =
赋值运算是双目运算, 一个操作数为变量, 一个操作数为表达式。
赋值运算符的右边是表达式, 左边是变量。
赋值运算时先计算右边表达式的值, 然后将右边表达式的值赋给左边变量, 即送给左边变量对应的存储单元, 并以此作为整个赋值表达式的值。
如:int i;
i=3+5%2;/*先计算3+5%2, 得到4, 然后将4 赋给i*/ 。
赋值运算的优先级为第14级, 次低, 结合自右向左。
赋值运算中的表达式操作数最简单的形式是一个常量, 这时让变量
得到一个初值。
如:int i; float x; char ch;
i=100; x=12.345;ch=′A′;
赋值运算中表达式操作数又可以是赋值表达式, 如此可辗转赋值。
如:int x, y, z;
x=y=z=0.0; /*相当于x=(y=(z=0.0)), x、y、z都得到值0.0*/
③②①
3.3.2 赋值类型转换
(1)将实型数据赋给整型变量时, 舍弃实数的小数部分。
如:int i;
i=1.23;/*结果i 的值为1*/
(2) 将整型数据赋给单、双精度型变量时, 数值大小不变, 但以浮点形式存储到变量中。
(3) 将字符型数据赋给整型变量时, 由于字符数据只占一个字节, 而整型变量为两个字节, 将字符的数据放整型变量的低8位, 而对整型变量的高8位进行扩充。
(4) 将基本整型数据赋给长整型变量时, 基本整型数据放长整型变量低16位, 高16位用符号位扩充。
反之将长整型数据赋给整型变量时,只需将长整型数据的低16位原封不动送整型变量中。
例如:
①int i=-1;
long int j;
j=i;
i 的二进制形式: 1000000000000001
j 的二进制形式: 11111111111111111000000000000001 ②long int i =-1;
int j;
j=i;
i 的二进制形式: 10000000000000000000000000000001 j 的二进制形式: 0000000000000001
(5) 将无符号整型数据赋给长整型变量时, 不存在符号扩展的问题, 只需将高位补0即可。
将无符号整型数据赋给一个占字节数相同的整型变量时, 将无符号整型数据原样送整型变量中,并将最高位当作符号位, 如果数据超出相应的整型范围, 将产生数据错误。
如果将整型数据赋给占字节数相同的无符号整型变量, 也是原样照赋, 最高位作数值处理。
例如:
①unsigned int i=65535;
int j;
j=i; /* j的值为-1*/
②int i =-1;
unsigned int j;
3.3.3 复合赋值运算
在基本赋值运算符“=”之前加上任一双目算术运算符及位运算符可构成复合赋值运算符, 又称带运算的赋值运算符。
算术复合赋值运算符: +=、-=、*=、/=、%=
位复合赋值运算符: &=、|=、^=、> >=、< <=
一般形式: 变量☆=表达式
等价于: 变量=变量☆表达式
☆代表任一双目算术运算符或位运算符。
复合赋值运算先进行所带运算, 再进行赋值运算。
复合
赋值运算的优先级同赋值运算。
(1) int a=3;
a+=2 等价于a=a+2
(2) float x=1.2, y=2.1;
y*=x+3.4 等价于y=y*(x+3.4) (3)int a=1, b=2;
b/=a+=1, 等价于b=b/(a=a+1)
例3 –3赋值运算。
/*程序3 -3 , 赋值运算*/
#include ″stdio.h″
main( )
{int i, j;
float x, y;
i=j=1;
x=y=1.1;
printf(″i=%d, j=%d\n″, i, j); x=i+j;
y+=1;
printf(″x=%4.2f, y=%4.2f\n″, x, y); i=i+++j;
x=2*x+y;
printf(″i=%d, x=%4.2f \n″, i, x);
}
运行结果:
i=1, j=1
x=2.00, y=2.10
i=2, x=6.10
3.4 逻辑表达式
3.4.1 关系表达式
关系表达式是逻辑表达式中的一种特殊情况, 关系表达式由关系运算符和操作数组成, 关系运算用于完成数的比较运算。
关系运算有<、<=、==、!=、>、>=, <、<=、>、>=的优先级相同, ==、!=的优先级相同, 前者的优先级高于后者。
关系运算符的优先级低于算术运算, 高于逻辑运算&&、||, 也高于赋值运算。
3.4.2 逻辑表达式
逻辑表达式由逻辑运算符和关系表达式或逻辑量组成, 逻辑表达式用于程序设计中的条件描述。
C语言有!、&&、||等三种逻辑运算。
! 运算的优先级高于算术运算, &&运算高于||运算, &&、||运算低于关系运算, 高于赋值运算。
&&和||是一种短路运算。
所谓短路运算, 是指在计算的过程中, 只要表达式的值能确定, 便不再计算下去。
如逻辑与运算时计算到某个操作数为假, 可确定表达式的值为假, 剩余的操作数不再考虑; 逻辑或运算时计算到某个操作数为真, 可确定表达式的值为真, 剩余的操作数也不再需要考虑。
例如:
(1) e1&&e2, 若e1 为0, 可确定表达式的值为0, 不再计算e2。
(2) e1||e2, 若e1为真, 则可确定表达式的值为真, 也
不再计算e2。
例3 —4用逻辑表达式描述下列条件: (1) x是3 的倍数。
x%3==0
(2) x是偶数。
x%2==0
(3) x是3 的倍数且x是偶数。
(x%3==0)&&(x%2==0)
(4) 100<x<200。
(x>100)&&(x<200)
(5) x等于2或8。
(x==2)||(x==8)
(6) 某年是否为闰年。
(year %400==0)||((year%4==0)&&(year%100!=0))
3.5 位运算表达式
3.5.1 位逻辑运算
1. 按位取反运算
运算符: ~
按位取反运算用来对一个二进制数按位取反, 即0 位变1, 1 位变0。
例如:~25, 25的二进制表示为:
0000000000011001
按位取反得结果:
1111111111100110, 即-26。
~运算常用于产生一些特殊的数。
如高8 位全1 低8 位全0 的数0xFF00, 按位取反后变为0x00FF。
~1, 在16 位与32 位的系统中, 都代表只有最低位为0 的整数。
~运算是位运算中唯一的单目运算, 也是唯一具有右结合性的位运算。
2. 按位与运算
运算符: &
按位与运算的规则是操作数的对应位都为1, 则该位的运算结果为1, 否则为0。
例如:0x29&0x37, 0x29 与0x37 的二进制表示为:0000000000101001与0000000000110111
按位与结果为: 00000000000100001, 即0x21。
按位与运算主要用途是清零及取数的某些位或保留数的某些位。
例如:
(1) a&0, 将a清0。
(2) a&0xFF00, 取数a的高8 位, 低8 位清0。
(3)&000FF取数的低8位高8位清0
3. 按位或运算
运算符: |
按位或运算的规则是操作数的对应位都为0, 则该位的运算结果为0, 否则为1。
例如:0x29 | 0x37, 0x29 与0x37 的二进制表示为: 0000000000101001与0000000000110111
按位或结果为:
0000000000111111, 即等于0x3f。
利用| 运算可将数的部分位或所有位置1。
例如:
(1)a | 0x000F, a的低4 位全置1, 其它位保留。
(2)a | 0xFFFF, a的每一位全置1。
4. 按位异或运算
运算符: ^
按位异或运算的规则是操作数的对应位相同, 则该位的运算结果为0, 否则为1。
例如:0x29^0x37 , 0x29 与0x37 的二进制表示为:
0000000000101001与0000000000110111
按位异或结果为:
0000000000011110, 即等于0x1e。
利用^运算将数的特定位翻转, 保留原值, 不用临时变量交换两个变量的值。
例如:
(1) a^0x00FF, 将数a的低8 位翻转, 高8 位不变。
(2) a^0, 保留数a的原值。
(3) a=a^b, b=b^a, a=a^b, 不用临时变量交换a#, b的值。
位逻辑运算自身的优先级由高到低依次是~、&、|、^, 在整个运算体系中~运算的优先级位于第二级, &、|、^ 运算低于关系运算, 高于逻辑运算。
位逻辑运算意义与逻辑运算一样, 但位逻辑运算对二进制位而言, 且必须对数的所有二进制位进行,而且操作数只能为整数。
如果参与运算的数据长度不同, 将对短的数据先进行高位符号扩展后再进行位运算。
3.5.2 移位运算
1. 左移位运算
运算符: <<
移位运算的左操作数是要进行移位的整数, 右操作数是要移的位数。
左移位运算规则是高位左移后溢出, 舍弃, 空出的右边低位补0。
例如, 15<<2, 15 的二进制表示为:0000000000001111
左移2位结果为:0000000000111100, 等于60。
左移1 位相当于该数乘以2, 左移2 位相当于该数乘以4(22)。
使用左移位运算可以实现快速乘2 运算。
2. 右移位运算
运算符: >>
右移位运算规则是低位右移后被舍弃,空出的左边高位对无符号数移入0。
如果是有符号数, 对正数空出的左边高位移入0; 如果是负数, 则左边移入0 还是1, 要取决于所用编译系统, 有的移入0(逻辑右移), 有的移入1(算术右移)。
例如, 15>>2, 右移2 位, 等于0000000000000011, 即3。
右移1 位相当于该数除以2, 右移2 位相当于该数除以4(2 2)。
使用右移位运算可以快速实现除2 运算。
移位运算低于算术运算,高于关系运算。
位运算与赋值运算可组合成复合赋值运算&=、|=、^ =、< <=、> >= 。
例3 —5取一个整数a的从右端开始的4~7 位。
/*程序3 —5, 取一个整数a的从右端开始的4~7 位*/
#include ″stdio.h″
main( )
{unsigned a, b, c, d;
scanf(″%o″, &a); /*八进制形式输入*/
b=a>>4; /*a右移4位*/
c=~(~0<<4); /*得到一低4位全为1, 其余位为0的数*/ d=b&c; /*取b的0~3位, 即得到a的4~7位*/
printf(″a=%o, a(4~7)=%o″, a, d);
}
输入数据: 331
运行结果: a=331, a(4~7)=15
3.6 其它表达式
3.6.1 条件表达式
条件表达式由条件运算符和操作数组成, 用以将条件语句以表达式的形式出现, 完成选择判断处理。
条件运算符由“?”和“: ”组成。
条件运算是三目运算, 有三个操作数, 一般形式如下: 表达式1?表达式2: 表达式3
表达式1 必须为逻辑表达式, 如表达式1为真, 计算表达式2, 并以此作为整个表达式的值。
如表达式1 为假, 计算表达式3, 并以此作为整个表达式的值。
(1) a>b?a: b
a大于b, 表达式的值为a; a小于b, 表达式的值为b。
实际上求a、b的最大值。
(2) a>0?a: -a
a大于0, 表达式的值为a; a小于等于0, 表达式的值为-a。
实际上求a 的绝对值。
条件运算的优先级为倒数第3 级, 高于赋值运算, 低于逻辑运算, 结合从右至左。
如a>b?a: c>d?c: d, 相当于a>b?a: (c>d?c: d)
例3 —6判断整数的正负。
/*程序3 —6, 判断整数的正负*/
#include ″stdio.h″
main( )
{ int x;
scanf(″%d″, ″&x″);
x>0?printf(″%s″, ″正数″): printf(″%c″, ″负数″);
}。