Skip to content

运算符

算术运算符

运算符术语示例结果
+10 + 515
-10 - 55
*10 * 550
/10 / 52
%取模 (取余)10 % 31
++前自增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;

示例代码:

c
#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;

示例代码:

c
#include <stdio.h>

int main() {
    int a = 10;
    a += 5;
    printf("a = %d\n", a);

    return 0;
}

运行结果:

a = 15

比较运算符

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

运算符术语示例结果
==相等于4 == 30
!=不等于4 != 31
<小于4 < 30
>大于4 > 31
<=小于等于4 <= 30
>=大于等于4 >= 11

示例代码:

c
#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 有一个为真,则结果为真,二者都为假时,结果为假。

示例代码:

c
#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 & 1012 个都为 1 才为 1,结果为 001
|按位或运算011 | 101有 1 个为 1 就为 1,结果为 111
^按位异或运算011 ^ 101不同的为 1,结果为 110
~取反运算~011100
<<左移运算1010 << 110100
>>右移运算1010 >> 10101

ps:取反、左右位移运算需要在补码的基础上运算。

& (与运算)

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

c
/**
     * 按位与(&)运算:位与位进行比较,如果都为 1,则为 1,否则为 0;
     * 示例:
     *      40    &     15    =     8
     *      0010 1000
     *    & 0000 1111
     * -------------------
     *      0000 1000
     */
printf("40 & 15 = %d\n", 40 & 15);

^ (异或运算)

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

c
/**
     * 按位异或运算:位与位进行比较,相同为 0,不同为 1;
     * 示例:
     *      40    ^     15    =     39
     *      0010 1000
     *    ^ 0000 1111
     * ------------------
     *      0010 0111
     */
printf("40 ^ 15 = %d\n", 40 ^ 15);

~ (取反运算)

按位取反运算:补码取反,再将取反后的补码转为原码;

ps:无符号的数据,取反后最高位为 1,也不需要逆运算。

c
/**
 * 按位取反运算:补码取反,再将取反后的补码转为原码。
 *      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 特殊情况也不需要逆运算;
c
    /**
     * 示例:
     *      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;

c
	/*
	  23:
      0001 0111【原码】
      0001 0111【反码】
      0001 0111【补码】 >> 2
	  -----------------
	  0000 0101【补码】 --->  5
	 */
	printf(" 23 >> 2 = %d \n" , 23 >> 2) ;


	/*
	  -23:
      1001 0111【原码】
      1110 1000【反码】
      1110 1001【补码】 >> 2
	  -----------------------------------------------
	  1111 1010【补码】 ->  1111 1001【反码】-> 1000 0110【原码】 ==> -6
	 */
	printf(" -23 >> 2 = %d \n" , -23 >> 2) ;

示例代码:

c
#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;
}

运行结果:

c
02
0b
09
f5
14
05

位运算 - 寄存器操作

c
// 将变量 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;

示例代码:

c
#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;
}

运算符优先级

  • 不同的运算符默认具备不同的优先级,符号较多不用记,现用现查就可以。

  • 当无法确定谁的优先级高时,加一个小括号就解决了。

  • 以下优先级表,优先级数值越小,优先级越高。

  • 优先级越高,执行的时机越早