目录
一 运算符
-
运算符的优先级:单目 > 双目 > 三目
-
运算符的目数:运算符操作数的个数
-
算术运算符 > 关系运算符 > 逻辑运算符 > 条件运算符 > 赋值运算符 > 逗号运算符
(一) 算术运算符(+,-,*,/,%,++,--)
1、基本算术运算符
-
结合方向:先左后右
-
%取余,必须为整数(浮点数取余有专用的函数fmod)
-
a/b取整:a、b均为整数;a/b除:表达式中需要有一个浮点型
printf("%d\n", 5 / 2); // 2 整数相除的结果仍为整数,小数会被舍去
printf("%d\n", -5 / 2); // -2,取整后向零靠拢
printf("%f\n", 5.0 / 2); // 浮点数与整数相除结果为浮点数,float型数据按double型数据处理
2、自增自减运算符
-
单目运算符
-
结合方向:先右后左
-
后置++/--比前置++/--的优先级要高,前置++/--的运算效率高
-
i++ 或 ++i 作为单独的指令时没有区别
int a = -1,b = 10,c;
// 前置++/--和后置++/--的区别
c = a++; // 后置++:先使用a的值-1赋值给c,c=-1,然后a再自增1,a=0
printf("a=%d c=%d\n", a, c);
c = ++b; // 前置++:先b自增1,b=11,然后再将b的值赋值给c,c=11
printf("b=%d c=%d\n", b, c);
(二) 关系运算符(>,<,>=,<=,==,!=)
-
运算的结果为逻辑值(逻辑真、逻辑假)
-
结合方向:从左至右
printf("%d\n", 1 > 2); // 1是否大于2?不大于,结果为假(0)
printf("%d\n", 1 != 2); // 1不等于2? 1是不等于2,结果为真(非0值/1)
printf("%d\n", 1 <= 2); // 1小于等于2?1是小于等于2,结果为真(非0值/1)
printf("%d\n", 1 == 2); // 1等于2?1不等于2,结果为假(0)
printf("%d\n", 2 == 2.0); // 2 == 2.0为关系表达式
int a = 1, b = 2, c = 3;
printf("%d\n", a == b); // a等于b? a不等于b,结果为假(0)
printf("%d\n", a+1 == b); // a等于b? a+1等于b,结果为真(非0值/1)
-
注意:将=右边看成一个整体
int data = 3;
// 将=右边看成一个整体
data *= 3+5; // data = data * (3+5);
printf("data = %d\n",data); // 24
// 判断c大于b大于a,关系表达式怎么写
printf("%d\n", c > b > a);
// 先算c>b,c的确大于b,结果为逻辑真(非0值/1),再算1>a,1是不大于a,结果为逻辑假(0)
c > b && b > a; // 再多个条件连续比较时,需要用到逻辑运算符作为连接
(三) 逻辑运算符(&&,||,!)
-
用于判断表达式的逻辑值,C语言中0为假,其他都为真
1、&&(逻辑与)
-
判断运算符两边的表达式是否同时为真,同真为真,否则为假
-
&&的短路特性:A&&B,如果A为假,则系统不会执行B
int num=10;
printf("比较之前num = %d\n",num);
(2>3) && (num=100); // 这里并没有执行
printf("比较之后num = %d\n",num);
-
运行结果:比较之前 = 10,比较之后 = 10
2、||(逻辑或)
- 判断运算符两边的表达式是否有一个为真,有真为真,否则为假
- ||的短路特性:A||B,只要A为真,系统不会判断B的真假
int num=10;
printf("比较之前num = %d\n",num); // 10
(3>2) || (num=100); // num=100得不到执行
printf("比较之后num = %d\n",num); // 10
3、!(逻辑非
- 真变假,假变真
-
优先级:!— 算数 — 关系 — && — || — 赋值(!最高)
(四) 条件运算符(?:)
-
?:(C语言中唯一一个三目运算符)
-
条件运算符的基本格式:表达式1?表达式2:表达式3
-
条件运算符的运算规则:首先判断表达式1结果的逻辑值,如果结果为逻辑真则执行表达式2,如果结果为逻辑假则执行表达式3,整个条件表达式为其所执行的表达式的值
(1 < 2) ? printf("小于\n") : printf("不小于\n"); // 小于
(1 > 2) ? printf("小于\n") : printf("大于\n"); // 大于
int a = 10, b = 20, c;
c = a >= b ? a : b; // 用于求两个数中的最大值
// a >= b ? a : b为条件表达式
printf("%d\n", c); // 20
printf("我这里有1万元和一个女朋友,请问你要哪个?\n");
printf("1、我要1万元\n");
printf("2、我要女朋友\n");
int n; // 你的选择
scanf("%d", &n); // 输入你的选择
n == 1 ? printf("我要1万元\n") :
(n == 2) ? printf("我要女朋友\n") : printf("小孩子才做选择,大人我全都要!\n"); // 条件运算符的嵌套(?:(?:)),可以理解成为套娃
// 禁止套娃
(五) 赋值运算符(=,+=,-=,*=,/=,%=)
1、用于给变量赋值,通常也涉及类型转换
-
浮点型(float、double)赋值给整型,保留整数
-
整型赋值给浮点型,数值不变,以指数形式存储
-
double型赋值给float型,注意数值范围不要溢出
-
字符型赋值给整型,ASCII码值给了整型
-
int、short、long赋值给char型,低八位传到char型数据 289 00000001 00100001 相当于把00100001传给char型数据变量
-
有符号变量传给无符号变量,照搬照抄
-
不同类型的整型数据传输按照存储单元中的存储形式直接传送
2、常用赋值运算符
-
结合方向:从右到左
int x = 100; // 定义了一个int整型变量x,并初始化赋值为100
x += 10; // x = x + 10; x = 100 + 10 = 110
x *= 10; // x = x * 10 = 100 * 10 = 1000
x %= 10; // x = x % 10 = 100 % 10 = 0
printf("x=%d\n", x);
(六) 逗号运算符(,)
-
一般起到分隔作用
int data1 = 0;
int data2 = 0;
data1 = 3,4,5,6; // 等号与3结合
data2 = (3,4,5,6); // 先进行括号里面的操作,最后6与等号结合
printf("data1 = %d\n",data1); // 3
printf("data2 = %d\n",data2); // 6
-
逗号表达式
int a[3][2]={(1,2),(3,4),(5,6)}; // 2,4
int a[2][3]={(1,2),(3,4),(5,6)}; // 2,4,6
(七) 位运算符(<<,>>,&,|,~,^)
-
对数据的二进制形式进行运算符
1、<<(左移运算符)
-
基本格式:数据<<移动位数
-
注意:移动的位数不要超过自身长度
-
左移运算符每移动一位相当于将一个数据乘以2
printf("%d\n", 1 << 1); // 结果为2
// 将数据1左移1个二进制位,右边补0
// 1的二进制为:00000000 00000000 00000000 00000001
// 1左移1位后为:00000000 00000000 00000000 00000010 = 2
printf("%d\n", 1 << 2);
// 1左移2位后为:00000000 00000000 00000000 00000100 = 4
2、>>(右移运算符)
-
基本格式:数据>>移动位数
// 将一个数据的二进制右移多少位,左边补符号位,符号位不变(算术右移)
printf("%d\n", 8 >> 2); // 结果为2
// 右移运算符每移动一位相当于将一个数据除以2
printf("%d\n", 7 >> 2); // 结果为1
// 7的二进制为:0000 0111
// 7右移2位后为:0000 0001 = 1
printf("%d\n", -7 >> 2); // 结果为-2
// 计算机中数据的表示和运算都是以二进制补码进行的
// -7的原码为:1000 0111
// -7的反码为:1111 1000
// -7的补码为:1111 1001
// -7的补码右移两位为:1111 1110
// -7的补码右移两位后再取反+1还原成原码 1000 0001 1000 0010 = -2
3、&(按位与)
-
双目运算符
-
运算规则:两个数据相与,各个二进制位分别做与运算,同真为真,否则为假
-
特点:和1相与,保持不变;和0相与,清零
-
应用场景:将固定位清零
printf("%d\n", 1 & 2); // 0
// 1的二进制:0000 0001
// 2的二进制:0000 0010
// 1&2二进制: 0000 0000
4、|(按位或)
-
双目运算符
-
运算规则:两个数据相或,各个二进制位分别做或运算,有真为真,否则为假
-
特点:和0相或,保持不变;和1相或,置1
-
应用场景:将固定位置1
printf("%d\n", 1 | 2); // 3
// 1的二进制:0000 0001
// 2的二进制:0000 0010
// 1&2二进制: 0000 0011
5、~(按位非)
-
单目运算符,按位取反
printf("%d\n", ~1); // -2
// 1的二进制:0000 0001(补码)
// ~1的二进制:1111 1110(补)->1000 0001(反)->1000 0010(原)
// 这里的负数已经是补码的形式
6、^(按位异或)
-
两个数据相异或,各个二进制位分别做异或运算,相同为0,相异为1
-
特点:和0异或,保持不变;和1异或,取反
-
应用场景:将固定的位发生高低电平翻转
printf("%d\n", 1^2); // 3
// 1的二进制:0000 0001
// 2的二进制:0000 0010
// 1^2的二进制:0000 0011 = 3
- 案例:将1010 1010 的第0位发生翻转
1010 1010
^ 0000 0001
-------------
1010 1011 // 低电平转换为高电平
^ 0000 0001
-------------
1010 1010
7、注意
-
比如我自己输入-1,那么这是原码,要转换成补码再操作
-
对负数的补码进行按位操作后再还原成原码读取
-
正数的补码、原码、反码一致
二 优先级
(一) 说明
-
优先级高先执行,同级别的优先级要看结合性
-
运算符优先级:初等运算符>单目运算符>算术运算符>关系运算符>逻辑运算符>条件运算符>赋值运算符>逗号运算符
(二) 记忆口诀
口诀 | 解释 |
---|---|
初等单目一二级 | 初等运算符、单目运算符为1、2级 |
乘除求余加减移 | 算术运算符、移位 |
关系等于不等于 | 关系运算符(< <= > >=) |
按位与来异或或 | 位运算符(& ^ |) |
逻辑与或条件弱 | 逻辑运算符(&& ||)、条件运算符 |
赋值逗号一点破 | 赋值、逗号最低 |
三、补充
(一)右移分类
1、逻辑右移
- 右边丢弃,左边补0
2、算术右移
- 无符号数:右边丢弃,左边补0
- 有符号数:右边丢弃,左边补符号位(正数补0,负数补1)
3、逻辑右移和算术右移是编译器决定,但是我们可以检测
(二)综合案例
- 将data的第1、5清0,第3、4位置1,其他位保持不变
unsigned char data = 0xaa; // 1010 1010
- 将data的第1、5位清0
data = data & 1101 1101;
1101 1101 = ~(0010 0010) =~(0010 0000 | 00000010)
0010 0000 = 0000 0001 << 5
0000 0010 = 0000 0001 << 1
1101 1101 = ~(0x01 <<5 | 0x01<<1)
data = data & ~(0x01<<5 | 0x01<<1);
- 将data的第3、4位置1
data = data | 0001 1000;
0001 1000 = 0001 0000 | 0000 1000 = 0x01<<4 | 0x01<<3
data = data | (0x01<<4 | 0x01<<3);