位运算常用操作总结

本文介绍了位运算在解决特定数学问题中的应用,如通过异或找出数组中唯一元素,二进制位翻转,以及不使用加减法计算平均值等。通过实际代码示例,展示了位运算的强大和高效。

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。
下面是我最近练习过程中遇到的一些位运算操作:
*在一组数组中找到只出现一次的数:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void fun(int  *p,int k)
{
	int i = 0;
	int tmp = 0;
	while (i<k)
	{
	tmp = tmp^*(p + i);
	i++;
	}
	printf("%d", tmp);
}
int main()
{
	int  a[5] = { 7, 3, 2, 3, 2 };
	fun(a,5);
	system("pause");
}

这段代码用了异或的位操作符,先设置变量temp,并让它的值等于0,0异或任何数都等于任何数,两个相同的数异或等于0,利用这两条性质让它异或等于数组里的每一个数,这样就可以很快捷的找出只出现一次的数。

*让一个数的二进制位翻转

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
unsigned int reverse_bit(unsigned int value)
{
	int tmp = 0;
	int i = 0;
	for (i = 32; i > 0; i--)
	{
		tmp += ((value >> (32 - i)) & 1) << (i - 1);//每次从右至左依次取出value的每一位与1按位与,就得到了该位的二进制值,再左移累加给tmp.
	}

	return tmp;
}
int main()
{
  unsigned int n = 25;
  int ret = 0;
	  ret= reverse_bit(n);
	printf("after:>%u\n", ret);
	system("pause");
	return 0;
}

这段代码用到了移位操作符,在这里要实现二进制位的翻转,首先来分析一下做这道题的思路,我们可以先取出最低位和1按位与,得到该位,然后再把这个数左移至最高位,也就是左移31位,相应的值加给temp,然后再去取第二个数,此时要先将这个数的二进制数右移一位,再和1按位与,得到第二个数,接着再把第二个数左移(i-1)位,此时的i=31,所以左移30位,得到的值加给temp,按照这个思想,累加给temp,就得到了这个数的二进制位翻转之后的值了。

*不用(a+b)/2计算两个数的平均值

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
   int fun(int n,int k)
{
	int tmp = 0;
	if (n >=k)
	{
		tmp = k + ((n - k) >> 1);//括号不能省,右移一位相当于除二
		return tmp;
	}
	else
	{
		tmp = n + ((k - n) >> 1);
		return tmp;
	}
}
int main()
{
	int n = 10;
	int k = 8;
	int ret = fun(n, k);
	printf("%d", ret);
	system("pause");
	return 0;
}

这段代码也用到了移位操作符,所不同的是这里用到了二进制位右移一位相当于除二,左移一位相当于乘以二的性质,所以不用(a+b)/2的方式也可以实现计算两个数的平均数,即用小的那个数加上大数减小数值的一半。
以上几段代码都是用位运算解决了一些数学问题,这里有一段口诀可以帮助我们记忆和应用:

清零取反要用与,某位置一可用或
若要取反和交换,轻轻松松用异或

在嵌入式系统、驱动开发和硬件寄存器配置中,**位运算**是非常常用的技术。通过对寄存器的某些进行**设置、清除、读或修改**,可以控制硬件的行为。 --- ## ✅ 寄存器配置中常用操作方式: ### 1. **设置某一位或某些为 1** ```c REG |= (1 << BIT_POS); // 设置单个 REG |= (0b111 << BIT_POS); // 设置多个 ``` - 使用 `|=` 和左移操作 `<<` 来将某设置为 1。 - 例如:`REG |= (1 << 3);` 会设置第 3 为 1,不影响其他。 --- ### 2. **将某一位或某些清零(设置为 0)** ```c REG &= ~(1 << BIT_POS); // 清除单个 REG &= ~((0b111) << BIT_POS); // 清除多个 ``` - 使用 `&=` 和按反 `~` 来清除某些。 - 例如:`REG &= ~(1 << 3);` 会将第 3 清零,不影响其他。 --- ### 3. **同时清除和设置某些常用于配置字段)** ```c REG = (REG & ~((0xf) << 4)) | ((value & 0xf) << 4); ``` - 先清除从第 4 开始的 4 ; - 再将 `value` 的低 4 写入这 4 ; - 这是配置寄存器字段的标准写法。 --- ### 4. **读一位的值** ```c if (REG & (1 << BIT_POS)) { // 第 BIT_POS 为 1 } else { // 第 BIT_POS 为 0 } ``` - 通过按与判断某是否为 1。 --- ### 5. **读一组连续的值** ```c uint32_t field = (REG >> 8) & 0x0F; // 读 bit8~bit11 ``` - 先右移将字段移到最低; - 再用掩码 `0x0F` 提字段值。 --- ## 🧠 示例:STM32 GPIO 寄存器配置 ```c // 假设我们要配置 GPIOx_MODER 寄存器的第 4 到第 5 (即 MODE4[1:0]) // 表示配置第 4 个 GPIO 引脚为输出模式 GPIOA->MODER = (GPIOA->MODER & ~(0x3 << (4 * 2))) | (0x1 << (4 * 2)); ``` 解释: - 每个 GPIO 引脚占 2 ; - 第 4 个引脚对应偏移量 `4 * 2 = 8`; - 掩码 `0x3 << 8` 覆盖第 8 和第 9 ; - `(0x1 << 8)` 设置为输出模式。 --- ## 🔧 常用宏定义(提高可读性和可维护性) ```c #define BIT(n) (1UL << (n)) // 设置单个 #define BITS(start, len) (((1UL << (len)) - 1) << (start)) // 设置从 start 开始的 len // 设置某段的值 #define SET_BITS(REG, start, len, val) \ REG = ((REG) & ~BITS(start, len)) | (((val) << (start)) & BITS(start, len)) // 获某段的值 #define GET_BITS(REG, start, len) (((REG) >> (start)) & ((1UL << (len)) - 1)) ``` ### 示例: ```c SET_BITS(GPIOA->MODER, 8, 2, 1); // 将 GPIOA 的第 4 设置为 0b01(输出模式) uint32_t mode = GET_BITS(GPIOA->MODER, 8, 2); // 读当前配置 ``` --- ## ✅ 总结:寄存器配置中常用操作 | 操作 | 代码示例 | 说明 | |------|----------|------| | 设置某 | `REG |= BIT(n);` | 将第 n 设置为 1 | | 清除某 | `REG &= ~BIT(n);` | 将第 n 清零 | | 设置多 | `REG |= (val << shift);` | 将多个设置为某个值 | | 清除多 | `REG &= ~((mask) << shift);` | 清除多个连续 | | 设置字段 | `REG = (REG & ~mask) | (val << shift);` | 设置寄存器中的字段 | | 读字段 | `val = (REG >> shift) & mask;` | 读寄存器字段 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值