位运算
位运算主要包括按位与(&)、按位或(|)、按位异或(^)、取反(~ )、左移(<<)、右移(>>)这几种,其中除了取反(~)以外,其他的都是二目运算符,即要求运算符左右两侧均有一个运算量。
进制转换
B:二进制数
O:八进制数
D:十进制数
H:十六进制数
(1)二进制转十进制
按权展开求和
(2)十进制转二进制
整数:除以2取余,逆序输出
小数:乘以2取整,顺序输出
其他进制同理(感性理解即可)
原码、补码和反码
这个博客讲的挺好的:https://www.cnblogs.com/author/p/8954127.html
原码
是计算机中一种对数字的二进制定点表示方法。原码表示法在数值前面前面有一位符号位(即最高位为符号位),正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。
原码是有符号数的最简单的编码方式,便于输入输出,但作为代码加减运算时较为复杂,故计算机一般不采用这种编码方式存储符号数。
反码
正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外
由于-0这个问题的存在,会使得计算机需要增加额外的物理硬件配合运算,所以在计算机发展的早期就已经抛弃了使用反码储存数据。
补码
正数和+0的补码是其原码,负数则先计算其反码,然后反码加上1,得到补码。
补码换算为原码的过程中,如果补码是正数或者+0的补码,则其原码就是补码本身;如果补码是负数或者-0的补码,则其原码的计算方法是,先将补码减掉1,得到反码,再将反码取反,得到原码。
按位与(&)
参加运算的两个数,换算为二进制(0、1)后,进行与运算。只有当相应位上的数都是1时,该位才取1,否则该位为0。
按位或(|)
参加运算的两个数,换算为二进制(0、1)后,进行或运算。只要相应位上存在1,那么该位就取1,均不为1,即为0。
按位异或(^)
参加运算的两个数,换算为二进制(0、1)后,进行异或运算。只有当相应位上的数字不相同时,该为才取1,若相同,即为0。
任何数与0异或,结果都是其本身。
异或还可以交换两个数
a = a ^ b;
b = b ^ a;
a = a ^ b;
取反(~)
参加运算的两个数,换算为二进制(0、1)后,进行取反运算。每个位上都取相反值,1变成0,0变成1。
左移(<<)
在二进制表示下把数字同时向左移动,低位以0填充,高位越界后舍弃
1 << n = 2 ^ n
n << 1 = 2n
右移(>>)
在二进制补码表示下把数字同时向右移动,高位以符号位填充,低位越界后舍弃(算术右移)
算术右移等于除以2向下取整
注意
c++中,两个数值执行算术运算时,以参与运算的最高数值类型为基准,与保存结果的变量类型无关,所以要用到强制转换。
快速幂
long long Pow(long long x, long long y)
{
long long t = 1;
while (y)
{
if (y & 1) t = t * x % mod;
y /= 2;
x = x * x % mod;
}
return t % mod;
}
快速乘
long long mul(long long x, long long y)
{
long long t = 0;
while (y)
{
if (y & 1) t = (t + x) % mod;
y /= 2;
x = (x + x) % mod;
}
return t % mod;
}
二进制状态压缩
指将一个长度为m的bool数组用一个m位二进制整数表示并存储的方法
操作 | 运算 |
---|---|
取出整数n在二进制表示下的第k位 | (n >> k) & 1 |
取出整数n在二进制表示下的第 0~k-1位 | n & ((1 << k) - 1) |
把整数n在二进制下的第k位取反 | n ^ (1 << k) |
对整数n在二进制下的第k位赋1 | n or (1 << k) |
对整数n在二进制下的第k为赋0 | n & (~(1 << k)) |
运算优先级
从高到低
! (逻辑非)、~(按位取反) |
---|
/(除)、*(乘)、%(取模) |
+((加)、-(减) |
<<(位左移)、>>(位右移) |
<(小于)、<=(小于等于)、>(大于)、>=(大于等于) |
==(恒等于)、!=(不等于) |
&(位与运算) |
^(位异或运算) |
or(位或运算) |
注:∧逻辑与,∨逻辑或
成对变换
当n为偶数时, n ^ 1 = n + 1
当n为奇数时, n ^ 1 = n - 1
lowbit运算
lowbit(n) 定义为非负整数 n 在二进制表示下“最低位的1及其后面所有的0”构成的数值
lowbit(n) = n & (~n + 1) = n & (-n)
(不行,放张zyl的图片洗洗脑子)
指针和引用
这篇博客:https://blog.csdn.net/weixin_42878758/article/details/82865314
指针 *
指针是指向变量的地址
简单的例子分析:
int main()
{
int a = 3;
int *b = &a;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
*b = 10;
cout << "&a:" << &a << endl;
cout << "b:" << b << endl;
cout << "a:" << a << endl;
system("pause");
}
结果:
a:3
b:00EFFE28
&a:00EFFE28
b:00EFFE28
a:10
分析:
b是a的指针,指向a的地址。(也就是a与b相连,只要修改*b的值,a的值也跟着改动)
引用 &
引用可说把a变量换了一个名字为b即:
&b=a
简单的例子分析:
int main()
{
int a = 3;
int &b = a;
int c = a;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
b = 10;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
cout << "&a:" << &a << endl;
cout << "&b:" << &b << endl;
cout << "&c:" << &c << endl;
system("pause");
}
结果:
a:3
b:3
c:3
a:10
b:10
c:3
&a:0019FD74
&b:0019FD74
&c:0019FD5C
分析:
& 引用 :比如说,一个人有多少外号,但都是指这个人,引用也是这样。如果b的值改动了,也就代表了a的值改动了。
(核心)函数的参数
1>函数传入普通参数
void fun(int a,int b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a = 1;
int b = 2;
cout << a << "," << b << endl;
fun(a, b);//a,b交换
cout << a << "," << b << endl;
system("pause");
return 0;
}
结果:
1,2
1,2
分析:
函数传入的是形参,不会改变main()中a,b的地址,也就是不会改变a,b的值。
2>函数传入指针参数
void fun(int *a, int *b)
{
int c = 0;
c = *a;
*a = *b;
*b = c;
}
int main()
{
int a = 1;
int b = 2;
cout << a << "," << b << endl;
fun(&a, &b);//a,b交换
cout << a << "," << b << endl;
system("pause");
return 0;
}
结果:
1,2
2,1
分析:
函数的参数传入的是指针,即地址。函数中a,b的交换是地址的交换。最后main()中的a,b的值也就改变了。
3>引用传入函数参数(简单–好操作)
void fun(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a = 1;
int b = 2;
cout << a << "," << b << endl;
fun(a, b);//a,b交换
cout << a << "," << b << endl;
system("pause");
return 0;
}
结果:
1,2
2,1
分析:
本质上是把main()中的a,b变量 换了个名字,即:函数中的a,b,函数中的a,b地址与main()中的地址是一样的。如果函数中的a,b值改变的,那么main()中的a,b的值也跟着改变。