[嵌入式 C 语言] 按位与、或、异或、取反、左移、右移 (含代码实现)

参考:嵌入式:按位与、或、异或、取反操作小结_或操作-CSDN博客

总结 

RoleUsage
按位于&判断状态if (data & 0x3F)
判断是否是某一状态if((data & targetState) == targetState)
清零data = data & 0x00;
判断奇偶性if(i & 1)
        printf("%d%s\n", i, "为奇数");
    else
        printf("%d%s\n", i, "为偶数");
按位或 |将一个数中的某个位置1data = data | 0x66;
调换并合并高八位与低八位combinedData = (lowByte << 8) | highByte;
按位异或^将一个数中指定的位翻转 

(与0异或保留原值,与1异或翻转)

data = data ^ 0xC3;

交换两数(直接使用位操作来实现,免去第三方变量的引入)
取反 ~将最低位清零data &= ~1
变换符号(正变负、负变正)data = ~data + 1;
求整数的绝对值(将int右移31位,则int的最高位变成了最低位,这一位就是符号位)
左移 <<取某一位    int blueMask = 1 << 4;     // 蓝灯
    int whiteMask = 1 << 5;    // 白灯
右移 >>取高八位highByte = combinedData >> 8;

一、按位与 &

  • 有0则0,全1则1
  • 1010 & 0011 =  0010
  • 0xef & 0xfe = 0xee ( 0x1110 1111 & 0x1111 1110 = 0x1110 1110)

1.1 配合左移运算符  <<  取指定的位

 说明:DEC表示十进制、BIN表示二进制、HEX表示十六进制

若协议中如下图所示: 

注意:

        长度为1,表示1个字节,也就是0xFF,也就是 1111 1111

(这里0xFF只是单纯表示一个数,也可以是其他数,这里需要注意的是1个字节的意思)

#include <stdio.h>

int main()
{   
    int data = 0x34;    // (DEC)64 = (BIN)0011 0100 = (HEX)0x34

    // 定义位掩码
    int greenMask = 1 << 0;    // 绿灯
    int yellowMask = 1 << 1;   // 黄灯
    int redMask = 1 << 2;      // 红灯
    int buzzerMask = 1 << 3;   // 蜂鸣器
    int blueMask = 1 << 4;     // 蓝灯
    int whiteMask = 1 << 5;    // 白灯
    
    // 检查并打印状态
    if((data & greenMask) == 0) printf("绿灯灭 "); else printf("绿灯亮 ");
    if((data & yellowMask) == 0) printf("黄灯灭 "); else printf("黄灯亮 ");
    if((data & redMask) == 0) printf("红灯灭 "); else printf("红灯亮 ");
    if((data & buzzerMask) == 0) printf("蜂鸣器停 "); else printf("蜂鸣器响 ");
    if((data & blueMask) == 0) printf("蓝灯灭 "); else printf("蓝灯亮 ");
    if((data & whiteMask) == 0) printf("白灯灭\n"); else printf("白灯亮\n");

    printf("\n\n");
    printf("Green Mask Value: 0x%x\n", greenMask);
    printf("Yellow Mask Value: 0x%x\n", yellowMask);
    printf("White Mask Value: 0x%x\n", whiteMask);

    return 0;
}

输出: 
    绿灯灭 黄灯灭 红灯亮 蜂鸣器停 蓝灯亮 白灯亮
    Green Mask Value: 0x1      // 0x1 = 0001
    Yellow Mask Value: 0x2     // 0x2 = 0010
    White Mask Value: 0x20     // 0x20 = 10 0000

在C语言中,`<<` 是位左移运算符。当你有一个整数值(在这个例子中是1)并对其使用左移运算符,意味着你将该数值的二进制表示向左移动指定位数。每向左移一位,数值就相当于乘以2(因为二进制系统下,每一位代表的权重是2的幂次)。

具体到你的代码示例:

  • int greenMask = 1 << 0;表示将1(二进制表示为`00000001`)向左移动0位,实际上没有移动,所以`greenMask`的值为1,对应二进制的最低位,这里是用来控制绿灯的。
  • int yellowMask = 1 << 1;将1向左移动1位,得到`00000010`,即十进制的2,用作黄灯的控制位。
  • int redMask = 1 << 2;向左移2位,得到`00000100`,即十进制的4,对应红灯控制位。
  • int buzzerMask = 1 << 3;移动3位,得到`00001000`,即十进制的8,用于蜂鸣器。
  • int blueMask = 1 << 4;移动4位,得到`00010000`,即十进制的16,对应蓝灯。
  • int whiteMask = 1 << 5;移动5位,得到`00100000`,即十进制的32,控制白灯。

这样,每个掩码变量都对应了一个特定的位,可以用来单独控制或检测某个功能的状态。在后续的条件判断中,通过按位与操作(`&`)检查`data`中的特定位是否为1,以此来确定对应设备的状态(开启或关闭)。

