字节和位运算
数据是如何存储的?
大家都知道在计算机中都是按照二进制存储的,所以大家常说计算机是由0和1组成的。二生万物!我们可以将计算机的存储器想想成下面这样的一个一个的小格子,每个小格子可以存储1位二进制数。
例如十进制数字145在二进制存储下应为10010001,它在计算机中存储如下所示:

每一个小格子只能有两种状态,要么为0要么为1。这种可以存储1位二进制数的小格子就是计算机中的最小单位,称为一个比特(bit)。 8个比特可以组成一个更大的单位称为字节(Byte) 即∶1字节=8bit
其他计量单位:
1KB=1024Byte 1MB=1024KB 1GB=1024MB 1TB=1024GB 1PB=1024TB
不同的数据类型的空间占用:
不同的数据类型在存储器中存储时占用的空间是不同的。下面分别是几种常见的数据类型所占用的空间,数据类型对存储空间的占用是按照字节来计算的,也就是说任何数据类型最少也会占用一个字节的存储空间:

通过上面的表格,我们已经很清楚的了解了每种数据类型所占用的空间了,那么大家能通过每个数据类型占用的空间推算出每种数据类型能够表示的数据范围吗?比如说,对于int类型,它可表示的数据范围是多少?
通过上面,我们发现int类型占用了4个字节,每个字节等于8个比特,因此int类型一共占用4*8=32个字节,每个字节相当于一个小格子,因此int类型最多占用32个小格子,当小格子里面的数字全部为1的时候,即int类型可表示的最大数字。

你是不是觉得这样就对了,其实并不是,因为int还可以存储负数,计算机用32bit中最左边的1位来标识正负数,0为正,1为负。故int类型最大表示范围如下:

因此,int类型的数据范围为:-231 ~231-1 231=2147483648≈2.147*109 因此在我们编程过程中,如果处理的数超过109,就要小心是否会超过int的范围,因为可能会出现数据溢出!

