2_运算符与表达式

1、表达式

什么是表达式? 
	表达式就是能够表达某种意思的式子
	a = b + c;		//把b和c相加,再把结果赋值a
	
在C语言中,表达式一般是指 运算符连接操作数的式子
	a+b   5  2+2   1000   ....
	
注意: 任何的表达式都有一个确定的值

2、运算符

我们用来进行运算的某种符号 
	比如: + - * / % .... 

	
几目运算符:
	表示该运算符需要连接几个操作数

	单目运算符:表示该运算符需要连接1个操作数,比如: ++ , --, ! , ... 
	双目运算符:表示该运算符需要连接2个操作数,比如: +, -, *, /, ...
	三目运算符:表示该运算符需要连接3个操作数,比如:  ? : (条件运算符)

	
结合性:
	代表运算时的优先顺序
	例子: 
		+ 	结合性:从左至右 
		
			a + b
			(1+2) + (3+4)
			这个表达式是 先算a,再算b
		所以,在C语言中,a+b 和 b+a 是两个不同的含义 



1)算术运算符 
	进行算术运算的运算符 
		
		单目运算符: ++ , --
		
		双目运算符: + , - , * , / , % 
			结合性:从左至右


	(1) / 取整 
		整数除以整数 其结果还是整数 ,如果要变成实数,那么在分子乘以1.0即可
		
			5/2 --> 2
			1.0*5 / 2  --> 2.5
			
	=======================
		隐式类型转换:高精度的数据和低精度的数据进行运算符时,结果会自动转换成高精度的
				比如: 
					int  double  -->  double
					int  short   -->  int 
					char short	 -->  int 
					char char    -->  int 
					无符号 有符号 --》 无符号
	
				例子:
					char a = '0';
					char b = 1;
					printf("%d\n", a+b);	// 49
	
	(2) % 取余 
		在C语言中,%的两个操作数必须为整数 
		
			5%3	   -->  2
			-5%3   -->  -2
			5%-3   -->  2
			-5%-3  -->  -2
		结论:余数的符号 和 被除数的符号 一致
			

	(3) 自增 ++ ,让这个操作数的值加1
		自减 -- ,让这个操作数的值减1
					C中++i和i++都不能作为左值
					C++中++i可以作为左值,i++不能作为左值
			注意: 
				1.自增和自减 只能作用于 变量
					i++;	--> i = i + 1;
					5++;	--> 5 = 5 + 1;	// error 
					(a+b)++;	// error 
					(a++)++;	// error 

				2.  
							整个表达式的值		执行后i的值 
					i++			i				i+1
					++i			i+1				i+1
					
					i--			i				i-1
					--i			i-1				i-1
					
						例子: 
							a = ++i;	==> {
												i = i+1;
												a = i;
											}

							b = i++;	==> {
												b = i;
												i = i+1;
											}

			练习:
				1) 
					int i = 5;
					int a = i++;	// a==5, i==6
					int b = ++i;	// b==7, i==7
					int c = i--;	// c==7, i==6
					int d = --i;	// d==5, i==5

				2)
					int i = 6;
					printf("%d %d\n", i++, ++i); // 6 8 ,实际结果为7 8

						printf()函数中的计算顺序是从右至左,打印则是从左至右
							但是 不同的编译器 会导致结果不同
							
						++/-- 前置的返回是i本身的值, 后置的返回是缓存区的值


					printf("%d\n", i++);
					printf("%d\n", ++i);


2)关系运算符 
	用来判断两个表达式的数值大小关系的运算符
		双目运算符 , 结合性:从左至右 
		
		>  >=  ==  <=  <  !=  

	关系表达式:
		由关系运算符连接操作数的表达式 
		关系表达式的值只有两种情况: 1(关系成立), 0(关系不成立)

		例子: 
			5>3		--> 	1(关系成立)
			3>4		--> 	0(关系不成立)
			5>4>3	-->		0(关系不成立)
					
					5>4>3  -->  (5>4) > 3
						   -->   1   >  3
						   -->   0