1.2 位与判断

要求: 检测所有器件是否全部停止

#include <stdio.h>

int main()
{   
    int data_0 = 0x34;     // (DEC)64 = (BIN)0011 0100 = (HEX)0x34
    int data_1 = 0x00;

    if (data_0 & 0x3F)   // 0x3F = 0011 1111
        printf("存在器件在运行\n");
    else 
        printf("所有已经停止\n");

    if (data_1 & 0x3F)   // 0x3F = 0011 1111
        printf("存在器件在运行\n");
    else 
        printf("所有已经停止\n");

    return 0;
}

输出:
        存在器件在运行
        所有已经停止

1.3 清零状态

#include <stdio.h>

int main() {
    // 定义位掩码
    int greenMask = 1 << 0;    // 绿灯
    int yellowMask = 1 << 1;   // 黄灯
    int redMask = 1 << 2;      // 红灯
    int buzzerMask = 1 << 3;   // 蜂鸣器
    
    // 假设初始状态
    int data = 0b0111; // 二进制表示,举例:绿灯亮、黄灯亮、红灯亮、蜂鸣器停

    // 打印原始状态
    printf("原始状态: ");
    if((data & greenMask) == 0) printf("绿灯灭 "); else printf("绿灯亮 ");
    if((data & yellowMask) == 0) printf("黄灯灭 "); else printf("黄灯亮 ");
    if((data & redMask) == 0) printf("红灯灭 "); else printf("红灯亮 ");
    if((data & buzzerMask) == 0) printf("蜂鸣器停 "); else printf("蜂鸣器响 ");

    data = data & 0x00;
    
    // 打印更新后的状态
    printf("清零灯状态后: ");
    if((data & greenMask) == 0) printf("绿灯灭 "); else printf("绿灯亮 ");
    if((data & yellowMask) == 0) printf("黄灯灭 "); else printf("黄灯亮 ");
    if((data & redMask) == 0) printf("红灯灭 "); else printf("红灯亮 ");
    if((data & buzzerMask) == 0) printf("蜂鸣器停 "); else printf("蜂鸣器响 ");

    return 0;
}

输出:
原始状态: 绿灯亮 黄灯亮 红灯亮 蜂鸣器停
清零灯状态后: 绿灯灭 黄灯灭 红灯灭 蜂鸣器停

1.4 判断奇偶性

//常用方法
for(int i = 0; i < 10; i++)
{
	if(i % 2 == 0)
		printf("%d%s\n", i, "为偶数");
	else
		printf("%d%s\n", i, "为奇数");
}
//位与方法(奇数末尾为1,只需将所求数和一个末尾位为1、其他位为全0的数相与即可判断):
for(int i = 0; i < 10; i++)
{
	if(i & 1)
		printf("%d%s\n", i, "为奇数");
	else
		printf("%d%s\n", i, "为偶数");
}

二、按位或 |

  • 有1则1,全0则0
  • 0110 | 0100 = 0110
  • 0xef | 0xfe = 0xff (即 0x1110 1111 | 0x1111 1110 = 0x1111 1111)

1.1 将一个数中的某个位置1

#include <stdio.h>

int main()
{
    unsigned int data = 0x99; // 0x99 = 1000 0001
    printf("Original data: 0x%x \n",data );
    data = data | 0x66;      // 0x66 = 0110 0110
    printf("Changed data: 0x%x \n",data );
    return 0;
}

Original data: 0x99 
Changed data: 0xff  // 0xff = 1111 1111

 1.2 调换并合并高八位与低八位

要求: 取一个数的高八位与低八位,并将二者的顺序替换

涉及操作:

  • 取一个数中的某些值
  • 将两个8位的数合并为一个16位的数
#include <stdio.h>
#include <stdint.h>

int main() {
	uint16_t combinedData = 0b0110011110110100; // (BIN) 0110011110110100 = (HEX) 0x67B4
	
	uint8_t highByte = combinedData >> 8;  // 取左边的八位
	uint8_t lowByte = combinedData & 0xFF; // 取右边的八位
	
	printf("Combined data in hexadecimal: 0x%x\n", combinedData);
	printf("highByte data in hexadecimal: 0x%x\n", highByte);
	printf("lowByte data in hexadecimal: 0x%x\n", lowByte);
	
	// 调换高八位与低八位顺序
    combinedData = (lowByte << 8) | highByte; // 右边的八位左移后变成16位,再与原本的左边八位取或
    
    printf("Combined data in hexadecimal: 0x%x\n", combinedData);
    
    return 0;
}

输出:
        Combined data in hexadecimal: 0x67b4
        highByte data in hexadecimal: 0x67
        lowByte data in hexadecimal: 0xb4
        Combined data in hexadecimal: 0xb467


比如是在串口接收的时候:

if(upAck->funcCode==0x03) // 表示要读寄存器时
    {
        // upAck->regAmt 为寄存器的数量,若一个寄存器为16位
        for(u16 i = 0; i < upAck->regAmt; i+=2) // 每两个字节一组进行高低字节交换
        {
            u16 lowByte = MeterAck->data[i];          // 保存低字节
            u16 highByte = MeterAck->data[i+1];       // 保存高字节
            
            // 组合成正确的16位值,此时lowByte已经是低字节,highByte是高字节
            u16 temp = (highByte << 8) | lowByte; 
            
            // 分别提取高字节和低字节到响应缓冲区
            upAck->rdata[i] = highByte;               // 高字节
            upAck->rdata[i+1] = lowByte;              // 低字节
        }
    }

三、按位异或

  • 相异为1,相同为0
  • 0110 ^ 0100 = 0010
  • 0xef ^ 0xfe = 0x11 (即 0x1110 1111 ^ 0x1111 1110 = 0x0001 0001)

3.1 将一个数中指定的位翻转(与0异或保留原值,与1异或翻转) 

#include <stdio.h>

int main()
{
    unsigned int data = 0xAA; // 0xAA = 1010 1010
    printf("Original data: 0x%x \n",data );
    data = data ^ 0xC3;      // 0x66 = 1100 0011
    printf("Changed data: 0x%x \n",data );
    return 0;
}

输出:
Original data: 0xaa 
Changed data: 0x69      // 0x69 = 0110 1001

3.2 交换两数(直接使用位操作来实现,免去第三方变量的引入)

#include "stdio.h"
//void Swap(int &a, int &b) {
void Swap(int a, int b) {
    if (a != b) {
        a ^= b;
        b ^= a;
        a ^= b;
    }
    printf("%s0x%x%s0x%x.\n", "交换后,a = ", a, ", b = ", b);
    //注:本来交换后的打印语句想写在main函数中,但是c语言不支持将引用“&”作形参,使用指针作形参时位操作又会报错,编者索性将打印语句写在了交换函数的函数体中,这是因为形参不为“&”或“*”时,函数体中对形参的一系列操作在外部看来“无效”
}
int main() {
    int a = 110, b = 111;
    printf("%s0x%x%s0x%x.\n", "交换前,a = ", a, ", b = ", b);
    Swap(a, b);
    //printf("%s0x%x%s0x%x.\n", "交换后,a = ", a, ", b = ", b);
    return 0;
}

输出:
交换前,a = 0x6e, b = 0x6f.
交换后,a = 0x6f, b = 0x6e.

四、取反操作

  • 1变为0,0变为1
  • ~0110 = 1001; ~0100 = 1011
  • ~0xf = 0xfffffff0; ~0xe = 0xfffffff1

 4.1 将最低位清零 data &= ~1

data &= ~1 就是将 a 的每一位与 11111110 的对应位进行逻辑与操作。由于我们只关心最后一个比特位,其他的比特位无论是什么值,只要与 11111110 相与,都不会影响它们的值。但最后一个比特位(即最低位),无论是0还是1,与 11111110 的对应位(即0)相与都会得到0。因此,这个操作的净效果就是将 data 的最低位清零,而保留其他位不变。

#include <stdio.h>

int main()
{
    unsigned int data = 0xFF;  
    printf("Original data: 0x%x \n",data );
    data &= ~1;      
    printf("Changed data: 0x%x \n",data );
    return 0;
}

输出:
Original data: 0xff  // 0xff = 1111 1111
Changed data: 0xfe   // 0xfe = 1111 1110

4.2 变换符号(正变负、负变正,只需对二进制按位取反再加1即可)

#include "stdio.h"
int TransformSign(int a) {
    return ~a + 1;
}
int main() {
    int a = 0xf;
    printf("%s0x%x%s%d.\n", "变换前,a = ", a, " = ", a);
    a = ~a + 1;
    printf("%s0x%x%s%d.\n", "变换后,a = ", a, " = ", a);
    return 0;
}

输出:
变换前,a = 0xf = 15.
变换后,a = 0xfffffff1 = -15.

 4.3 求整数的绝对值(将int右移31位,则int的最高位变成了最低位,这一位就是符号位)

#include "stdio.h"
int abs_1(int a) {
	//16位操作系统中,int占16位;32位OS中,int占32位、4字节;64位OS中,int同样占32位,long或long long占64位
    int i = a >> 31;
    return i == -1 ? (~a + 1) : a;
}
int abs_2(int a) {
    int i = a >> 31;
    //若a是负数,则i是-1,二进制位0xFFFF,任何数与-1异或相当于取反,而-i是+1,则下列语句表示对负数取反加一得绝对值
    //若a是正数,则i是0,任何数与0异或都不变,则下列语句无意义
    return ((a ^ i) - i);
}
int main() {
    int a = -15;
    printf("%s%d.\n", "a的原值为 ", a);
    printf("%s%d.\n", "a的绝对值为 ", abs_1(a));
    printf("%s%d.\n", "a的绝对值为 ", abs_2(a));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值