本章介绍:
- 各种操作符的全方位讲解。
- 表达式求值
操作符分类:
1 算术操作符
+ − ∗ / %
- 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
- 除法 / 得到的是商
- 除法操作符的两个操作数都是整数的话,执行的是整数除法 。 用(%d打印)
- 除法操作符的两个操作数只要有一个浮点数,执行的是小数除法。用(%lf打印)
- $取模 % (取余)得到的是余数
- 取模操作符的操作数必须是整数
2 移位操作符
<<左移操作符
> > 左移操作符
注:移位操作符的操作数只能是整数。
- <<左移操作符
1: 左移动操作符:左边丢弃,右边补0,(符号位也跟着丢)
2: 每左移一位是乘2的效果
被左移时候的值是不变的,更动的赋值那个数
相当于 int a = -3; int b = a - 1;a的值是不跟着变化的
- >>左移操作符
移位规则:
每右移一位是除2的效果
首先右移运算分两种:
- 1.逻辑移位
左边用0填充,右边丢弃
1. 算书移位
左边用原该值的符号位填充,右边丢弃
移位图解:
- 右移的时候,到底采用的是算术右移?还是逻辑右移,是取决于编译器的!
在 V S 编译器里采用的是算术右移
(要用负数去测试)
如果逻辑右移,移动一位时候符号位变成0,是正数
如果算术左移,移动一位时候符号位变成1,是负数
警告⚠:
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:
int num = 10; num>>-1; //error
3 位操作符
(位操作都是二进制位进行计算 )
位操作符有:
& 按位与 ( 有0出0,全1出一)
∣ 按位或 ( 全0出0, 有1出1 )
ˆ 按位异或 ( 相异出1, 相同出0)
注:他们的操作数必须是整数。
为了更好让大家理解,我还是以举二个例子的方式进行讲解~
- 编写代码实现:求一个整数存储在内存中的二进制中1的个数
方法一:按位与 &
按位循环1~32位,并分别与1按位与,因为&1时二进制只最低位是1,每次右移一次都会跟1比较( 有0出0,全1出一),当得到1的时候count++就可以得出二进制中有多少个1,代码如下:
整数在内存中存储的是: 补码(2进制)
15的二进制有4个10000000000000000000000001111
方法二:a&(a-1);
a&(a-1),第一时间肯定有点懵吧?不知道什么意思,我们举一个例子方便理解
)
每次 a & ( a − 1 ) 后 c o u n t + +
每次 a & ( a − 1 ) 当 32 位都为 0 的时候,这个值就为 0 了,也就数完 a 中有几个 1 了
- 不能创建临时变量(第三个变量),实现两个数的交换。
在交换两个数的时,常常我们想到的方法是先创建一个临时变量,将一个变量放到临时变量里,然后把另一个变量放到第一个变量里,最后把第一个存起来的变量再放到另一个变量,但是这题限制了创造临时变量。
方法一:
这可能是大多数人想出来的办法,先把a,b两个值加起来,存到a里面,然后把a-b存到b里面,最后再把a的值解去b的值,得到两个数的交换
这个代码存在一定的问题
如果a和b比较大,a和b相加会溢出!(因为int形的取值范围是-32768~32767)
方法二:
^ 按位异或
这样完美的解决了不能创建临时变量交换两个数的问题
但是这样的代码可读性不好,可能想半天都不知道干什么
所以在实际写代码时,还是创造两个临时变量来交换两个数
这里让大家更好的了解 ^的知识
4 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重 weight = 89;//不满意就赋值
赋值操作符可以连续使用,比如:
int a = 10; int x = 0; int y = 20; a = x = y+1;//连续赋值 这样的代码感觉怎么样? 那同样的语义,你看看: x = y+1; a = x; 这样的写法是不是更加清晰爽朗而且易于调试。
- 复合赋值符:
+=
− =
∗ =
/ =
> > =
< < =
& =
∣ =
% =
这些运算符都可以写成复合的效果。
比如:
int x = 10; x = x+10; x += 10;复合赋值 其他运算符一样的道理。这样写更加简洁。
5 单目操作符
单目操作符介绍
! 逻辑反操作
− 负值
+ 正值
& 取地址
s i z e o f 操作数的类型长度(以字节为单位)
− − 前置、后置 − −
+ + 前置、后置 + +
∗ 间接访问操作符 ( 解引用操作符 )
( 类型 ) 强制类型转换
~ 对一个数的二进制按位取反
- ! 逻辑反操作
如果flag为真第一个if就进去判断条件
如果flag为假 !(逻辑反操作)让flag变成真 if进去判断条件
- &地址
- ∗ 间接访问操作符 ( 解引用操作符 )
这两个操作符跟指针又密切的关系,都是搭配使用的
- sizeof:操作数的类型长度(以字节为单位)
- ++前置、后置++
- − − 前置、后置 − −
- -前置:(先–,后使用) + +前置:(先+ +,后使用)
后置- -:(先使用,后–) 后置+ +:(先使用,后++)
总归前置是先++或者- -再使用
后置先使用再加减
()强制类型转换
如果一个运算符两遍的运算数据类型不同,先要将其转换为相同的类型,强制类型转换可以消除程序中的警告,即确保写代码的程序员自己清楚类型转换,允许丢失一定精度,或做类型匹配
如果两个不同类型的数据进行一起运算,会报出程序警告
强制类型转换就可以消除程序中的警告
- ~ 对一个数的二进制按位取反
把数据的每个二进制位取反,即把0变成1,把1变成0。
6 关系操作符
>
> =
<
< =
! = 用于测试“不相等”
= = 用于测试“相等”
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
警告:
在编程的过程中 = = 和 = 不小心写错,导致的错误。
7 逻辑操作符
&& 逻辑与(都要是真的才可以执行)(并且)
∣ ∣ 逻辑或 (只要一个为真就执行)(或者)
- 区分逻辑与和按位与
- 区分逻辑或和按位或
(&&)(||):逻辑与和或 :只关注真假(非0就是真,0就是假)
(&)(|) 按位与和按位或:二进制位进行计算
举例:
1&2----->0
1&&2---->1
1|2----->3
1||2---->1
- 为了更好理解逻辑与和逻辑或,习题方式进行讲解
以下代码输出的是什么
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
解析:
答案是 a=1 b=2 c=3 d=4
&& 是控制求值顺序的,当运算 a++ 的时候,因为 a++ 是后置++(先使用后++),所以 a++ 这时还是 0,a为0,逻辑或有一个为假,表达式不成立,则后面的运算就不用再算了,相当于电路中的‘短路’.
如果把代码改成这样输出的又是什么
int main() { int i = 0, a = 0, b = 2, c = 3, d = 4; i = a++||++b||d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
解析:
a=1 b=3 c=3 d=4
||逻辑或(只要一个为真就执行)
||(逻辑或)它是只要遇到 (非0值)为真,表达式整体为真,后面表达式就不要再算了。当遇到 a++ 是后置++(先使用后++),所以 a++ 这时还是 0 ,到下一个 ++b 运算,b为2(非0为真)表达式整体为真 那就停下来了,所以 d++ 不运算,所以最后d的值是不变的。