···························································· 第3 章 chapter3 运算符与表达式 本章重点 . 算术运算符和算术表达式。 . 赋值运算符和赋值表达式。 . 各种运算符的优先级。 3.1 运算符与表达式概述 在C语言中,经常需要对数据进行运算或操作,最基本的运算形式通常由一些表示 特定的数学或逻辑操作的符号描述,这些符号称为运算符或操作符,被运算的对象(数 据)称为运算量或操作数。由运算符连接的运算对象组成的符合C语言语法规则的式子 称为表达式,表达式描述了对哪些对象按什么顺序进行什么样的操作。 C语言提供了丰富的运算符,能构成多种表达式,表达式把基本操作都作为运算符 处理,使得C语言处理问题的功能强、范围广。 C语言的运算符可以分为以下13类。 (1)算术运算符(+、-、*、/、%、++、--) (2)关系运算符(>、<、>=、<=、==、!=) (3)逻辑运算符(!、&&、||) (4)位运算符(~、&、|、^、<<、>>) (5)赋值运算符(=及其扩展赋值运算符) (6)条件运算符(? :) (7)强制类型转换运算符(类型) (8)逗号运算符(,) (9)指针运算符(*、&) (10)求字节运算符(sizeof) (11)分量运算符(.、->) (12)下标运算符([]) (13)其他(如圆括号、函数调用运算符()) 本章主要介绍算术运算符、赋值运算符、逗号运算符和强制类型转换运算符等。其 2 8 ◆C 语言程序设计教程 他运算符将在后面的章节中陆续介绍。 在学习运算符时要掌握运算符的几个属性,包括运算符的目数、优先级和结合性等, 同时要注意运算符要求的运算量类型及结果类型。 3.2 基本算术运算符与算术表达式 1.基本算术运算符 算术运算符中,加或正号(+)、减或负号(-)、乘(*)、除(/)、求余(%)为基本算术 运算符,功能和数学中的类似,但用法有很大的不同,以下几点需要特别注意。 (1)+(加法)、-(减法)、*(乘法)、/(除法)、%(求余)为双目运算符,即参加运算时 要求有两个运算量。运算过程中先乘除、后加减,即乘、除(*,/,%)的优先级高于加减 (+,-)。若优先级相同,则按自左至右的结合方向计算,这一点和数学中的运算规则一 致。C语言中,当一个运算量两侧的运算符优先级相同时,则根据运算符的结合性进行 运算。运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左),这5 个算术运算符都是左结合的,例如,运算2-3*4表达式时,3的两侧的运算符为-和*, 按优先级先乘后加,而运算1+2-3时,2的两侧的运算符的优先级相同,则根据运算符 的左结合性按从左到右的方向运算。 (2)+(正号)和-(负号)为单目运算符,即只要求有一个运算量。C语言中,所有单 目运算符的优先级均高于双目运算符,所有单目运算符都是右结合的,即优先级相同时 按从右到左的方向运算。 (3)+、-、*、/的运算量可以是整型或浮点型数据,结果的类型由运算对象的类型 决定,而“%”要求两个运算量都必须为整型数据,结果也是整型数据,是两个整数相除后 的余数。另外,运算结果的正负号与被除数的符号一致。例如,7%4的值为3,9%10的 值为9,5/2%3的值为2,9%(-2)的值为1。 (4)两个整数相除的结果为整数,舍弃小数部分,如5/3的值是1。但是如果被除数 或除数中有一个负数,则舍入的方向是不固定的。例如,-5/3的运算结果在有的系统中 是-1,而在有的系统中是-2。但多数系统均采用“向0取整”的规则,即取整数时向0 靠拢。例如,5/3的结果是1,-5/3的结果是-1。 2.算术表达式 用算术运算符和圆括号将运算对象连接起来,且符合C语言语法规则的式子称为C 算术表达式,运算量包括常量和变量等。将数学中的表达式写成C算术表达式时,必须 满足C语言的语法规则。例如,12 ab写成C算术表达式应为1.0/2*a*b或1/2.0*a*b, 请考虑为什么不能写成1/2*a*b。 3.C算术表达式的常用规则 (1)乘号不能省。如:ab应写成a*b,否则系统会认为ab为一个变量名。 第◆3 章 运算符与表达式2 9 (2)C算术表达式中可以使用圆括号改变运算顺序,因为圆括号是C语言中优先级 最高的运算符之一,在一个表达式中会最先执行圆括号这个运算符,圆括号可以嵌套使 用,但左右括号必须匹配。 (3)应避免两个运算符并置。如:a*b/-c应写为a*b/(-c)。 (4)由于两个整数相除的结果仍为整数,因此若想让得到的结果为实数,就必须把除 数或被除数转换成实数。如:5/12应写成5.0/12或5/12.0。 (5)表达式中的所有符号要写成一行。如:12 应写成1/2.0。 (6)上下角标不能直接写,需要转换。如:x2→x*x,x1→x1。 (7)调用标准数学函数时,自变量应写在一对括号内。如:|-216|应写成fabs(-216), sin12应写成sin(12),ex 应写成exp(x)。常用的标准数学库函数见附录D。 (8)三角函数的自变量应使用弧度。如:sin50°应写成sin(50*3.1415926/180)。 【例3.1】 把下面的数学表达式写成C算术表达式。 2a +sin(45°) |x -y|e2.3 解: (2*a+sin(45*3.1415926/180))/(fabs(x-y)*exp(2.3)) 【例3.2】 编写程序,区分整型数据和浮点型数据参与除法运算的不同。 #include <stdio.h> int main(){ int a=10; float x,y; x = a/20; y = a/20.0; printf("x=%f\n",x); printf("y=%f\n",y); return 0; } 运行结果: x=0.000000 y=0.500000 当参与除法运算的数据都是整型数据和浮点型数据时,结果是完全不同的,如同我 们在学习、生活和工作中要严格依法依章办事,讲规则,守规则,做任何事情都要一丝不 苟,要做遵纪守法的文明人。 3.3 赋值运算符与赋值表达式 1.赋值运算符 C语言中,“=”称为赋值运算符,用来连接两个操作数,其一般形式为 3 0 ◆C 语言程序设计教程 变量=表达式 其作用是将“=”右面的表达式的值赋给左面的变量,赋值运算符是有方向的,意义 和数学中的等号不同,C语言中用“==”表示相等。 例如: a=3; /*把整数3 赋给变量a,即存储到a 中*/ b=4; a=b; /*把b 的值赋给a 后,a 和b 的值都是4*/ b=a; /*假设赋值之前,还是a=3,b=4,把a 的值赋给b 后,a 和b 的值都是3 */ 赋值运算符是双目运算符,是右结合的,赋值运算符的优先级比较低,仅高于逗号运 算符,例如: a=a+1; /*先计算a+1 的值,然后赋值给变量a,变量a 的存储单元中的原值被覆盖*/ 2.赋值表达式 由赋值运算符将一个变量和一个表达式连接起来的式子称为赋值表达式。既然是 表达式,就应该有一个确定的值,这个值就是被赋值后左边变量的值,例如,表达式a= 3+2的值就是5,可以将一个赋值表达式再赋给一个变量,例如表达式: b=a=3+2 在a的两侧有两个赋值运算符,先运算哪一个由运算符的结合性决定,赋值运算符是右 结合的,所以运算顺序为从右至左,先运算右边的赋值表达式a=3+2,前面的表达式相 当于b=(a=3+2),先运算a=3+2,a的值是5,赋值表达式a=3+2的值也是5,再将5 赋给b,变量b的值为5,同时表达式b=a=3+2的值也是5。 3.复合的赋值运算符 赋值运算符与算术运算符和位运算符的结合形成了10个复合赋值运算符,包括: +=,-=,*=,/=,%= (与算术运算结合) <<=,>>=,&=,^=,|= (与位运算符结合) 后5种复合赋值运算符与位运算有关,将在第9章介绍,本章主要介绍前5种。 例如: a+=3等价于a=a+3。 x*=y+8等价于x=x*(y+8)。 a+=a-=a*=a /*若a 的初值为3,则表达式的值为0*/ 此表达式为复合的赋值表达式,它的求解过程如下。 ① 按结合方向用加括号的方法确定计算顺序:a+=(a-=(a*=a))。 ② 改写成常规的写法:a=a+(a=a-(a=a*a))。 ③ 依次计算:a=a+(a=a-(a=9));a=a+(a=a-9);a=a+(a=0);a=a+0; 第◆3 章 运算符与表达式3 1 a=0+0;a=0。 在上面的复合赋值表达式的求解过程中,当运算表达式a=9时,a中的数据3被替 换成了9,因此在运算后面的表达式a-9时,参与运算的a的值是9,而不再是3。 赋值表达式可以包括在其他表达式中。如“printf("%d",a=b);”,执行此输出语句 时,首先计算赋值表达式a=b的值,然后把表达式的值输出。 3.4 不同数据类型的转换 当把一个常量赋值给一个变量或不同的数据类型一起参与运算时,通常要求它们的 类型是一致的,若它们的类型不一致,但都是数值型或字符型,就要先把数据转换成相同 类型后再执行相应的运算。这个转换过程包括两种方式:自动转换和强制转换。 3.4.1 自动转换 自动转换是由C 语言的编译器自动执行的,赋值时的自动转换主要包括以下6 方面: (1)将浮点型数据(包括单、双精度)赋给整型变量时,舍弃实数的小数部分。例如: int i; i=3.54; 当执行上述语句时,变量i中存储的值是右端数据的整数部分3。 (2)将整型数据赋值给浮点型变量时,数值不变,但以实数形式存储到变量中。例如: float a=12; 当上述语句执行结束后,变量a中存储的值为12.0,按%f格式输出时会得到 12.000000。 (3)将double型数据赋给float变量时,截取其前7位有效数字,放到float变量的存 储单元中;将一个float型数据赋给一个double型数据,数值不变,有效位扩展到16位。 (4)将字符型数据赋给整型变量时,将字符型数据在内存中的存储内容(占1字节) 放到整型变量存储单元的低8位(1字节);对于高8位,不同的系统有不同的处理,有的 补0,有的补1。将整型数据赋给一个char型变量时,只将其内存中的低8位原样赋给 char型变量,高8位截去。例如: char c; int i=321; c=i; 当把整型变量i中的321赋给字符型变量c时,只把低8位的数据赋给变量c,即c 的值是65,用%c输出时,输出的结果是字符'A'。赋值情况如图3.1所示。 (5)将带符号的int型数据赋给long型变量时,将int型数据放入long型变量的低 16位;高16位实行符号位扩展,即若int型数据为正数,则long型变量的高16位补0,否 3 2 ◆C 语言程序设计教程 图3.1 整型数据赋给字符型变量 则补1。将long型数据赋给int型变量时,只需要将long型数据的低16位放入int型变 量,高16位截去,此时数据可能发生变化。 例如: long a=65536; int b; b=a; 将长整型变量a中的65536赋给整型变量b时,只把低16位赋给了b,即b的值是 0,赋值情况如图3.2所示。 图3.2 长整型数据赋给整型变量 (6)将unsignedint型数据赋给longint型变量时,把unsigned型数据放入long型 数据的低16位,高16位补0。将unsigned型数据赋给一个所占字节相同的整型变量时, 数据将原样放入非unsigned型变量,但可能会因超出范围而出错,这是由于unsigned型 数据的范围要比相应的非unsigned型数据的范围大。将非unsigned型数据赋给长度相 同的unsigned型变量时,也是原样照赋(符号位的数字作为数值一起传送)。 图3.3 不同类型的数据 转换规则 从以上几方面的赋值情况可以看出,不同类型的整型数据之间的赋值实质上就是按 存储单元中的存储形式直接传送。将占用字节数少的整型数据赋给占用字节数多的变 量时,要对高位部分补0或补1;相反,将占用字节数多的整型数据赋给占用字节数少的 变量时,只截取低位部分赋值,高位部分舍掉,因为内存单元中 的数据是按补码存放的,这里涉及补码的知识,不再详述。 整型、浮点型和字符型数据可以进行混合运算,不同类型 的数据在同一表达式中参与运算时也会发生自动转换,将不同 类型的数据先转换成同一类型,然后进行运算。87ANSIC的 转换规则如图3.3所示。 在图3.3中,向左的箭头表示必定的转换。例如,若表达 式中出现了short型或char型,则必定转换为int型进行运 算;若为float型,则必定转换为double型,以提高运算精度。 第◆3 章 运算符与表达式3 3 向上的箭头表示当运算对象为不同类型时的转换方向。例如,int型和float型数据 进行运算时,首先float型必定转换为double型,int型直接转换为double型,然后两个 double型进行运算,结果为double型。 从图3.3中可以看出,表达式中只要有一个float型或double型数据,结果就必定是 double型。例如,若已知:“intx;floaty;doublem;longn;”,则计算表达式1+'b'+ x/y-m*n时,按各运算符的优先级和结合性,表达式的运算顺序如下。 ① 进行x/y运算,将x和y都转换成double型,然后相除,结果为double型。 ② 将n转换成double型,然后进行m*n运算,结果为double型。 ③ 进行1+'b'的运算,将'b'转换为int型数据98,然后相加,结果为int型数据99。 ④ 将1+'b'的结果转换成double型后与x/y的运行结果相加,结果为double型。 ⑤ 将1+'b'+x/y的结果与m*n的结果相减,最后表达式的结果类型为double型。 3.4.2 强制类型转换 强制类型转换通过强制类型转换运算符实现。 一般形式为 (类型标识符)表达式 例如: int a; float b,c; (int)b /*将b 中的值转换为int 型*/ (int)(b+c) /*先计算b+c,然后将b+c 的值转换成int 型*/ 【说明】 (1)“(类型标识符)”称为强制类型转换运算符,作用是将其后面的表达式的值转换 为括号中指定的类型。C语言的运算符非常丰富,优先级各不相同,强制类型转换运算 符的优先级高于算术运算符,在执行表达式(int)b+c时,首先把b的值转换成int型,然 后进行加法运算。 (2)在对变量进行强制类型转换时,得到的结果并没有存放到原来的变量中,即不改 变原来变量的类型,只是得到一个中间值。 【例3.3】 强制类型转换。 程序如下: #include "stdio.h" int main() { float a; int b; a=3.56; b=(int)a; printf("a=%f,b=%d",a,b); } 3 4 ◆C 语言程序设计教程 运行结果: a=3.560000,b=3 【说明】 根据运行结果可以看出,被强制类型转换的变量a中的值并没有改变,因 此可以说明转换时只是得到了一个中间结果。 3.5 自增、自减运算符 自增运算符和自减运算符都是单目运算符,它们只能和一个单独的变量组成表达 式。自增运算符和自减运算符的符号表示分别为“++”和“--”,它们的作用分别是使 变量的值增加1和减少1。例如,设i为int型变量,若想使i的值增加1,则可以用表达 式++i或i++,此时++i和i++的结果是相同的。 当++i和i++作为其他表达式的一部分时,结果是不同的。这是由于++i是先使i 的值加1,然后i参与表达式的运算;而i++是i先参与表达式的运算,然后使i的值加1,这 样就会使整个表达式的运算结果不同。自减运算与自增运算类似,只是把加1改成减1。 【例3.4】 自增运算符的应用。 程序如下: #include "stdio.h" int main() { int i,j,m,n; i=8;j=8; m=i++; /*相当于m=i; i=i+1;的组合*/ n=++j; /*相当于j=j+1; n=j;的组合*/ printf("i=%d,m=%d\n",i,m); printf("j=%d,n=%d",j,n); } 运行结果: i=9,m=8 j=9,n=9 【说明】 (1)自增运算符和自减运算符只能用于变量,不能用于常量或表达式。 (2)“++”和“--”是单目运算符,优先级高于所有的双目运算符,仅次于圆括号运 算符,结合方向是右结合的。 3.6 逗号运算符与逗号表达式 在C语言中,逗号不仅可以作为分隔符,还可以作为一种运算符,用来把两个或多个 表达式连接起来。逗号表达式的一般形式为 第◆3 章 运算符与表达式3 5 表达式1,表达式2,…,表达式n 逗号运算符也称顺序求值运算符,逗号表达式的运算过程是按从左到右的顺序逐个 运算每个表达式,即先运算表达式1,再运算表达式2,以此类推,直到表达式n运算结 束,整个逗号表达式的值和类型就是最右边的表达式n的值和类型。 逗号运算符是双目运算符,优先级最低,其结合方向是从左到右。 例如: a=3*5,a*4 /*先计算赋值表达式的值,结果是a 的值为15,逗号表达式的值为60*/ (a=4*5,a*3),a+10 /*先进行圆括号内的表达式运算,结果a= 20,括号内的逗 号表达式的值为60,整个逗号表达式的值为30*/ 3.7 小 结 本章主要介绍了一些常用的运算符,包括赋值运算符、算术运算符、自增自减运算 符、强制类型转换运算符和逗号运算符。其中,赋值运算符的符号是“=”,它的作用是将 “=”右面的数据赋给左面的一个变量,它是有方向的,它不是数学中的等号。算术运算 符中,注意“/”和“%”的特别之处,其中,除法运算符“/”两边的运算量若都为整型,则结 果为整型,若有一边为浮点型,则结果为浮点型。而求余运算符“%”要求运算符两边的 运算量必须为整型。自增和自减运算符“++”和“--”在使用时有很多的细节问题,一 些写法的运行结果可能会出乎你的预料,在不知道确切的结果时不要随意使用,例如当 i=2时,经过赋值运算j=i+++i+++i++之后,j=6,i=5。所以在使用这两个运算 符时,应该仅使用其最简单的形式。 学习运算符必须掌握各个运算符的相关属性,如运算符的目数、优先级和结合性。 例如,自增自减运算符是单目运算符;逗号运算符的优先级最低,其次是赋值运算符。所 有单目运算符都是右结合的,所有单目运算符的优先级都高于双目运算符等。 在C程序中,当涉及计算数学中的表达式的值时,要注意写成符合C语言语法规则 的C算术表达式,要保证值不变,包括分数的处理、添加一些必要的圆括号等。 习 题 3 一、选择题 1.若有以下类型的说明语句: char w; int x; float y; double z; 则表达式w*x+z-y的结果为类型。 A.float B.char C.int D.double 3 6 ◆C 语言程序设计教程 2.若x为int型变量,x=6;则执行以下语句后,x的值为。 x+=x-=x*x; A.36 B.-60 C.60 D.-24 3.若题中变量已正确定义并赋值,则下列符合C语言语法的表达式是。 A.a%=7.6 B.a++,a=7+b+c C.int(12.3)%4 D.a=c+b=a+7 4.已知a和b是int型变量,x和y是float型变量,且各变量已经被有效赋值。正确 的赋值语句是。 A.a=1,b=2 B.y=(x%2)/10; C.x*=y+2; D.a+b=x; 二、阅读程序,写出运行结果 #include "stdio.h" int main() { int m=1,n=10,a,b; a=m++; b=++n; printf("a=%d,b=%d,m=%d,n=%d\n",a,b,m,n); } 运行结果: 。 三、编程题 1.将华氏温度转换为摄氏温度和绝对温度(下式中,c表示摄氏温度,f表示华氏温 度,k表示绝对温度)。 c=59 (f-32) k=273.16+c 在程序中使用scanf输入f的值,然后计算c和k的值。程序运行两次,为f输入的 值分别是34和100(注意程序中数据类型和输入/输出语句的格式)。 2.已知整型变量a=6,请编程计算并输出下列赋值表达式的值。 a+=a-=a*=a 3.输入三角形的三边长,求三角形的面积。求三角形面积的方法为s(s-a)(s-b)(s-c), 其中,a,b,c为三边长,s=(a+b+c)/2。