位运算符

位运算符

所谓的位运算其实是就是针对于二进制数据的运算。在 C 语言中,常用的位运算符有以下几种:

  • 按位与运算符:&
  • 按位或运算符:|
  • 按位异或运算符:^
  • 按位取反运算符:~
  • 左移运算符:<<
  • 右移运算符:>>

按位与

按位与运算(&)是对两个操作数的每个二进制位进行与操作,只有当对应位上的两个操作数都为1时,结果位才为1,否则为0。

关于按位与的特点可以精简为一句话:0 和任何数(0和1)都为 0。

int a = 2;
int b = 4;
int result = a & b;
  • 整形数 2 对应的二进制数是10
  • 整形数 4 对应的二进制数是100
  010
& 100
------
  000

通过按位与操作,我们最终得到的结果为 0。

使用按位与可以快速判断整数的奇偶性,奇数的二进制表示的最低位为1,偶数的二进制表示的最低位为0。示例:

int num = 9;
if (num & 1) 
{
   // num 是奇数
} 
else 
{
   // num 是偶数
}

按位或

按位或运算(|)是对两个操作数的每个二进制位进行或操作,只要对应位上的两个操作数中至少有一个为1,结果位就为1,否则为0。

关于按位或的特点可以精简为一句话:1 和任何数(0和1)都为 1。

int a = 2;
int b = 4;
int result = a | b;
  • 整形数 2 对应的二进制数是10
  • 整形数 4 对应的二进制数是100
  010
| 100
------
  110

通过按位或操作,我们最终得到的二进制结果为 110,也就是十进制的 6。

按位异或

基本使用

按位异或运算(^)是对两个操作数的每个位进行异或操作,当对应位上的两个操作数不同时,结果位为1,否则为0。

关于按位异或的特点可以精简为一句话:相同为0,不同为 1。

int a = 2;
int b = 6;
int result = a ^ b;
  • 整形数 2 对应的二进制数是10
  • 整形数 4 对应的二进制数是110
  010
^ 110
------
  100

通过按位异或操作,最终得到的二进制结果为 100,也就是十进制的 4。

实际应用

按位异或(^)在实际应用中有以下两个常见用途(第二个个例子有循环, 可以在学完后面的知识点之后,再返回来消化吸收一下):

  1. 交换两个变量的值:通过按位异或可以交换两个变量的值,而不需要使用临时变量。示例:

    int a = 5;
    int b = 7;
    a = a ^ b;
     b = a ^ b;
    a = a ^ b;
    // 现在 a = 7, b = 5
    
  2. 加密与解密:按位异或可用于简单的加密和解密算法。通过将数据与一个密钥按位异或,可以对数据进行加密。同样地,将加密后的数据再与密钥按位异或,可以解密数据。示例:

    unsigned char data[] = {0x45, 0x23, 0x7F, 0x18};
    unsigned char key = 0xAB;
    // 加密
    for (int i = 0; i < sizeof(data); i++) 
    {
        data[i] = data[i] ^ key;
    }
    
    // 解密
    for (int i = 0; i < sizeof(data); i++) 
    {
        data[i] = data[i] ^ key;
    }
    

这些是按位异或运算符在实际应用中的几个常见用途,但也可以根据具体需求进行其他更复杂的运算和处理。按位异或操作的特性使得它在某些场景下非常有用,可以提供高效且简单的实现方式。

按位取反

按位取反运算(~)是对操作数的每个位进行取反操作,将1变为0,将0变为1。

int a = 1;
int b = 5;
a = ~a;
b = ~b;

接下来,我们将十进制数值转换为二进制进行分析:

// 十进制的 1
~01 = 1111 1111 1111 1111 1111 1111 1111 1110
补码: 1111 1111 1111 1111 1111 1111 1111 1110
// 原码 = 补码的数据位取反 + 1 (符号位不变)
原码: 1000 0000 0000 0000 0000 0000 0000 0010
    
===============================================
    
// 十进制的 5
~101 =  1111 1111 1111 1111 1111 1111 1111 1010
补码: 1111 1111 1111 1111 1111 1111 1111 1010
// 原码 = 补码的数据位取反 + 1 (符号位不变)
原码: 1000 0000 0000 0000 0000 0000 0000 0110

根据上面的推导就可以得到如下结果:a = -2, b = -6

左移和右移

左移运算(<<)是将一个数的二进制表示向左移动指定的位数,右侧空出的位用0填充。

右移运算(>>)是将一个数的二进制表示向右移动指定的位数,左侧空出的位根据类型来填充:

  • 对于有符号数,使用算术右移,用符号位填充左侧空位
  • 对于无符号数,用 0 填充左侧空位
int main()
{
    int a = 6;
    int b = 32;
    int c = -64;
    a = a << 2;
    b = b >> 2;
    c = c >> 3;   
    printf("a = %d, b = %d, c = %d\n", a, b, c);
    return 0;
}

程序输出的结果为:

a = 24, b = 8, c = -8
  • 对于正数,右移一位相当于除以 2,向左移一位相当于乘以 2
  • 在右移操作中,负数右移一位并不严格等于除以2,而是依赖于具体的编译器实现方式,因为 C 语言中没有明确规定。一种常见的实现方式是使用算术右移,即保留符号位并用符号位填充左侧空出的位。因此,负数右移一位并不一定等于除以2。
  • 对于除以2的操作,除非对被除数是无符号整数,否则建议使用除法运算符(/)来达到目的。
posted @ 2023-09-08 16:01  ihuahua1415  阅读(59)  评论(0)    收藏  举报
*/