嵌入式知识点总结 C/C++ 专题提升(七)-位操作

  针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。

目录

1.位操作基础

2.如何求解整型数的二进制表示中1的个数 ?

3.如何求解二进制中0的个数

4.交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3:

5.给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。


1.位操作基础

位操作(Bitwise Operations)是直接对二进制位进行操作的一类运算,广泛应用于嵌入式开发、系统编程、算法设计等领域。以下是常用的位操作及其作用、示例。

按位与 (&)

将两个数的每个位进行与运算。

规则:1 & 1 = 1,其余为 0。

作用:用于清零某些位,提取特定位。

示例:清除一个数的低 4 位

int x = 0b11010101; // 213
int result = x & 0b11110000; // 清除低4位
printf("Result: 0x%x\n", result); // 输出: 0xd0

按位或 (|)

将两个数的每个位进行或运算。

规则:0 | 0 = 0,其余为 1。

作用:用于设置某些位为 1。

示例:设置某数的第 3 位为 1

int x = 0b11000001; // 193
int result = x | 0b00000100; // 设置第3位
printf("Result: 0x%x\n", result); // 输出: 0xc5

按位异或 (^)

将两个数的每个位进行异或运算。

规则:相同为 0,不同为 1。

作用:用于翻转特定位,或无进位加法。

示例:翻转某数的第 3 位

int x = 0b11000001; // 193
int result = x ^ 0b00000100; // 翻转第3位
printf("Result: 0x%x\n", result); // 输出: 0xc5

按位取反 (~)

将每个位取反,0 变 1,1 变 0。

作用:用于生成补码、求反值。

示例:取反某数

int x = 0b00001111; // 15
int result = ~x; // 取反
printf("Result: 0x%x\n", result); // 输出: 0xfffffff0 (补码表示)

左移 (<<)

将二进制位左移,低位补 0。

作用:快速乘以 2 的幂。

示例:将某数左移 2 位

int x = 5; // 0b0101
int result = x << 2; // 左移2位
printf("Result: %d\n", result); // 输出: 20

右移 (>>)

将二进制位右移,高位补符号位(算术右移)或 0(逻辑右移)。

作用:快速除以 2 的幂。

示例:将某数右移 2 位

int x = 20; // 0b00010100
int result = x >> 2; // 右移2位
printf("Result: %d\n", result); // 输出: 5

在嵌入式开发中,位操作非常常见,以下是一些典型应用场景和代码示例:

1. 控制寄存器的位操作

设置某些位

设置寄存器中某些位为 1,比如配置 GPIO 为输出模式。

#define GPIO_DIR_REG  (*(volatile unsigned int *)0x40020000) // 假设寄存器地址
#define GPIO_PIN_3    (1 << 3) // 第3位表示GPIO3

void set_gpio_output() {
    GPIO_DIR_REG |= GPIO_PIN_3; // 设置第3位为1
}

清除某些位

清除寄存器中某些位为 0,比如禁用某外设功能。

void disable_feature() {
    GPIO_DIR_REG &= ~GPIO_PIN_3; // 清除第3位
}

2. 检测某个位的状态

判断某引脚状态

检测某引脚的高低电平。

#define GPIO_INPUT_REG (*(volatile unsigned int *)0x40020010) // 输入寄存器

int is_pin_high() {
    return (GPIO_INPUT_REG & GPIO_PIN_3) ? 1 : 0; // 检查第3位是否为1
}

3. 翻转某个位

翻转 LED 状态

嵌入式中控制 LED 灯时,经常需要翻转某 GPIO 的状态。

#define GPIO_OUTPUT_REG (*(volatile unsigned int *)0x40020004) // 输出寄存器

void toggle_led() {
    GPIO_OUTPUT_REG ^= GPIO_PIN_3; // 翻转第3位
}

4. 提取寄存器的特定位

获取外设状态

从状态寄存器中提取某外设的状态位。

#define STATUS_REG  (*(volatile unsigned int *)0x40020020) // 状态寄存器
#define DEVICE_READY_BIT (1 << 7) // 第7位表示设备准备好

int is_device_ready() {
    return (STATUS_REG & DEVICE_READY_BIT) >> 7; // 提取第7位
}

5. 多位配置操作

设置多位

一次性设置多个位,比如配置多个 GPIO 为输出模式。

#define GPIO_OUTPUT_MASK (GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5)

