C运算符与类型转换
写在前面:本文承接《数据的表现形式》,继续整理C语言中“数据如何参与运算”这一部分内容。文章从初学阶段最常见的运算符出发,说明它们的基本用法、优先级、结合性以及混合运算中的类型转换。部分较复杂、暂不影响后续学习的规则不在本文展开。如有错误或不足,烦请斧正。
数据有了表现形式,还要经过运算才能真正参与程序的处理。加减乘除只是其中最直观的一部分;比较、逻辑判断、赋值和类型转换,同样由运算符完成。
C语言中的运算符很多。按常见教材的分类,主要包括算术运算符、关系运算符、逻辑运算符、强制类型转换运算符、赋值运算符、条件运算符、逗号运算符、求字节数运算符、指针运算符、位运算符、成员运算符、下标运算符、函数调用运算符,本文主要介绍和说明前8种运算符。
算术运算符
算术运算符包括:加号+ 减号- 乘号* 除号/ 求余运算符% 自增运算符++ 自减运算符–
其中加减乘号同数学。
除号:
当两个运算对象都是整数时,进行整数除法,结果仍为整数;若结果含有小数部分,则直接向零截断,而不是四舍五入。例如,
5 / 2的结果为2,-5 / 2的结果为-2。当至少一个运算对象是浮点型时,系统会先按照通常算术转换确定两者的共同类型,再进行浮点除法。例如,
float型与int型相除,结果类型为float;double型与int型相除,结果类型为double。商的正负遵循数学的符号运算规则。除数不能为
0,否则程序的行为未定义。
求余运算符:
%的两个运算对象必须都是整数,结果也为整数。结果不为0时,其符号与被除数相同。例如,-5 % 2的结果为-1。除数同样不能为0。自增运算有
i++和++i两种形式。i++产生的是i自增前的值,同时使i的值加1;++i先使i的值加1,再产生自增后的值。只有当表达式的值被使用时,二者的区别才会体现出来;若单独写成i++;或++i;,效果相同。#include<stdio.h>intmain(){intm,i=1;m=i++;printf("%d %d",m,i);return0;}/* 输出结果: 1 2 */#include<stdio.h>intmain(){intn,i=1;n=++i;printf("%d %d",n,i);return0;}/* 输出结果: 2 2 */自减运算符同理。
二元(双目)算术运算符中,
*、/、%的优先级相同,并且高于+、-;同一优先级内从左向右结合。自增、自减等单目运算符的优先级见文末汇总。
关系运算符
关系运算符有大于(
>)、大于或等于(>=)、小于(<)、小于或等于(<=)、等于(==)、不等于(!=),其中>>=<<=优先级相同,==!=优先级相同,前四者优先级高于后两者。用关系运算符将两个数值或数值表达式连接起来的式子,称为关系表达式。关系表达式的结果类型为
int,关系成立时值为1,不成立时值为0。关系表达式自左向右结合。
若a=1,b=3,c= -1, 则a>b的值为0,a>c的值为1; d=a>b>c,则d的值为1,因为a>b的值为0,下一步0>c的值为1,赋给d. 若d=5,则d==a>b>c的值为0,注意区分=和==,前者是赋值运算符,后者是等于号, d=a>b>c是连接数值和关系表达式的赋值表达式,最终把值赋给d, d==a>b>c是关系表达式。 d = a > b > c等价于d = ((a > b) > c); d == a > b > c等价于d == ((a > b) > c)。因此,C语言中的
a > b > c并不表示数学中的“a > b且b > c”。若要表达这一含义,应写成a > b && b > c。
逻辑运算符
逻辑运算符按优先级从高到低有非(
!)、与(&&)、或(||)。C语言在判断条件时,将
0视为“假”,将任何非零值视为“真”。但“非零为真”不表示非零值会自动变成1;关系运算和逻辑运算的结果才是int型的0或1。&&只有两个运算对象均为“真”,表达式才为“真”,||只要两个运算对象中有一个为“真”,表达式就为“真”。&&和||都从左向右判断,并具有短路特性:对于&&,若左边已经为假,右边不再计算;对于||,若左边已经为真,右边不再计算。a=4,则!a的值为0; 表达式5>4&&2>0的值为1; 表达式5>4||2>0的值为1; 表达式5>4||2<0的值为1; 表达式5<4||2<0的值为0;
强制类型转换运算符
强制类型转换运算符可以将一个表达式的值转换为指定类型,一般形式为
(类型名)表达式;表达式较复杂时,可以写成(类型名)(表达式).(double)a// 将a的值转换为double型(float)(7%3)// 将7 % 3的结果转换为float型强制类型转换会产生一个指定类型的转换结果,但不会因此改变原变量本身的类型。仅仅写出
(double)a,a的类型和值都不会改变。类型转换过程中可能丢失信息,例如(int)3.8的结果为3。
赋值运算符
将右边的值赋给左边的变量,即存放数据,符号为
=,结合性为自右向左。inta;a=3;//将3赋给变量a除基本赋值运算符
=外,赋值运算符还包括+=、-=、*=、/=、%=等复合赋值运算符。基本赋值会将右边的值转换为左侧变量的类型,再存入左侧变量,结合性为从右向左。
条件运算符
一般形式为:
表达式1?表达式2:表达式3,若表达式1为非零值,只计算表达式2,并将其结果作为整个条件表达式的值;若表达式1为0,只计算表达式3。如
(a>b)?x:y,含义是:如果a>b成立,则该条件表达式的值为x,否则为y.
逗号运算符
一般形式:
表达式1,表达式2,表达式3,...,表达式n,各表达式按照从左向右的顺序依次计算,整个逗号表达式的值与最后一个表达式的值相同。inta,b,c;a=1,b=2,c=a+b;//先把1赋给变量a,再把2赋给变量b,最后把a+b的值赋给c,//整个表达式的值也为3.注意,变量声明和函数参数列表中的逗号只是分隔符,不是逗号运算符。例如,
int a, b, c;中的两个逗号都不是逗号运算符。
求字节数运算符
sizeof用于求某个表达式的类型或指定类型所占的字节数,一般写作sizeof(表达式)或sizeof(类型名)。它是运算符,不是函数,结果类型为size_t。#include<stdio.h>intmain(void){inta;printf("%zu\n",sizeof(a));return0;}//多数情况下输出结果为4,因为多数编译系统给基本整型分配4字节
不同数据间的混合运算与类型转换
- 本文涉及的运算符,优先级从高到低可概括为:后缀自增、自减 > 单目运算符与强制类型转换 >
*、/、%>+、-><、<=、>、>=>==、!=>&&>||> 条件运算符 > 赋值运算符 > 逗号运算符。圆括号可以用来明确表达式的组合方式。 - 优先级和结合性决定的是表达式怎样组合,不等于各部分实际的求值顺序。遇到复杂表达式时,最好主动加上括号,或者将其拆成多条语句。
- 同一优先级内,本文涉及的大多数二元运算符从左向右结合;单目运算符、条件运算符和赋值运算符从右向左结合。
- 不同类型的数据一起进行算术运算时,C语言通常会先把本次运算中使用的值临时转换为同一种类型,再进行计算,原变量的类型不会因此改变。例如,
int型与double型运算时,int型的值会临时转换为double型;int型与float型运算时,int型的值会临时转换为float型。char、short等较小的整数类型只和整数一起运算时,通常会先转换为int型,这称为“整数提升”。赋值或强制类型转换则会把值转换为目标类型,在这个过程中可能丢失信息。
运算符本身并不难,真正容易出错的是把数学中的直觉直接带进C语言:整数相除不会保留小数,连续关系运算不等于数学中的连写,非零代表“真”却不一定等于1,优先级也只决定表达式如何组合。
遇到稍复杂的表达式时,可以先判断运算对象的类型,再用括号明确组合方式,最后分步验证。至此,数据不再只是被存储,也开始真正参与计算、比较与判断。
学习路上,我们常在。