当前位置: 首页 > news >正文

C语言操作符详解:从基础到进阶

在C语言中,操作符是构建表达式的基础,掌握各类操作符的用法、优先级及特性,对写出高效且正确的代码至关重要。本文将系统梳理C语言操作符的核心知识点,包含实例代码与详细解析,助你彻底搞懂操作符。

1. 操作符的分类

C语言操作符可按功能分为以下几类,清晰的分类有助于理解其特性:

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

2. 二进制与进制转换

计算机底层使用二进制存储数据,掌握进制转换是理解位操作的基础。

2.1 二进制与十进制转换

  • 二进制转十进制:按权重展开求和(权重为20,21,22,...2^0, 2^1, 2^2,...20,21,22,...,从右向左)。
    例:二进制1101转十进制:
    1×23+1×22+0×21+1×20=8+4+0+1=131×2^3 + 1×2^2 + 0×2^1 + 1×2^0 = 8 + 4 + 0 + 1 = 131×23+1×22+0×21+1×20=8+4+0+1=13

  • 十进制转二进制:除2取余,余数倒序排列。
    例:十进制125转二进制:
    125 ÷ 2 = 62 余1
    62 ÷ 2 = 31 余0
    31 ÷ 2 = 15 余1
    15 ÷ 2 = 7 余1
    7 ÷ 2 = 3 余1
    3 ÷ 2 = 1 余1
    1 ÷ 2 = 0 余1
    结果:1111101

2.2 二进制与八/十六进制转换

  • 二进制转八进制:从右向左每3位一组,不足补0,每组对应1个八进制数(0-7)。
    例:二进制01101011 → 分组01 101 011 → 转换为八进制153(前缀0表示八进制)。

  • 二进制转十六进制:从右向左每4位一组,不足补0,每组对应1个十六进制数(0-9, a-f)。
    例:二进制01101011 → 分组0110 1011 → 转换为十六进制0x6b(前缀0x表示十六进制)。

3. 原码、反码、补码

整数在内存中以补码存储,原因是:统一符号位与数值位处理,简化加法运算(CPU仅需加法器)。

  • 原码:直接表示符号位(0正1负)和数值位。
    例:int a = 3 → 原码00000000 00000000 00000000 00000011
    int b = -3 → 原码10000000 00000000 00000000 00000011

  • 反码:原码符号位不变,数值位取反。
    例:-3的反码11111111 11111111 11111111 11111100

  • 补码:反码+1(负数补码);正数原码=反码=补码。
    例:-3的补码11111111 11111111 11111111 11111101

4. 移位操作符

移位操作符仅作用于整数,分为左移和右移。

4.1 左移操作符 <<

  • 规则:左边丢弃,右边补0。
  • 示例
#include <stdio.h>
int main() {int num = 10; // 二进制:00000000 00000000 00000000 00001010int n = num << 1; // 左移1位:00000000 00000000 00000000 00010100 → 20printf("n = %d\n", n); // 输出:20printf("num = %d\n", num); // 输出:10(原变量不变)return 0;
}

4.2 右移操作符 >>

  • 逻辑右移:左边补0,右边丢弃(无符号数)。
  • 算术右移:左边补符号位,右边丢弃(有符号数,主流编译器采用)。

示例(算术右移):

#include <stdio.h>
int main() {int num = -1; // 补码:11111111 11111111 11111111 11111111int n = num >> 1; // 算术右移1位:11111111 11111111 11111111 11111111 → 仍为-1printf("n = %d\n", n); // 输出:-1return 0;
}

⚠️ 注意:禁止移动负数位(如num >> -1,行为未定义)。

5. 位操作符

位操作符直接对二进制位进行操作,操作数为整数。

操作符功能示例(二进制)
`&`按位与(同1则1)1010 & 0111 = 0010
`|`按位或(有1则1)1010 | 0111 = 1111
`^`按位异或(不同则1)1010 ^ 0111 = 1101
`~`按位取反(0变1,1变0)~1010 = 0101(假设4位)
|

