0x00 | 前言
C语言中可以使用union完成二进制的划分,但这种方法最多只能精确到1Byte,而非1bit。在许多场景下,直接对二进制进行位精度的操作效率要远远高于其他算法,本文主要介绍了C语言中简单实现按位读写二进制的方法及其原理。
0x01 | 理论基础
在学习离散数学以及几乎所有的编程语言时,我们都曾接触过真值表
。如果我们记或运算为
∨
\vee
∨ ,与运算为
∧
\wedge
∧,则易知有如下的真值表。
∨ \vee ∨ | 1 | 0 |
---|---|---|
1 | 1 | 1 |
0 | 1 | 0 |
∧ \wedge ∧ | 1 | 0 |
---|---|---|
1 | 1 | 0 |
0 | 0 | 0 |
所以对于单个数1
和0
,有如下推论
∵
\because
∵
0
∨
1
=
1
0 \vee 1 = 1
0∨1=1
1
∨
1
=
1
1 \vee 1 = 1
1∨1=1
0
∧
0
=
0
0 \wedge 0 = 0
0∧0=0
1
∧
0
=
0
1 \wedge 0 = 0
1∧0=0
∴
\therefore
∴
∀
x
∈
{
0
,
1
}
,
x
∨
1
=
1
\forall x \in \{0,1\}, x \vee 1 = 1
∀x∈{0,1},x∨1=1
∀
x
∈
{
0
,
1
}
,
x
∧
0
=
0
\forall x \in \{0,1\}, x \wedge 0 = 0
∀x∈{0,1},x∧0=0
我们的代码就从这里写起。
0x02 | 代码实现
注:代码中的bool
类型来自标准库头文件stdbool.h
,使用bool仅仅是因为1 = true,0 = false
先来看几个二进制数,以及他们的十进制值:
二进制 | 十进制 |
---|---|
0000 0000 0000 0000 0000 0000 0000 0000 | 0 |
0000 0000 0000 0000 0000 0000 0000 0001 | 1 |
0000 0000 0000 0000 0000 0000 0000 0010 | 2 |
0000 0000 0000 0000 0000 0000 0000 0011 | 3 |
观察上述二进制,我们可以轻松地将之前的逻辑推论转化为代码:
// 设置n的第i位为f
// i从0数起
void setBinary(int* n,int i,bool f)
{
if(f)
*n |= (1l << i);
else
*n &= ~(1l << i);
}
至于按位读取,虽然我们确实无法用一串二进制与n
直接进行逻辑运算得出结果,但我们可以将视野从一整个二进制串转移到单个的二进制位来。
使用C语言中的按位右移动
运算符,我们得以写出这样的代码:
// 返回n的第i位
// i从0数起
bool getBinary(int n,int i)
{
return (n >> i & 1 == 1);
}
0x03 | 测试
我们使用如下代码进行测试:
#include <stdio.h>
#include <stdbool.h>
void setBinary(int* n,int i,bool f)
{
if(f)
*n |= (1l << i);
else
*n &= ~(1l << i);
}
bool getBinary(int n,int i)
{
return (n >> i & 1 == 1);
}
int main(void)
{
int n = 1;
printf("%d\n",n);
// 检查第二位是否是1
printf("%s\n",getBinary(n,1)?"true":"false");
// 试图修改第二位为1
setBinary(&n,1,1);
printf("%d\n",n);
// 检查修改后的第二位是否是1
printf("%s\n",getBinary(n,1)?"true":"false");
return 0;
}
输出如下:
1
false
3
true
结合上表可知,测试通过。
0x04 | 应用
力扣上有一道直接考这一点的题目:190. 颠倒二进制位 - 力扣(Leetcode)
你可以尝试一下如下代码:
void setBinary(int* n,int i,bool f)
{
if(f)
*n |= (1l << i);
else
*n &= ~(1l << i);
}
bool getBinary(int n,int i)
{
return (n >> i & 1 == 1);
}
uint32_t reverseBits(uint32_t n)
{
uint32_t ret;
for(int i = 0;i < 32;i++)
setBinary(&ret,i,getBinary(n,31 - i));
return ret;
}