尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C语言操作符详解:从“二进制“到“位运算“,一次把操作符吃透
写在前面操作符不是靠死记优先级学会的学 C 语言到操作符这一章很多人的第一反应就是优先级表好长、记不住。可真正让人在写代码时踩坑的往往不是优先级本身而是位运算那一堆、|、^、、——看着都认识一上手算二进制就懵再加上原码、反码、补码这套负数表示更是绕成一团。这篇复盘我按先打地基、再上操作符、最后聊那些玄学的求值顺序的顺序把操作符这一讲重新捋了一遍配上图争取一次讲明白。一、操作符的分类先看清全貌C 语言的操作符教材里一般分这么几类先整体扫一眼心里有个地图算术操作符、-、*、/、%移位操作符、位操作符、|、^、~赋值操作符、、-、*、/、%、、、、|、^单目操作符!、、--、、*、、-、~、sizeof、(类型)关系操作符、、、、、!逻辑操作符、||条件操作符? :逗号表达式,下标引用[]函数调用()结构成员访问.、-算术、赋值、逻辑、条件这些之前都讲过了今天重点啃和二进制相关的移位、位运算以及它们的地基——进制转换和原反补码。二、二进制与进制转换位运算的地基我们常听到 2 进制、8 进制、10 进制、16 进制它们到底是什么说白了只是同一个数值的不同表示形式罢了。就拿数值15来说它在四种进制下长这样二进制1111八进制17十进制15十六进制F两个小约定先记住十六进制前面写0x八进制前面写0比如0x6b是十六进制0153是八进制。2.1 二进制转十进制看权重我们先从最熟的 10 进制讲起10 进制满 10 进 1每一位都是 0~9 的数字。二进制也一样满 2 进 1每一位都是 0~1 的数字。那十进制123为什么是一百二十三因为每一位有权重——从右往左是个位、十位、百位权重是10^0、10^1、10^2……。二进制也是同样的道理只不过每一位的权重变成了2^0、2^1、2^2……。看图就懂了11011×8 1×4 0×2 1×113。2.2 十进制转二进制反过来十进制转二进制就是用 2 反复除、取余数倒着排。比如 15 除以 2一路余 1、1、1、1倒过来就是1111。2.3 二进制转八进制、十六进制转八进制八进制每一位是 0~7写成二进制最多 3 位就够7 的二进制是111。所以从二进制右边低位开始每 3 位换算成 1 个八进制位不够 3 位直接换算。例01101011→0153。转十六进制十六进制每一位是 0~9、a~f写成二进制最多 4 位f 的二进制是1111。所以从右边低位开始每 4 位换算成 1 个十六进制位。例01101011→0x6b。三、原码、反码、补码负数在内存里长啥样整数的二进制表示有三种原码、反码、补码。有符号整数的三种表示都分符号位和数值位两部分二进制序列里最高位当符号位其余是数值位。符号位 0 表示正、1 表示负。正整数的原码、反码、补码都相同负整数三种表示各不相同原码直接按正负翻译成二进制反码符号位不变其余位按位取反补码反码 1。而补码再变回原码也是取反 1运算过程一模一样。IMG2为什么内存里存的是补码因为用补码可以把符号位和数值域统一处理加法和减法也能统一CPU 只有加法器而且补码和原码互转的运算过程相同不需要额外的硬件电路。这一条是理解后面位运算结果会正负乱跳的关键。四、移位操作符 和 左移、右移操作数只能是整数。4.1 左移操作符规则就一句左边抛弃、右边补 0。#include stdio.h int main() { int num 10; int n num 1; printf(n %d\n, n); // 20 printf(num %d\n, num); // 10原值不变 return 0; }注意num 1只是算了个结果num本身没变。左移一位等价于乘以 2。4.2 右移操作符右移分两种行为不一样逻辑右移左边用0填充右边丢弃算术右移左边用原值的符号位填充右边丢弃。#include stdio.h int main() { int num 10; int n num 1; printf(n %d\n, n); // 5 printf(num %d\n, num); // 10 return 0; }IMG3⚠️ 警告移位运算符不要移动负数位这是标准未定义行为。比如num -1就是错的。五、位操作符 | ^ ~位操作符有四个按位与、|按位或、^按位异或、~按位取反。同样操作数必须是整数。IMG4直接上代码#include stdio.h int main() { int num1 -3; int num2 5; printf(%d\n, num1 num2); // 5 printf(%d\n, num1 | num2); // -3 printf(%d\n, num1 ^ num2); // -8 printf(%d\n, ~0); // -1 return 0; } 这里记住一个异或的好用结论任何数和 0 异或是它自己任何数和它自己异或是 0a^a0、a^0a。5.1 一道变态面试题不创建临时变量交换两个整数不给第三个变量怎么交换a和b用异或#include stdio.h int main() { int a 10; int 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); return 0; }原理就靠a^a0、a^0a这两条一步步消掉了。这个方法面试常问但实际工程里可读性差慎用。5.2 练习1求二进制中 1 的个数方法一除 2 取余对负数不友好会死循环int count 0; while (num) { if (num % 2 1) count; num num / 2; }方法二逐位和1i按位与要循环 32 次int count 0; for (int i 0; i 32; i) { if (num (1 i)) count; }方法三num num (num - 1)每次消掉最低位的一个 1有几个 1 就循环几次最优int count 0; while (num) { count; num num (num - 1); }n (n-1)这个技巧很经典它会把 n 的二进制里最低位的一个 1 变成 0。方法三虽然难想但效率最高面试加分项。5.3 练习2把某一位置 0 或置 1把 13 的二进制第 5 位改成 1再改回 0#include stdio.h int main() { int a 13; // 0000...01101 a a | (1 4); // 第5位置10000...11101 printf(a %d\n, a); // 29 a a ~(1 4); // 第5位置00000...01101 printf(a %d\n, a); // 13 return 0; }套路记牢置 1 用| (1n)置 0 用 ~(1n)。六、单目操作符单目操作符的特点是只有一个操作数!、、--、、*、、-、~、sizeof、(类型)。其中取地址和*解引用这两个留到学指针时再讲。七、逗号表达式exp1, exp2, exp3, … expN逗号表达式就是用逗号隔开的多个表达式从左向右依次执行整个表达式的结果是最后一个表达式的结果。int a 1; int b 2; int c (a b, a b 10, a, b a 1); // c 是多少c 13b a1 121 13逗号表达式在while里能把多步操作挤成一句while (a get_val(), count_val(a), a 0) { // 业务处理 }八、下标[] 和 函数调用()这两个也是操作符[]下标引用操作数是一个数组名 一个索引。arr[9] 10里[]的两个操作数就是arr和9。()函数调用第一个操作数是函数名剩下的是传参。test2(hello bit.)里的()就是函数调用操作符。#include stdio.h void test1() { printf(hehe\n); } void test2(const char *str) { printf(%s\n, str); } int main() { test1(); // 这里的 () 就是函数调用操作符 test2(hello bit.); return 0; }九、结构成员访问. 和 -C 语言只有 char、short、int、float、double 这些内置类型还不够——想描述一个学生、一本书单一类型搞不定于是有了结构体这种自定义类型。结构是一些值的集合这些值叫成员变量每个成员可以是不同类型标量、数组、指针甚至别的结构体。9.1 结构的声明struct Stu { char name[20]; // 名字 int age; // 年龄 char sex[5]; // 性别 char id[20]; // 学号 }; // 分号不能丢定义和初始化struct Stu s1 {zhangsan, 20}; // 普通初始化 struct Stu s2 {.age 20, .name lisi}; // 指定顺序初始化9.2 成员的访问直接访问用.结构体变量.成员名比如p.x。间接访问拿到的是指向结构体的指针时用-结构体指针-成员名比如ptr-x。#include stdio.h struct Point { int x; int y; }; int main() { struct Point p {3, 4}; struct Point *ptr p; ptr-x 10; ptr-y 20; printf(x %d y %d\n, ptr-x, ptr-y); return 0; }十、优先级与结合性操作符有两个重要属性决定了表达式求值的顺序优先级一个表达式里有多个运算符时谁先算。比如3 4 * 5乘法优先级高先算4*5。结合性两个运算符优先级相同时从左到右还是从右到左。大部分是左结合少数如赋值是右结合。比如5 * 6 / 2*和/同级左结合先算5*6再/2。优先级从高到低记住大概即可其他查表圆括号()自增/自减--单目/-乘*除/加减-关系 赋值圆括号优先级最高所以想改变顺序、又不想背表加括号最省心。十一、表达式求值那些玄学的结果11.1 整型提升C 语言里整型算术运算至少以 int 的精度进行。表达式里比 int 短的 char、short运算前会先被转成 int这叫整型提升。为什么因为 CPU 的整型运算器ALU操作数长度一般就是 int两个 char 相加实际执行前也得先转成 int 的标准长度否则 CPU 不好直接算。提升规则有符号整数按符号位提升负数高位补 1正数补 0无符号整数高位补 0。char c1 -1; // 补码 11111111 // 提升高位补符号位 1 → 11111111 11111111 11111111 11111111 char c2 1; // 补码 00000001 // 提升高位补 0 → 00000000 00000000 00000000 0000000111.2 算术转换如果操作符两边的操作数类型不同就得先把排名靠后的转成另一边的类型再算。层次从高到低long double double float unsigned long int long int unsigned int int11.3 问题表达式别写这种未定义的代码就算有优先级和结合性有些表达式的求值顺序依然无法唯一确定结果就是未定义行为不同编译器结果不同。举几个反例a * b c * d e * f; // 优先级只能保证 * 比 早但第三个 * 和第一个 谁先执行不确定 c --c; // -- 在 前但 的左操作数何时取值未知结果有歧义 answer fun() - fun() * fun(); // 三个 fun() 的调用顺序无法确定 int i 1; int ret (i) (i) (i); // GCC 和 VS 结果不同结论就一句话即使懂了优先级和结合性也别写出那种靠猜的复杂表达式。括号一加、拆成几步又清楚又安全何乐不为。总结最后收个尾把这一讲的骨架拎出来进制二进制满 2 进 1转十进制看权重转八/十六进制靠分组3 位 / 4 位。原反补负数内存里存的是补码取反1符号位 0 正 1 负。移位左移左丢右补 0右移分逻辑补 0和算术补符号位别移负数位。位运算置 0 / 提取位、|置 1、^翻转与交换、~取反记住a^a0、n(n-1)消最低位 1。求值优先级结合性决定顺序但别写靠猜的复杂表达式。一句话收尾位运算不难难的是把二进制那套地基先打牢。拓展学习青蛙跳台阶 / 汉诺塔上节递归的拓展可用位运算或递归继续练深入理解整数在内存中的补码存储与大小端字节序
RELATED

