算术运算符

运算符 术语 示例 结果
+ 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;

示例代码:

  1. #include <stdio.h>
  2. int main() {
  3. // 2数相除,要想得到小数的结果,分子分母必须有一个数是小数
  4. double c = 5/2; // 5, 2个都是整数,只会取整
  5. printf("c1 = %lf\n", c);
  6. c = 5.0/2;
  7. printf("c2 = %lf\n", c);
  8. c = 5/2.0;
  9. printf("c3 = %lf\n", c);
  10. int i = 0;
  11. // 前置++
  12. // 先加后用
  13. int b = ++i;
  14. printf("前置:b = %d, i = %d\n", b, i);
  15. // 后置++
  16. // 先用后加
  17. i = 0;
  18. b = i++;
  19. printf("后置:b = %d, i = %d\n", b, i);
  20. return 0;
  21. }

运行结果:

  1. c1 = 2.000000
  2. c2 = 2.500000
  3. c3 = 2.500000
  4. 前置:b = 1, i = 1
  5. 后置: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;

示例代码:

  1. #include <stdio.h>
  2. int main() {
  3. int a = 10;
  4. a += 5;
  5. printf("a = %d\n", a);
  6. return 0;
  7. }

运行结果:

  1. a = 15

比较运算符

C 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。

运算符 术语 示例 结果
== 相等于 4 == 3 0
!= 不等于 4 != 3 1
< 小于 4 < 3 0
> 大于 4 > 3 1
<= 小于等于 4 <= 3 0
>= 大于等于 4 >= 1 1

示例代码:

  1. #include <stdio.h>
  2. int main() {
  3. int a = 10;
  4. int b = 20;
  5. printf("%d\n", a == b);
  6. printf("%d\n", a != b);
  7. printf("%d\n", a > b);
  8. printf("%d\n", a < b);
  9. printf("%d\n", a >= b);
  10. printf("%d\n", a <= b);
  11. return 0;
  12. }

逻辑运算符

运算符 术语 示例 结果
! !a 如果a为假,则!a为真;
如果a为真,则!a为假。
&& a && b 如果a和b都为真,则结果为真,否则为假。
|| a || b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。

示例代码:

  1. #include <stdio.h>
  2. int main() {
  3. // &&(与),可以理解为并且
  4. // 案例:请判断班长和他女朋友是否符合法定结婚年龄
  5. int boy = 25;
  6. int girl = 21;
  7. int result = boy >= 22 && girl >= 20;
  8. printf("%d\n", result);
  9. // ||(或),可以理解为或者
  10. // 案例:班长女朋友玩原神没有原石了,请帮班长判断是否有足够的钱
  11. double wx_money = 100;
  12. double alipay_money = 300;
  13. result = wx_money >= 398 || alipay_money >= 398 || wx_money+alipay_money >= 398;
  14. printf("%d\n", result);
  15. // !(非),可以理解为不是
  16. printf("%d\n", !0);
  17. printf("%d\n", !!1);
  18. // 短路规则
  19. // && 左边为假,右边不执行
  20. 0 && printf("我是右边\n");
  21. // || 左边为真,右边不执行
  22. 1 || printf("我是右边\n");
  23. return 0;
  24. }

位运算符

常见的位运算符号有&、|、^、~、>>、<<,分别代表着如下含义:

运算符 术语 示例 结果
& 按位与运算 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. /**
  2. * 按位与(&)运算:位与位进行比较,如果都为1,则为1,否则为0;
  3. * 示例:
  4. * 40 & 15 = 8
  5. * 0010 1000
  6. * & 0000 1111
  7. * -------------------
  8. * 0000 1000
  9. */
  10. printf("40 & 15 = %d\n", 40 & 15);

| -(或运算)

按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;

  1. /**
  2. * 按位或(|)运算:位与位进行比较,如果都为0,则为0,否则为1;
  3. * 示例:
  4. * 40 | 15 = 47
  5. * 0010 1000
  6. * | 0000 1111
  7. * ----------------
  8. * 0010 1111
  9. */
  10. printf("40 | 15 = %d\n", 40 | 15);

^ -(异或运算)

按位异或运算:位与位进行比较,相同为0,不同为1;

  1. /**
  2. * 按位异或运算:位与位进行比较,相同为0,不同为1;
  3. * 示例:
  4. * 40 ^ 15 = 39
  5. * 0010 1000
  6. * ^ 0000 1111
  7. * ------------------
  8. * 0010 0111
  9. */
  10. printf("40 ^ 15 = %d\n", 40 ^ 15);

~ -(取反运算)

