最近上计算机系统cs213,这周的作业就需要用到很多奇奇怪怪的位运算,我觉得真的很难啊...所以补一下相关知识。
首先,我们都知道三个逻辑运算符,&&代表逻辑与,||代表逻辑或,!代表单目逻辑非。
(Plus,如果对一个数字进行两次单目逻辑非,得到的是其逻辑值)
int a=10,b=0;
cout<<"We use 1 to denote truth and 0 to denote false"<<endl;
cout<<"(5<a)&&b is:"<<((5<a&&b))<<endl;
然后我们再看看为逻辑运算符和位逻辑表达式:
位运算算是C语言的特色内容了,位运算可以实现位的设置、清零、取反、取补操作。利用位运算可以实现只有部分汇编语言才能实现的功能。
位逻辑运算符:
& 代表 按位逻辑与
| 代表 按位逻辑或
^ 代表 按位 异或
~ 代表 按位取反 唯一一个单目运算符,以上都是双目运算符
我们假设A是60 (A = 0011 1100)
<< 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。A >> 2 将得到 15,即为 0000 1111
程序中,位逻辑运算符一般被我们用作开关标志。较低层次的硬件设备驱动程序经常需要对I/O设备进行位操作。
我们举个例子来康康:
#include <iostream>
using namespace std;
int main()
{
unsigned int a = 60; // 60 = 0011 1100
unsigned int b = 13; // 13 = 0000 1101
int c = 0;
c = a & b; // 12 = 0000 1100
cout << "Line 1 - c 的值是 " << c << endl ;
c = a | b; // 61 = 0011 1101
cout << "Line 2 - c 的值是 " << c << endl ;
c = a ^ b; // 49 = 0011 0001
cout << "Line 3 - c 的值是 " << c << endl ;
c = ~a; // -61 = 1100 0011
cout << "Line 4 - c 的值是 " << c << endl ;
c = a << 2; // 240 = 1111 0000,*4
cout << "Line 5 - c 的值是 " << c << endl ;
c = a >> 2; // 15 = 0000 1111,看似是/4,但其实说不清楚
cout << "Line 6 - c 的值是 " << c << endl ;
return 0;
}
在进行右移操作时对于有符号数需要注意符号位的问题,如果是+数,符号位为0;为负数的时候,最高位置是补0还是补1取决于编译系统的规定,移入0进去的话为逻辑右移,1的话为算术右移。大部分机器都是算术右移的。
(说得比较模糊,这么讲:
首先说明一下这两个概念:
逻辑右移:右移后左边添加0
算术右移:右移后添加的位与原数的符号位相同
在C语言中,对于移位操作执行的是逻辑左移和算术右移,不过对于无符号类型,所有的移位操作都是逻辑的。
)
可以举个例子来康康:
可见,在dev-C++上的默认编译器上,-30右移动三位得到-4,是采取算术右移方式。用2进制表示,前面30位都是1,后面两位是0,算出来这个x>>3=-最高位位权+其余位位权*位的值,就等于-{1+2(+1)}=-4。
我们再看看循环移位:
顾名思义,循环移位就是将将移除的低位放到高位,移除的高位放到低位,有点像...循环链表?
我们这里稍微介绍一下循环左移:(高位向左出“栈顶”,回归“栈底”)。
比如对x串,向左循环左移n位:
首先将x的左端n位,在位串内部向右移动,我们假定这台机器里面int占4个字节(小声哔哔
z=x>>(32-n);
然后将x左移n位,右边补零:
y=x<<n;
最后将y、z按位“或”运算
y=y|z;
啊哈,大功告成
我们具体实现一下循环左移:从键盘中读入一个八进制数,然后输入想要移位的位数,最后循环左移的结果打印在控制台上。
#include <stdio.h>
LeftShift(unsigned value,int n)
{
unsigned z;
z=(value>>(32-n)|(value<<n));
return z;
}
int main()
{
unsigned a;
int n;
printf("hey boy,please input an octal number in here:\n");
scanf("%o",&a);
printf("hey boy,please input the number of leftshift displacement:\n");
scanf("%d",&n);
printf("The result is shown below:\n %o in octal form",LeftShift(a,n));
return 0;
}
而循环右移和循环左移是一个道理,不予赘述。
位段我们就先不介绍了,如有必要再另行补充。
补充一:
a&b 9&8
1001
1000
结果是1000
而a&&b 9&&8 结果是1
补充二:
1.十进制:除表示正负的符号外,以1~9开头,由0~9组成。如,128,+234,-278。
2,八进制:以0开头,由0~7组成的数。如,0126,050000.
3,十六进制:以0X或0x开头,由0~9,A~F或a~f 组成。如,0x12A,0x5a000.
——————————————————————————————————————————作业中最简单的一道练习题:
假设我们将一个w位的字中的字节从0(最低位)到w/8 -1(最高位)编号,写出下面的C函数的代码,他会返回一个无符号的值,其中参数x的字节i被替换成字节b:
比如,从第0个字节移到第一个字节,需要移动8位,而从第零个字节到第i个字节,就要移动i<<3=i*2^3=8*i位。
unsigned replace_byte(unsigned x,int i,unsigned char b);
//form:
replece_byte(0x12345678,2,0xAB) -->0x12AB5678
replace_byte(0x12345678,0,0xAB) -->0x123456AB
//solution:
unsigned replace_byte(unsigned x, unsigned char b, int i)
{
return (x & ~(0xFF<<(i<<3))) | (b << (i<<3)); //(i<<3可以写成8*i)
}