相关推荐

如何用ElasticSearch进行机器学习

如何用ElasticSearch进行机器学习

前言工业实践中, 如今机器学习已获得广泛应用。而作为强大搜索引擎的, 6.3起也开始内置对机器学习的支持。也在6.3开始内置了对机器学习的支持概述假使你存在异常侦测以及数据回归需求, 且数据无需特别处理, 能直接从es接入, 从应用角度而言, 可选用Es内置的机器学习功能, 这般…

📅 2026/10/9 2:37:10
导师说文献综述像流水账?2026年AI生成文献综述从入门到精通

导师说文献综述像流水账?2026年AI生成文献综述从入门到精通

"你这不是文献综述,是文献罗列。"这是研究生最怕收到的评语之一。读了不少文献,写出来却只是"张三认为……李四指出……王五发现……"的排队报菜名,这是文献综述最常见的死法。2026 年,AI 生成文献综述的工具…

📅 2026/9/17 17:19:32
开题报告写不出来?2026年AI生成开题报告的正确用法与避坑清单

开题报告写不出来?2026年AI生成开题报告的正确用法与避坑清单

开题答辩前一周,文档里还只有标题。研究背景空着、研究意义空着、技术路线更是毫无头绪——这种"开题焦虑"几乎每个研究生都经历过。2026 年,AI 生成开题报告已经成了不少人的救急方案,但用错方法的人也不少:有人照搬 A…