3)逻辑运算符  
	表达某种数理逻辑的运算符
		&&	逻辑与		双目运算符	“并且”		结合性:从左至右
		||	逻辑或		双目运算符	“或者”		结合性:从左至右  
		!	逻辑非		单目运算符  “取反”

	逻辑表达式:
		由逻辑运算符连接操作数的表达式 
		逻辑表达式的值只有两种情况:1(真,非0),  0(假)
		
		任何非0的数都表示真,但是逻辑表达式为真,其值为1

		例子:
			int a=4, b=5;
			
			a && b	===> 1
			a && 0	===> 0  只要有一个为0,整个表达式就为0
			a || b  ===> 1
			a || 0  ===> 1	只要有一个为1,整个表达式就为1

			0与上任何数都为0,1或上任何数都为1

		练习:
			int a=1, b=2, c=3, d=4, m=1, n=1;
			(m = a>b) && (n = c>d);
			printf("%d %d %d %d %d %d\n",a,b,c,d,m,n);	// 1 2 3 4 0 1

		惰性运算: 
			如果事先知道了表达式的值,那么它就不会运算后面的表达式了

			比如: 
				a && b && c
					只有当a的值为真(非0),才会去运算b的值
					只有当a和b的值都为真(非0),才会去运算c的值
					
				a || b || c 
					只有当a的值为假(0)时,才会去运算b的值
					只有当a和b的值为假(0)时,才会去运算c的值


			练习:
				请用逻辑表达式来判断某一年是否为闰年 
					1)能够被4整除但是不能被100整除		
					2)能够被400整除

					int year;
					if(  (year%4 == 0 && year%100 != 0) || year%400 == 0   )
					{
						//闰年
					}