void configure_multiple_gpio() {
    GPIO_DIR_REG |= GPIO_OUTPUT_MASK; // 设置GPIO3、GPIO4、GPIO5为1
}

清除多位

清除多个位。

void clear_multiple_gpio() {
    GPIO_DIR_REG &= ~GPIO_OUTPUT_MASK; // 清除GPIO3、GPIO4、GPIO5
}

6. 数据压缩与解压

压缩数据

将多个小数据合并到一个 32 位变量中。

unsigned int pack_data(unsigned char a, unsigned char b, unsigned char c, unsigned char d) {
    return (a << 24) | (b << 16) | (c << 8) | d;
}

解压数据

从一个变量中提取多个字段。

void unpack_data(unsigned int packed, unsigned char *a, unsigned char *b, unsigned char *c, unsigned char *d) {
    *a = (packed >> 24) & 0xFF;
    *b = (packed >> 16) & 0xFF;
    *c = (packed >> 8) & 0xFF;
    *d = packed & 0xFF;
}

2.如何求解整型数的二进制表示中1的个数 ?

#include <stdio.h>

int func(int x) {
    int countx = 0; // 计数器初始化
    while (x) {
        countx++;
        x = x & (x - 1); // 清除最低位的1
    }
    return countx;
}

int main() {
    printf("%d\n", func(9999)); // 调用函数并打印结果
    return 0;
}

func 函数

用于计算输入整数 x 的二进制表示中有多少个 1

x = x & (x - 1) 的作用是清除 x 中最低位的 1,直到 x 变为 0

每次清除一个 1 时,countx 增加 1。

main 函数

调用 func(9999),计算 9999 的二进制表示中有多少个 1

使用 printf 输出结果。

输出结果

9999 的二进制表示为 10011100001111,其中有 8 个 1

3.如何求解二进制中0的个数还有1的个数

#include <stdio.h>

void count_ones_and_zeros(int x) {
    int count_ones = 0, count_zeros = 0;
    while (x) {
        if (x & 1) {
            count_ones++;  // 如果最低位是 1
        } else {
            count_zeros++;  // 如果最低位是 0
        }
        x >>= 1;  // 右移一位,检查下一位
    }
    // 如果 x 最后的结果为 0,还需要考虑 x 可能有零填充的位
    // 假设我们处理的整数位数为 32 位
    int total_bits = sizeof(x) * 8;  // 通常为 32 位(对于 32 位整数)
    count_zeros = total_bits - count_ones - count_zeros;

    printf("1's: %d, 0's: %d\n", count_ones, count_zeros);
}

int main() {
    int numbers[] = {25, 15, 5};
    for (int i = 0; i < 3; i++) {
        printf("For %d: ", numbers[i]);
        count_ones_and_zeros(numbers[i]);
    }
    return 0;
}

4.交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3:

#include <stdio.h>

int main() {
    int a = 3, b = 5;
    
    printf("Before swap: a = %d, b = %d\n", a, b);
    
    a = a + b; // a 变为 8 (3 + 5)
    b = a - b; // b 变为 3 (8 - 5)
    a = a - b; // a 变为 5 (8 - 3)
    
    printf("After swap: a = %d, b = %d\n", a, b);
    
    return 0;
}

a = a + ba 保存了 ab 的和。

b = a - bb 通过从 a 的和中减去原来的 b 值,得到原来的 a 值。

a = a - ba 通过从和中减去新的 b 值,得到原来的 b 值。

#include <stdio.h>

int main() {
    int a = 3, b = 5;
    // a 0011  b 0101
    printf("Before swap: a = %d, b = %d\n", a, b);

    a = a ^ b; // a 变为 6 (3 ^ 5) 0011 0101  - 0110
    b = a ^ b; // b 变为 3 (6 ^ 5) 0110 0101  - 0011
    a = a ^ b; // a 变为 5 (6 ^ 3) 0110 0011  - 0101

    printf("After swap: a = %d, b = %d\n", a, b);

    return 0;
}

a = a ^ ba 变成了 ab 的异或值。

b = a ^ bb 通过异或 a(当前是 a ^ b)得到原来的 a 值。

a = a ^ ba 通过异或 b(当前是原来的 a 值)得到原来的 b 值。

5.给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。

