C语言学习笔记-运算符与表达式
前言:
了解程序中常用的数据类型后,还应该了解如何操作这些数据。因此需要掌握C语言中的各种运算符和表达式。
一、何为表达式?
C语言中,表达式由操作符和操作数组成。简单的表达式只包含一个操作符,复杂的表达式包含两个或者两个以上操作符。例如:
23+23
num+29
iBase+(iPay*iDay)
//num,iBase、iPay、iDay均为变量
表达式可出现在赋值语句的右侧或函数的参数中。表达式可返回一个结果,其数据类型取决于组成表达式的变量和常量的类型。
例:下面的例子定义了3组不同的变量分别是整型变量(int)、浮点型(float)、字符型(char)。
#include <stdio.h>
int main() {
int a;//设置a变量为int类型
float b;//设置b变量为浮点型
char c[] = "This is C program";//设置C变量为字符型,并置c变量为字符串[]
a = 520;//定义a为520
b = 3.14;//定义b为浮点数3.14
printf("%d %f %s\n", a, b, c);打印a,b,c变量
return 0;
}
二、赋值运算符与赋值表达式
程序中通常会遇到的赋值符号"="就是赋值运算符,其作用就是将一个数据赋给一个变量。例如:
iAge=520;
这是赋值操作,将常量赋值给变量iAge。也可以将一个表达式赋值给一个变量。例如:
Total = Counter*3;
1、变量赋初值
在声明变量时,可以为其赋一个初值,就是将一个常数或者一个表达式的结果赋值给一个变量,变量中保存的内容就是这个常量或者赋值语句中表达式的值。这就是为变量赋初始值。
1.1将常数赋值给变量
类型 变量名 = 常数;
int a = 12;
其中的变量名也可以称为变量的标识符。例如:
char cChar = 'A';
int iFirst = 100;
float fPlace = 1430.477f;
1.2通过赋值表达式为变量赋初值
通过赋值语句,可以把一个表达式的结果值赋给一个变量。
类型 变量名 = 表达式;
int a = 12 + 12;
可以看到,与常数赋值的一般形式是相似的。例如:
int iAmount = 12 + 26;
float fPrice = fBase+Day*3;
在上面的代码中,得到赋值的变量iAmount和fPrice称为左值,因为它出现在赋值语句的左侧。
在定义变脸的同时直接为其赋值的操作称为赋初值,也就是变量的初始化。先定义变量,再进行变量的赋值操作也是可以的。例如:
int iMonth;
iMonth = 212;
C语言例子:计算乘坐出租车的费用:
出租车每千米路程需要3元,李女士要到10千米远的地方,后来发现走错了,之后又坐出租车返回3千米,计算她所需要花费的车费:
#include <stdio.h>
int main(){
int a=3;
int go=10;
int back=3;
int money;
money = a*(go+back);
printf("need cost %d",money);
return 0;
}
2、自动类型转换
数值类型有多种,如字符型、整型、长整型、实型等,其变量、长度和符号特性都不相同,取值范围也不同。除此之外,不同数据类型变量间相互赋值时也会进行自动类型转换。例如:把较短的数值类型变量的值赋给较长的数值类型变量时,较短的数值类型变量其值会自动升级为较长的数值类型,数据信息并不会丢失。但把较长的数据类型变量赋值给短的数值类型变量时,数据会降低级别显示。当数据大小超过较短的数值类型的可表示范围时,就会发生数据截断。
ps:有些编译器遇到这种情况时会发出警告:
float i = 10.1f;
int j = i;
3、强制类型转换
通过自动类型转换的介绍可知,如果数据类型不同,系统会根据实际情况自动进行类型转换,但此时编译器会提示告警信息。如果强制类型转换告知编译器,就不会出现警告。
例:强制类型转换的一般形式如下:
(类型名) (表达式)
例如,再上述不同变量类型转换时使用强制类型转换:
float i = 10.1f;
int j = (int)i; /*强制类型转换*/
再代码中可以看到,在变量前使用包含要转换的类型的括号,就对变量进行了强制转换
例子:计算载货区摆放箱子的数量,一辆货车运输箱子,载货区宽2米,长4米,一个箱子宽1.5米,长1.5米,计算可以运输多少个箱子?箱子必须为整数
#include <stdio.h>
int main(){
int width=2,length=4,num;
float f=1.5f;
num = (int)(width/f)*(int)(length/f);
printf("载货区一层能放:%d\n",num);
return 0;
}
解释:
(1)程序首先定义了3个整型变量:width,length,num。其中width赋值为2,length赋值为4,num为计算结果,然后定义了一个实型变量,f表示货物箱子的长、宽。最后用强制类型转换计算承载数。
(2)在强制转换类型代码中,用载货区的宽度除以箱子的宽度再强制转换成int类型。载货区的长度除箱子的长度再强制转换成Int类型,两者相乘,即可算出最大能装载的箱子数量。
练习1:模拟超市抹零,购物时,如果买的东西总价多出1角、2角的情况下,收银员会抹掉,只收取整钱。模拟超市抹零结账的场景,输出结果如下:
实际应付313.04元
抹零后应付313元
代码如下
#include <stdio.h>
int main(){
float cost = 213.04f;
int a = (int)cost;
printf("%d", a);
return 0;
}
练习2:买鞋时,如果37码的鞋子小,38码的鞋子大,则说明脚的尺寸是37.5码。因为没有37.5码的鞋,所以我们一般会买38码的鞋子。利用强制类型转换来模拟此场景。输出结果如下:
您的脚是37.5码的尺寸
您应该购买38码的鞋子
#include <stdio.h>
int main(){
float a = 37.5f;
int b = (int)a;
printf("您的鞋码是%.1f, 您应该购买%d号鞋子\n", a, b);
return 0;
}
三、算术运算符与算术表达式
C语言中,运算符分为单目运算符(使用一个操作数)和双目运算符(使用两个操作数)两种。其中,算术运算符用于实现正、负、加、减、乘、除、求余运算。单目正和单目负运算符的优先级最高。双目运算符中,乘法、除法和取模运算符的优先级比加法和减法运算符高。
1、算术运算符
算术运算符包括两个单目运算符(正/负)和5个双目运算符(即乘法、除法、取模、加法、减法)。具体符号和对应功能如下表所示:
符号 | 功能 | 符号 | 功能 |
---|---|---|---|
+ | 正 | % | 取模 |
- | 负 | + | 加法 |
* | 乘法 | - | 减法 |
/ | 除法 |
其中,取模运算符“%”用于计算两个整数相除得到的余数,运算符的两侧均为整数,如7%4的结果是3.“-”作为减法运算符用时为双目运算符,如5-3;作负值运算符时为单目运算符,如-5等。
说明:
单目正运算符是冗余的,只是为了与单目负运算构成一对才存在。单目正运算符不会改变任何数值,更不会将一个负值表达式改为正值。
1.1算术表达式
使用算术运算符的表达式称为算术表达式,例如:
Number = (3+5)/Rate;
Height = Top-Bottom+;
Area = Height * Width;
需要说明的是,两个整数相除的结果为整数,如7/4的结果为1,舍去的是小数部分。但是,如果其中的一个数是负数时会出现说明情况呢?此时机器会采取“向零取整” 的方法,即为-1,取整后向0靠拢。
注意
参与算术运算的两个数中只要有一个为实型,整个表达式的结果就是double型,这是因为所有实数都是按double型设定的。
例子:用算术表达式计算加速度:
平均加速度,即速度变化量除以这个变化所需要的时间。现有一辆轿车用来8.7秒,从每小时0千米加速到每小时100千米,计算并输出这辆轿车的平均加速度。
//(0+1000)/8.7
#include <stdio.h>
int main(){
int V1=0/3600,V2=100000/3600;
float t = 8.7f;
double a;
a = (V2-V1)/t;
printf("加速度=%d",a);
return 0;
}
2、优先级与结合性:
C语言中规定了各种运算符的优先级和结核性,首先来看一下有关算术运算符的优先级。
2.1算术运算符的优先级
表达式求值时,通常会按照运算符的优先级别从高到低依次执行。算术运算符中,“*”、“/”、“%”的优先级别高于“+”、“-”。如果同时出现“ * ”和“+”,应该先算乘法(与数学运算法则一致)例如:
R=x+y*z
上述表达式中,先进行y*z计算,最后再加上x。使用括号将表达式改为(a+b)*c,可将“+”的运算级别提高,使其先进行运算。注意,括号在所有的运算符优先级别最高。
2.2算术运算符的结合性:
当算术运算符的优先级相同时,结合方向为“自左向右”。例如:
a-b+c
这里C语言解释器将会先从a-b算起,与数学中运算规则相同。
2.3自增/自减运算符
C语言中还有两个特殊的运算符,即自增运算符“++”和自减运算符“–”。自减、自增运算符对变量的操作效果分别是使变量增加1和减少1.
自增和自减运算符可以放在变量的前面和后面,放在变量前面的称为前缀,放在后面称为后缀。一般的使用方法是:
--Counter; //自减前缀符号
Grade--; //自减后缀符号
++Age; //自增前缀符号
Age--; //自减后缀符号
上述是一个简单的代码例子,只有自增和自减,没有其他的表达式运算,因此,运算符是前缀或者后缀不重要,得到的结果是一样的,自减就是变量减1,自增就是变量加1。
注意
当自增、自减运算符出现在表达式内部,作为运算的一部分,前缀和后缀对运算结果的影响是不一样的。如果运算符放在变量前,那么变量将先完成自增或自减运算,再以自增减后的结果参与表达式运算;如果运算符放在变量后,那么变量将先参加表达式运算,之后再进行自增和自减。
例:千变万化的自增、自减运算:
再本实例中定义一些变量,为变量赋相同的值,然后观察表达式中自增、自减运算符作为前缀和后缀时的不同结果。
#include <stdio.h>
int main(){
int iNumber1=3;
int iNumber2=3;
int iResultPreA,iResultLastA;
int iResultPreB,iResultLastB;
iResultPreA=++iNumber1;
iResultLastA=iNumber2++;
printf("This Addself..\n");
printf("the iNumber1 is:%d\n",iNumber1);
printf("the iResultPreA is:%d\n",iResultPreA);
printf("the iNumber2 is:%d\n",iNumber2);
printf("the iResultPreA is:%d\n",iResultLastA);
iNumber1=3;
iNumber2=3;
iResultPreB=--iNumber1;
iResultLastB=iNumber2--;
printf("This Deleteself..\n");
printf("the iNumber1 is:%d\n",iNumber1);
printf("the iResultPreB is:%d\n",iResultPreB);
printf("the iNumber2 is:%d\n",iNumber2);
printf("the iResultLastB is:%d\n",iResultLastB);
}
(1)在程序代码中,定义iNumber1和iNumber2两个变量用来进行自增、自减运算。
(2)在进行自增运算,分别前缀自增和后缀自增。通过程序最终的显示结果可以看到,自增变量iNumber1和iNumber2的结果同为4,但是得到表达式结果的两个变量iResultPreA和iResultLastA却不一样。iResultPreA和iResultLastA的值为3,因为前缀自增使得iResultLastA的变量先进行自增操作,然后进行赋值操作;后缀自增操作是先进行赋值操作,然后进行自增操作。因此两个变量得到表达式的结果值是不一样的。
(3)在自增减运算中,前缀自减和后缀自减与自增运算方式是相同的,前缀自减是先进行减1操作,然后赋值操作;而后缀自减是先进行赋值操作,再进行自减操作。
运行结果如下:
注意
自增、自减运算符是单目运算符,因此表达式和常量不可进行自增、自减运算。例如:,5++,(a+5),++都是不合法的。
练习:
利用自增运算符模拟统计综艺节目《演员的诞生》中丫头的得票数。每个导师投一票,得票数就会增加。输出的结果如下:
观众投票后丫头得156票
导师一投给丫头后得157票
导师二投给丫头后得158票
导师三投给丫头后得159票
以下是代码:
#include <stdio.h>
int main(){
int a = 156;
a++;
a++;
a++;
printf("%d\n",a);
return 0;
}
ps:这里我比较笨蛤,有一个非常简单的办法,如下:
#include <stdio.h>
int main(){
int a = 156;
a += 3; // 将a++三次合并为一次增加3
printf("%d\n", a);
return 0;
}
四、关系运算符与关系表达式
在数学中,经常需要包括>、≥、<、≤、=、≠,6种运算符。
符号 | 功能 |
---|---|
> | 大于 |
< | 小于 |
<= | 小于等于 |
>= | 大于等于 |
!= | 不等于 |
== | 等于 |
1、关系表达式
关系运算符可对两个值进行比较,返回一个真值或者假值。真值为1,表示指定的关系成立;假值为0,表示指定的关系不成立。例如:
7>5
416>=
'a'<'b'
52.46<=45.5
'c'=='c1'
'y'!='t'
误区警示
要注意区分"=“和”= = “,这两个运算符的含义截然不同,前者是赋值运算符,用于将等号右边的值或表达式赋给左边;后者是等于运算符,用于判断左右两侧的值是否相等。
例如:i= =3中”= ="是关系运算符,i=3中“=”是赋值运算符。
注意由于markdown文档原因实际= = 中没有空格
关系运算符常用来构造条件表达式,用在流程控制语句中。
例如:下方if条件语句中,if(iCount<10),用于判断iCount小于10这个关系是否成立,如果成立,则为真,执行大括号中的语句块;如果不成立,则为假,不执行大括号中的语句块。
#include <stdio.h>
int main(){
int a = 10;
if(a!=10)
{
printf("ee");
}else
printf("aa");
return 0;
}
1.1优先级与结合性
关系运算符的结合性是自左向右。使用关系运算符时常常会判断两个表达式的关系,但时由于运算符存在着优先级的问题,因此如果不小心处理则会出现错误。例如:先对一个变量进行赋值,然后判断这个赋值的变量是否不等于一个常数,代码如下:
#include <stdio.h>
int main(){
int a = 10;
if(a=a*2!=20)
{
printf("不等于20");
}else
printf("等于20");
return 0;
}
因为"!=“运算符比”="的优先级要高,所以a!=20的判断操作会在赋值之前实现,变量a得到的就是关系表达式的真值或者假值,这样并不会按照之前的意愿执行。
之前介绍过括号运算符,其优先级具有最高性,因此应该使用括号来表示需要优先进行计算的表达式,例如:
#include <stdio.h>
int main(){
int a = 10;
if(a=(a*2)!=20)
{
printf("不等于20");
}else
printf("等于20");
return 0;
}
输出结果如下:
练习:可乐VS矿泉水
一个小孩跑完步去买水,如果他手里的零钱多于2元,卖家会说:“你可以买可乐”。如果手里的钱少于2元,卖家会说”你可以买矿泉水“试用if语句模拟两人对话的场景:
#include <stdio.h>
int main(){
int money;
printf("请输入金额");
scanf("%d",&money);
if (money <= 2)
{
printf("你可以买矿泉水\n");
}
else
{
printf("你可以买可乐\n");
}
}
代码解释:
(1)定义变量money,用scanf函数接收用户输入的值。
(2)利用If语句进行判断,在判断条件中使用了关系表达式,判断钱数是否成立,如果成立,则返回可以买矿泉水,如果不成立则返回买可乐。
五、逻辑运算符与逻辑表达式
逻辑运算符可根据表达式的真假属性返回真值或假值。在C语言中,表达式的值非零,那么其值为真。非零的值用于逻辑运算,则等价于1;假值总是为0.
1、逻辑运算符
逻辑运算符有3种,如下表所示:
符号 | 功能 | 示例 | 含义 |
---|---|---|---|
&& | 逻辑与 | A&&B | A、B两个表达式都为真时,逻辑运算结果为真。 |
A、B两个表达式只要有一个为假,逻辑运算结果为假 | |||
ll | 逻辑或 | AllB | A、B两个表达式都为假时,逻辑运算结果为假 |
A、B两个表达式只要有一个为真,逻辑运算结果为真 | |||
! | 逻辑非 | !A | A表达式为真,逻辑运算结果为假 |
A表达式为假,逻辑运算结果为真 |
2、逻辑表达式
之前介绍过,关系运算符可用于对两个操作数进行比较。使用逻辑运算符,可以将多个关系表达式的结果合并在一起进行判断。其一般形式如下:
表达式 逻辑运算符 表达式
例如:使用逻辑运算符的几个示例:
Result = Func1&&Func2; //Func1和Func2都为真时,Result为真
Result = Func1||Func2; //Func1、Func2只要有一个为真,Result就为真
Result = 1Func2; //如果Func2为真,Result就为假
逻辑与运算符和逻辑或运算符可用于复杂的表达式中。例如,在后面要学的if、for、while语句等流程控制语句中构造条件表达式。
在程序中,通常使用单目逻辑非运算符“!”把一个变量的数值转换为相应的逻辑真值或假值,也就是1或0。
例如:
Result = !Value; //转换成逻辑值;
2.1优先级与结合性
“&&”和“||”是双目运算符,要求有两个操作数,结合方向自左至右;“!”是单目运算符,要求有一个操作数,结合方向自右向左。逻辑运算符的优先级从高到低依次为:“!”、“&&”、“||”。
1、例子:
模特公司招聘:某模特公司招聘,要求年龄大于等于25岁且身高大于等于1.7米,才通知参与面试和笔试,否则就不能进行面试和笔试。模拟此场景,实现代码:
#include <stdio.h>
int main(){
printf("请输入年龄和体重:");
int age;
float height;
scanf("%d %f",&age,&height);
if(age>=25 && height>=1.7)
{
printf("你过关!\n");
}else
printf("该罚!\n");
return 0;
}
2、编程训练:判断用户是否可以登录网站
在明日学院网站首页中,可以使用账户名登录,也可以使用手机号登录,还可以使用电子邮箱地址登录。请判断某用户是否可以登录。已知服务器中有如下记录,账户名:张三,手机号:1234567890,电子邮箱:zhangsan@163.com。输出结果如下:
the 1 is true,0 is false
用户是否可以登录:0
代码:
#include <stdio.h>
#include <string.h> // 引入字符串处理函数
int main(){
int a = 1234567890;
char email[] = "zhansan@163.com"; // 使用字符数组存储字符串
char name[] = "zhansan"; // 使用字符数组存储字符串
int phone;
printf("请输入用户名:");
scanf("%s", nameInput); // 使用 %s 读取字符串
printf("请输入邮箱号:");
scanf("%s", emailInput); // 使用 %s 读取字符串
printf("请输入手机号:");
scanf("%d", &phone); // 读取整数
printf("用户是否可以登录?\n");
if (phone == a && strcmp(emailInput, email) == 0 && strcmp(nameInput, name) == 0)
{
printf("你可以登录\n");
}
else
{
printf("你不可以登录\n");
}
return 0;
}
六、逗号运算符与逗号表达式
C语言中,可以用逗号用逗号运算符将多个表达式分隔开来。逗号表达式的一般形式如下:
表达式1,表达式2,表达式3,......,表达式n
逗号表达式又称为顺序求值运算符,其求解过程是:先求解表达式1,再求解表达式2,一直求解到n为止。整个逗号表达式的值是表达式n的值。
观察下列的逗号表达式:
Value = (2+5,1+2,5+7);
上述语句中,Value的值为7,而非12.为什么逗号表达式的值不是最后一个表达式的值12呢?问题在于优先级。赋值运算的优先级比逗号运算符高,因此先执行赋值运算。如果要先执行逗号运算,则可以使用括号运算符,代码如下:
Value = (2+5,1+2,5+7);
使用括号后,Value的值为12;
例:逗号运算符:
在本实例中,通过逗号运算符将其他运算符结合在一起的形成表达式,再将表达式的最终结果赋值给变量。根据变量的值分析逗号表达式的计算过程。
#include <stdint.h>
#include <stdio.h>
int main(){
int iValue1,iValue2,iValue3,iResule;
iValue1 = 10;
iValue2 = 43;
iValue3 = 26;
iResule = 0;
iResule = (iValue1++,--iValue2,iValue3+4);
printf("the result is :%d\n",iResule);
return 0;
}
七、复合赋值运算符
复合赋值运算符是C语言所独有的,实际这种操作是一种缩写形式,可使变量操作的描述方式更为简洁。例如:为变量Value赋值,值为Value自身与3相加的结果,代码如下:
Value = Value+3;
使用复合赋值运算符,可以实现同样的操作,但描述更为简洁。代码如下:
Value+=3;
这样可以更为高效的表达逻辑运算,同时也可以简化程序,使代码更为简洁,从而提高编译效率。
对于简单赋值运算符,如Func=Func+1中,表达式Func计算两次;但对于复合赋值运算符而言,只需要计算一次。这种区别对于程序运行没有太大的影响,但如果表达式中存在某个函数的返回值,那么函数将被调用2次。
例:
在本实例中,定义一个整型变量iValue = 7,计算iValue +=iValue*=IValue/=iValue-5的值。
#include <stdio.h>
int main()
{
int iValue;
iValue = 7;
iValue+=iValue*=iValue/=iValue-5;
printf("the reslut is :%d\n",iValue);
return 0;
}
程序运行后输出的结果如下:
1、编程训练:求n的阶乘
阶乘的计算公式为:n!=1x2x3x…xn。例如:4!=1x2x3x4=24。输入n的值,用程序计算出阶乘。输出结果如下:
请输入n的值:
5
5的阶乘是120
八、运算符的优先级总结
1、运算符总结表
优先级 | 运算符 | 含义 | 结合性 |
---|---|---|---|
1 | ( ) | 圆括号 | 自左向右 |
[ ] | 下标运算符 | ||
-> | 指向结构体成员运算符 | ||
. | 结构体成员运算符 | ||
2 | ! | 逻辑非运算符(单目运算符) | 自右向左 |
~ | 按位取反运算符(单目运算符) | ||
++ | 自增运算符(单目运算符) | ||
- - | 自减运算符(单目运算符) | ||
- | 负号运算符(单目运算符) | ||
* | 指针运算符(单目运算符) | ||
& | 地址与运算符(单目运算符) | ||
sizcof | 长度运算符(单目运算符) | ||
3 | *、/、% | 乘法、除法、取余运算符 | 自左向右 |
4 | +、- | 加法、减法运算符 | |
5 | <<、>> | 左移、右移运算符 | |
6 | <、<=、>、>= | 小于、小于等于、大于、大于等于运算符 | |
7 | ==、!= | 等于、不等于运算符 | |
8 | & | 按位与运算符 | |
9 | ^ | 按位异或运算符 | |
10 | I | 按位或运算符 | |
11 | && | 逻辑与运算符 | |
12 | II | 逻辑或运算符 | |
13 | ?: | 条件运算符(三目运算符) | 自右向左 |
14 | =、+=、-=、*=、/=、%=、>>=、<<=、&=、^=、I= | 赋值运算符 | |
15 | , | 逗号运算符(顺序求职运算符) | 自左向右 |
九、实践与练习
1、综合计算:连加计算
编写程序,用户输入3个数字,输出连加结果。3个数字需要分别输入,不可以一起输。例如,输入一个数字后,按Enter键可输入第二个数字;3个数字输完后,按Enter键输出计算结果。输出结果如下:
请输入3个数,实现连加
3
5
12
三个数的和为:20
代码如下:
#include <stdio.h>
int main(){
int a, b, c, d;
printf("请输入第一个数字:");
scanf_s("%d", &a);
printf("请输入第二个数字:");
scanf_s("%d", &b);
printf("请输入第三个数字:");
scanf_s("%d", &c);
d = a + b + c;
printf("三个数的和为:%d\n", d);
return 0;
}
2、人生路程计算器
英国专家研究发现,居住在现代城市中的人一生中大约会步行80500千米。如果人的平均寿命为70岁,一年为365天,计算现代城市人每天需要走多少千米。
输出结果如下:
每天要走3.150685千米
每小时要走0.131279千米
代码如下:
#include <stdio.h>
int main() {
int a = 80500;
int b = 70;
int c = 365;
float d;
float e;
d = (float)(b * c) / a;
e = (b * c * 24) / a;
printf("每天要走:%.6f千米\n", d);
printf("每小时要走:%.6f千米\n", e / 24);
return 0;
}
3、港珠澳大桥的长度
港珠澳大桥位于广东省伶仃洋区域内,桥隧道全长55千米,其主桥29.6千米,香港口岸至珠澳口岸41.6千米;桥面为双向六车道高速公路,设计速度100千米/小时。将港珠澳大桥的全长转换成古代的丈、尺单位(1丈=10尺,3尺=1米)表示。输出结果如下:
港珠澳大桥全长378600.000000尺
每小时要走0.131279丈
#include<stdio.h>
int main()
{
double chi ,zhang;
chi= (55 + 29.6 + 41.6) * 1000 * 3;
zhang = chi / 10;
printf("港珠澳大桥全长%lf尺\n", chi);
printf("港珠澳大桥全长%lf丈\n", zhang);
return 0;
}