4)位运算符 
	位运算符是按bit位来进行运算的
		注意:位运算符 只能作用于整数 
		
		~	按位取反
		&	按位与
		|	按位或
		^	按位异或
		<<	按位左移
		>>  按位右移
		
		只有按位取反是单目运算符,其余都是双目运算符,结合性:从左至右
		
		运算步骤:将整数分解成bit序列(补码),然后再去按bit位进行运算 
		
	例子:
		1)按位取反 ~   0->1 , 1->0 
		
			int a = ~3;
			printf("%d\n",a);
			printf("%u\n",a);
	
				3	00000000 00000000 00000000 0000 0011	(int)
				  ~ 11111111 11111111 11111111 1111 1100	a (int)	

					%d : 补码还原 
							11111111 11111111 11111111 1111 1100	a (int)	
							11111111 11111111 11111111 1111 1011	-1
							00000000 00000000 00000000 0000 0100	~   绝对值4
						==》 -4	
					%u : 2^32 - 4

					
			int a = ~(-3);
			printf("%d\n",a);
			printf("%u\n",a);

				3	00000000 00000000 00000000 0000 0011
				  ~ 11111111 11111111 11111111 1111 1100
				 	11111111 11111111 11111111 1111 1101   -->  -3 
				  
				~	00000000 00000000 00000000 0000 0010   a  

					%d : 2
					%u : 2


		2)按位与 & 
			a	b		a&b
			1	1		1
			1	0		0
			0	1		0
			0	0		0
			结论:只有两个bit位都为1时,结果才为1,否则就为0     “有0则0,全1为1”
		
			例子:
				int a = 3&5;
				printf("%d\n",a);
				printf("%u\n",a);

					3	00000000 00000000 00000000 0000 0011
					5   00000000 00000000 00000000 0000 0101
					  & 00000000 00000000 00000000 0000 0001  --》 1


			结论: 
				任何一个bit位与0进行“按位与&”运算时,结果都为0
						x & 0 == 0
						
				任何一个bit位与1进行“按位与&”运算时,结果都为保留原值 
						x & 1 == x

		3)按位或 | 
			a	b		a|b
			1	1		1
			1	0		1
			0	1		1
			0	0		0
			只有当两个bit位都为0时,结果才为0,否则为1    “有1则1,全0才0”
			
			例子:
				int a = 3|5;
				printf("%d\n",a);
				printf("%u\n",a);

					3	00000000 00000000 00000000 0000 0011
					5   00000000 00000000 00000000 0000 0101
					  | 00000000 00000000 00000000 0000 0111   --> 7

			结论:
				任何一个bit位 与0 进行“按位或|”运算时,结果保留原值
						x | 0 == x
						
				任何一个bit位 与1 进行“按位或|”运算时,结果为1
						x | 1 == 1


		4)按位异或 ^ 
			异或:求异
				a	b		a^b
				1	1		0
				1	0		1
				0	1		1
				0	0		0
			“相同为0,不同为1”
				
				例子: 
					int a = 3^5;
					printf("%d\n",a);
					printf("%u\n",a);

						3	00000000 00000000 00000000 0000 0011
						5   00000000 00000000 00000000 0000 0101
						  ^ 00000000 00000000 00000000 0000 0110	--> 6 

			结论: 
				任何一个bit位 与0 进行“按位异或^”运算时,结果保留原值
						x ^ 0 == x

				任何一个bit位 与1 进行“按位异或^”运算时,其结果取反
						x ^ 1 == ~x

			练习:
				交换两个整数a和b的值,不能使用临时变量
					int a = 5;
					int b = 6;
					
				方法一	/*
						//临时变量法 
						int temp;
						temp = a;
						a = b;
						b = temp;
					*/

				方法二	
					//异或的方法 
					a = a^b;
					b = b^a;
					a = a^b;
			
			注意: 
				位运算只和当前bit位相关,既没有借位,也没有进位  
				
					a	0000 0101	5
					b	0000 0110 	6
					^	0000 0011	3    -->  a = a^b;
			
					b	0000 0110 	6
					a	0000 0011	3
					^	0000 0101	5	--> b = b^a; -->  b --> 5

					a	0000 0011	3
					b	0000 0101	5
					^	0000 0110	6	--> a = a^b;  --> 	a --> 6


				方法三: 
					a = a + b;
					b = a - b;
					a = a - b;



		5)按位左移 <<  
			按bit位整体向左移  
			
				a << n 
				规则:
					把a转换成二进制bit序列(补码),再按bit位整体向左偏移n位
						高位直接舍弃n个位,低位补n个0
					
				例子: 
					char a = 5 << 3;
						
						5	  0000 0101 
						<<2	  0001 0100		--> 20 		5 * 2^2
						
						<<3	  0010 1000		--> 40 		5 * 2^3

						5 << 4
							  0101 0000		--> 80		5 * 2^4
						5<<5
							  1010 0000		--> 160		5 * 2^5
						5<<6
							  0100 0000		--> 64		高位舍弃了1,不满足了
							  
				结论:
					如果按位左移舍弃的高位全是0,那么左移n位后,其值就变为 原值 乘以 2的n次方
		
				练习:
					假设有一个整型变量a,要把a的第5bit位变为0,其他bit位保持不变 
					
						a	xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 
						
						&	11111111 11111111 11111111 11011111 
							
						~	00000000 00000000 00000000 00100000
						
					  1<<5  00000000 00000000 00000000 00000001	 --> 1
						
							xxxxxxxx xxxxxxxx xxxxxxxx xx0xxxxx 
					
								a = ~(1<<5) & a ;

								
		6)按位右移 >> 
			按bit位整体向右移 
			
				a >> n
				规则: 把a转换成二进制bit序列(补码),再按bit位整体向右偏移n位 
							低位直接舍弃n位,高位补什么呢?  
							
							如果无符号的概念,高位就直接补0
							如果有符号的概念,高位就补符号位
					
				例子: 
					int a = -1;
					a = a >> 31;
					printf("%d\n",a);
					printf("%u\n",a);
				
						1	00000000 00000000 00000000 00000001 
							11111111 11111111 11111111 11111110		~
						-1	11111111 11111111 11111111 11111111    +1

					  >>31  11111111 11111111 11111111 11111111 
					  
						%d : 补码还原    -1
						%u : 2^32 - 1
			

					unsigned int a = -1;
					a = a >> 31;
					printf("%d\n",a);
					printf("%u\n",a);
				
						1	00000000 00000000 00000000 00000001 
							11111111 11111111 11111111 11111110		~
						-1	11111111 11111111 11111111 11111111    +1

					>>31	00000000 00000000 00000000 00000001
								%d : 1
								%u : 1
				
				
					unsigned char a = -1;
					a = a >> 31;
					printf("%d\n",a);
					printf("%u\n",a);
					
						-1	11111111 11111111 11111111 11111111
						a	11111111
						
					>>31	00000000
							%d : 0
							%u : 0
					


5)赋值运算符  =
	双目运算符 ,结合性:从右至左 

		a = x 
		
		把x的值求出来,再把整个值 赋值给a
		赋值运算符要求左边的操作数必须是一个可写的地址(左值)

			5 = 3;		//error 
			2+3 = 5;	//error
			i++ = 5;	//error 
			i = 6;		//right 

	赋值表达式:
		由赋值运算符连接操作数的表达式 
		赋值表达式的值就是最后赋值给左边变量的那个值

			a = b = 6;
		--> a = (b=6);

		注意:
			赋值时 涉及到类型转换,赋值运算符右边的值需要转换成左边变量的类型
			左边变量的类型 即 赋值表达式的结果的类型
			
				float a = 1;

	
	复合赋值运算符
		赋值运算符可以 和算术运算符、位运算符 组成 复合赋值运算符 
		
			+=  -=  *=  /=  %=  >>=  <<=  &=  |=  ^=  ~= 
			
			例子: 
				i += 6;		<==>  i = i + 6;
				i += 4*5;	<==>  i = i + (4*5);
					

