C++中的位运算和操作符(运算符)重载 《从C语言过渡到C++和虚幻引擎中的C++编程》教程⑤

前言

在初学C++的时候,你们可能会困惑cout和cin为什么要用<<和>>这种奇怪的操作符号,其实<<和>>在C语言和C++中原本都是左移和友移的位运算符号。只是在中重载来当作输入输出符号罢了。

众所周知,学校里教的C语言非常皮毛,很多学校的C语言课连位运算可能都没讲。所以这里补充下C/C++中的位运算的知识。

位运算

按位与运算(运算符为&)

在高中的时候,我们学逻辑关系的时候,我们知道命题A且命题B(或者说命题A与B)也就是: A ⋀ B A\bigwedge B AB,只有在A和B都为真的时候才成立。
在计算机学科中,我们往往用1表示真命题,用0表示假命题。
那么就有:

  • 1 ⋀ 1 = 1 1\bigwedge 1=1 11=1
  • 1 ⋀ 0 = 0 1\bigwedge 0=0 10=0
  • 0 ⋀ 1 = 0 0\bigwedge 1=0 01=0
  • 0 ⋀ 0 = 0 0\bigwedge 0=0 00=0

而二进制数A与B进行按位与,就是让两个数的对应位上的二进制数字进行“且”的操作,而空位视为0.
例如:

10&01==0
11&1==1//第二个数字比第一个数字位数少一位,就把空位视为0和其进行“且”的操作
10001&100==0
11111&11==11

C++代码:

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 8, b = 1;
	int c = a & b;//a和b在计算机中是以二进制形式存储的,所以这里它会2和1转化为二进制表示后进行位运算
	cout << c<<endl;
}

输出:

0

按位或运算(运算符为|)

按位或,其实就是两个二进制数A和B的对应位数上的二进制数字进行“或"的操作,也就是对对应位数字进行 A ⋁ B A\bigvee B AB,空位视为0

⋀ \bigwedge 类似, ⋁ \bigvee 也有:

  • 1 ⋁ 1 = 1 1\bigvee 1=1 11=1
  • 1 ⋁ 0 = 1 1\bigvee 0=1 10=1
  • 0 ⋁ 1 = 1 0\bigvee 1=1 01=1
  • 0 ⋁ 0 = 0 0\bigvee 0=0 00=0

所以有

扫描二维码关注公众号,回复: 14893102 查看本文章
10|10==10
11|10==11
11|1==11
111110|1000=111110

C++代码:

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 2, b = 1;
	int c = a | b;//2以二进制表示为10,1的二进制是1,10|1==11,转化为十进制后为3
	cout << c<<endl;
}

输出:

3

按位异或(运算符为^)

按位异或,其实就是两个二进制数A和B的对应位数上的二进制数字进行比较,相同就为0,不同就为1。例如:

1^0==1
1^1==0
10111^11111==1000
11^1110==1101

C++代码:

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 3, b = 1;
	int c = a ^b;
	cout << c<<endl;
}

输出:

2

根据异或的性质,我们也不难发现一个定理:若a异或b=x,则a=b异或x,b=x异或a

取反(运算符~)

取反简单来说就是如果你是1我就设为0,如果你是0我就设为1
当然你要注意的是,计算机内存中数的存储不是说1就存个1

比如在C++中的int类型的数,它有四个字节(一个字节等于8位二进制数),也就是用32位的二进制数来保存,那么int类型的数1在内存空间中是这样存的:

00000000 00000000 00000000 00000001

我们把它进行按位取反就变成了:

11111111 11111111 11111111 11111110

这个数字是多少呢?
在告诉你答案前,你需要先知道int类型的负整数在内存空间是怎么存储的。

int类型的负整数在内存中以什么形式存储:

比如int类型的负整数-x,我们要在内存中表示的话有如下几步:

  • 取-x的绝对值x
  • 对x进行按位取反
  • 在按位取反后加上1

比如-1这个数,
第一步我们会取其绝对值1,也就是:

00000000 00000000 00000000 00000001

然后按位取反:

11111111 11111111 11111111 11111110

然后+1:

11111111 11111111 11111111 11111111

就得到了-1在内存中存储的形式。

int的最左边的第一位,是符号位,当其为0时,就为非负整数,为1时就为负整数。

那么我们刚刚看到的int类型的数:

11111111 11111111 11111111 11111110

是多少呢?

我们知道了负整数转化为int类型的存储方式,那我们发过来就是了。

先对它进行-1

11111111 11111111 11111111 11111101

再按位取反,翻转回来:

00000000 00000000 00000000 00000010

也就是2,再加上个负号,那么就成了-2。
所以那个数其实是-2

我们可以试试C++用对1进行按位取反,验证结果:

#include<iostream>
using namespace std;
int main()
{
    
    
   int a = 1;
   int c = ~a;
   cout << c<<endl;
}

输出:

-2

所以int类型的1取反后就等于-2

根据以上内容,我们还不难证明一个定理:
int类型的整数,在按取反后,总是等于其相反数-1
例如:2取反后是-3,3取反后是-4,4取反后是-5。-1取反后是0。

浮点数在计算机内存中存储的方式较为复杂,如果你也好奇浮点数的存储方式的话可以看看这篇文章:
浮点数在内存中的存储方式

左移(运算符为<<)

左移运算符就是把二进制数整体往左移动,空出来的位置用0填充。比如:

100<<2==10000//向左移动2位
110<<2==11000
1<<1==1//向左移动一位
1<<3=1000//向左移动三位

C++代码:

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 1;
	int c = a<<1;
	cout << c<<endl;
}

输出:

2

根据二进制数的性质,我们也不难发现一个数每被左移一位,数值就要扩大为原来的2倍数,即a<<1等于a*2

右移(运算符为>>)

右移运算符就是把二进制数整体往右移动,比如:

100>>2==1
1>>1==0
111>>3==0
10>>1=1

C++代码:

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 1;
	int c = a>>1;
	cout << c<<endl;
}

输出:

0

根据二进制数的性质,我们也不难发现一个数每被右移一位,数值就要缩小为原来的二分之一,即a>>1等于a/2

C++中的操作符重载(运算符重载)

C++中可以重载的操作符:
C++中可以重载的操作符
C++中不可以重载的操作符:
C++中不可以重载的操作符
使用操作符重载,我们可以定义新的运算方式,比如我们创建了向量类,就可以重载+操作符,让我们使用+号让两个向量轻松相加,而不需要写非常麻烦的函数去专门做这个事情,例如:

#include<iostream>
using namespace std;
class Vector3f
{
    
    
public:
	float x, y, z;
	Vector3f(float x, float y, float z) :
		x(x),
		y(y),
		z(z)
	{
    
    
	}
	//operator关键字表示这是操作符重载
	Vector3f operator +(const Vector3f& b)//const是C++中的一个语法糖,被其声明的变量不能在后续代码中被修改
	{
    
    
		Vector3f ans = Vector3f(b.x + x, b.y + y, b.z + z);
		return ans;
	}
};
int main()
{
    
    
	Vector3f a = Vector3f(1, 2, 3), b = Vector3f(4, 5, 6);
	Vector3f c = a + b;
	cout << c.x << " " << c.y << " " << c.z;
}

输出:

5 7 9

现在你应该能明白,cout和cin所使用的<<和>>操作符,其实是从左移和右移操作符重载来用的。

猜你喜欢

转载自blog.csdn.net/lifesize/article/details/128628102