C语言的位运算

news2024/11/24 9:21:36

1. 位操作符综述

位操作有逻辑运算和移位运算,如位与、位或、位取反、按位异或、移位等操作。位运算通常会和底层代码寄存器的操作结合在一起使用,比如想要让寄存器中的任意1位或者任意几位位设置为1,或者设置为0,从而实现对寄存器位的控制。

1.1 位与 &

真值表:Y = A + B

ABY
000
010
100
111

特点:同一二进制位上,只有1和1进行与运算,其结果才为1,其余全是0。

该特点可以对特定的二进制位清0,或者只取所需要的某几个 bit 位。比如,清除 1111 0000 的 bit4 和 bit5 :

1111 0000 & 1100 0000 = 1100 0000    // 对 1111 0000 的 bit4/bit5 清0

1111 1001 & 0000 1111 = 0000 1001    // 只取 1111 1001 的低4位。与0位与可去除不需要的位,与1位与,保留所需的位

对特定位 & 1 进行运算,其实就是保留原来的值。

1.2 位或 |

真值表:Y = A + B

ABY
000
011
101
111

特点:同一二进制位上,只有0和0进行或运算,其结果才为0,其余全是1。

该特点可以对某个特定位置1,比如:

0011 0000 | 0000 1111 = 0011 1111;  // 即对 0011 0011 操作数的低4位置1了

对特定位进行 | 0,则保留原值。

1.3 位取反 ~

位取反就是对操作数的二进制位按位进行取反操作,0 取反则为1,1取反则为0,如下:

~0 = 1;
~1 = 0;

~1100 = 0011;

按位取反,和非运算(!)是不一样的,非运算只有0或者1的结果。

1.4 按位异或 ^

真值表:Y = A + B

ABY
000
011
101
110

特点:同一二进制位上,两个位相等则为0,否则为1。

该特点,可以对操作数的特定位进行反转操作。

1100 1111 | 1111 0000 = 0011 1111;     // 即把 1100 1111 操作数的高4位进行了反转

对特定为进行 ^ 0,则保留原值。

1.5 左移 <<

对一个操作数的二进制位进行左移 n 位操作。其中左边移出去的二进制位进行丢弃,右边空出的二进制位补0。

如对 0x05 左移 3 位:

0000 0101 << 3 = 0010 1000 = 40

左移1位相当于该操作数乘以2,左移n位,则是乘以 2 的 n 次方, (x<<n) = x*2^n。5 * (2^3 ) = 40。所以 5 左移 3 位,即 5 * (2^3 ) = 40.

1.6 右移 >>

对一个操作数的二进制位进行右移 n 位操作。其中右边移出去的丢弃,左边空出的高位是补0还是补1则要看操作数是有符号数还是无符号数。

  • 无符号数右移:空出的高位补0,这种情况称为逻辑右移。
  • 有符号数右移:空出的高位全部以符号位进行填充,即正数补0,负数补1。这种情况称为算数右移。

如对 5 和 -5 右移 2 位:

// 5 和 -5 的二进制展开形式分别是:
// 00000101(5) 11111101(-5)    
00000101 >> 2 = 00000001;  // 十进制就是1
11111101 >> 2 = 11111110;  // 十进制就是0

右移1位相当于该操作数除以2,右移n位,则是除以 2 的 n 次方,(x>>n) = x/2^n

2. 如何操作寄存器位

SOC中的每个寄存器基本占32bit,每个寄存器位可能占1个bit或多个bit,我们如何操作寄存器特定的1个或几个bit位,但又不能影响寄存器的其他bit呢?

可以遵循读-改-写的策略。先把整个寄存器值读出来,然后修改特定的bit位,然后再整体写回寄存器中。

2.1 对寄存器特定位清0 &

我们想要对寄存器特定的一位或几位进行清0操作,但不能影响其他bit位,可以先构造出一个合适的数(想要对哪个位清0,那么该位构造为0,其他位保持为1),然后使用这个数和寄存器原来的值进行位与操作