6)条件运算符 
	
	? :  三目运算符,结合性:从右至左 
	
	语法: 
			表达式1 ? 表达式2 : 表达式3 
	求值顺序:
		先计算表达式1的值,
			如果表达式1的值为真(非0),则整个表达式的值 就为 表达式2的值
			如果表达式1的值为假(0),则整个表达式的值 就为 表达式3的值
	
		例子: 
			int a = 5>4 ? 3 : 2;
			
				a --> 3


7)逗号运算符  , 
	双目运算符,结合性:从左至右 

	语法: 
		表达式1,表达式2
	求值顺序:
		先求表达式1的值,再求表达式2的值,整个逗号表达式的值为表达式2的值 

		int a=5;
		int b=6;
		a = (a=6, a+b);
		printf("%d\n",a);	//12


	逗号表达式的扩展形式:
		表达式1,表达式2,表达式3,表达式4, ... 表达式n 
	求值顺序:
		先求表达式1的值,再求表达式2的值,... 最后求表达式n的值,
		整个逗号表达式的值为表达式n的值


8)指针运算符 
	& 取地址符
	* 指向符


9)求字节运算符 sizeof() 
	单目运算符
	用来求一个对象或类型所占的内存空间的字节数
	
	例子: 
		sizeof(int)	-->	4
		sizeof(1.0)	-->	8	(double) 
		sizeof(1.0f) --> 4 (float)
		
		short s;
		sizeof(s+1); --> 4  (int)


	=================
		求数据类型   typeof() 
			
			如果有一个数据x,这个数据的类型  typeof(x)  ---- x的数据类型 
			
			例子: 
				int a = 0;		//定义一个整型变量 
				
				定义一个和变量a 类型一样的变量  
				
					typeof(a) b;	//typeof 适用于 对未知类型变量的定义 
					
					
				typeof(a)   表示该数据的类型 
					typeof(a)  <==>  int 
					
					
				typeof(3)	 -->  int
				typeof(3.14) -->  double 
			

10) 分量运算符  
	. 
	->  
			
	引用结构体的成员变量 
		student.name
		studeng.num 
		stu->score
			
			
11)下标运算符 []
	引用数组的元素 
		int a[10];
			a[0],  a[1], ... a[9]
			
			
12)强制类型转换运算符  () 
	语法: 
		(数据类型)对象
			
	例子: 
		float a = 3.6;
		int b = (int)a;		// 3
			
			
13)其他 			

3、优先级

我们在运算一个表达式的时候,先看优先级,再看是否有惰性运算,再看结合性


	()  []  ->  .				从左至右
	!	~	++	--				从右至左			单目运算符
	*   /	%					从左至右			算术运算符
	+	-						
	<   <=  >  >=				从左至右			关系运算符
	==	!=
	&	^ 	|	<<	>>			从左至右			位运算符
	&&							从左至右			逻辑运算符
	||
	? : 						从右至左			条件运算符
	=	+=	-=	*= /=			从右至左			赋值运算符
	,							从左至右			逗号运算符
			
		编程建议:如果实在记不住,请自觉打括号	

4、练习

	1)取整型变量x中 第p个bit位开始的n个bit
	
		假设 p=3, n=4
	
		 	
		xxxxxxxx xxxxxxxx xxxxxxxx xabcdxxx
	>>p	xxxxxxxx xxxxxxxx xxxxxxxx xxxxabcd
		00000000 00000000 00000000 00001111
		
	1	00000000 00000000 00000000 00000001
	1<<n00000000 00000000 00000000 00010000		// 2^n  --> 1<<n
	-1	00000000 00000000 00000000 00001111
		
		00000000 00000000 00000000 0000abcd
		
		
		x = (x>>p) & ( (1<<n) -1);
		

	2)将整型变量x中 第p个bit位开始的n个bit 取反,其他位保持不变 

			x^0 = x
			x^1 = ~x
			
		xxxxxxxx xxxxxxxx xxxxxxxx xabcdxxx
		
	^	00000000 00000000 00000000 01111000
	<<p	00000000 00000000 00000000 00001111
	
	1	00000000 00000000 00000000 00000001
	1<<n00000000 00000000 00000000 00010000		// 2^n  --> 1<<n
	-1	00000000 00000000 00000000 00001111	
	
		x = (((1<<n)-1)<<p) ^ x ;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RoyLee0826

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值