#include <stdio.h>
#define BIT3 (1<<3)
static int a=5;
void Set_Bit3(void){
    a |= BIT3;
}
void Clear_Bit3(void){
    a &= ~BIT3;
}
int main() {
    Set_Bit3();
    Clear_Bit3();
    printf("%d",a);
    return 0;
}

假设 a 的初始值为 5,其二进制为 00000101

初始值打印:5

设置第3位后:

BIT3 = 1 << 3 = 00001000

a | BIT3 = 00000101 | 00001000 = 00001101,结果为 13

打印:13

清除第3位后:

~BIT3 = ~00001000 = 11110111

a & ~BIT3 = 00001101 & 11110111 = 00000101,结果为 5

打印:5

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

两台局域网电脑通过飞秋传输大文件失败的解决方案

问题描述&#xff1a; 局域网两台电脑之间传输大文件&#xff08;超过20G&#xff09;&#xff0c;不想太复杂&#xff0c;就各装个飞秋。但是通过直接发送文件发现总是失败&#xff0c;一会就中断了。 解决方法&#xff1a; 主界面上有一个文件共享的按钮&#xff0c;通过文…

Picsart美易照片编辑器和视频编辑器

使用Picsart美易照片编辑器和视频编辑器&#xff0c;将您的创意变为现实。制作专业水准的拼贴画、设计并添加贴纸、快速移除和更换背景&#xff0c;体验流行编辑&#xff0c;比如 黄金时刻、镜中自拍、复古噪点滤镜或千禧滤镜。Picsart美易是一款一体式编辑器&#xff0c;拥有众…

AR智慧点巡检系统探究和技术方案设计

一、项目背景 随着工业生产规模的不断扩大和设备复杂度的提升&#xff0c;传统的人工点巡检方式效率低下、易出错&#xff0c;难以满足现代化企业对设备运行可靠性和安全性的要求。AR&#xff08;增强现实&#xff09;技术的发展为点巡检工作带来了新的解决方案&#xff0c;通…

游戏设备升级怎么选?RTX4070独显,ToDesk云电脑更具性价比

过新年、添喜气&#xff01;正逢节期来临不知道各位是否都跟小编一样在考虑购置生活中的各样所需呐&#xff1f; 25年可谓是3A游戏大作之年&#xff0c;例如《GTA6》《文明7》《死亡搁浅2》《刺客信条&#xff1a;影》下半年落地的《塞尔达传说&#xff1a;新篇章》《生化危机9…

算法刷题笔记——图论篇

这里写目录标题 理论基础图的基本概念图的种类度 连通性连通图强连通图连通分量强连通分量 图的构造邻接矩阵邻接表 图的遍历方式 深度优先搜索理论基础dfs 与 bfs 区别dfs 搜索过程深搜三部曲所有可达路径广度优先搜索理论基础广搜的使用场景广搜的过程 岛屿数量孤岛的总面积沉…

怎么使用python 调用高德地图api查询位置和导航?

环境&#xff1a; python 3.10 问题描述&#xff1a; 怎么使用python 调用高德地图api查询位置和导航? 解决方案&#xff1a; 要使用Python调用高德地图API查询位置和导航&#xff0c;需要先注册高德开发者账号并获取API Key。以下是基本步骤&#xff1a; 1. 注册高德开…

【阿里云】使用docker安装nginx后可以直接访问