按位取反运算:补码取反,再将取反后的补码转为原码; ps:无符号的数据,取反后最高位为1,也不需要逆运算。

  1. /**
  2. * 按位取反运算:补码取反,再将取反后的补码转为原码。
  3. * 1、正数取反:由于正数的原码和补码是相同的,取反的方式简便了些。
  4. * 补码(原码) -> 取反 -> 补码逆运算 -> 反码逆运算(符号位不变) -> 取反后的原码
  5. * 2、负数取反:
  6. * 原码 -> 反码 -> 补码 -> 取反 -> 取反后的补码即原码
  7. * 示例:
  8. * 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
  9. * ~40 = 0010 1000 -> 1101 0111 -> 1101 0110 -> 1010 1001 = -41
  10. *
  11. * 原码(补码) 取反的补码 补码逆运算-1 反码逆运算
  12. * ~15 = 0000 1111 -> 1111 0000 -> 1110 1111 -> 1001 0000 = -16
  13. *
  14. * 原码 反码 补码 取反
  15. * ~-15 = 1000 1111 -> 1111 0000 -> 1111 0001 -> 0000 1110 = 14
  16. */
  17. printf("~40 = %d\n", ~40);
  18. printf("~15 = %d\n", ~15);
  19. printf("~-15 = %d\n", ~(-15));

<< -(左移运算符)

将数字的二进制补码全部向左移动,空出来的位置补0,超出范围的二进制数丢弃; 有符号的数据左移后最高位如果为1,则需要进行逆运算; 注意事项:

  • 无符号的数据,左移后最高位为1,也不需要逆运算;
  • -128:1000 0000 特殊情况也不需要逆运算;
  1. /**
  2. * 示例:
  3. * 40 << 4 = 0010 1000 << 4 = 1000 0000 = -128 (特殊的不需要逆运算)
  4. * 41 << 4 = 0010 1001 << 4 = 1001 0000 = 1000 1111 = 1111 0000 = -112
  5. * 7 6 5 4 3 2 1 0
  6. * 1 0 0 1 0 0 0 0
  7. */
  8. int8_t p = 40;
  9. p <<= 4; // p = p << 4;
  10. printf("40 << 4 = %d\n", p);

>> -(右移运算符)

将数字的二进制补码全部向右移动,空出来的位置补0,超出范围的二进制数丢弃;

  1. /**
  2. * 示例:
  3. * 15 >> 4 = 0000 1111 >> 4 = 0000 0000 = 0
  4. *
  5. * 特殊情况,默认情况下计算机采用逻辑右移方式,即左侧空出来的位置补0,但如果是算术右移,那么左侧空出来的位置需要根据符号位的值来补位。
  6. *
  7. * -15 >> 2 = 1000 1111 >> 2 (原码)
  8. * = 1111 0000 >> 2 (反码,注意:负数反码符号位不变)
  9. * = 1111 0001 >> 2 (补码)
  10. * = 1111 1100 (右移后的补码,注意:算术右移左侧空出来的位置需要根据符号位来补位)
  11. * = 1111 1011 (右移后的反码,补码转反码只需-1即可)
  12. * = 1000 0100 (右移后的原码)
  13. * = -4
  14. */
  15. int8_t q = 15;
  16. q >>= 4;
  17. printf("15 >> 4 = %d\n", q);
  18. int8_t m = -15;
  19. m >>= 2;
  20. printf("-15 >> 2 = %d\n", m);

示例代码:

  1. #include <stdio.h>
  2. #include <inttypes.h>
  3. int main() {
  4. uint8_t a = 3; // 0000 0011
  5. uint8_t b = 10; // 0000 1010
  6. // 打印显示2个字符,个数不够,左边补0
  7. printf("%02x\n", a & b); // 0000 0010,16进制为02
  8. printf("%02x\n", a | b); // 0000 1011,16进制为0b
  9. printf("%02x\n", a ^ b); // 0000 1001,16进制为09
  10. uint8_t c = 10; // 0000 1010
  11. uint8_t temp = ~c; // 1111 0101
  12. printf("%02x\n", temp); // 1111 0101,16进制为f5
  13. printf("%02x\n", c << 1); // 0001 0100,16进制为14
  14. printf("%02x\n", c >> 1); // 0000 0101,16进制为05
  15. return 0;
  16. }

运行结果:

  1. 02
  2. 0b
  3. 09
  4. f5
  5. 14
  6. 05

