自学内容网 自学内容网

C++—位运算

1.简介

C++中的位运算包括与(&)或(|)异或(^)非(~)左移(<<)右移(>>)等,这些运算直接作用于整数的二进制表示。

  • 按位与(Bitwise AND):对两个数的每一位进行与操作,只有两个对应的位都为1时结果才为1,否则为0。
  • 按位或(Bitwise OR):对两个数的每一位进行或操作,只要有一个对应的位为1,结果就为1。
  • 按位取反(Bitwise NOT):对一个数的每一位进行取反操作,0变为1,1变为0。
  • 按位异或(Bitwise XOR):对两个数的每一位进行异或操作,当两个对应的位不同时结果为1,相同时结果为0。
  • 左移(Left Shift):将一个数的所有位向左移动指定的位数,右边空出的位用0填充。
  • 右移(Right Shift):将一个数的所有位向右移动指定的位数,对于无符号数,左边空出的位用0填充;对于有符号数,具体的行- 取决于编程语言和编译器,有的语言会用符号位(即最左边的位)来填充左边的空位。

2.与(&)

2.1 简介

与运算是C++中的一种基本位运算,它直接对两个整数的二进制表示进行操作。对于两个数的每一位,只有当两个数的对应位都为1时,结果的对应位才为1,否则为0。位与运算的符号是&。

2.2 示例

/*
 * @brief:    Bitwise operation
 * @compile:  g++ -g bm_main.cc -o d -std=c++11
 * @author:   your name
 * @date:     2024/10/12
 * @lastEditorDate:
 */

#include <iostream>

void printBinary(int num) 
{ 
    const int bit_count = 8;   //为方便这里采用了8位
    for (int i = bit_count - 1; i >= 0; --i)
    {
        
        if ((num >> i) & 1) 
        {
            std::cout << '1';
        }
        else 
        {
            std::cout << '0';
        }
    }
}

int main(int argc, char* argv[])
{
    int a = 7;
    int b = 6;
    int r = a & b;

    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;

    std::cout << "a & b value is: : " << r << std::endl;    

    return 0;
}

输出

a value(bit) is: 00000111
b value(bit) is: 00000110
r value(bit) is: 00000110
a & b value is: : 6

在输出的结果中可以直观的看到,a和b的计算逻辑,当两个数的对应位都为1时,结果的对应位才为1,否则为0。为方便演示,这里只打印出8位。

2.3 应用

2.3.1 计算方面

  • 清零特定位:通过将某个数与一个掩码(mask)进行位与运算,可以将该数的特定位清零。掩码是一个在特定位上为0,其他位上为1的整数。
  • 检查特定位:通过与一个只在特定位上为1,其他位上为0的整数进行位与运算,可以检查一个数的特定位是否为1。如果结果为0,则该位为0;否则,该位为1。
  • 提取特定位:通过与一个只在特定位上为1,其他位上为0的整数进行位与运算,并右移适当位数,可以提取一个数的特定位的值。

int main(int argc, char* argv[])
{
    uint8_t binary_template = 0b00000010;   //8位二进制模板的定义

    int a = 7;    
    int r = a & binary_template;  //保留右边第二位,其它位置清零

    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;    
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;    

    return 0;
}
a value(bit) is: 00000111
r value(bit) is: 00000010

2.3.2 图像处理

在图像处理领域,灰度图中的每一个像素的取值都是0-255,也就是8位的二进制数值。可以通过位与运算,可以提取、修改或合并图像中个灰度值。
在图像处理领域,彩色图像是由RGB 3个颜色通道组成,每一个颜色通道和灰度图一样,3个颜色通道组成了一张彩色图像。同样可以通过位与运算,可以提取、修改或合并图像中的颜色分量。

3.或(|)

3.1 简介

C++中的或运算是一种基本的逻辑运算方式,符号表示为“|”。它按照二进制位进行运算,当参与运算的两个二进制位中只要有一个为1时,所得结果的该位数字即为1;当两个二进制位都为0时,所得结果的该位数字才为0。这种运算方式在处理二进制数据时非常有用,特别是在需要进行数据位操作的场景中。