经典案例

  1. 不创建临时变量交换两数
#include <stdio.h>
int main() {int a = 10, b = 20;a = a ^ b; // a = 10^20b = a ^ b; // b = (10^20)^20 = 10a = a ^ b; // a = (10^20)^10 = 20printf("a = %d, b = %d\n", a, b); // 输出:a=20, b=10return 0;
}
  1. 求二进制中1的个数
// 方法3(最优):每次消去最后一个1,循环次数=1的个数
#include <stdio.h>
int count_one(int num) {int count = 0;while (num) {num &= num - 1; // 消去最后一个1count++;}return count;
}
int main() {printf("%d\n", count_one(10)); // 1010 → 2个1 → 输出2return 0;
}

6. 单目操作符

单目操作符仅需一个操作数,重点如下:

操作符功能示例
!逻辑非(真变假,假变真)!0 = 1!5 = 0
++自增(前置先增后用,后置先用后增)int a=3; printf("%d", ++a); // 4
--自减(类似++)int a=3; printf("%d", a--); // 3
sizeof求类型/变量所占字节数sizeof(int) = 4sizeof(a) = 4(a为int)
(类型)强制类型转换(int)3.14 = 3

7. 逗号表达式

形式:exp1, exp2, ..., expN

  • 执行顺序:从左到右依次执行。
  • 结果:最后一个表达式的值。

示例:

#include <stdio.h>
int main() {int a = 1, b = 2;int c = (a > b, a = b + 10, a, b = a + 1); // 执行:a>b(假)→ a=12 → 取a=12 → b=13 → 结果c=13printf("c = %d\n", c); // 输出:13return 0;
}

8. 下标访问与函数调用

  • 下标访问[]:操作数为数组名+索引,如arr[3]等价于*(arr+3)

    int arr[5] = {1,2,3,4,5};
    printf("%d\n", arr[2]); // 输出:3(访问第3个元素)
    
  • 函数调用():操作数为函数名+参数列表。

    #include <stdio.h>
    void print_hello() {printf("Hello, C!\n");
    }
    int add(int a, int b) {return a + b;
    }
    int main() {print_hello(); // 函数调用,无参数printf("3+5 = %d\n", add(3, 5)); // 函数调用,有参数 → 输出8return 0;
    }
    

9. 结构体成员访问

结构体成员访问有两种方式:

  • .:直接访问(结构体变量)。
  • ->:间接访问(结构体指针)。

示例:

#include <stdio.h>
#include <string.h>// 定义学生结构体
struct Stu {char name[20];int age;
};int main() {struct Stu s = {"张三", 20}; // 结构体变量struct Stu* ps = &s; // 结构体指针// 访问成员printf("name: %s, age: %d\n", s.name, s.age); // .访问 → 张三, 20strcpy(ps->name, "李四"); // ->访问:修改姓名ps->age = 22; // ->访问:修改年龄printf("name: %s, age: %d\n", s.name, s.age); // 李四, 22return 0;
}

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

表达式求值由优先级和结合性决定:

  • 优先级:决定操作顺序(如*优先级高于+)。
    例:3 + 4 * 5 → 先算4*5=20,再算3+20=23

  • 结合性:优先级相同时,左结合(从左到右)或右结合(从右到左,如赋值=)。
    例:5 * 6 / 2 → 左结合,先算5*6=30,再算30/2=15

优先级运算符描述结合性
1++ --
()
[]
.
->
(type){list}
后缀自增与自减
函数调用
数组下标
结构体与联合体成员访问
结构体与联合体成员通过指针访问
复合字面量(C99)
从左到右
2++ --
+ -
! ~
(type)
*
&
sizeof
_Alignof
前缀自增与自减[注 1]
一元加与减
逻辑非与逐位非
转型
间接(解引用)
取址
取大小[注 2]
对齐要求(C11)
从右到左
3* / %乘法、除法及余数从左到右
4+ -加法及减法
5<< >>逐位左移及右移
6< <=
> >=
分别为 < 与 ≤ 的关系运算符
分别为 > 与 ≥ 的关系运算符
7== !=分别为 = 与 ≠ 关系
8&逐位与
9^逐位异或(排除或)
10单竖线逐位或(包含或)
11&&逻辑与
12双竖线 逻辑或
13?:三元条件[注 3]从右到左
14[注 4]=
+= -=
*= /= %=
<<= >>=
`&= ^=
简单赋值
以和及差赋值
以积、商及余数赋值
以逐位左移及右移赋值
以逐位与、异或及或赋值
15,逗号从左到右

11. 表达式求值注意事项

11.1 整型提升

长度小于int的整型(如charshort)在运算时会提升为intunsigned int,避免精度丢失。
例:char a = 127, b = 1; int c = a + b;

  • a提升为00000000 00000000 00000000 01111111
  • b提升为00000000 00000000 00000000 00000001
  • 相加后c = 128(若直接存char会溢出)。

11.2 避免歧义表达式

部分表达式因优先级/结合性无法确定唯一执行顺序,结果依赖编译器,应避免:

  • c + --c:无法确定+左右操作数的求值顺序。
  • (++i) + (++i) + (++i):不同编译器结果不同(如GCC输出10,VS输出12)。

总结

操作符是C语言的基础,掌握其分类、特性及求值规则,能帮助我们写出更高效、无歧义的代码。重点关注位操作符的灵活应用(如交换变量、统计1的个数)、补码的存储逻辑,以及优先级对表达式的影响。实际开发中,复杂表达式建议用括号明确顺序,减少歧义。

希望本文对你理解C语言操作符有帮助,欢迎留言交流! 😊

http://www.lryc.cn/news/602827.html

相关文章:

  • linux find命令使用教程
  • 【数学建模论文学习笔记】基于历史数据的蔬菜类商品定价与补货决策模型
  • 1688 item_search_shop 接口参数说明与测试指南
  • 源代码管理工具有哪些?有哪些管理场景?
  • MGER综合实验
  • 椭圆曲线加密(ECC)实战:从原理到区块链应用
  • 机器学习(重学版)基础篇(算法与模型一)
  • 热斑漏检率↓78%!陌讯多模态算法在无人机光伏巡检的轻量化实践
  • PBR技术
  • 利用软件定义无线USRP X410、X440 电推进无线原型设计
  • 5.Linux ssh远程登录配置及sftp,scp命令
  • 排序算法 (Sorting Algorithms)-Python示例
  • 一个高效的阿里云漏洞库爬虫工具,用于自动化爬取和处理CVE数据
  • AW2013 LED驱动芯片 工作方式介绍
  • 阿里云Ubuntu 22.04 ssh隔一段时间自动断开的解决方法
  • 解决 nginx 加载css文件时无效问题、解决 nginx 加载css文件识别成 text/plan 的问题
  • github copilot接入openai-compatible模型以及去除安全限制的方法
  • 嵌入式开发学习———Linux环境下数据结构学习(四)
  • UV安装并设置国内源
  • golang--函数栈
  • 学习lxml库:Python XML/HTML处理利器
  • 微型化IMU如何突破无人机与机器人的性能边界?
  • Vue 工程化
  • Facenet(MTCNN+InceptionResnetV1)人脸考勤项目(有缺点,但可用)
  • 前端实现PDF在线预览的8种技术方案对比与实战
  • 【kafka】消息队列
  • 专题:2025医药生物行业趋势与投融资研究报告|附90+份报告PDF、原数据表汇总下载
  • 4、如何生成分布式ID?
  • C++入门自学Day2-- c++类与对象(初识2)
  • Deepseek + browser-use 轻松实现浏览器自动化