五、位运算

​ 位运算主要计算内存中每个小格的数据

1、输出二进制内容
头文件调用 语法 示例
include <bitser> std::bitset<要显示的二进制位数>(要显示的变量) std::cout << std::bitset<16>(a);
//二进制内容输出
#include <iostream>
#include <bitset>

int main()
{
    int a{ 0b100000001100 };
    std::cout << std::bitset<16>(a);  
}

2、左移、右移

1)左移:左移相当于*2,即a*(2n),如a左移2位,即a*(22)

#include <iostream>
#include <bitset>

int main()
{
    int a{ 0b100000001100 };
    std::cout <<"左移8位前:" << std::bitset<16>(a) << std::endl;

    a <<= 8;  //a=a<<8
    std::cout <<"左移8位前:"<< std::bitset<16>(a)<<std::endl;
}

2)右移:右移相当于/2,即a/(2n),如a右移2位,即a/(22)

#include <iostream>
#include <bitset>

int main()
{
    int a{ 0b100000001100 };
    std::cout <<"左移8位前:" << std::bitset<16>(a) << std::endl;

    a <<= 8;  //a=a<<8    左移8位
    std::cout <<"左移8位前:"<< std::bitset<16>(a)<<std::endl;

    a >>= 8;  //右移8位
    std::cout << "右移8位后:" << std::bitset<16>(a) << std::endl;

}

注:位运算左右移,正数补0,负数补1

#include <iostream>
#include <bitset>

int main()
{
    //int a{ 0b100000001100 };
    int a{ -3 };
    std::cout << a << std::endl;
    std::cout << "左移8位前:" << std::bitset<16>(a) << std::endl;

    a <<= 8;  //a=a<<8    左移8位
    std::cout << "左移8位前:" << std::bitset<16>(a) << std::endl;

    a >>= 8;  //右移8位
    std::cout << "右移8位后:" << std::bitset<16>(a) << std::endl;

}

注:在某些CPU下,*2操作会自动转化为位运算,因位运算运算较快。位运算时最好用unsigned类型,因为unsigned类型,在任何CPU下一定是补1,而int类型可能补0或者补1

3、求反运算、与运算

1)求反运算(~):按位取反,0变1、1便0

#include <iostream>
#include <bitset>

int main()
{
	int test{ 0xFFFF };
	std::cout << "求反之前:"<< std::bitset<32>(test) << std::endl;

	test = ~test; //求反
	std::cout << "求反之后:"<< std::bitset<32>(test) << std::endl;
}

2)与运算(&):见0为0,一般用于保留高位

//获取地址高位
#include <iostream>
#include <bitset>

int main()
{
	using std::showbase;   //显示八进制和十六进制前缀
	int test{ 0x2833 };
	std::cout << "正常  输出:" << std::hex<<showbase<<test << std::endl;

	test = test & 0xFF00; //与运算

	std::cout << "与运算之后:" << std::hex <<showbase<<test<< std::endl;
}

4、或运算、异或运算

1)或运算( | ):见1则为1,常用于,只要灯没打开,就把它打开

#include <iostream>
#include <bitset>

//直接将0x2833,调整位0xFF33,即将高位写满
int main()
{
	using std::showbase;   //显示八进制和十六进制前缀
	int test{ 0x2833 };
	std::cout << "正常  输出:" << std::hex << showbase << test << std::endl;

	test = test | 0xFF00; //或运算

	std::cout << "或运算之后:" << std::hex << showbase << test << std::endl;
}

2)异或运算( ^ ):相同为0,不同为1

#include <iostream>
#include <bitset>

int main()
{
	using std::showbase;   //显示八进制和十六进制前缀
	int test{ 0x2833 };
	std::cout << "正常  输出:" << std::hex << showbase << test << std::endl;

	test = test ^ 0xFF00; //异或运算

	std::cout << "或运算之后:" << std::hex << showbase << test << std::endl;
}

5、案例:完善游戏激活码,需求如下

1)我们收到了一位用户的激活请求,用户的硬件码为:930529060092641281,我们需要回馈给用户一个激活码,该激活码是N组16进制数,每组4位。例如:192E-987E-675E-3E98-9172-972E。设计我们游戏中的激活部分程序,通过输出该激活码可以激活用户的游戏,

2)激活码中包含了16个等级的初始金币礼包,计算方式为:等级*等级*10000,包含了用户初始幸运属性,分为16个等级,这将影响到用户日后游戏中的爆率。

3)我们还赠送了三件装备,分别是武器、护甲、首饰、这三件装备等级分为16个阶,适用不同的等级阶段,最高可以强化至16,比如武器,可以是16阶强化+16。根据激活码对用户初始装备进行强化

