(9)位运算

news/2024/11/2 0:45:59/

1. 位运算的概念

位运算操作的是整数在内存中的二进制位。C 语言提供了以下几种位运算操作符:

按位与(&)
  • 运算规则:将两个操作数对应的二进制位进行与运算。只有当两个对应位都为 1 时,结果位才为 1,否则为 0。
  • 示例代码
#include <stdio.h>int main() {int num1 = 5;  // 二进制表示为 0101int num2 = 3;  // 二进制表示为 0011int result = num1 & num2;  // 0101 & 0011 = 0001printf("5 & 3 的结果: %d\n", result);return 0;
}
按位或(|)
  • 运算规则:将两个操作数对应的二进制位进行或运算。只要两个对应位中有一个为 1,结果位就为 1。
  • 示例代码
#include <stdio.h>int main() {int num1 = 5;  // 二进制表示为 0101int num2 = 3;  // 二进制表示为 0011int result = num1 | num2;  // 0101 | 0011 = 0111printf("5 | 3 的结果: %d\n", result);return 0;
}
按位异或(^)
  • 运算规则:将两个操作数对应的二进制位进行异或运算。当两个对应位不同时,结果位为 1;相同时,结果位为 0。
  • 示例代码
#include <stdio.h>int main() {int num1 = 5;  // 二进制表示为 0101int num2 = 3;  // 二进制表示为 0011int result = num1 ^ num2;  // 0101 ^ 0011 = 0110printf("5 ^ 3 的结果: %d\n", result);return 0;
}
取反(~)
  • 运算规则:对操作数的每一位进行取反操作,0 变为 1,1 变为 0。需要注意的是,取反操作符是单目运算符,它对一个操作数进行操作。
  • 示例代码
#include <stdio.h>int main() {int num = 5;  // 二进制表示为 00000101int result = ~num;  // 取反后为 11111010(在有符号整数表示中,这是 -6 的补码形式)printf("~5 的结果: %d\n", result);return 0;
}
左移(<<)
  • 运算规则:将操作数的所有二进制位向左移动指定的位数,右边空出的位用 0 填充。左移一位相当于乘以 2。
  • 示例代码
#include <stdio.h>int main() {int num = 5;  // 二进制表示为 00000101int result = num << 2;  // 左移 2 位后为 00010100,即 20printf("5 << 2 的结果: %d\n", result);return 0;
}
右移(>>)
  • 运算规则:将操作数的所有二进制位向右移动指定的位数。对于无符号数,左边空出的位用 0 填充;对于有符号数,如果是算术右移(大多数编译器的实现方式),左边空出的位用符号位填充。右移一位相当于除以 2(对于无符号数和正数)。
  • 示例代码
#include <stdio.h>int main() {int num = 12;  // 二进制表示为 00001100int result = num >> 2;  // 右移 2 位后为 00000011,即 3printf("12 >> 2 的结果: %d\n", result);return 0;
}

2. 位段的概念

位段(bit - field)是一种特殊的结构体成员,它允许以位为单位指定结构体成员所占的存储空间大小。通过位段,可以更紧凑地存储数据,尤其是在处理一些硬件相关的数据或者需要节省内存空间的情况下。

以下是位段的示例:

#include <stdio.h>// 定义一个包含位段的结构体
struct BitFieldExample {unsigned int field1 : 3;  // 位段 field1 占 3 位unsigned int field2 : 5;  // 位段 field2 占 5 位unsigned int field3 : 4;  // 位段 field3 占 4 位
};int main() {struct BitFieldExample example;example.field1 = 3;  // 二进制为 011,存储在低 3 位example.field2 = 7;  // 二进制为 00111,存储在接下来的 5 位example.field3 = 9;  // 二进制为 1001,存储在再接下来的 4 位// 注意:位段的存储顺序和字节顺序(大端序或小端序)有关,这里假设为小端序// 结构体总共占 2 个字节(3 + 5 + 4 = 12 位,向上取整到字节)unsigned short int *ptr = (unsigned short int *)&example;printf("存储的值(十六进制): 0x%x\n", *ptr);return 0;
}

在这个示例中,struct BitFieldExample结构体中的成员field1field2field3都是位段。它们在内存中是紧凑存储的,总共占用不超过一个整数的空间(这里假设为 2 个字节,因为总共 12 位)。

3. 在程序中应用位运算

设置和清除特定的位
  • 设置某一位为 1(使用按位或)
    假设要将一个整数num的第n位(从右往左数,最低位为第 0 位)设置为 1。可以使用以下代码:
#include <stdio.h>int main() {int num = 10;  // 二进制为 1010int n = 1;     // 设置第 1 位num = num | (1 << n);  // 将第 1 位设置为 1,结果为 1010 | 0010 = 1010printf("设置第 %d 位后的结果: %d\n", n, num);return 0;
}
  • 清除某一位为 0(使用按位与和取反)
    要将一个整数num的第n位清除为 0,可以使用以下代码:
#include <stdio.h>int main() {int num = 10;  // 二进制为 1010int n = 2;     // 清除第 2 位num = num & ~(1 << n);  // 将第 2 位清除为 0,结果为 1010 & 1011 = 1000printf("清除第 %d 位后的结果: %d\n", n, num);return 0;
}
检查特定的位是否为 1(使用按位与)
#include <stdio.h>int main() {int num = 10;  // 二进制为 1010int n = 1;     // 检查第 1 位if (num & (1 << n)) {printf("第 %d 位是 1\n", n);} else {printf("第 %d 位是 0\n", n);}return 0;
}
位运算在枚举中的应用(使用位掩码)

可以使用位运算来实现枚举类型中的多个标志位。例如:

#include <stdio.h>// 定义枚举类型,每个枚举值对应一个位掩码
enum Options {OPTION_A = 1 << 0,  // 0001OPTION_B = 1 << 1,  // 0010OPTION_C = 1 << 2   // 0100
};int main() {int options = OPTION_A | OPTION_C;  // 设置选项 A 和 C,二进制为 0101if (options & OPTION_A) {printf("选项 A 被选中\n");}if (options & OPTION_B) {printf("选项 B 被选中\n");}if (options & OPTION_C) {printf("选项 C 被选中\n");}return 0;
}

在这个示例中,通过位运算可以方便地组合和检查枚举类型中的多个选项。位运算在底层编程、硬件驱动开发、数据压缩、加密算法等领域都有广泛的应用。通过灵活运用位运算,可以提高程序的效率和优化内存使用。

希望以上内容能满足您的需求,如果您还有其他问题,请随时提问。


http://www.ppmy.cn/news/1543732.html

相关文章

UE 引入 IOS framework库的坑

一、我明明已经把framework库进行签名的却在 上传到开发者后台时一直报错 90034 签章遗失 或者 未签 这个问题我最近遇到 极其坑爹 我是这个情况 这是我的framework库的目录 关键就在这了 多出了这个文件 就影响了 上传到开发者后台 就报错 90034 将其删除就好 &…

Gradio DataFrame分页功能详解:从入门到实战

Gradio DataFrame分页功能详解&#xff1a;从入门到实战 1. 引言2. 为什么需要分页&#xff1f;3. 环境准备4. 基础知识准备5. 代码实现5.1 创建示例数据5.2 分页状态管理5.3 分页核心逻辑5.4 创建Gradio界面 6. 关键功能解析6.1 页码计算6.2 数据切片 7. 使用示例8. 实用技巧9…

LeetCode994. 腐烂的橘子(2024秋季每日一题 54)

在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单元格中没有…

系统安全架构的深度解析与实践:Java代码实现

引言 系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师&#xff0c;设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解&#xff0c;还需要熟练掌握各种安全技术和工具。本文将详细介绍系统安全架构的概念&#xff0c;并从前后分层、业务切割、…

Flink CDC系列之:学习理解核心概念——Data Pipeline

Flink CDC系列之&#xff1a;学习理解核心概念——Data Pipeline 数据管道sourcesink管道配置Table IDroutetransform案例 数据管道 由于 Flink CDC 中的事件以管道方式从上游流向下游&#xff0c;因此整个 ETL 任务被称为数据管道。 管道对应于 Flink 中的一系列操作。 要描…

【STM32+HAL】STM32CubeMX学习目录

一、基础配置篇 【STM32HAL】微秒级延时函数汇总-CSDN博客 【STM32HAL】CUBEMX初始化配置 【STM32HAL】定时器功能小记-CSDN博客 【STM32HAL】PWM呼吸灯实现 【STM32HAL】DACDMA输出波形实现-CSDN博客 【STM32HAL】ADCDMA采集(单通道多通道)-CSDN博客 【STM32HAL】三重A…

TS 项目中给常用的路径定义一个别名 tsconfig.json

TS 项目中给常用的路径定义一个别名 tsconfig.json 在 TS 项目中&#xff0c;可以定义一些自定义的别名&#xff0c;来取代经常需要引用的一些文件路径。 比如 Vue 项目中你可以需要经常从 /src 中取文件&#xff0c;在每个层级的文件中引用时的相对路径 ../../src ../src 都不…

std::optional与函数返回值的讨论

这个话题是因为&#xff0c;最近一段时间看到有人在问std::optional有什么用&#xff0c;以及不理解为什么要有这个类。所以打算简单介绍一下std::optional&#xff0c;重点讨论返回值相关的内容。 1 std::optional 类模板 std::optional 管理一个可选的包含值&#xff0c;即…