前言
主要参考 《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 位,这个过程就是 整形提升 而且这个过程并不是在 程序运行期间完成的,而是在程序编译期间就完成的
最后
感谢观赏,一起提高,慢慢变强