3.2 示例

/*
 * @brief:    Bitwise operation
 * @compile:  g++ -g bm_main.cc -o d -std=c++11
 * @author:   your name
 * @date:     2024/10/12
 * @lastEditorDate:
 */

#include <iostream>

void printBinary(int num) 
{ 
    const int bit_count = 8;   //为方便这里采用了8位
    for (int i = bit_count - 1; i >= 0; --i)
    {
        
        if ((num >> i) & 1) 
        {
            std::cout << '1';
        }
        else 
        {
            std::cout << '0';
        }
    }
}

int main(int argc, char* argv[])
{
    int a = 7;
    int b = 6;
    int r = a | b;

    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;

    std::cout << "a & b value is: : " << r << std::endl;

    return 0;
}

输出

a value(bit) is: 00000111
b value(bit) is: 00000110
r value(bit) is: 00000111
a & b value is: : 7

3.3 应用

应用领域主要包括特殊场景下的计算、图像处理和加密领域的优化算法等。

4.异或(^)

4.1 简介

C++中的异或运算是一种基本的位运算,符号为“^”。异或运算的规则是:当两个操作数的对应位不同时,结果为1;当两个操作数的对应位相同时,结果为0。这种运算方式在处理二进制数据时非常有用,特别是在需要进行数据位操作的场景中。

4.2 示例

/*
 * @brief:    Bitwise operation
 * @compile:  g++ -g bm_main.cc -o d -std=c++11
 * @author:   your name
 * @date:     2024/10/12
 * @lastEditorDate:
 */

#include <iostream>

void printBinary(int num) 
{ 
    const int bit_count = 8;   //为方便这里采用了8位
    for (int i = bit_count - 1; i >= 0; --i)
    {
        
        if ((num >> i) & 1) 
        {
            std::cout << '1';
        }
        else 
        {
            std::cout << '0';
        }
    }
}


int main(int argc, char* argv[])
{
    int a = 10;
    int b = 18;
    int r = a ^ b;

    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;

    std::cout << "a & b value is: : " << r << std::endl;

    return 0;
}

输出

a value(bit) is: 00001010
b value(bit) is: 00010010
r value(bit) is: 00011000
a & b value is: : 24

在输出的结果中可以看出,当两个操作数的对应位不同时,结果为1;当两个操作数的对应位相同时,结果为0,相同的情况包括全部为0,或者全部为1。

4.3 应用

4.3.1 交互两个变量的值

计算的关键

//a和b交互数值
a = a ^ b;
b = a ^ b;
a = a ^ b;
int main(int argc, char* argv[])
{
    int a = 10;
    int b = 18;

    std::cout << "raw data" << std::endl;
    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl << std::endl;

    std::cout << "calculation process" << std::endl;
    a = a ^ b; // a现在为a与b的异或结果  
    std::cout << "a[a ^ b] value(bit) is: "; printBinary(a); std::cout << std::endl;
    b = a ^ b; // b现在为a的原始值(因为a现在是a^b,所以a^b^b=a)  
    std::cout << "b[a ^ b] value(bit) is: "; printBinary(b); std::cout << std::endl;
    a = a ^ b; // a现在为b的原始值(因为b现在是a,所以a^a^b=b)  
    std::cout << "a[a ^ b] value(bit) is: "; printBinary(a); std::cout << std::endl<<std::endl;

    std::cout << "result" << std::endl;
    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl << std::endl;
        

    std::cout << "a value is: : " << a << std::endl;
    std::cout << "b value is: : " << b << std::endl;

    return 0;
}

输出

raw data
a value(bit) is: 00001010
b value(bit) is: 00010010

calculation process
a[a ^ b] value(bit) is: 00011000
b[a ^ b] value(bit) is: 00001010
a[a ^ b] value(bit) is: 00010010

result
a value(bit) is: 00010010
b value(bit) is: 00001010

a value is: : 18
b value is: : 10

4.3.2 检测奇偶数

