操作符详解:从入门到精通

操作符详解:从入门到精通

前言

操作符是C语言的灵魂,掌握好操作符的使用,就能写出更加高效、优雅的代码。本文将全面系统地讲解C语言中的各类操作符,包括它们的分类、使用方法和注意事项,帮助你彻底搞懂操作符的方方面面。

1. 操作符的分类

C语言中的操作符非常丰富,大致可以分为以下几类:

类别 操作符
算术操作符 + - * / %
移位操作符 << >>
位操作符 & `
赋值操作符 = += -= *= /= %= <<= >>= &= `
单目操作符 ! ++ -- & * + - ~ sizeof (类型)
关系操作符 > >= < <= == !=
逻辑操作符 && `
条件操作符 ?:
逗号表达式 ,
下标引用 []
函数调用 ()
结构成员访问 . ->

今天我们将重点介绍与二进制相关的操作符(移位、位操作符),以及其他尚未详细讲解的操作符。

2. 二进制与进制转换

在深入位操作符之前,我们需要先了解二进制的基础知识。

2.1 什么是进制?

2进制、8进制、10进制、16进制只是数值的不同表示形式。比如数值15:

  • 2进制:1111
  • 8进制:17(以0开头)
  • 10进制:15
  • 16进制:F(以0x开头)

2.2 2进制转10进制

2进制每一位都有权重,从右向左依次是:(2^0, 2^1, 2^2, \ldots)

例如:二进制 1101 = (1 \times 8 + 1 \times 4 + 0 \times 2 + 1 \times 1 = 13)

2.3 10进制转2进制

使用除2取余法,不断除以2,将余数从下往上排列。

例如:125转换为2进制 → 1111101

2.4 2进制转8进制和16进制

  • 转8进制:从右向左每3位一组,转换为对应的8进制数字(最高位不足3位时直接转换)
  • 转16进制:从右向左每4位一组,转换为对应的16进制数字

💡 小贴士:8进制数以0开头,16进制数以0x开头。

3. 原码、反码、补码

整数的二进制表示有三种形式:原码、反码、补码

3.1 基本规则

类型 符号位 正数 负数
原码 最高位,0正1负 同原码 数值位不变
反码 不变 同原码 符号位不变,数值位取反
补码 不变 同原码 反码+1

重要 :整数在内存中存储的是补码

3.2 为什么用补码?

  • 符号位和数值位可以统一处理
  • 加法和减法可以统一处理(CPU只有加法器)
  • 补码与原码的转换过程相同,不需要额外硬件

4. 移位操作符

移位操作符的操作数只能是整数

4.1 左移操作符 <<

规则:左边抛弃,右边补0

c 复制代码
int num = 10;  // 二进制:1010
int n = num << 1;  // 结果:10100 = 20

4.2 右移操作符 >>

右移分为两种:

类型 规则
逻辑右移 左边补0,右边丢弃
算术右移 左边补符号位,右边丢弃

⚠️ 警告 :不要移动负数位,如 num >> -1 是未定义行为!

5. 位操作符:&、|、^、~

位操作符的操作数必须是整数

操作符 名称 规则
& 按位与 对应位都是1,结果为1
` ` 按位或
^ 按位异或 对应位不同,结果为1
~ 按位取反 0变1,1变0
c 复制代码
int num1 = -3;
int num2 = 5;
printf("%d\n", num1 & num2);  // 按位与
printf("%d\n", num1 | num2);  // 按位或
printf("%d\n", num1 ^ num2);  // 按位异或
printf("%d\n", ~0);           // 按位取反

5.1 经典面试题:不创建临时变量交换两个整数

c 复制代码
int a = 10, b = 20;
a = a ^ b;
b = a ^ b;  // b = (a ^ b) ^ b = a
a = a ^ b;  // a = (a ^ b) ^ a = b
printf("a = %d, b = %d\n", a, b);

原理 :异或运算满足自反性:(a ^ b) ^ b = a

5.2 练习1:求二进制中1的个数

方法1(有缺陷,不能处理负数):

c 复制代码
while(num) {
    if(num % 2 == 1) count++;
    num /= 2;
}

方法2(循环32次):

c 复制代码
for(i = 0; i < 32; i++) {
    if(num & (1 << i)) count++;
}

方法3(最优解):

c 复制代码
while(num) {
    count++;
    num = num & (num - 1);  // 每次消去最右边的1
}

💡 num & (num - 1) 这个技巧非常经典,可以快速消去二进制中最右边的1。

5.3 练习2:二进制位置0或置1

将13的第5位(从0开始计数)修改为1,再改回0:

c 复制代码
int a = 13;
// 第5位置为1
a = a | (1 << 4);
// 第5位置为0
a = a & ~(1 << 4);

6. 单目操作符

单目操作符的特点是只有一个操作数

操作符 名称 说明
! 逻辑反 真变假,假变真
++ 自增 前置或后置
-- 自减 前置或后置
& 取地址 获取变量的地址
* 解引用 通过地址访问变量
+ 正号 正数
- 负号 负数
~ 按位取反 二进制位取反
sizeof 取大小 计算类型或变量的大小
(类型) 强制类型转换 转换数据类型

7. 逗号表达式

逗号表达式从左向右依次执行,整个表达式的结果是最后一个表达式的结果

c 复制代码
int a = 1, b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
// c的结果是 b = a + 1 = 13

// 实用场景:简化while循环
while (a = get_val(), count_val(a), a > 0) {
    // 业务处理
}

8. 下标访问与函数调用

8.1 下标引用操作符 []

操作数:数组名 + 索引值

c 复制代码
int arr[10];    // 创建数组
arr[9] = 10;    // [] 的操作数是 arr 和 9

8.2 函数调用操作符 ()

操作数:函数名 + 传递给函数的参数

c 复制代码
test1();              // () 的操作数是 test1
test2("hello");       // () 的操作数是 test2 和 "hello"

9. 结构成员访问操作符

9.1 结构体声明

c 复制代码
struct Stu {
    char name[20];  // 名字
    int age;        // 年龄
    char sex[5];    // 性别
    char id[20];    // 学号
};

9.2 直接访问:.

c 复制代码
struct Stu s = {"张三", 20};
printf("%s %d\n", s.name, s.age);

9.3 间接访问:->

c 复制代码
struct Stu *ptr = &s;
ptr->age = 21;      // 等价于 (*ptr).age = 21

10. 操作符的优先级与结合性

10.1 优先级

优先级决定哪个运算符先执行。乘法优先级高于加法:

c 复制代码
3 + 4 * 5;  // 先算 4*5=20,再加3得23

10.2 结合性

当优先级相同时,结合性决定执行顺序:

  • 左结合:从左到右执行(大部分运算符)
  • 右结合:从右到左执行(赋值运算符等)
c 复制代码
5 * 6 / 2;  // 左结合,先算5*6=30,再除以2得15

10.3 常用优先级速查表

优先级 运算符 结合性
1(最高) () [] . -> 左到右
2 ++ -- & * + - ~ ! sizeof 右到左
3 * / % 左到右
4 + - 左到右
5 << >> 左到右
6 < <= > >= 左到右
7 == != 左到右
8 & 左到右
9 ^ 左到右
10 ` `
11 && 左到右
12 `
13 ?: 右到左
14 = 及复合赋值 右到左
15(最低) , 左到右

