【207】VS2022 C++对unsigned char某一位(bit)的数据进行读写
在一些和硬件相关的场景下,为了节约设备空间,会给一个字节长度的变量中不同的位赋予不同的涵义。比如西门子 PLC 中布尔型变量就是占据一个字节(byte)中的一位(bit)。因此本文讨论了如何对字节中的某一位进行读写。
一、预备
01 与运算:两个数字都是 1 的时候,结果是 1 ;其他情况结果都是 0;也就是说 0 和任何数字做与运算结果都是 0
01 或运算:两个数字都是 0 的时候,结果是 0 ;其他情况结果都是 1;也就是说 1 和任何数字做或运算结果都是 1
二、读取
一个字节共 8 位,从低到高在代码中命名为第 0 位到 第 7 位。
当读取第零位数据的时候,把原数据和 1 做按位与,比如原数据 4 和 1 做按位与,如下:
00000100 # 4
00000001 # 1
--------
00000000 # 和 1 按位与,只有第 0 位是原始值,其他位因为和 0 按位与,都变 0
显然 4 的第 0 位是 0,和 1 按位与后还是 0。1 的第一到第七位都是 0,和 4 按位与后结果都是0。
如果换成 5 ,那么就是第 0 位是1,按位与后结果是 1。
当读取第一位数据的时候,把原数据和二进制数字 00000010
做按位与,也就是和 2 做按位与。结果要么是 0,要么是 2。这里可以利用 C++ if
语句对非 0 整数判真,对 0 判假的特性来写 C++ 代码。
以此类推,想要读取第 x 位的数据,就找一个第 x 位是 1、其他位是 0 的二进制数字来跟原数据做按位与,然后用 if
语句判断是不是 0 即可。由于二进制数字的数学特性,从第 0 位到第 7 位对应的数字分别是:
也就是说,根据要读取的位置不同,原数据要和对应的 1、2、4、8、16、32、64、128 做按位与。读者可以看一下后面代码中的 int readBit(unsigned char c, int bitPosition)
函数。
三、写入
一个字节共 8 位,从低到高在代码中命名为第 0 到 第 7 位。根据需求把字节中的某一位设置成 1 或 0。
我们要分两种情况讨论,分别是写入 1 和写入 0
3.1 写入 1
当向第零位写入 1 的时候,把原数据和 1 做按位或运算,比如原数据 4 和 1 做按位或,如下:
00000100 # 4
00000001 # 1
--------
00000101 # 和 1 按位或,只有第 0 位变成 1,其他位因为是按位或,值不变。
显然 4 的第 0 位是 0,和 1 做或运算变成 1,1 的第一到第七位都是 0 ,和 4 做按位或后值不变。结果是 5。
当向第一位写入 1 的时候,就需要让原数据和二进制数字 00000010
做按位或,也就是和 2 做按位或。
以此类推,想把第 x 位的数据置为 1,就把原数据和第 x 位是一、其他位是零的二进制数字做按位或,由于二进制数字的数学特性,从第 0 位到第 7 位对应的数字分别是:
即根据要写入 1 的位置不同,原数据要和对应的 1、2、4、8、16、32、64、128 做按位或运算。
3.2 写入 0
当向第 0 位写入 0 的时候,把原数据和 254 做按位与运算,比如原数据 5 和 1 做按位与,如下:
00000101 # 5
11111110 # 254
--------
00000100 # 和 254 按位与,只有第零位变成 0,其余位都是不变的。
显然 5 的第零位是 1;254 只有第零位是 0,第一到第七位都是 1;做按位与的时候只有 5 的第零位变成 0,其余位保持不变。结果是 4
当向第 1 位写入 0 的时候,需要让原数据和二进制数字 11111101
(即 253) 做按位与。
以此类推,要把第 x 位设置成 0,需要让原数据和一个第 x 位是0、其他位是 1 的二进制数字做按位与运算。由于二进制数字的数学特性,从第 0 位到第 7 位对应的数字分别是:
即根据要写入 0 的位置不同,原数据要和对应的 254、253、251、247、239、223、191、127 做按位与运算。
本文的代码中,int writeBit(unsigned char inCh, int bitPosition, bool data, unsigned char* outCh)
函数就是负责处理写入的。
四、代码
下面是 C++ 代码实现,在 VS 2022 上调试通过:
#include <stdio.h>
#include <stdlib.h>#include <random>// 从一个字节(byte)的数据中,读取某一位(bit)的数据。
// 一个字节的数据是8位的二进制数据,从低到高是 0 到 7 位,正常情况是返回 0 或 1.
// 如果有错误返回 -1
// unsigned char c 原数据
// int bitPosition 位置,范围从 0 到 7.
int readBit(unsigned char c, int bitPosition) {if (bitPosition < 0 || bitPosition > 7) {return -1;}unsigned char c2 = 0;switch (bitPosition) {case 0:c2 = c & 1;break;case 1:c2 = c & 2;break;case 2:c2 = c & 4;break;case 3:c2 = c & 8;break;case 4:c2 = c & 16;break;case 5:c2 = c & 32;break;case 6:c2 = c & 64;break;case 7:c2 = c & 128;break;}if (c2) {return 1;}else {return 0;}
}// 设置一个字节中某一位为0或1
// unsigned char inCh 输入参数,原来的单字节数据
// int bitPosition 字节中的位置,从低到高,数字是 0 到 7,(含 0 和 7 )
// bool data 要设置的数据,true 是 1,false 是 0。
// unsigned char* outCh 输出参数,inCh 变更后的结果。
// 返回0表示正常,返回-1表示出错。
int writeBit(unsigned char inCh, int bitPosition, bool data, unsigned char* outCh) {if (bitPosition < 0 || bitPosition > 7) {return -1;}unsigned char result = 0;if (data) {switch (bitPosition) {case 0:result = inCh | 1;break;case 1:result = inCh | 2;break;case 2:result = inCh | 4;break;case 3:result = inCh | 8;break;case 4:result = inCh | 16;break;case 5:result = inCh | 32;break;case 6:result = inCh | 64;break;case 7:result = inCh | 128;break;}}else {switch (data) {case 0:result = inCh & 254; // 255 - 1break;case 1:result = inCh & 253; // 255 - 2break;case 2:result = inCh & 251; // 255 - 4break;case 3:result = inCh & 247; // 255 - 8break;case 4:result = inCh & 239; // 255 - 16break;case 5:result = inCh & 223; // 255 - 32break;case 6:result = inCh & 191; // 255 - 64break;case 7:result = inCh & 127; // 255 - 128break;}}(*outCh) = result;return 0;
}// 生成 0 到 255 的随机数,包含0和255
unsigned char genRandom() {// 使用随机设备作为种子std::random_device rd;// 使用 Mersenne Twister 引擎std::mt19937 gen(rd());// 定义分布范围 [0, 255]std::uniform_int_distribution<> distr(0, 255);// 生成并输出随机数int result = distr(gen);return (unsigned char)result;
}int main(int argc, char** argv) {system("color 02");printf("argc=%d\n", argc);int ret = -1;// 生成随机数unsigned char n = genRandom();printf("n=%d\n", n);printf("readBit:\n");// 读取随机数的每一位数据,并以01的形式展现出来。for (int i = 7; i >= 0; i--) {ret = readBit(n, i);if (ret > -1) {printf("%d", ret);}else {printf("readBit Error !\n");}}printf("\n\n");unsigned char result;ret = writeBit(n, 0, false, &result);printf("writeBit:\n");if (0 != ret) {printf("writeBit Error! \n");}else {printf("%d\n", result);}return 0;
}