C语言位操作

时间:2022-05-16 01:00:56


深入到字节的内部,讨论如何直接操作字节内部的二进制位

十进制转二进制

短除法&位权法

先读高位、后读低位从下往上逆序读右侧的余数101010。这就是十进制数42所对应的二进制。

C语言位操作


将二进制转换为十进制,可以把二进制中的各位乘以其所在位的位权,再将所有乘法的积累加起来,即可得到转换后的十进制结果。

例如:

二进制101010,高位在左,低位在右

C语言位操作

十进制转二进制函数

#include<stdio.h>
void printBinary(unsigned char dec) {
// 若dec为0,输出0并返回
if (dec == 0)
{
printf("0\n"); return;
}
// 若dec非0,短除计算余数,逆序输出
char bits[8];
int count = 0;
int quotient;
int remainder;
while (dec > 0)
{
remainder = dec % 2;
quotient = dec / 2;
dec = quotient;
bits[count] = remainder;
count++;
}
for (int i = count - 1; i >= 0; i--)
printf("%d", bits[i]);
putchar('\n');
}
int main() {
printBinary(42);//以42为例
}

正确输出

101010

对于​​unsigned char​​​类型的变量​​dec​​​来说,它拥有8个二进制位。而十进制数42,只占6个二进制位,还有两个二进制位为0。若需要把8个二进制位全部输出,可以将​​bits​​​数组初始化为0。计算完余数后,从数组的最后一个元素开始,逆序输出直到数组第一个元素。此时,无需对​​dec​​​为0做特殊处理了。由于数组​​bits​​​初始化为0,​​dec​​​为0时,不进入​​while​​循环,直接输出8个0。

#include<stdio.h>
void printBinary(unsigned char dec) {
char bits[8] = {0};
int count = 0;
int quotient;
int remainder;
while (dec > 0)
{
remainder = dec % 2;
quotient = dec / 2;
dec = quotient;
bits[count] = remainder;
count++;
}
for (int i = 8 - 1; i >= 0; i--)
printf("%d", bits[i]);
putchar('\n');
}
int main() {
printBinary(42);
}

补齐0

00101010

逻辑运算符

逻辑运算符会把运算对象的数值,看做真或假进行逻辑运算。若运算对象的数值为非0值,则看做真。若数值为0值,则看做假。
下面分别复习一下这3种逻辑运算符。

逻辑与&&

逻辑与​​&&​​运算符会对左右两边,两个运算对象进行运算。

  1. 若两个运算对象同时为真时,运算结果为真,用数值1表示。
  2. 否则,运算结果为假,用数值0表示。

​12345 && 67890​​​ 左右有两个运算对象,分别为​​12345​​与​​67890​​。左边的运算对象​​12345​​为非0值,看做真。右边运算对象​​67890​​为非0值,看做真。两个运算对象同时为真,运算结果为真,即1。
若将其中一边变为0,表达式如下:
​12345 && 0​​ 左边的运算对象​​12345​​为非0值,看做真。右边运算对象​​0​​为0值,看做假。两个运算对象不同时为真,运算结果为假,即0。

逻辑或||

逻辑或​​||​​运算符会对左右两边,两个运算对象进行运算。

  1. 若两个运算对象同时为假时,运算结果为假,用数值0表示。
  2. 否则,运算结果为真,用数值1表示。

​12345 || 0​​​ 左边的运算对象​​12345​​为非0值,看做真。右边运算对象​​0​​为0值,看做假。两个运算对象不同时为假,运算结果为真,即1。
​0 && 0​​ 左边的运算对象​​0​​为0值,看做假。右边运算对象​​0​​为0值,看做假。两个运算对象同时为假,运算结果为假,即0。

逻辑非!

逻辑与​​!​​运算符会对右边一个运算对象进行运算:

  1. 若运算对象为真,运算结果为假。
  2. 若运算对象为假,运算结果为真。

​!12345​​​ 右边运算对象​​12345​​为非0值,看做真。运算结果为假,即0。
​!0​​ 右边运算对象​​0​​为0值,看做假。运算结果为真,即1。

位运算符

在上面的逻辑运算中,会把运算对象的数值根据非0值或0值,看做真或假,再进行逻辑运算。
还有另外一类运算符,它将深入运算对象的内部,把运算对象内部的二进制位,根据非0值或0值,看做真或假,再进行逻辑运算。

位逻辑运算符:

  1. 位逻辑与​​&​
  2. 位逻辑或​​|​
  3. 位逻辑异或​​^​
  4. 位逻辑非​​~​

将十进制170作为函数​​printBinary​​​的参数,它将打印出十进制170的二进制​​10101010​​​。
将十进制102作为函数​​​printBinary​​​的参数,它将打印出十进制102
的二进制​​​01100110​​​。
接下来,我们让这两个数值分别做各种位逻辑运算,看看它们都有什么现象。

位逻辑与&

printf("%hhu\n", 170 & 102);
printBinary(170 & 102);

位逻辑与&它将深入字节内部,对二进制位进行逻辑与运算。

  1. 若两个位同时为真,运算结果为真,用数值1表示。
  2. 否则,运算结果为假,用数值0表示。

前四位为例

C语言位操作


C语言位操作


C语言位操作


C语言位操作

位逻辑或|

  1. 若两个位同时为假时,运算结果为假,用数值0表示。
  2. 否则,运算结果为真,用数值1表示。

C语言位操作

位逻辑异或^

  1. 若两个位不同时,运算结果为真,用数值1表示。
  2. 否则,运算结果为假,用数值0表示。

C语言位操作

位逻辑非~

位逻辑非​​~​​它将深入字节内部,对二进制位进行逻辑非运算。

  1. 若二进制位为真时,运算结果为假,用数值0表示。
  2. 二进制位为假时,运算结果为真,用数值1表示。

换句话说,位逻辑非运算会翻转运算对象的所有二进制位。二进制位1变为0,0变为1。

C语言位操作

左移右移

左移运算符​​<<​

左移运算符将数据对象内部的二进制全部向左移动指定位,空出来的位置用0填充。

#include<stdio.h>
void printBinary(unsigned char dec) {
char bits[8] = {0};
int count = 0;
int quotient;
int remainder;
while (dec > 0)
{
remainder = dec % 2;
quotient = dec / 2;
dec = quotient;
bits[count] = remainder;
count++;
}
for (int i = 8 - 1; i >= 0; i--)
printf("%d", bits[i]);
putchar('\n');
}
int main() {
printBinary(231);
printBinary(231 << 1);
printBinary(231 << 2);
printBinary(231 << 3);
}

输出结果

11100111
11001110
10011100
00111000

C语言位操作

右移运算符

右移运算符将数据对象内部的二进制全部向右移动指定位,对于无符号类型,空出来的位置用0填充。
对于有符号类型,空出来的位置用0或1填充取决于编译器。

#include<stdio.h>
void printBinary(unsigned char dec) {
char bits[8] = {0};
int count = 0;
int quotient;
int remainder;
while (dec > 0)
{
remainder = dec % 2;
quotient = dec / 2;
dec = quotient;
bits[count] = remainder;
count++;
}
for (int i = 8 - 1; i >= 0; i--)
printf("%d", bits[i]);
putchar('\n');
}
int main() {
printBinary(231);
printBinary(231 >> 1);
printBinary(231 >> 2);
printBinary(231 >> 3);
}

输出结果

11100111
01110011
00111001
00011100

C语言位操作