位运算符详解,交换、修改指定位、整形提升都藏在比特位中

前言

主要参考 《C语言深度剖析第二版》也结合自己的部分感受,分享出这篇博客,主要面向C/C++ 方向的一点基础素养和良好注释习惯。
所有代码均使用VS2019编译运行,经测试没有问题。

位运算符

  • | 按位 或,二进制位按照每一位进行 或 运算,得到最后结果(二进制数)
  • & 按位 与,二进制位按照每一位进行 与 运算,得到最后结果(二进制数)
  • ^ 按位 异或,二进制位按照每一位进行 异或 运算(不同为1,相同为0),得到最后结果(二进制数)
  • ~ 按位 逻辑反,二进制位按照每一位进行 取反 运算
#include <stdio.h>


int main()
{
	printf("%d\n", 2 | 3);	// 10|11 -> 11
	printf("%d\n", 2 & 3);	// 10&11 -> 10
	printf("%d\n", 2 ^ 3);	// 10^11 -> 01
    //按位 取反,符号位也会参与取反
	printf("%d\n", ~0);		// 00000000 -> 11111111 对应的就是十进制的-1

/*
运行结果:
		3
		2
		1
		-1
*/
    return 0;
    
}

异或运算:的基本运算规则

#include <iostream>

using namespace std;

int main()
{
    /*按位异或运算:
	* 0100
	* 0011
	* --------
	* 0111  -> 将二进制转换为十进制,由于最高位为0,表示正数,就是 十进制的7
	*/
	cout << (4 ^ 3) << "\n";	//要加 括号,否则会识别不清
    
    /*
输出结果为:7
*/
    return 0;
}

任何数据域0 异或的结果都是本身,即与 0 异或都可以保留其本身

异或运算支持交换律和结合律,比如在下面的异或结果,都是相同的:

printf("%d\n",5^4^5);	//交换律
printf("%d\n",5^5^4);	//结果为4
printf("%d\n",5^(4^5);	//结合律

巧妙的交换操作

#include <iostream>
using namespace std;

int main()
{
    //交换两个变量 的值
	int a = 10;
	int b = 20;
	cout << "before:  a = " << a << "\tb = " << b<<"\n";
	a = a + b;
	b = a - b;
	a = a - b;
	cout << "after:  a = " << a << "\tb = " << b << "\n";
    //使用这种方法会有一定的问题,并不是很推荐使用,因为如果a 和 b 是两个大整数,则他们的和是有可能会溢出的,如果这样在进行一下的操作,必然会造成精度缺失(截断),造成结果不符合预期。(目前VS编译器会自动屏蔽这个问题,如果使两数之和超过int(四个字节对应十进制:2147483647)的最大值,并不会出现溢出问题)
    
    //使用异或 实现数据交换
    a = a ^ b;
	b = a ^ b;	//这里可以理解成是 b= a^b^b; 就是将a 等价替换下来,这里根据结合律,就是 a^0(因为相同的异或为0),得到的结果就是 b=a;
	a = a ^ b;	//这里就可以理解成 a= a^b (a^b^b); 就是将上面的a 和 b 等价下来,由上就是 a=a^b^a,得到的就是 a=b;
    
    return 0;
}

使用 异或 运算,进行交换,由于异或 不会产生进位,所以不会发生溢出问题

这里要搞清楚一个概念,就是数据的溢出截断 的产生,数据在进行运算的时候,都是在CPU 内部完成的,或者说是数据是暂时存放到 寄存器中,然后由 CPU 进行运算,在计算完成之后就有可能会产生 溢出 问题(超出原数据的比特位),在计算完成后,再次写入内存的时候,由于比特位 与原 比特位不同,原比特位无法存放这些内容,就会发生 截断

位运算在使用时需要用宏定义好后,再进行使用

将指定比特位设为1

#include <iostream>

//任何数字与0 进行 按位或( | ) 运算 都保持不变
//任何数字与1 进行 按位或( | ) 运算 都被设置为1
//包含参数的宏定义,参数为变量 和 该变量的第n位比特位
#define SETBIT(x, n) ((x) |= (1<<(n-1)))	//指定比特位为1,如果要修改第n位,就要左移 n-1位,修改那一位
using namespace std;
void showBits(int x)
{
    //大小*每个字节的比特数就可得到比特数,但是要涉及左移 操作,要注意左移零位,就是最低位的比特位
    int num = sizeof(x)* 8 - 1;
    while (num >= 0)
    {
        if (x & (1<<num))
        {
        	printf("1 ");
    	} 
        else
        {
    		printf("0 ");
    	} 
        num--;
    } 
    printf("\n");
}

int main()
{
    int x=0;
    //设置指定比特位为1
    SETBIT(x,5);
    //设置显示int 的左右比特位
    showBits(x);
    
    return 0;
}

将指定比特位设为0

//将上述代码中的 宏定义 进行修改即可
#define CLRBIT(x,n) (x &= (~(1<<(n-1) )))

将上述过程,合并到同一段代码中,就有如下程序:

#include <iostream>

//将指定比特位 置 1
#define SETBIT(x, n) ((x) |= (1<<(n-1)))
//将指定比特位 置 0
#define CLRBIT(x,n) (x &= (~(1<<(n-1) )))

using namespace std;
void showBits(int x)
{
    //大小*每个字节的比特数就可得到比特数,但是要涉及左移 操作,要注意左移零位,就是最低位的比特位
    int num = sizeof(x)* 8 - 1;
    while (num >= 0)
    {
        if (x & (1<<num))
        {
        	printf("1 ");
    	} 
        else
        {
    		printf("0 ");
    	} 
        num--;
    } 
    printf("\n");
}

int main()
{
    int x=0;
    //设置指定比特位为1
    SETBIT(x,5);
    //设置指定比特位为0
    CLRBIT(x,5);
    
    //设置显示int 的左右比特位
    showBits(x);
    
/*
运行结果:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
*/
    
    return 0;
}

整形提升问题

先看这段代码:

#include <stdio.h>
#include <windows.h>
int main()
{
    char c = 0;
    printf("sizeof(c): %d\n", sizeof(c)); 			//1
    printf("sizeof(c): %d\n", sizeof(~c)); 			//4
    printf("sizeof(c): %d\n", sizeof(c << 1));	 	//4
    printf("sizeof(c): %d\n", sizeof(c >> 1)); 		//4
    
    //在 VS中,下式得到的结果是 1,在gcc 编译器下就是 4(整形提升)
     printf("sizeof(c): %d\n", sizeof(!c)); 		//1
    
    system("pause");
    return 0;
}

出现这样的问题,就是因为整形提升 导致的,因为 char 在进行运算的时候,并不是使用一个字节进行计算的,而是与 int 一样,是四个字节!

无论任何运算符,最终都是在计算机中进行计算的,而计算机中只有 CPU 具有运算能力,而且计算的数据都在内存汇总,所以在 进行计算之前,所有的数据都必须要拿到CPU中,就是要拿到 寄存器中,等待使用 CPU 进行计算,而寄存器由于计算机位数的不同,寄存器的位数也有不同,一般在 32位下,寄存区的位数是32位,所以 char 类型(只有8字节),在放入寄存器中时,就会进行填补,使其 变为 32 位,这个过程就是 整形提升 而且这个过程并不是在 程序运行期间完成的,而是在程序编译期间就完成的

最后

感谢观赏,一起提高,慢慢变强

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值