位操作
数据的表示
进制
对于整数的表示形式有:十进制、二进制、八进制、十六进制。例如:char a= 17, b = 0B00010001, c = 021, d = 0X11; 其实 a, b, c ,d 是都相等的。写个demo验证一下
#include <stdio.h>
int main(int argc, char *argv[])
{
/*
* 二进制前缀: 0B 或 0b
* 八进制前缀: 0 (数字0,不是字母o)
* 十六进制前缀:0x 或 0X
*/
char a= 17, b = 0B00010001, c = 021, d = 0X11;
printf("a: %d\n", a);
printf("b: %d\n", b);
printf("c: %d\n", c);
printf("d: %d\n", d);
return 0;
}
结果:

其中 char 是一个较小的整型而已,无符号取值范围:0~255,有符号取值范围:-127~127。或许你可能会问:为什么要叫字符型? 因为它主要用用来存储字符的。这个时候你又会有疑问:char 的取值范围是 -127~127,存储又是字符,那么字符就是是整数? 没错,字符就是以整数形式存储的,字符 'A' ~ 字符 'Z' 对应的整数是 65 ~ 90。字符 'a' ~ 字符 'z' 对应的整数是 97 ~ 122。其他对应的字符可以查 ASCII码对照表
注意:不管是什么进制表示,最终在计算机中都是以二进制取运算和存储,因为计算机就是处理0101这样二进制数据,什么十进制、八进制、十六进制仅为了方便开发人员阅读而已
补码
对于char 用二进制表示 0 ~ 127,即 0000 0000 ~ 0111 1111。那么用二进制表示 -127 ~ -1 怎么处理?换一句话说:负数怎么用二进制表示? 补码,补码计算口诀:负数的原码取反,再加1
-127 的在计算机的表示形式:补码,其计算过程如下:
-127的原码:1111 1111 (左边第一位是符号位)
-127的反码:1000 0000 (符号位不变,其他位取反)
-127的补码:1000 0001 (反码的基础上加1)
注意:正数的反码、补码都与原码相同
那么为什么整数要用到补码呢?
-
可以解决了负数的二进制表示
-
消除0 和 -0的歧义,总不能一个0有两种表示形式吧
不能char有 0000 0000 和 1000 0000 表示 0 和 -0 -
使用补码进行计算
在计算机中整数是如何进行计算的?
使用补码进行计算的,例如:3 - 5 的计算过程,当然你可能口算就知道了,但是对于计算来说没有那么简单。
对于3和-5用char类型就可以存储了。
3的补码: 0000 0011
-5的补码:1111 1011
表示式:3 - 5,对于计算机来说就是加法运算,即3 + (-5),用补码做加法运算结果是:1111 1110(补码),转换成十进制过程,补码-1,即 1111 1101,然后取反(符号位不变),即1000 0010,就是十进制的-2
演绎推算完这个过程,觉得计算机的人类先驱真的NB!!! 冯. 诺伊曼 , 图灵,肯.汤普森,丹尼斯.里奇,...
位操作
位操作是针对整数的。位操作运算肯定需要位操作运算符:
- ~:按位取反
- &:按位与
- |:按位或
- ^:按位异或
看到上面这些关键词,这不是数字电路里面的知识嘛,这些东西怎么跑这来了,仔细想想,计算机玩的不就是数字电路嘛。
写个demo验证一下位操作运算符:
#include <stdio.h>
int main(int argc, char *argv[])
{
// ~ :按位取反,即0变1,1变0
unsigned char a = 0X0f;
printf("~(0X0f): 0X%02hhx\n\n", ~a);
// &:按位与,即相同位置的值都1,结果才为1
unsigned char b = a & 0Xf0;
unsigned char c = a & 0Xff;
printf("0X0f & 0Xf0: 0X%02hhx\n", b);
printf("0X0f & 0Xff: 0X%02hhx\n\n", c);
// |:按位或,即相同位置的值有一个为1,结果就为1
unsigned char d = a | 0Xf0;
unsigned char e = a | 0Xff;
printf("0X0f | 0Xf0: 0X%02hhx\n", d);
printf("0X0f | 0Xff: 0X%02hhx\n\n", e);
// ^: 按位异或,即相同位置的值不相等,结果就为1
unsigned char f = a ^ 0Xf0;
unsigned char g = a ^ 0Xff;
printf("0X0f ^ 0Xf0: 0X%02hhx\n", f);
printf("0X0f ^ 0Xff: 0X%02hhx\n\n", g);
return 0;
}
结果:

掩码
通过按位与&操作可以检测某一个位的值是0还是1,例如:0B0000 0101,想检测第1位的值,可以进行这样的运算 --> 0B0000 0101 & 0B0000 0010, 若运算结果为非0值,则说明目标的第1位的值是1
通过按位或|操作可以设置某一个位的值是0还是1,例如:0B0000 0101,想设置第1位的值为1,可以进行这样的运算 --> 0B0000 0101 | 0B0000 0010, 若运算结果为 0B0000 0111,则说明设置成功。
规律总结:
-
不管该位原来的值是0还是1,它跟0进行&运算,得到的结果都是0,而跟1进行&运算,将保持原来的值不变;
-
不管该位原来的值是0还是1,它跟1进行|运算,得到的结果都是1,而跟0进行|运算,将保持原来的值不变。
写个代码,验证一下:
#include <stdio.h>
int main(int argc, char *argv[])
{
// 检测第1位的状态
unsigned char a = 0B11110010, b = 0B11110000;
unsigned char mask = 0B00000010;
printf("0B11110010 & 0B00000010: 0X%02x\n", a & mask);
printf("0B11110000 & 0B00000010: 0X%02x\n\n", b & mask);
// 设置第1位的状态
unsigned char d = 0B11110010, e = 0B11110011;
printf("0B11110010 | 0B00000010: 0X%02x\n", d | 0B00000010);
printf("0B11110011 | 0B00000000: 0X%02x\n\n", e | 0B00000000);
return 0;
}
结果:

移位操作
-
左移<<: 整体全部向左移动N位
-
右移>>: 整体全部向右移动N位
直接通过代码验证:
示例一:简单了看1的移动过程
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned char a = 0B00000001;
unsigned char b = 0B10000000;
// 左移<<
for (int i = 0; i < 8; i++)
{
printf("a左移%d位: 0X%02x\n", i, a << i);
}
printf("\n");
// 右移>>
for (int i = 0; i < 8; i++)
{
printf("b右移%d位: 0X%02x\n", i, b >> i);
}
return 0;
}
结果:

示例二:看多个1的移动过程
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned char a = 0B00000011;
unsigned char b = 0B11000000;
// 左移<<
for (int i = 0; i < 8; i++)
{
printf("a左移%d位: 0X%02hhx\n", i, (unsigned char)a << i);
}
printf("\n");
// 右移>>
for (int i = 0; i < 8; i++)
{
printf("b右移%d位: 0X%02hhx\n", i, b >> i);
}
return 0;
}
结果:

目标匹配
有一个uinst32_t的数,需要验证:第8位~第12位是00100,第21位是1。
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
bool checkBits(uint32_t num, int start, int end, uint32_t pattern)
{
uint32_t mask = ((uint32_t)1 << (end - start + 1)) - 1; // 创建掩码
mask <<= start; // 将掩码移到正确的位置
// 使用掩码将不需要的位清零,然后与pattern进行比较
return ((num & mask) >> start) == pattern;
}
int target_check(uint32_t *arr, int cnt)
{
int rc = 0;
for (int i = 0; i < cnt; i++)
{
uint32_t binaryNumber = arr[i];
bool isPatternCorrect = checkBits(binaryNumber, 8, 12, 0b00100);
bool isBit21Set = checkBits(binaryNumber, 21, 21, 0b1);
if (isPatternCorrect && isBit21Set)
{
rc = 0;
printf("%d --> the value is true, (0X%08x)\n\n", i + 1, binaryNumber);
}
else
{
rc = 1;
printf("%d --> the value false , (0X%08x)\n\n", i + 1, binaryNumber);
}
}
return rc;
}
int main(int argc, char *argv[])
{
/*
* 0B00000000001000000000010000000000, 从右到左,分别是第0位,第1位,... ,第31位。
* 可以使用无符号32位的整型存储, uint32_t num = 0B00000000001000000000010000000000;
* 32位的二进制可以用十六进制表示,uint32_t num = 0X00200400; 即4位二进制可用1位十六进制表示
*
* 要求检测目标数值的第8位至第12位是00100,第21位是1
*/
uint32_t arr[] = {
0X00200400,
0X01200400,
0Xab300400,
0X00100400, // error
0X00200500, // error
0X00800300, // error
0B00000000001000000000010000000000,
0B00000000001000000001010100000000,
};
int cnt = sizeof(arr) / sizeof(arr[0]);
target_check(arr, cnt);
return 0;
}
结果:


浙公网安备 33010602011771号