案例需求:

  1. // 将变量a的第2位设置为1,其他位保持不变
  2. uint8_t a = 0b10110011; // 0xb3;
  3. // 将变量b的第2位、第6位设置为1,其他位保持不变
  4. uint8_t b = 0b10110011; // 0xb3;
  5. // 将变量c的第5位设置为0,其他位保持不变
  6. uint8_t c = 0b10110011; // 0xb3;
  7. // 将变量d的第0~3位设置为0,其他位保持不变
  8. uint8_t d = 0b11111111; // 0xff;
  9. // 将变量e的第2位取反,其他位保持不变
  10. uint8_t e = 0b10110011; // 0xb3;
  11. // 将变量f取出8-15位
  12. uint32_t f = 0x12345678;

示例代码:

  1. #include <stdio.h>
  2. #include <inttypes.h>
  3. int main() {
  4. // 将变量a的第2位设置为1,其他位保持不变
  5. uint8_t a = 0b10110011; // 0xb3;
  6. a |= (1 << 2); // 或者 x = x | (1 << 2);
  7. printf("%02x\n", a); // b7, 10110111
  8. // 将变量b的第2位、第6位设置为1,其他位保持不变
  9. uint8_t b = 0b10110011; // 0xb3;
  10. b |= (1 << 2 | 1 << 6);
  11. printf("%02x\n", b); // f7,11110111
  12. // 将变量c的第5位设置为0,其他位保持不变
  13. uint8_t c = 0b10110011; // 0xb3;
  14. c &= ~(1 << 5);
  15. printf("%02x\n", c); // 93,10010011
  16. // 将变量d的第0~3位设置为0,其他位保持不变
  17. uint8_t d = 0b11111111; // 0xff;
  18. d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
  19. printf("%02x\n", d); // f0,11110000
  20. // 将变量e的第2位取反,其他位保持不变
  21. uint8_t e = 0b10110011; // 0xb3;
  22. e ^= (1 << 2);
  23. printf("%02x\n", e); // b7, 10110111
  24. // 将变量f取出8-15位
  25. uint32_t f = 0x12345678;
  26. uint32_t temp = (f & 0x0000ff00) >> 8;
  27. printf("%#x\n", temp);
  28. return 0;
  29. }

运算符优先级

  • 不同的运算符默认具备不同的优先级,符号较多不用记,现用现查就可以。
  • 当无法确定谁的优先级高时,加一个小括号就解决了。 | 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 | | —- | —- | —- | —- | —- | —- | | 1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | — | | | () | 圆括号 | (表达式)/函数名(形参表) | | — | | | . | 成员选择(对象) | 对象.成员名 | | — | | | -> | 成员选择(指针) | 对象指针->成员名 | | — | | 2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 | | | ~ | 按位取反运算符 | ~表达式 | | | | | ++ | 自增运算符 | ++变量名/变量名++ | | | | | | 自减运算符 | —变量名/变量名— | | | | | * | 取值运算符 | 指针变量 | | | | | & | 取地址运算符 | &变量名 | | | | | ! | 逻辑非运算符 | !表达式 | | | | | (类型) | 强制类型转换 | (数据类型)表达式 | | — | | | sizeof | 长度运算符 | sizeof(表达式) | | — | | 3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 | | | ** | 乘 | 表达式表达式 | | | | | % | 余数(取模) | 整型表达式%整型表达式 | | | | 4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 | | | - | 减 | 表达式-表达式 | | | | 5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 | | | >> | 右移 | 变量>>表达式 | | | | 6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 | | | >= | 大于等于 | 表达式>=表达式 | | | | | < | 小于 | 表达式<表达式 | | | | | <= | 小于等于 | 表达式<=表达式 | | | | 7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 | | | != | 不等于 | 表达式!= 表达式 | | | | 8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 | | 9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 | | 10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 | | 11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 | | 12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 | | 13 | ?: | 条件运算符 | 表达式1?
    表达式2: 表达式3 | 右到左 | 三目运算符 | | 14 | = | 赋值运算符 | 变量=表达式 | 右到左 | — | | | /= | 除后赋值 | 变量/=表达式 | | — | | | **
    = | 乘后赋值 | 变量*=表达式 | | — | | | %= | 取模后赋值 | 变量%=表达式 | | — | | | += | 加后赋值 | 变量+=表达式 | | — | | | -= | 减后赋值 | 变量-=表达式 | | — | | | <<= | 左移后赋值 | 变量<<=表达式 | | — | | | >>= | 右移后赋值 | 变量>>=表达式 | | — | | | &= | 按位与后赋值 | 变量&=表达式 | | — | | | ^= | 按位异或后赋值 | 变量^=表达式 | | — | | | |= | 按位或后赋值 | 变量|=表达式 | | — | | 15 | ,** | 逗号运算符 | 表达式,表达式,… | 左到右 | — |