比如:对一个32位的寄存器的 2~5 这几个位清0,那么就构造一个 2~5 bit都是0,其余位均为1,那么这个数就是(可以使用计算器工具去获得这个数,下文会介绍构造这个数的技巧):0xFFFFFFC3

在这里插入图片描述

那么对寄存器 bit2~5 清0操作就是:

reg &= 0xFFFFFFC3;

2.2 对寄存器位置1 |

对特定的寄存器位置1操作而不影响其他bit,使用位或运算。同样的,我们可以构造一个合适的数,然后和这个寄存器原来的值进行位或操作。而这个合适的数构造原则就是:需要置1的位构造为1,其余为保持为0.

比如,对一个32位的寄存器的 2~5 这几个位置1,那么构造出来的数就是:0x00000060

那么对寄存器 bit2~5 清0操作就是:

reg |= 0x00000060;

2.3 对寄存器特定位取反 ^

如果我们想要吧寄存器的某些特定位,从1变为0,0变为1,也就是取反的操作,但不能影响其他bit位。同样也是可以构造一个合适的数的,然后这个数和寄存器值进行异或操作。异或要构造的数的规则,与置1的操作是一样的。

比如,一个寄存器原来的值是 0xAAAAAA0F,我想要对 bit0-bit7 进行取反操作,那么构造的这个数就是:0x000000FF

reg ^= 0x000000FF;   

最终结果是:0xAAAAAAF0,这样就可以把寄存器的bit0~bit7进行取反操作了。

3. 使用位操作构造任意二进制数

从上面的操作来看,想要对寄存器某个或某些特定位进行清0、置1、取反操作,其最重要的就是要构造一个数。根据不同的操作,构造这个数也会不同,比如清0,那么构造的数某个位为0。置1,则构造的数某个位为1,取反构造数的规则与置1是一样的。然后我们根据这个规则,通过计算器计算出来的,或者你也可以想象出来。

但是我们其实可以提供位操作,可以去构造出任意的二进制数,这比计算器或者自己想出来好得多了。

3.1 构造特定bit位为1的二进制数

我们可以使用移位操作,来获取任意bit位为1的二进制数。

比如上面的介绍的,为了让 bit2~5 位置1,我们需要构造 bit2~5 为1(其他位为0,下面不在说明)的一个二进制数,可以先这样做:

  • bit2~5 一共有 5-2+1=4个bit,4个bit就是0xf

  • 那么获得构造的数就是 0x0f << 2

通过上面两步,我们就可以获得任意bit位为1的二进制数了。

但是我们想要构造 bit2~5 和 bit19~20 为1的二进制数呢?这时可以先构造bit25的数,以及bit1920的数,然后使用位或操作。如下:

  • bit2~5:构造的基准数为0x0f

  • bit19~20:构造的基准数为 0x03

  • 移位加上位或运算:(0x0f<<2) | (0x03<<19)

上面三步就可以构造这种需要叠加bit位的情况了。而且可读性比你使用计算器算出来一个最终的结果好多了。

3.2 结合位取反构造特定bit位为0的二进制数

构造一个 bit3~11 的位为0,其余均为1的数,如何操作?我们根据上面的步骤,然后加入位取反的操作即可构造出特定bit位为0的数。

比如说我们要构造bit3~9为0,其余位均为1的二进制数:

  • bit3~9 一共 9-3+1=7个bit。bit39为0,那么反码bit39为1,即这个基准数就是:0x7f
  • 然后进行移位操作,0x7f<<3
  • 然后进行取反操作 ,~(0x7f<<3)

这样构造出来的数,可读性好多了。

总结:

  1. 如果你要构造的数大部分bit位为0,少部分是1,那么可以连续多个1左移n位得到;
  2. 如果你要构造的数大部分bit位为1,少部分是0,那么在先构造该数的反码,然后再进行位取反即可;
  3. 如果要构造的数,连续 1(或者0)是由多个部分组成了,可以先构造各个部分的数,然后再使用位或操作即可。