关键:
异或运算还可以用于检测一个整数是奇数还是偶数。由于偶数的二进制表示中最低位(即最右边的位)总是0,而奇数的最低位总是1,因此可以通过将该整数与1进行异或运算,然后判断结果是否为0来判断该整数是奇数还是偶数。如果结果为0,则该整数为偶数;如果结果不为0,则该整数为奇数。


int main(int argc, char* argv[])
{
    int a = 11;
    int b = 18;

    std::cout << "raw data" << std::endl;
    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;  
    int r = a ^ 1;
    std::cout << "1 value(bit) is: "; printBinary(1); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;

    if ((a ^ 1) == 0) 
    {
        std::cout << a << " is even." << std::endl;
    }
    else {
        std::cout << a << " is odd." << std::endl;
    }    

    std::cout << std::endl;
    std::cout << "raw data" << std::endl;
    std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
    r = b ^ 1;
    std::cout << "1 value(bit) is: "; printBinary(1); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;

    if ((b ^ 1) == 0)
    {
        std::cout << b << " is even." << std::endl;
    }
    else {
        std::cout << b << " is odd." << std::endl;
    }

    return 0;
}

输出

raw data
a value(bit) is: 00001011
1 value(bit) is: 00000001
r value(bit) is: 00001010
11 is odd.

raw data
b value(bit) is: 00010010
1 value(bit) is: 00000001
r value(bit) is: 00010011
18 is odd.

5.非(~)

5.1 简介

位运算非用符号“~”表示,用于对一个整型或字符型数据进行按位取反操作。当应用于整数时,按位取反运算符会将整数的二进制表示中的每一位取反,即将0变为1,将1变为0。

这里只介绍正数的按位取反结果,因为负数的按位取反结果涉及到补码表示和整数的存储方式,比较复杂。

5.2 示例

int main(int argc, char* argv[])
{
    int a = 10;
    int r = ~a;

    std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
    std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl << std::endl;

    std::cout << "a value is: : " << a << std::endl;
    std::cout << "r value is: : " << r << std::endl;

    return 0;
}

输出

a value(bit) is: 00001010
r value(bit) is: 11110101

a value is: : 10
r value is: : -11

这里的取反为什么是-11???

这里先说两个概念,1.是有符号和无符号的整数表示;2.是负数的表示方法

I. 有符合和无符号 以8位为例,有符合其表示的数值范围为:0-255;无符号其表示的数值范围为:-128-127;对于有符号而言,通常使用最高位(最左边的位)作为符号位,0表示正数,1表示负数。剩余的7位用于表示数值的大小。
正数的范围是从0到 2^7 −1(因为符号位为0,所以只有7位用于表示数值):

  • 最小值:0
  • 最大值:127 负数的范围是从 −2^7 到 -1(因为符号位为1,所以7位表示的是正数,但整体表示负数):
  • 最小值:-128
  • 最大值:-1 所以,8位有符号二进制数的表示范围是:-128到127。

II.负数的表示 在计算机科学中,负数通常使用二进制补码来表示。补码表示法是一种用于表示有符号整数的二进制编码方式,它允许使用相同的位数来表示正数和负数,并且使得加法、减法和乘法等运算在二进制层面上的实现更为简单和统一。

以下是负数使用二进制补码表示的步骤:

  1. 确定整数的位数:首先,需要确定用来表示整数的二进制位数,比如8位、16位、32位或64位等。这个位数决定了能够表示的最大正数和最小负数。
  2. 找到正数的二进制表示:对于要表示的负数,先找到其绝对值的二进制表示。例如,如果要表示-5,先找到5的二进制表示。
  3. 按位取反:将正数的二进制表示的每一位都翻转(即将0变为1,将1变为0)。这一步相当于对该数进行了一次按位取反操作。
  4. 加1:在按位取反的结果上加1。这一步是将上一步得到的反码转换为补码。
  5. 结果:得到的结果就是该负数在补码表示法下的二进制编码。

例如,用8位二进制数来表示-5:
5的二进制表示是 00000101。
按位取反后得到 11111010。
在 11111010 上加1,得到 11111011。
因此,-5在8位二进制补码表示法下是 11111011。