4)同时要求我们的激活码还具备一定的防伪能力,能够与用户的硬件码相呼应,即在其他的电脑中不能使用(我们假设用户提交的硬件码是唯一的)

#include <iostream>

int main()
{
	long long mcode{ 930529060092641281 }; //用户提供的硬件码
	long long test_code{ (int)0xF2349876EF56CA24LL }; //内置测试码
	long long end = mcode ^ test_code;  //第一次异或,计算出激活码
	//std::cout << end << std::endl;     
	std::cout << "给予用户的激活码为:"<< std::hex << end << "2556595f" << std::endl;  //给用户的激活码,f316 18bf ef56 ca25 2556 595f

	//模拟用户输入激活码,1、2、3、4组代表机器码,5、6组代表用户初始属性
	unsigned short in_code1, in_code2, in_code3, in_code4, in_code5, in_code6;
	std::cin >> std::hex;    //因激活码为16进制数,因此切换输入流为16进制
	std::cout << "请输入第一组激活码:";
	std::cin >> in_code1;
	std::cout << "请输入第二组激活码:";
	std::cin >> in_code2;
	std::cout << "请输入第三组激活码:";
	std::cin >> in_code3;
	std::cout << "请输入第四组激活码:";
	std::cin >> in_code4;
	std::cout << "请输入第五组激活码:";
	std::cin >> in_code5;
	std::cout << "请输入第六组激活码:";
	std::cin >> in_code6;

	//将用户输入的激活码,按照每组进行取出
	long long end_final{};
	long long ls{}; //临时变量
	ls = in_code1;
	ls <<= 48;      //获取到第一组激活码
	//std::cout << ls << std::endl;  //f316000000000000
	end_final |= ls;

	ls = in_code2;
	ls <<= 32;
	end_final |= ls;    
	//std::cout << ls << std::endl; //18bf00000000

	ls = in_code3;
	ls <<= 16;
	//std::cout << ls << std::endl; //ef560000
	end_final |= ls;
	end_final |= in_code4;
	std::cout <<"取出的用户激活码为:"<< end_final << std::endl;  //将用户输入的激活码,完整取出

	long long mcode_calc = end_final ^ test_code;     //将用户输入的激活码和游戏内置测试码进行比较,若计算结果不是用户机器码,则不予激活
	std::cout << "计算出的用户机器码为:"<<std::dec << mcode_calc << std::endl;

	if (mcode_calc == mcode)
	{
		std::cout << "你输入的激活码正确,游戏激活成功!!!\n";
		//f316 18bf ef56 ca25 2556 595f
		//定义第1位至8为分别代表金币礼包等级、幸运等级、武器的阶、武器的强化等级、护甲的阶、护甲的强化等级、首饰的阶、首饰的强化等级、
		unsigned gmoney{};   //金币礼包等级
		gmoney = (in_code5 & 0xF000) >> 12;  //0x2000
		gmoney *= gmoney;
		gmoney *= 10000;
		std::cout << "你获得的初始金币为:" << gmoney << std::endl;
		
		char gluck{};  //角色幸运等级
		gluck = (in_code5 & 0x0F00) >> 8;  //0x0500
		std::cout << "你的幸运值为:" << (short)gluck << std::endl;

		char weapon_lv; //武器的阶
		weapon_lv = (in_code5 & 0x00F0) >> 4;
		std::cout << "你的武器阶为::" << (short)weapon_lv << std::endl;

		char weapon_ev; //武器的强化
		weapon_ev = (in_code5 & 0x000F) ;
		std::cout << "你的武器阶为::" << (short)weapon_ev << std::endl;

		char army_lv{}; //护甲的阶
		army_lv = (in_code6 & 0xF000) >> 12;
		std::cout << "你的护甲阶为::" << (short)army_lv << std::endl;

		char army_ev{}; //护甲的等级
		army_ev = (in_code6 & 0x0F00) >> 8;
		std::cout << "你的护甲等级为:" << (short)army_ev << std::endl;

		char neck_lv{}; //首饰的阶
		neck_lv = (in_code6 & 0x00F0) >> 4;
		std::cout << "你的首饰阶为:" << (short)neck_lv << std::endl;

		char neck_ev{}; //首饰的等级
		neck_ev = (in_code6 & 0x000F);
		std::cout << "你的首饰等级为:" << (short)neck_ev + 1<< std::endl;
	}
	else
	{
		std::cout << "你输入的激活码错误,游戏激活失败!!!\n";
	}
}