算术运算符
运算符 |
术语 |
示例 |
结果 |
+ |
加 |
10 + 5 |
15 |
- |
减 |
10 - 5 |
5 |
* |
乘 |
10 * 5 |
50 |
/ |
除 |
10 / 5 |
2 |
% |
取模(取余) |
10 % 3 |
1 |
++ |
前自增 |
a=2; b=++a; |
a=3; b=3; |
++ |
后自增 |
a=2; b=a++; |
a=3; b=2; |
-- |
前自减 |
a=2; b=--a; |
a=1; b=1; |
-- |
后自减 |
a=2; b=a--; |
a=1; b=2; |
示例代码:
#include <stdio.h>
int main() {
// 2数相除,要想得到小数的结果,分子分母必须有一个数是小数
double c = 5/2; // 5, 2个都是整数,只会取整
printf("c1 = %lf\n", c);
c = 5.0/2;
printf("c2 = %lf\n", c);
c = 5/2.0;
printf("c3 = %lf\n", c);
int i = 0;
// 前置++
// 先加后用
int b = ++i;
printf("前置:b = %d, i = %d\n", b, i);
// 后置++
// 先用后加
i = 0;
b = i++;
printf("后置:b = %d, i = %d\n", b, i);
return 0;
}
运行结果:
c1 = 2.000000
c2 = 2.500000
c3 = 2.500000
前置:b = 1, i = 1
后置:b = 0, i = 1
赋值运算符
运算符 |
术语 |
示例 |
结果 |
= |
赋值 |
a=2; b=3; |
a=2; b=3; |
+= |
加等于 |
a=0; a+=2; 等同于 a = a + 2; |
a=2; |
-= |
减等于 |
a=5; a-=3; 等同于 a = a - 3; |
a=2; |
*= |
乘等于 |
a=2; a*=2; 等同于 a = a * 2; |
a=4; |
/= |
除等于 |
a=4; a/=2; 等同于 a = a / 2; |
a=2; |
%= |
模等于 |
a=3; a%=2; 等同于 a = a % 2; |
a=1; |
示例代码:
#include <stdio.h>
int main() {
int a = 10;
a += 5;
printf("a = %d\n", a);
return 0;
}
运行结果:
a = 15
比较运算符
C 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。
运算符 |
术语 |
示例 |
结果 |
== |
相等于 |
4 == 3 |
0 |
!= |
不等于 |
4 != 3 |
1 |
< |
小于 |
4 < 3 |
0 |
> |
大于 |
4 > 3 |
1 |
<= |
小于等于 |
4 <= 3 |
0 |
>= |
大于等于 |
4 >= 1 |
1 |
示例代码:
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
printf("%d\n", a == b);
printf("%d\n", a != b);
printf("%d\n", a > b);
printf("%d\n", a < b);
printf("%d\n", a >= b);
printf("%d\n", a <= b);
return 0;
}
逻辑运算符
运算符 |
术语 |
示例 |
结果 |
! |
非 |
!a |
如果a为假,则!a为真; 如果a为真,则!a为假。 |
&& |
与 |
a && b |
如果a和b都为真,则结果为真,否则为假。 |
|| |
或 |
a || b |
如果a和b有一个为真,则结果为真,二者都为假时,结果为假。 |
示例代码:
#include <stdio.h>
int main() {
// &&(与),可以理解为并且
// 案例:请判断班长和他女朋友是否符合法定结婚年龄
int boy = 25;
int girl = 21;
int result = boy >= 22 && girl >= 20;
printf("%d\n", result);
// ||(或),可以理解为或者
// 案例:班长女朋友玩原神没有原石了,请帮班长判断是否有足够的钱
double wx_money = 100;
double alipay_money = 300;
result = wx_money >= 398 || alipay_money >= 398 || wx_money+alipay_money >= 398;
printf("%d\n", result);
// !(非),可以理解为不是
printf("%d\n", !0);
printf("%d\n", !!1);
// 短路规则
// && 左边为假,右边不执行
0 && printf("我是右边\n");
// || 左边为真,右边不执行
1 || printf("我是右边\n");
return 0;
}
位运算符
常见的位运算符号有&、|、^、~、>>、<<,分别代表着如下含义:
运算符 |
术语 |
示例 |
结果 |
& |
按位与运算 |
011 & 101 |
2个都为1才为1,结果为001 |
| |
按位或运算 |
011 | 101 |
有1个为1就为1,结果为111 |
^ |
按位异或运算 |
011 ^ 101 |
不同的为1,结果为110 |
~ |
取反运算 |
~011 |
100 |
<< |
左移运算 |
1010 << 1 |
10100 |
>> |
右移运算 |
1010 >> 1 |
0101 |
ps:取反、左右位移运算需要在补码的基础上运算。
& -(与运算)
按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;
/**
* 按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;
* 示例:
* 40 & 15 = 8
* 0010 1000
* & 0000 1111
* -------------------
* 0000 1000
*/
printf("40 & 15 = %d\n", 40 & 15);
| -(或运算)
按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;
/**
* 按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;
* 示例:
* 40 | 15 = 47
* 0010 1000
* | 0000 1111
* ----------------
* 0010 1111
*/
printf("40 | 15 = %d\n", 40 | 15);
^ -(异或运算)
按位异或运算:位与位进行比较,相同为0,不同为1;
/**
* 按位异或运算:位与位进行比较,相同为0,不同为1;
* 示例:
* 40 ^ 15 = 39
* 0010 1000
* ^ 0000 1111
* ------------------
* 0010 0111
*/
printf("40 ^ 15 = %d\n", 40 ^ 15);
~ -(取反运算)
按位取反运算:补码取反,再将取反后的补码转为原码;
ps:无符号的数据,取反后最高位为1,也不需要逆运算。
/**
* 按位取反运算:补码取反,再将取反后的补码转为原码。
* 1、正数取反:由于正数的原码和补码是相同的,取反的方式简便了些。
* 补码(原码) -> 取反 -> 补码逆运算 -> 反码逆运算(符号位不变) -> 取反后的原码
* 2、负数取反:
* 原码 -> 反码 -> 补码 -> 取反 -> 取反后的补码即原码
* 示例:
* 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
* ~40 = 0010 1000 -> 1101 0111 -> 1101 0110 -> 1010 1001 = -41
*
* 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
* ~15 = 0000 1111 -> 1111 0000 -> 1110 1111 -> 1001 0000 = -16
*
* 原码 反码 补码 取反
* ~-15 = 1000 1111 -> 1111 0000 -> 1111 0001 -> 0000 1110 = 14
*/
printf("~40 = %d\n", ~40);
printf("~15 = %d\n", ~15);
printf("~-15 = %d\n", ~(-15));
<< -(左移运算符)
将数字的二进制补码全部向左移动,空出来的位置补0,超出范围的二进制数丢弃;
有符号的数据左移后最高位如果为1,则需要进行逆运算;
注意事项:
- 无符号的数据,左移后最高位为1,也不需要逆运算;
- -128:1000 0000 特殊情况也不需要逆运算;
/**
* 示例:
* 40 << 4 = 0010 1000 << 4 = 1000 0000 = -128 (特殊的不需要逆运算)
* 41 << 4 = 0010 1001 << 4 = 1001 0000 = 1000 1111 = 1111 0000 = -112
* 7 6 5 4 3 2 1 0
* 1 0 0 1 0 0 0 0
*/
int8_t p = 40;
p <<= 4; // p = p << 4;
printf("40 << 4 = %d\n", p);
>> -(右移运算符)
将数字的二进制补码全部向右移动,空出来的位置补什么,取决于原来的最高位是什么。原来的最高是1就补1, 原来的最高位是0 就补0 。也可以转化成这样的一句话: 正数补0, 负数补1;
/*
23: 0001 0111【原码】 ---- 0001 0111【反码】 ---- 0001 0111 【补码】
>> 2
-----------------------------------------------
0000 0101【补码】 ---> 5
*/
printf(" 23 >> 2 = %d \n" , 23 >> 2) ;
/*
123: 1001 0111【原码】 ---- 1110 1000【反码】---- 1110 1001【补码】
>> 2
-----------------------------------------------
1111 1010【补码】 ---> 1111 1001【反码】- ----- 1000 0110 【原码】===> -6
*/
printf(" -23 >> 2 = %d \n" , -23 >> 2) ;
示例代码:
#include <stdio.h>
#include <inttypes.h>
int main() {
uint8_t a = 3; // 0000 0011
uint8_t b = 10; // 0000 1010
// 打印显示2个字符,个数不够,左边补0
printf("%02x\n", a & b); // 0000 0010,16进制为02
printf("%02x\n", a | b); // 0000 1011,16进制为0b
printf("%02x\n", a ^ b); // 0000 1001,16进制为09
uint8_t c = 10; // 0000 1010
uint8_t temp = ~c; // 1111 0101
printf("%02x\n", temp); // 1111 0101,16进制为f5
printf("%02x\n", c << 1); // 0001 0100,16进制为14
printf("%02x\n", c >> 1); // 0000 0101,16进制为05
return 0;
}
运行结果:
02
0b
09
f5
14
05
案例需求:
// 将变量a的第2位设置为1,其他位保持不变
uint8_t a = 0b10110011; // 0xb3;
// 将变量b的第2位、第6位设置为1,其他位保持不变
uint8_t b = 0b10110011; // 0xb3;
// 将变量c的第5位设置为0,其他位保持不变
uint8_t c = 0b10110011; // 0xb3;
// 将变量d的第0~3位设置为0,其他位保持不变
uint8_t d = 0b11111111; // 0xff;
// 将变量e的第2位取反,其他位保持不变
uint8_t e = 0b10110011; // 0xb3;
// 将变量f取出8-15位
uint32_t f = 0x12345678;
示例代码:
#include <stdio.h>
#include <inttypes.h>
int main() {
// 将变量a的第2位设置为1,其他位保持不变
uint8_t a = 0b10110011; // 0xb3;
a |= (1 << 2); // 或者 x = x | (1 << 2);
printf("%02x\n", a); // b7, 10110111
// 将变量b的第2位、第6位设置为1,其他位保持不变
uint8_t b = 0b10110011; // 0xb3;
b |= (1 << 2 | 1 << 6);
printf("%02x\n", b); // f7,11110111
// 将变量c的第5位设置为0,其他位保持不变
uint8_t c = 0b10110011; // 0xb3;
c &= ~(1 << 5);
printf("%02x\n", c); // 93,10010011
// 将变量d的第0~3位设置为0,其他位保持不变
uint8_t d = 0b11111111; // 0xff;
d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
printf("%02x\n", d); // f0,11110000
// 将变量e的第2位取反,其他位保持不变
uint8_t e = 0b10110011; // 0xb3;
e ^= (1 << 2);
printf("%02x\n", e); // b7, 10110111
// 将变量f取出8-15位
uint32_t f = 0x12345678;
uint32_t temp = (f & 0x0000ff00) >> 8;
printf("%#x\n", temp);
return 0;
}
运算符优先级
- 不同的运算符默认具备不同的优先级,符号较多不用记,现用现查就可以。
- 当无法确定谁的优先级高时,加一个小括号就解决了。
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
-- |
() |
圆括号 |
(表达式)/函数名(形参表) |
-- |
||
. |
成员选择(对象) |
对象.成员名 |
-- |
||
-> |
成员选择(指针) |
对象指针->成员名 |
-- |
||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
~ |
按位取反运算符 |
~表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
|||
-- |
自减运算符 |
--变量名/变量名-- |
|||
* |
取值运算符 |
*指针变量 |
|||
& |
取地址运算符 |
&变量名 |
|||
! |
逻辑非运算符 |
!表达式 |
|||
(类型) |
强制类型转换 |
(数据类型)表达式 |
-- |
||
sizeof |
长度运算符 |
sizeof(表达式) |
-- |
||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
|||
% |
余数(取模) |
整型表达式%整型表达式 |
|||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
|||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
|||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
|||
< |
小于 |
表达式<表达式 |
|||
<= |
小于等于 |
表达式<=表达式 |
|||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
|||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
-- |
/= |
除后赋值 |
变量/=表达式 |
-- |
||
*= |
乘后赋值 |
变量*=表达式 |
-- |
||
%= |
取模后赋值 |
变量%=表达式 |
-- |
||
+= |
加后赋值 |
变量+=表达式 |
-- |
||
-= |
减后赋值 |
变量-=表达式 |
-- |
||
<<= |
左移后赋值 |
变量<<=表达式 |
-- |
||
>>= |
右移后赋值 |
变量>>=表达式 |
-- |
||
&= |
按位与后赋值 |
变量&=表达式 |
-- |
||
^= |
按位异或后赋值 |
变量^=表达式 |
-- |
||
|= |
按位或后赋值 |
变量|=表达式 |
-- |
||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
-- |