int main(int argc, char* argv[])
{
    int a1 = 0;
    int a2 = 1;
    int a3 = 127;
    int a4 = -1;
    int a5 = -127;
    int a6 = -128;
    
    std::cout << "a1[   0] value(bit) is: "; printBinary(a1); std::cout << std::endl;
    std::cout << "a2[   1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
    std::cout << "a3[ 127] value(bit) is: "; printBinary(a3); std::cout << std::endl;
    std::cout << "a4[  -1] value(bit) is: "; printBinary(a4); std::cout << std::endl;
    std::cout << "a5[-127] value(bit) is: "; printBinary(a5); std::cout << std::endl;
    std::cout << "a6[-128] value(bit) is: "; printBinary(a6); std::cout << std::endl;

    return 0;
}

输出

a1[   0] value(bit) is: 00000000
a2[   1] value(bit) is: 00000001
a3[ 127] value(bit) is: 01111111
a4[  -1] value(bit) is: 11111111
a5[-127] value(bit) is: 10000001
a6[-128] value(bit) is: 10000000

-127 是127[01111111]取反等于 10000000,再加1,等于10000001;
-128 是128 [10000000] 取反等于 01111111,再加1,等于10000000;

这里解释为什么10取反之后是-11 10的二进制为 00001010 取反之后位 11110101
根据负数的表示方法,11110101减1等于11110100,再取反等于
00001011,00001011表示的是11,所以10取反之后是-11

5.3 应用

按位取反运算符常用于位运算中,如掩码操作、位翻转等。此外,在某些算法中,按位取反运算符也可以用于实现特定的逻辑功能。

6.左移(<<)

6.1 简介【用于无符号数据】

左移位运算是C++中的一种位运算操作符,符号为 <<。它主要用于将一个数的二进制表示向左移动指定的位数,右边空出的位用0填充。左移位运算通常用于快速乘以2的幂次(即乘以2、4、8、16等)。

左移位运算的操作方式是将一个整数的二进制位向左移动若干位,右边空出的位用0填充。例如,如果你有一个整数 x,并且你将其左移 n 位,其效果等同于 x * 2^n(在不考虑溢出的情况下)。

6.2 示例


int main(int argc, char* argv[])
{
    int a1 = 9;
    int a2 = a1 << 1;
    int a3 = a1 << 2;
    int a4 = a1 << 3;

    std::cout << "a1: " << a1 << std::endl;
    std::cout << "a2: " << a2 << std::endl;
    std::cout << "a3: " << a3 << std::endl;
    std::cout << "a4: " << a4 << std::endl;
    

    std::cout << "a1[       ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
    std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
    std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
    std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
    

    return 0;
}

输出

a1: 9
a2: 18
a3: 36
a4: 72
a1[       ] value(bit) is: 00001001
a2[a1 << 1] value(bit) is: 00010010
a3[a1 << 2] value(bit) is: 00100100
a4[a1 << 3] value(bit) is: 01001000

6.3 应用

  • 快速乘法:左移位运算可以用来快速计算2的幂次。例如,x << 3 相当于 x * 2^3,即 x * 8。
  • 位掩码操作:在处理位掩码时,左移位运算可以用来设置或清除特定位。
  • 算法优化:在某些算法中,左移位运算可以用来代替乘法运算,从而提高性能。

注意:

  • 左移位操作可能会导致数据溢出,特别是当移位后的结果超过了数据类型的最大范围时。
  • 对于有符号整数,左移位操作的行为是未定义的(对于超出数据类型的位数进行左移)。因此,通常建议对无符号整数进行左移位操作。

7.右移(>>)

7.1 简介

右移位运算是C++中的另一种位运算操作符,符号为 >>。它主要用于将一个数的二进制表示向右移动指定的位数,左边空出的位根据数的符号进行填充:对于无符号数,左边空出的位用0填充;对于有符号数,左边空出的位通常用符号位(即最左边的位,0表示正数,1表示负数)的值来填充,这称为算术右移。