4. 寄存器操作综合应用

1)对特定bit位清0或置1

比如说,对bit3置位,前面说过,置位使用 | 运算,我们先构造 bit3 为1的数,即 1<<3,然后进行位或操作:

reg |= (a<<3);

又比如对bit20清0,那么使用 & 运算,我们先构造 bit20 为0的数,即 ~(1<<20) ,然后再进行位与操作:

reg &= ~(1<<20);

使用下面宏定义封装起来:

#define CLR_BIT(reg, n) ((reg) &= ~(1 << (n)))
#define SET_BIT(reg, n) ((reg) |= (1 << (n)))

2)对连续几个bit位清0或者置1

这是对连续的几个bit进行置位或者清零,其实和上面对一个bit置位或清零一样的。比如:

bit11~14位置1:

// 1. 先构造bit11~16位为1的数:0x3f<<11
// 2. 然后进行位或操作:reg |= (0x3f<<11)
reg |= (0x3f<<11)

bit17~23位清0:

// 1. 先构造bit17~23位为0的数,23-17+1=7个bit,那么这个数就是:~(0x7f<<17)
// 2. 然后进行位与操作:reg &= ~(0x7f<<17)
reg &= ~(0x7f<<17);

// 对连续 n~m bit为进行清0操作,使用宏定义进行封装
#define  CLR_BIT_N_TO_M(reg, n, m)     (reg &= ~((~(0U) << (m-n+1)) << n))

3)取出寄存器bit5~9的值

取出bit59的值,那么就是bit59的值保持不变,其余位则为0,然后再右移5位,即可把bit5~9取出来。

在前面的位运算综述就介绍过,要取出特定的bit位,使用位与 & 运算。那取出 bit5~9 位的值思路如下:

  • 先构造 bit5~9 为1的数:0x1f << 5
  • 把 bit5~9 位保留,其余位全部清0,即位与运算:reg &= (0x1f << 5)
  • 最后把得到的值,在右移 5bit,即可取出bit5~9位的值:reg >>= 5

4)对某个或连续几个特定bit位取反

前面就介绍过,对寄存器特定位取反操作,可以使用按位异或。

  • 对第 n 位进行取反:

    // 1. 先构造一个第 n 位为1的数,即: 1<<n
    // 2. 然后再进行异或操作:reg ^= (1<<n)
    reg ^= (1<<n);
    
    // 使用宏定义进行封装
    #define  REVERSE_BIT(reg, n)     ((reg) ^= (1<<(n)))
    
  • 对连续几个特定bit位取反

    比如说,对bit4~10取反。思路也是一样的,先构造 bit4~10 为 1 的数,然后再进行异或操作。

    // 1. 先构造一个 bit4~10 位为1的数,10-4+1=7,即一共7个bit位为1。那么这个数就是 0x7f<<4
    // 2. 然后再进行异或操作:reg ^= (0x7f<<4); 这样就可以对 bit4~10 进行取反
    reg ^= (0x7f<<4);
    
    // 对连续 n~m bit为进行按位取反操作,使用宏定义进行封装
    #define  REVERSE_BIT_N_TO_M(reg, n, m)     (reg ^= ((32U - (m-n+1)) << n))
    

5)对寄存器 bit9~17 赋值0xaa

思路(注意:整个操作过程中是不能影响到其他寄存器位的。):

  • 先将 bit9~17 位清0
  • 然后再把0xaa设置到 bit9~17。

那么具体操作如下:

// 1. 构造bit9~17位为0的数,17-9+1=9,即一共9个bit位为1,。那么这个数就是:~(0x1ff<<9)
// 2. bit9~17位清0, reg &= ~(0x1ff<<9)
// 3. 构造 bit9~17位的数为 0xaa,即:0xaa<<9
// 4. 最后再进行位或操作:reg |= (0xaa<<9); 即可把 0xaa 写入到 bit9~17