📅 2026/10/7 20:02:16
MORE NEWS

更多资讯

📰

JavaWeb学生选课系统毕业设计:三层架构与数据库设计详解

简介:一份基于Java的学生选课系统设计毕业论文文档,面向计算机相关专业需要完成毕业设计或课程设计的在校学生,尤其适合以教务管理系统为选题方向的读者。文档以河南工业大学毕业设计论文为原型,包含中英文摘要、关键词、目录及正…

📰

JDK 8u391 Linux部署避坑指南:版本校验、环境配置与多版本管理

简介:本资源为Oracle官方JDK 1.8的64位Linux发行版完整安装包,面向Java初学者、Linux环境开发者及企业级应用维护人员,解决Java开发环境快速部署与稳定运行的核心需求。压缩包共1589个文件,涵盖701个核心jar库(含rt.ja…

📰

Spring Boot+Vue3在线考试系统毕设实战(含防作弊与工程化细节)

简介:本资源是一套完整的毕业设计级在线考试系统实现方案,面向计算机专业本科生及Web开发初学者,解决课程考核与自主测评场景下的全流程线上考试需求。系统支持模拟练习与正式考试双模式:练习时学生可自选科目章节组卷、即时查看答…

📰

C语言实现哈夫曼编码无损文件压缩:原理、代码与调试全攻略

简介:面向数据结构课程设计的C语言哈夫曼压缩实现,包含完整源码与实验报告,适合计算机及相关专业学生完成文件压缩类课设或理解哈夫曼树原理。资源以ASCII字符频率为基础构建Huffman树,生成对应编码写入文件实现压缩,同…

📰

抽象工厂与单件模式:C++双数据库切换代码实现深度解析

简介:一份软件构造课程实验报告,面向学习设计模式与数据库抽象层开发的软件工程专业学生。报告以徐奕同学2019年完成的抽象工厂模式与单件模式编程实验为基础,针对MySQL与SQL Server两种数据库,给出用户、部门、项目三类对象的具体…

📰

双 11 运维智能体接入实战:基于 MCP 协议构建自动化数据库慢查询自愈助手

封网前最后一个工作日,下午两点刚过,公司 DBA 老林就在内部群里连发了三个“抓狂”表情:“求求各位后端大爷,别再私聊让我查慢 SQL 和看索引了!我手头还有 6 个核心分库的参数调优没做完,谁再让我连生产库跑…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