右移位运算的操作方式是将一个整数的二进制位向右移动若干位,左边空出的位根据数的类型进行填充。例如,如果你有一个整数 x,并且你将其右移 n 位,其效果等同于 x / 2^n(在不考虑舍入和溢出的情况下,且对于无符号数或算术右移的有符号数)。

7.2 示例

无符号


int main(int argc, char* argv[])
{
    int a1 = 9;
    int a2 = a1 >> 1;
    int a3 = a1 >> 2;
    int a4 = a1 >> 3;
    int a5 = a1 >> 4;
    int a6 = a1 >> 5;

    std::cout << "a1: " << a1 << std::endl;
    std::cout << "a2: " << a2 << std::endl;
    std::cout << "a3: " << a3 << std::endl;
    std::cout << "a4: " << a4 << std::endl;
    std::cout << "a5: " << a5 << std::endl;
    std::cout << "a6: " << a6 << std::endl;
    

    std::cout << "a1[       ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
    std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
    std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
    std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
    std::cout << "a5[a1 << 4] value(bit) is: "; printBinary(a5); std::cout << std::endl;
    std::cout << "a6[a1 << 5] value(bit) is: "; printBinary(a6); std::cout << std::endl;
    

    return 0;
}

输出

a1: 9
a2: 4
a3: 2
a4: 1
a5: 0
a6: 0
a1[       ] value(bit) is: 00001001
a2[a1 << 1] value(bit) is: 00000100
a3[a1 << 2] value(bit) is: 00000010
a4[a1 << 3] value(bit) is: 00000001
a5[a1 << 4] value(bit) is: 00000000
a6[a1 << 5] value(bit) is: 00000000

有符号


int main(int argc, char* argv[])
{
    int a1 = -9;
    int a2 = a1 >> 1;
    int a3 = a1 >> 2;
    int a4 = a1 >> 3;
    int a5 = a1 >> 4;
    int a6 = a1 >> 5;

    std::cout << "a1: " << a1 << std::endl;
    std::cout << "a2: " << a2 << std::endl;
    std::cout << "a3: " << a3 << std::endl;
    std::cout << "a4: " << a4 << std::endl;
    std::cout << "a5: " << a5 << std::endl;
    std::cout << "a6: " << a6 << std::endl;
    

    std::cout << "a1[       ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
    std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
    std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
    std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
    std::cout << "a5[a1 << 4] value(bit) is: "; printBinary(a5); std::cout << std::endl;
    std::cout << "a6[a1 << 5] value(bit) is: "; printBinary(a6); std::cout << std::endl;
    

    return 0;
}

输出

a1: -9
a2: -5
a3: -3
a4: -2
a5: -1
a6: -1
a1[       ] value(bit) is: 11110111
a2[a1 << 1] value(bit) is: 11111011
a3[a1 << 2] value(bit) is: 11111101
a4[a1 << 3] value(bit) is: 11111110
a5[a1 << 4] value(bit) is: 11111111
a6[a1 << 5] value(bit) is: 11111111

7.3 应用

  • 快速除法:右移位运算可以用来快速计算除以2的幂次。例如,x >> 3 相当于 x / 2^3,即 x / 8(对于无符号数或算术右移的有符号数)。
  • 位掩码操作:在处理位掩码时,右移位运算可以用来右移特定位,从而进行特定的位操作。
  • 算法优化:在某些算法中,右移位运算可以用来代替除法运算,从而提高性能。

注意

  • 对于有符号整数,右移位操作可能是算术右移(用符号位填充)或逻辑右移(用0填充),这取决于编译器和平台。在C++标准中,对于有符号数的右移位操作,算术右移是首选行为,但逻辑右移也是允许的。因此,为了编写可移植的代码,最好避免对有符号数进行右移位操作,除非你确定平台的行为。
  • 右移位操作也可能导致数据溢出或符号扩展问题,特别是当移位后的结果超出了数据类型的范围时。
  • 对于无符号整数,右移位操作总是用0填充左边空出的位。

原文地址:https://blog.csdn.net/shouhu010/article/details/142870051

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!