// C语言表示就是:
reg &= ~(0x1ff<<9);   // 先对bit9~17清0
reg |= (0xaa<<9);     // 再对bit9~17设置为0xaa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-pythongame怎么样

Python Game&#xff1a;打造属于自己的游戏 Python是一种全球流行的编程语言&#xff0c;因其简洁易懂、高效稳定&#xff0c;被广泛应用于各类软件、网站与游戏的开发领域。其中&#xff0c;Python Game成为许多开发者的关注焦点&#xff0c;不同于传统游戏开发的复杂与繁琐…

【音视频开发】摄像头和ISP的基本介绍

文章目录 一、摄像头的基本知识1.1 摄像头结构1.2 摄像头模组的种类1.3 摄像头的工作原理 二、Sensor的基本知识2.1 Sensor的工作原理2.2 Sensor的分类2.3 Sensor的封装形式2.4 常见的Sensor厂商2.5 Sensor的基本框图 三、ISP的基本知识3.1 ISP的定义3.2 ISP的工作原理 四、ISP…

拥抱生成式大模型 --- 提示工程篇

本文为系列的第二篇&#xff0c;主要是学习和总结chatgpt类模型的提示工程。感谢吴恩达老师的开源课程。 引言 随着大型语言模型&#xff08;LLM&#xff09;的发展&#xff0c;LLM 大致可以分为两种类型&#xff0c;即基础LLM和指令微调LLM。基础LLM是基于文本训练数据&…

栈和队列 - C语言实现

目录 栈 栈的概念 栈的实现 队列 队列的概念 队列的实现 栈 栈的概念 栈是一种后进先出 (LIFO - last in first out) 的数据结构&#xff0c;通常利用数组或链表实现。栈只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另…

JavaWeb14 - 异步请求 - 02 - Ajax

1. 概述 1.1 官方文档 Ajax 在线文档&#xff1a;https://www.w3school.com.cn/js/js_ajax_intro.asp 1.2 Ajax 基本介绍 1.2.1 Ajax 是什么 AJAX 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML)Ajax 是一种浏览器异步发起请求(指定发哪些数据)&…

LeetCode刷题集(七)(LeetCode70.爬楼梯)

学习目标&#xff1a;拿下LeetCode70.爬楼梯 学习完本文章之后拿下LeetCode70题 题目实例&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 样例1、输入&#xff1a;n 2 输出&#xff1…

Java学习路线(7)——面向对象基础

1、概念 对象&#xff1a; 是实际存在的具体实例。类&#xff1a; 是对象共同特征的描述 。 2、类的组成 成员变量 成员变量是类中的全局变量&#xff0c;它的作用域是class car的“{}”之内。 public class car{String carTypeName; //汽车类型名称double minPrice; //最低…

OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)

OpenCV是一个非常强大的图形处理框架&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上&#xff0c;在自动驾驶、智能家居、人脸识别、图片处理等方面提供了非常丰富且功能强大的api&#xff0c;在图片处理方便&#xff0c;基本上可以满足对图片处理的所有需求…

Fourier分析入门——第4章——频率域

目录 第 4 章 频率域(The Frequency Domain) 4.1 频谱分析(Spectral Analysis) 4.2 物理单位(Physics units) 4.3 笛卡尔坐标形式与极坐标形式对比 4.4 频谱分析的复数形式 4.5 复数值Fourier系数 4.6 复数值的和三角的Fourier系数之间的关系 4.7 2维或多维离散Fouri…

SpringBoot+Redis实现浏览量+1

当用户点击新闻查看详情后&#xff0c;数据库新闻的浏览量字段要加一&#xff0c;当在高并发场景下&#xff0c;很多人查看新闻详情直接操作数据库使浏览量字段加一对数据库压力过大&#xff0c;并且容易造成脏数据&#xff0c;这里结合redis实现浏览量加一。 业务逻辑&#x…

