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

【C语言】第二课 位运算

运算符名称描述示例结果(基于示例)
&按位与两个位都为1时,结果才为15 & 31 (0b0001)
``按位或两个位中只要有一个为1,结果就为1`5
^按位异或两个位相同时为0,不同时为15 ^ 36 (0b0110)
~按位取反将操作数的每一位取反(0变1,1变0)~5-6 (依赖位数)
<<左移将操作数的二进制位向左移动指定的位数,右边空位补05 << 110 (0b1010)
>>右移将操作数的二进制位向右移动指定的位数,左边空位补符号位(算术右移)或0(逻辑右移)5 >> 12 (0b0010)

🧮 位运算详解与示例

1. 按位与(&)

逐位比较,只有两个对应的位都为1时,结果位才为1。常用于屏蔽(清零)特定位检查特定位是否为1

#include <stdio.h>
int main() {unsigned int a = 0b11001100; // 204unsigned int b = 0b10101010; // 170unsigned int result = a & b; // 0b10001000 (136)printf("a & b = 0x%02X\n", result); // 输出: a & b = 0x88return 0;
}
2. 按位或(|)

逐位比较,只要两个对应的位中有一个为1,结果位就为1。常用于将特定位设置为1

#include <stdio.h>
int main() {unsigned int a = 0b11001100; // 204unsigned int b = 0b10101010; // 170unsigned int result = a | b; // 0b11101110 (238)printf("a | b = 0x%02X\n", result); // 输出: a | b = 0xEEreturn 0;
}
3. 按位异或(^)

逐位比较,如果两个对应的位不同,则结果位为1;相同则为0。常用于翻转特定位交换两个变量的值而不使用临时变量

#include <stdio.h>
int main() {unsigned int a = 0b11001100; // 204unsigned int b = 0b10101010; // 170unsigned int result = a ^ b; // 0b01100110 (102)printf("a ^ b = 0x%02X\n", result); // 输出: a ^ b = 0x66// 交换两个变量a = a ^ b;b = a ^ b; // 等价于 (a ^ b) ^ b = aa = a ^ b; // 等价于 (a ^ b) ^ a = b (此时b已经是原来的a)printf("After swap: a = %u, b = %u\n", a, b);return 0;
}
4. 按位取反(~)

将操作数的每一位取反(0变成1,1变成0)。这是一个单目运算符。

#include <stdio.h>
int main() {unsigned int a = 0b00001111; // 15unsigned int result = ~a; // 0b11110000 (240) (假设8位)// 但实际中unsigned int是32位或64位,结果会很大printf("~a (unsigned) = 0x%X\n", result); int b = 15; // 有符号整数int signed_result = ~b; // 所有位取反,包括符号位printf("~b (signed) = %d\n", signed_result); // 输出: -16return 0;
}

注意:对无符号整数和有符号整数进行取反操作,其结果的理解有所不同。

5. 左移(<<)

将操作数的二进制位整体向左移动指定的位数,右边空出的位用0填充。左移一位相当于乘以2(在没有溢出的情况下)。

#include <stdio.h>
int main() {unsigned int a = 0b00001111; // 15 (二进制 ...00001111)unsigned int result = a << 2; // 0b00111100 (60) (...00111100)printf("a << 2 = %u\n", result); // 输出: 60int b = 1;printf("1 << 31 (on 32-bit system): %d\n", 1 << 31); // 可能产生负数或溢出return 0;
}

注意:左移操作需要小心溢出符号位问题。

6. 右移(>>)

将操作数的二进制位整体向右移动指定的位数。右移一位相当于除以2(对于非负整数,且向下取整)。

  • 逻辑右移:对于无符号整数,左边空出的位用0填充。
  • 算术右移:对于有符号整数,左边空出的位通常用符号位填充(即保持符号不变)。
#include <stdio.h>
int main() {unsigned int u_num = 0b10001100; // 140 (无符号)u_num = u_num >> 2; // 逻辑右移: 0b00100011 (35)printf("u_num >> 2 (unsigned) = %u\n", u_num);int s_num = -80; // 有符号负数 (其二进制表示是补码形式)s_num = s_num >> 2; // 算术右移: 保持符号位,结果仍是负数printf("s_num >> 2 (signed) = %d\n", s_num); // 输出: -20return 0;
}

⚙️ 位运算在寄存器操作中的应用(逆向工程重点)

在嵌入式系统和驱动开发中,经常需要直接操作硬件寄存器。这些寄存器中的每一个位或几个位可能控制着特定的功能。位运算,尤其是按位与(&)、按位或(|) 结合移位操作(<<, >>),是实现这种精确控制的利器。

1. 寄存器位操作常用技巧

假设我们有一个32位的控制寄存器 *reg

  • 置位(Set a Bit):将特定位置1,而不影响其他位。
    *reg |= (1 << 3);   // 将第3位置1 (从0开始计数)
    
  • 清零(Clear a Bit):将特定位清0,而不影响其他位。
    *reg &= ~(1 << 5);  // 将第5位清0
    
  • ** toggle位(Toggle a Bit)**:将特定位翻转(1变0,0变1)。
    *reg ^= (1 << 7);   // 翻转第7位
    
  • 检查位(Check a Bit):检查特定位是否为1。
    if (*reg & (1 << 2)) { // 检查第2位是否为1// 该位为1
    }
    
2. 实例:模拟LED控制

假设一个LED由某个GPIO端口寄存器的第5位控制(1点亮,0熄灭)。

#include <stdio.h>
#include <unistd.h> // 用于sleep函数// 假设的寄存器地址和映射(实际中根据芯片手册定义)
#define GPIO_OUT_REG (*(volatile unsigned int *)0x40010000)void turn_on_led() {GPIO_OUT_REG |= (1 << 5);  // 第5位置1,点亮LED
}void turn_off_led() {GPIO_OUT_REG &= ~(1 << 5); // 第5位清0,熄灭LED
}int main() {turn_on_led();printf("LED is ON\n");sleep(1); // 延迟1秒turn_off_led();printf("LED is OFF\n");return 0;
}

说明

  • volatile 关键字告诉编译器该变量的值可能会被硬件或其他线程意外改变,禁止编译器对其进行优化,确保每次访问都从内存读取或写入。
  • 实际寄存器地址和位定义需参考具体的芯片数据手册。

🧠 位运算符的优先级

位运算符的优先级有时会带来意想不到的结果。以下是从高到低的优先级顺序(同一行优先级相同):

优先级运算符结合性
最高~ (按位取反)右到左
<<, >>左到右
& (按位与)左到右
^ (按位异或)左到右
最低`` (按位或)

强烈建议:在复杂的表达式中大量使用括号()来明确指定运算顺序,避免依赖记忆优先级而引入错误。

// 一个可能令人困惑的例子
int value = 0xFF;
int result = ~value & 0xF; // 等价于 (~value) & 0xF,而不是 ~(value & 0xF)// 清晰的写法是:
int clear_result = (~value) & 0xF;

💡 位运算的实用技巧

位运算的魅力在于其高效和巧妙。

  1. 判断奇偶性

    if (x & 1) {// x是奇数
    } else {// x是偶数
    }
    
  2. 交换两个变量的值(不借助临时变量)

    a = a ^ b;
    b = a ^ b; // b now = original a
    a = a ^ b; // a now = original b
    
  3. 检查2的幂次

    if ((x & (x - 1)) == 0) {// x是2的幂(且x不为0)
    }
    
  4. 提取RGB颜色分量

    unsigned int color = 0x00FF00; // 绿色
    unsigned char red = (color >> 16) & 0xFF;   // 红色分量 0x00
    unsigned char green = (color >> 8) & 0xFF; // 绿色分量 0xFF
    unsigned char blue = color & 0xFF;         // 蓝色分量 0x00
    
  5. 位掩码(Bit Mask)
    使用位掩码可以高效地管理多个布尔状态或选项。

    #define OPTION_A (1 << 0) // 0b00000001
    #define OPTION_B (1 << 1) // 0b00000010
    #define OPTION_C (1 << 2) // 0b00000100unsigned char flags = 0;flags |= OPTION_A | OPTION_C; // 设置选项A和C
    if (flags & OPTION_B) { ... } // 检查选项B是否设置
    flags &= ~OPTION_A;           // 清除选项A
    

文章转载自:

http://gVU8dgkq.nktyq.cn
http://C1vyR18Z.nktyq.cn
http://HxEqyZxV.nktyq.cn
http://zZ5oyV8o.nktyq.cn
http://JhmdNMwn.nktyq.cn
http://acZz9yNw.nktyq.cn
http://RD30L2hm.nktyq.cn
http://3CRz5nXV.nktyq.cn
http://XTPZCKYh.nktyq.cn
http://O2AuzNJo.nktyq.cn
http://bnBnLwYs.nktyq.cn
http://AQjA2FIK.nktyq.cn
http://DcOkyMMb.nktyq.cn
http://aAU7Kt1V.nktyq.cn
http://auF8bgrx.nktyq.cn
http://XuEnsV9T.nktyq.cn
http://Nepi5Oh3.nktyq.cn
http://8TNR0a3E.nktyq.cn
http://s6XfWTNI.nktyq.cn
http://hn8ZYHtw.nktyq.cn
http://ccUQ3zGp.nktyq.cn
http://pDAyvpCl.nktyq.cn
http://sts9mzar.nktyq.cn
http://9Aex8nyx.nktyq.cn
http://w16X9mKz.nktyq.cn
http://4CvfjeOE.nktyq.cn
http://JPhKIT2W.nktyq.cn
http://I3Y1jndS.nktyq.cn
http://gGSGLZBl.nktyq.cn
http://f3w3s5lg.nktyq.cn
http://www.dtcms.com/a/367258.html

相关文章:

  • 【正则表达式】 正则表达式匹配位置规则是怎么样的?
  • 【LeetCode数据结构】设计循环队列
  • Python 第三方自定义库开发与使用教程
  • Browser Use 浏览器自动化 Agent:让浏览器自动为你工作
  • AI代码管家:告别烂代码的自动化魔法
  • 数据结构_二叉平衡树
  • 君正交叉编译链工具mips-gcc540-glibc222-64bit-r3.3.0.smaller.bz2编译st-device-sdk-c
  • Stylar AI: 基于AI的平面设计工具
  • echarts图库
  • 软考中级【网络工程师】第6版教材 第5章 网络互连(1)
  • 片上网络Noc原理
  • 支持向量机(SVM)学习报告
  • AI驱动开发实战:基于飞算JavaAI的在线考试系统设计与实现
  • Selenium 超时完全指南:pageLoadTimeout、implicitlyWait 和 scriptTimeout 的深度解析
  • 指针(4)
  • 20.36 QLoRA微调实测:59%显存暴降+3倍提速,95%性能保留惊呆业界!
  • 【数学建模学习笔记】机器学习分类:XGBoost分类
  • Mybatis入门、操作数据、配置xml映射、数据封装
  • STM32实践项目(激光炮台)
  • NotePad++下载安装与设置
  • 深入解析 UDT 协议:突破数据传输瓶颈的高性能解决方案
  • MySQL分区表实战:提升大表查询性能的有效方法
  • c# .net中using的使用
  • AI大模型企业落地指南-笔记05
  • InheritedWidget
  • 2025数学建模国赛高教社杯C题思路代码文章助攻
  • 超细整理,全链路性能测试-容量评估与规划,看这篇就够了...
  • Java ConcurrentModificationException 深度剖析开发调试日志
  • 从群体偏好到解构对齐:大模型强化学习从GRPO到DAPO的“认知”进化
  • https + 域名 + 客户端证书访问模式