unsigned int无符号整型,无符号在这里的含义是不存在负数,也就是说unsiged类型的变量无法存储负数。
263= 9223372036854775808 ≈9.22*1018 如果整数超过这个范围就需要使用高精度算法了。
例题1:以下变量的定义、运算和赋值是否正确。
int a = 100000; 正确
int b = 2000000000; 正确
int c = 3000000000; 错误∶ 超出int范围
int d = a * a; 错误∶ a*a超出int范围
int e = 1000000000; 正确
int f = b + e; 错误∶ b+e超出了int范围
long long g = 123456789987654321; 正确
long long h = g * 10; 错误
什么是位运算?
程序中所有数在计算机中都是以二进制的形式存储的。位运算就是直接对整数在内存中的二进制位进行操作。在C++中位运算一般包含6种,分别是按位与 & ,按位或 | ,按位异或 ^ ,取反 ~ ,左移 << 和右移 >> 。下面带大家来详细了解下什么是位运算。
与运算&
这是一个双目运算符,参加运算的两个数据,按二进制位进行按位"与"运算。运算规则∶ 0&0=0; 0&1=0;1&0=0; 1&1=1;即∶两位同时为 1,结果才为 1,否则为 0。
示例 9&5=1 ,21&18=16 (请你说说这是为什么?)
与运算&的用途
(1)清零
若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合以下条件:原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。
例:原数为43,即00101011,另找一个数,设它为148,即10010100
将两者按位与运算: 00101011 & 10010100,得00000000
(2)取一个数中某些指定位
若有一个整数a(2byte),想要取其中的低字节,只需要将a与8个1按位与即可。
a 00101100 10101100 & b 00000000 11111111,得c 00000000 10101100
(3)保留指定位:
与一个数进行“按位与”运算,此数在该位取1。
例如:有一数84,即01010100,想把其中从左边算起的第3,4,5,7,8位保留下来.
运算如下:
01010100 & 00111011,得 00010000 即:a=84,b=59 c=a&b=16
(4)判断奇偶
只要根据最末位是0还是1即可判断整数的奇偶性。例如整数n,可以用if((n & 1) == 0)来判断,要比if(n % 2 == 0)判断奇偶性效率高。
(5)判断一个整数是不是2的整数次方
如果一个整数是2的整数次方,那么它的二进制标识中一定有且只有一位是1,而其他所有位均为0.解决方案:把这个整数减去1之后再和本身做与运算,这个整数中唯一的一个1就会变成0.所以只要判断是不是等于0即可。
或运算 |
或运算是计算机中一种基本的逻辑运算方式,符号表示为 | 。这是一个双目运算符,参加运算的两个数据,按二进制位进行按位“或”运算。运算规则:0 | 0 = 0; 0 | 1 = 1; 1 | 0 = 1; 1 | 1 = 1;即:两位同时为 0,结果才为 0,否则为 1。
示例: 9|5=13 ,21|18=23 (请你说说这是为什么?)
或运算|的用途:按位或运算常用来对一个数据的某些位定值为1。如果想使一个数a的低4位改为1,则只需要将a与15进行按位或运算即可。
异或运算∧
异或运算是计算机中一种基本的逻辑运算方式,符号表示为 ∧。这是一个双目运算符,参加运算的两个数据,按二进制位进行按位“异或”运算。运算规则:0 ∧ 0=0; 0∧1=1; 1 ∧0=1; 1 ∧ 1=0;即:两位不同,结果才为 1,否则为 0。
示例:9∧5=12 ,21∧18=7 (请你说说这是为什么?)
异或运算的用途
(1)使特定位翻转
设有数01111010,想使其低4位翻转,即1变0,0变1。可以将其与00001111进行“异或”运算。即: 01111010 ∧ 00001111,得01110101
运算结果的低4位正好是原数低4位的翻转。可见,要使哪几位翻转就将与其进行∧运算的该几位置为1。
(2)与0相“异或”,保留原值
例如:012∧ 00=012 即 00001010 ∧00000000,得00001010 。因为原数中的1与0进行异或运算得1,0∧0得0,故保留原数。
(3) 交换两个值,不用临时变量
例如:a=3,即11;b=4,即100。想将a和b的值互换,可以用以下赋值语句实现:
a=a∧b;
b=b∧a;
a=a∧b;
a=011
(∧)b=100
a=111(a∧b的结果,a已变成7)
(∧)b=100
b=011(b∧a的结果,b已变成3)
(∧)a=111
a=100(a∧b的结果,a已变成4)
等效于以下两步:
① 执行前两个赋值语句:“a=a∧b;”和“b=b∧a;”相当于b=b∧(a∧b)。
② 再执行第三个赋值语句: a=a∧b。由于a的值等于(a∧b),b的值等于(b∧a∧b),因此,相当于a=a∧b∧b∧a∧b,即a的值等于a∧a∧b∧b∧b,等于b。
(4) 快速判断两个值是否相等
例如:判断两个整数a = 1,b = 1是否相等,则可通过异或实现。
取反
取反是一个一元运算符,用于求整数的二进制反码(变换符号),即分别将操作数各二进制位上的1变为0,0变为1。
示例: ~9=6 ,~21=10(请你说说这是为什么?)
左移运算<<
左移运算的语法格式为:x<<n( x:需要移位的数字;n:移位的次数 )
运算规则:按二进制形式把所有数字向左移动相应的位数,高位移出(舍弃),低位的空位补 0。相当于乘以 2 的 n 次方。左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负值),其右边空出的位用0填补,高位左移溢出则舍弃该高位。左移1位相当于该数乘以2,左移2位相当于该数乘以2*2=4,15<<2=60,即乘了4。但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。
示例:1<<3 = 8 3<<4 = 48 (请你说说这是为什么?)
右移运算>>
右移运算的语法语法格式:x>>n( x:需要移位的数字;n:移位的次数 )
运算规则:按二进制形式把所有数字向右移动相应的位数,低位移出(舍弃),高位的空位补 0。相当于除以 2 的 n 次方。右移运算符是用来将一个数的各二进制位右移若干位,移动的位数由右操作数指定(右操作数必须是非负值),移到右端的低位被舍弃,对于无符号数,高位补0。
注意:对无符号数,右移时左边高位移入0;对于有符号的值,如果原来符号位为0(该数为正),则左边也是移入0。如果符号位原来为1(即负数),则左边移入0还是1,要取决于所用的计算机系统。有的系统移入0,有的系统移入1。移入0的称为“逻辑移位”,即简单移位;移入1的称为“算术移位”。
示例:8>>2 = 2 10>>2= 2 (请你说说这是为什么?)
| 运算符 | 含义描述 |
|---|---|
| & 按位与 | 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0 |
| I 按位或 | 两个相应的二进制位中只要有一个为1,该位的结果值为1 |
| ^ 按位异或 | 如果参加运算的两个二进制位值相同则为0,否则为1 |
| ~ 取反 | ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0 |
| << 左移 | 用来将一个数的各二进制位全部左移N位,右补0 |
| >> 右移 | 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0 |
下面我们一起来看看位运算的题目吗,本章内容了解即可。
例题1:判断x的n位(二进制中)是否为1。
#include<bits/stdc++.h>
using namespace std;
int main(){
int x,n;
cin>>x>>n;
if(x&(1<<n))
cout<<"yes"<<endl;
else
cout<<"no"<<endl;
return 0;
}
例题2:将x的n位(二进制中)清0。
#include<bits/stdc++.h>
using namespace std;
int main(){
int x,n;
cin>>x>>n;
x=x&~(1<<n);
cout<<x<<endl;
return 0;
}
例题3:将x的n位(二进制中)置1。
#include<bits/stdc++.h>
using namespace std;
int main(){
int x,n;
cin>>x>>n;
x=x|(1<<n);
cout<<x<<endl;
return 0;
}
例题4:海明距离
海明距离是在指二进制情况下,一个整数变成另外一个整数需要翻转的位数。比如2转换到3需要翻转1位,所以2到3的海明距离是1。给你两个正整数x和y,(x,y<=1,000,000,000)求它们的海明距离。
输入格式:输入第一行是一个整数N,表示接下来有N对数据。接下来N行,每行两个整数x和y。
输出格式:N行:每对数据的海明距离。
输入样例1:
2
1 2
4 7
输出样例1:
2
2
解题思路:① 所有数据用二进制表示时,数码为 0 和 1; ② 对比数据 x1 和 x2 的每一位(二进制中的每一位); ③ 用异或运算^来对比:y=x1^x2; ④ 输出结果 y 中数码 1 的个数;⑤ >>和&联合的方式:y&1,y>>1。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,x1,x2,y,sum;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>x1>>x2;
y=x1^x2;
sum=0;
while(y)
{
if(y&1) sum++;
y=y>>1;
}
cout<<sum<<endl;
}
return 0;
}
例题5:与运算
给出一个范围,[m, n](0 <= m <= n <= 2147483647),返回这些数字的与运算结果。
输入格式:1行:两个以空格分割的数字m和n。
输出格式:1行:数字m到数字n之间所有数字的与运算结果。
输入样例1:
5 7
输出样例1:
5 15
解题思路:① 由于数据范围较大,直接枚举后进行&操作会超时。② 仔细观察,几个连续的二进制码肯定会有相同的前缀,比如5~7,相同前缀为01;③ 除了相同的前缀外,将之后的各位置为0即为所求∶0100,也就是4。④ 如何求这个相同前缀?⑤ 将m和n同时右移,直到两数相等为止,记录移位次数t。再将移位后的m左移t 位即可。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t=0,y,m,n;
cin>>m>>n;
while(m!=n)
{
m>>1;
n>>1;
t++;
}
y=m<<t;
cout<<y<<endl;
return 0;
}
例题6:进制转换
请你编一程序实现两种不同进制之间的数据转换。
输入格式:输入数据共有三行,第一行是一个正整数,表示需要转换的数的进制n(2≤n≤16),第二行是一个n进制数,若n>10则用大写字母A~F表示数码10~15,并且该n进制数对应的十进制的值不超过1000000000,第三行也是一个正整数,表示转换之后的数的进制m(2≤m≤16)。
输出格式:输出仅一行,包含一个正整数,表示转换之后的m进制数。
输入样例1:
16
FF
2
输出样例1:
11111111
解题思路:① 数据范围较大,数据类型用 long long; ② 如何实现两种不同进制之间的数据转换?以十进制为媒介:将数据先转换为十进制,再转换为其他③ 输入的数据 x 和输出的结果 y 应该是字符串类型。
#include<bits/stdc++.h>
using namespace std;
//将p进制数s转换成10进制数
long long transD(int p,string s)
{
long long len,sum=0;
len=s.size();
int zh=1;
for(int i=len-1;i>=0;i--)
{
if(s[i]>='0'&&s[i]<='9')
sum=sum+(s[i]-'0')*zh;
else
sum=sum+(s[i]-('A'-10))*zh;
zh=zh*p;
}
}
//将十进制数x转换为q进制数st
string trans(int x,int q)
{
string st;
char c;
int j=0;
while(x)
{
if(x%q>=10) c=x%q+('A'-10);
else c=c%q+'0';
st.push_back(c);
x=x/q;
}
int n=st.size(); //st需要逆序
for(int i=0;i<n/2;i++) //i<n/2,不能i<n
{
char t;
t=st[i];
st[i]=st[n-1-i];
st[n-1-i]=t;
}
return st;
}
int main()
{
long long p,q,y1;
string x,y2;
cin>>p>>x>>q;
y1=transD(p,x);
y2=trans(y1,q);
cout<<y2<<endl;
return 0;
}
931

被折叠的 条评论
为什么被折叠?