一、创建目录 mkdir -p config/{cert,conf.d} html logs二、上传nginx.conf的配置文件 user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_connections 1024; }http {include /etc/ngin…

Fisco-Bcos-java-SDK 利用java与fisco-Bcos区块链上的智能合约交互(以HelloWorld为例)

Fisco-Bcos-java-SDK 利用java与fisco-Bcos区块链上的智能合约交互&#xff08;以HelloWorld为例&#xff09; 一、部署智能合约 1、编写智能合约 此处用最简单的HelloWorld合约作为例子 包含两个方法和一个构造函数 构造函数&#xff1a;当合约部署的时候 执行构造函数 将…

DearMom婴儿车:书籍点亮希望,为乡村留守儿童架起知识桥梁

近日&#xff0c;DearMom婴儿车携手中国社会福利基金会来到河南上蔡赵庄小学&#xff0c;成功举办了一场意义非凡的公益助学活动&#xff0c;这是他们第二次以实际行动诠释企业社会责任。此次活动&#xff0c;品牌方致力于以书籍为媒介&#xff0c;为乡村留守儿童开拓视野、丰富…

备赛蓝桥杯之第十五届职业院校组省赛第二题:分享点滴

提示&#xff1a;本篇文章仅仅是作者自己目前在备赛蓝桥杯中&#xff0c;自己学习与刷题的学习笔记&#xff0c;写的不好&#xff0c;欢迎大家批评与建议 由于个别题目代码量与题目量偏大&#xff0c;请大家自己去蓝桥杯官网【连接高校和企业 - 蓝桥云课】去寻找原题&#xff0…

阿里云-银行核心系统转型之业务建模与技术建模

业务领域建模包括业务建模和技术建模&#xff0c;整体建模流程图如下&#xff1a; 业务建模包括业务流程建模和业务对象建模 业务流程建模&#xff1a;通过对业务流程现状分析&#xff0c;结合目标核心系统建设能力要求&#xff0c;参考行业建 模成果&#xff0c;形成结构化的…

吴恩达深度学习——如何实现神经网络

来自吴恩达深度学习&#xff0c;仅为本人学习所用。 文章目录 神经网络的表示计算神经网络的输出激活函数tanh选择激活函数为什么需要非激活函数双层神经网络的梯度下降法 随机初始化 神经网络的表示 对于简单的Logistic回归&#xff0c;使用如下的计算图。 如果是多个神经元…

【Rust自学】14.4. 发布crate到crates.io

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 14.4.1. 创建并设置crates.io账号 在发布任何 crate 之前&#xff0c;你需要在 crates.io并…

13 网络编程

1 网络基础 1.1 什么是网络 把分布在不同地理区域的计算机、外部硬件设备用通信线路互连成一个规模大、功能强的网络系统&#xff0c;使系统的各个终端可以方便地互相传递信息&#xff0c;共享硬件、软件、数据信息等资源。 按照覆盖范围大小&#xff0c;网络可以分为&#x…

Android系统开发(八):从麦克风到扬声器,音频HAL框架的奇妙之旅

引言&#xff1a;音浪太强&#xff0c;我稳如老 HAL&#xff01; 如果有一天你的耳机里传来的不是《咱们屯里人》&#xff0c;而是金属碰撞般的杂音&#xff0c;那你可能已经感受到了 Android 音频硬件抽象层 (HAL) 出问题的后果&#xff01;在 Android 音频架构中&#xff0c…

第18个项目:微信开发入门:获取access_token的Python源码

源码下载地址:https://download.csdn.net/download/mosquito_lover1/90301829 功能特点: 输入AppID和AppSecret,点击按钮后异步获取access_token 1、自动保存功能: 当用户输入或修改 AppID 和 AppSecret 时自动保存 获取到新的 access_token 时自动保存 所有数据都保存在…

ESP8266 MQTT服务器+阿里云

MQTT私有平台搭建&#xff08;EMQX 阿里云&#xff09; 阿里云服务器 EMQX 搭建私有MQTT平台 1、搜索EMQX开源版本 2、查看各版本EMQX支持的UBUNTU版本 3、查看服务器Ubuntu版本 4、使用APT安装模式 5、按照官网指示安装并启动 6、下载安装MQTTX测试工具 7、设置云服务…

【面试总结】FFN(前馈神经网络)在Transformer模型中先升维再降维的原因

FFN&#xff08;前馈神经网络&#xff09;在Transformer模型中先升维再降维的设计具有多方面的重要原因&#xff0c;以下是对这些原因的总结&#xff1a; 1.目标与动机 高维映射空间&#xff1a;FFN的设计目的是通过一系列线性变换来拟合一个高维的映射空间&#xff0c;而不仅…

C语言程序设计十大排序—希尔排序

文章目录 1.概念✅2.希尔排序&#x1f388;3.代码实现✅3.1 直接写✨3.2 函数✨ 4.总结✅ 1.概念✅ 排序是数据处理的基本操作之一&#xff0c;每次算法竞赛都很多题目用到排序。排序算法是计算机科学中基础且常用的算法&#xff0c;排序后的数据更易于处理和查找。在计算机发展…

mac 电脑上安装adb命令

在Mac下配置android adb命令环境&#xff0c;配置方式如下&#xff1a; 1、下载并安装IDE &#xff08;android studio&#xff09; Android Studio官网下载链接 详细的安装连接请参考 Mac 安装Android studio 2、配置环境 在安装完成之后&#xff0c;将android的adb工具所在…