chatgpt赋能Python-pythonfoo

Pythonfoo: 优秀的Python库提高开发效率 Python是一门被广泛应用的动态编程语言&#xff0c;提供了各种各样的库来帮助开发人员完成不同类型的任务。在这些Python库中&#xff0c;Pythonfoo是一款旨在提高开发效率和代码简洁性的出色的Python库。 什么是Pythonfoo&#xff1f…

一种用于超低功耗无线传感器网络的消息传递算法(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 无线传感器网络是由一定数量的无线传感器节点组成的网络系统,各节点可采集环境数据并通过无线通信进行数据传输,目前已广泛应用…

使用cubeMX生成USB HID设备

一&#xff0c;简介 本文主要介绍如何使用stm32F407开发板和cubeMX生成USB FS HID设备&#xff0c;供参考。 二&#xff0c;操作步骤 本次总共分以下几个步骤&#xff1a; 1&#xff0c;创建cubeMX工程&#xff0c;并配置HID&#xff0c;生成工程代码&#xff1b; 2&#xf…

光伏防逆流系统的介绍

安科瑞虞佳豪 5月17日&#xff0c;新疆和田地区洛浦县国家电投洛浦光伏电站&#xff0c;今年2月刚刚并网发电的200兆瓦光伏发电项目坐落于戈壁滩上&#xff0c;占地5500亩的368004块光伏面板在阳光照射下熠熠生辉&#xff0c;为和田地区经济社会发展持续提供着绿色能源。 洛浦…

HNU-操作系统OS-实验Lab6

OS_Lab6_Experimental report 湖南大学信息科学与工程学院 计科 210X wolf (学号 202108010XXX) 实验目的 理解操作系统的调度管理机制熟悉 ucore 的系统调度器框架,以及缺省的Round-Robin 调度算法基于调度器框架实现一个(Stride Scheduling)调度算法来替换缺省的调度算…

第12章_MySQL数据类型精讲

第12章_MySQL数据类型精讲 1. MySQL中的数据类型 类型类型举例整数类型TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT浮点类型FLOAT、DOUBLE定点数类型DECIMAL位类型BIT日期时间类型YEAR、TIME、DATE、DATETIME、TIMESTAMP文本字符串类型CHAR、VARCHAR、TINYTEXT、TE…

chatgpt赋能Python-pythondeque函数

Python deque函数&#xff1a;高效地操作双向队列 介绍 在Python中&#xff0c;deque函数是一个非常重要的内置函数&#xff0c;用于创建双向队列。双向队列是一个序列容器&#xff0c;可以从两端添加或删除元素&#xff0c;这使得它成为许多常见问题的解决方案。deque函数是…

Vue--》深入理解 Vue 3 导航守卫,掌握前端路由的灵魂技能!

目录 vue3导航守卫讲解与使用 element-ui的安装与使用 配置路由和设置路径别名 设置登录页面并实现规则跳转 设置导航前置守卫 设置导航后置守卫 其他路由相关操作 vue3导航守卫讲解与使用 导航守卫是在 Vue Router 中提供的一种功能&#xff0c;它允许你在切换路由之前…

chatgpt赋能Python-pythonfirst

PythonFirst&#xff1a;Python编程新手的最佳起点 作为一门简洁而又强大的编程语言&#xff0c;Python在过去的几年中得到了越来越多的关注和应用。它广泛应用于数据分析、人工智能、Web开发、自动化等领域&#xff0c;成为了许多程序员的首选语言。如果你也是刚刚开始接触Py…

有哪些简单而知道的人少的excel操作技巧?

以下是 Excel 里鲜为人知而又简单、逆天的操作技巧&#xff1a; 1. 快速选中数据区域&#xff1a;双击数据区域左上角的方格即可快速选中整个数据区域。 2. 使用自动筛选快速查找和筛选数据&#xff1a;在 Excel 数据表中&#xff0c;使用自动筛选可以快速找到和筛选特定数据…