💡 建议 :记不住优先级没关系,使用圆括号 () 明确表达式的计算顺序即可!

11. 表达式求值的陷阱

11.1 整型提升

C语言中,charshort等类型在进行算术运算时,会先转换为intunsigned int,这就是整型提升

提升规则

  • 有符号数:按符号位提升
  • 无符号数:高位补0
c 复制代码
char a, b, c;
a = b + c;  // b和c先提升为int,计算后再截断存入a

11.2 算术转换

当操作数类型不同时,会进行算术转换,向更高精度转换:

复制代码
long double > double > float > unsigned long > long > unsigned int > int

11.3 问题表达式

有些表达式虽然语法正确,但计算结果不确定,应避免写出:

c 复制代码
// 问题1:操作数的求值顺序不确定
a*b + c*d + e*f;

// 问题2:i的修改和读取顺序不确定
c + --c;

// 问题3:函数调用顺序不确定
fun() - fun() * fun();

// 问题4:不同编译器结果不同
int ret = (++i) + (++i) + (++i);

⚠️ 核心原则:不要写出过于复杂的表达式,保持代码清晰、可读性强!

总结

本文全面介绍了C语言中的各类操作符,包括:

  1. 进制转换:理解二进制、八进制、十六进制
  2. 原反补码:理解整数在内存中的存储
  3. 移位和位操作:高效处理二进制位
  4. 优先级与结合性:理解表达式求值顺序
  5. 常见陷阱:避免写出有歧义的表达式

掌握好这些知识,你就能写出更加高效、准确的C语言代码。记住:简单清晰的代码比巧妙的代码更有价值


如果觉得这篇文章对你有帮助,欢迎点赞、收藏、分享!

相关推荐
山上三树1 小时前
C/C++ 高频报错速查表(开发通用版)
c语言·开发语言·c++
Tian_Hang1 小时前
Factory Method | 工厂方法
开发语言·c++
elseif1232 小时前
【C++】vector 详细版
开发语言·c++·算法
cany10003 小时前
C++ -- 原子变量
c++
cany10003 小时前
C++ -- 队列std::queue
开发语言·c++
周末也要写八哥4 小时前
C++中单线程方式之无脑上锁
java·开发语言·c++
cany10004 小时前
C++ -- 动态内存分配和释放(new/delete)
开发语言·c++
xcyxiner4 小时前
ubuntu下 cmake初始化脚本 以及 qt依赖
c++·qt
周末也要写八哥4 小时前
Visual C++6.0下载安装流程及PDF学习手册资源
c++·学习·pdf