最近慢慢开始看CSAPP了,学完了前两章来做个lab巩固一下前面所学的知识。
该篇博客用于记录从零开始完成CSAPP datalab。
环境准备
-
我一开始配置的是VMware虚拟机及建了一个CentOS7操作系统虚拟机。(后面发现在CentOS7上面有点麻烦,重新建了一个ubuntu操作系统)
-
VMware虚拟机的配置我是之前看的Linux网课配置的,可以去自行寻找资源配置
-
关于unbuntu的配置可以参考这篇博客ubuntu的配置
-
配置好ubuntu后在虚拟机打开终端执行以下命令(若遇bug参考下面注意)
apt-get update 更新apt软件源
sudo apt-get install build-essential 安装C/C++编译环境
sudo apt-get install gcc-multilib 补充gcc完整环境
sudo apt-get install gdb 安装gdb
注意:
在虚拟机打开终端执行上面操作时,有一个小细节,给我包括我其他的同学都造成了一点障碍。
我们可能会因为权限不够问题无法执行上述指令,这时可以通过su指令将前面的$(普通终端用户)变为#(超级用户),如果你前面的符号成功变为#,那么恭喜你,下面的这些内容你可以跳过了,但如果你和我一样出现了以下问题:
那么你就可以去参考一下报错su:认证失败这篇文章了。
接下来就可以去官网CS:APP3e, Bryant and O’Hallaron (cmu.edu)下载datalab-handout开始操作了。
下载的文件如下图所示:
我们的目标是修改bits.c文件中的内容,使其通过btest中的所有测试,且同时不违反任何编码准则。
需要注意的是我们操作的是bits.c文件里的代码,完成代码后保存,要在datalab-handout文件下在终端打开,执行以下指令就可以查看我们的得分以及答案是否正确了
make && ./btest
当我们执行make && ./btest出现以下形式时,即配置好了
下面我们就开始动手做题吧。
1.bitXor
本题要求是让我们通过~和&实现异或。首先我们要知道什么是异或,异或就是对于两个数x,y,相等时为0,不等时为1,用x^y表示异或运算。
学过离散数学的都知道,x⊕y = (¬x ∧ y) ∨ (x ∧¬y)=¬(¬(¬x ∧ y)∧¬(x ∧¬y)),没有学过的可以自行百度学一下哈哈哈。
所以答案就是
return ~(~(~x&y)&~(x&~y));
2.tmin
本题要求我们返回二进制形式下,补码表示的最小int值。在本实验int是4个字节,故用32位表示。此时Int的最小取值为-2^31,用补码表示,只需将1左移31为。
故答案为
return (1<<31);
3.isTmax
本题让我们判断x是不是二进制形式下补码表示的最大数。如果是,返回1。不是,返回0。(建议先理解第五题再来做这道题)
先理解~x+1与(~x)+1的区别。
我们对tmax取反后发现,它就是tmin,而tmin有个特征,就是tmin取反就是它本身,通过tmin^(~tmin+1)从而就能判断这个数是不是tmax,但要注意的是,0取反也是它本身,只需再特判一下。
故本题答案为
x=~x;
return !(x^(~x+1)) & (!!x);
x如果是0,!!x就为0;x如果为其他任意常数,!!x就为1,从而可以特判x是否为0。
4.allOldBits
本题让我们判断给定的二进制数,是不是全部的奇数位都为1(如10101010)。
我们只需将给定的数&上1010…1010,看最后得到的答案满不满足条件即可。
int old1=0xAA;//10101010
int old2=(old1<<8)+old1;//1010101010101010
int old3=(old2<<16)+old2;//10101010101010101010101010101010
return !((old3&x)^old3);
5.negate
本题要求返回一个数的相反数。在二进制补码中,取反加1就是其相反数。
故答案为
return (~x)+1;
6.isAsciiDigit
本题让我们判断一个数是否位于0x30
到0x39
之间。在时返回1,不在返回0。
要做本题,我们需要先了解一下掩码运算(CSAPP原书38页)。大致思路为先判断10位是否为3,再判断个位是否小于等于9。关于如何判断个位是否小于等于9,我们应该知道要判断y>=x,只需判断y+(-x)>=0,故我们要判断a是否小于等于9,只需判断9+(-a)>=0,可以通过移位操作得到这个数的符号位,从而来判断一个数是否大于0。
int y=x&0xf;//掩码运算得到x的个位
return !((x>>4)^0x3) & !((0x9+(~y+1))>>31);
7.conditional
本题让我们实现x ? y : z,即x不为0时返回y,为0时返回x。
x=0时我们可以通过(0xfffffff&y)|(0x00000000&z)来得到答案 ,同时x!=0时我们可以通过(0x00000000&y)|(0xffffffff&z)来得到答案。故我们只需要判断x是否等于0再来判断是0xfffffff&y还是0x00000000&y。
int checkZero=!!x;//x=0,checkZero=0;x!=0,checkZero=1
int check=~checkZero+1;//checkZero=0,check=0x00000000;checkZero=1,check=0xffffffff
return (check&y)|(~check&z);
8.isLessOrEqual
本题让我们判断x<=y。只需分别判断同号和异号时的情况。
异号时要判断y>=x,操作和第六题我们判断个位数是否小于等于9一样。
int checkx=x>>31;//判断x的正负
int checky=y>>31;//判断y的正负
int checkxy=checkx^checky;//判断x,y是否异号
int xx=~x+1;
return (checkx&checky&!checkxy)|(!checkxy&!((y+xx)>>31));
9.logicalNeg
本题让我们实现!运算,也就是x=0时返回1,否则返回0。我们只需要知道只有0与其相反数或后,所有位才为0。正数负数的相反数必为负数或正数,符号位必不同,或之后符号位必为1。
return ((x|(~x+1))>>31)+1;
10.howManyBits
本题要求我们返回一个数在二进制补码的表达形式下,最少需要多少位才能表达出来。
要做本题,我们要清楚以下结论:
当x为正数时,假设y表示1的最高位置,该数需要y+1位表达出来;如果x为负数时,将x取反,假设y表示1的最高位置,该数需要y+1位表达出来。
所有我们可以通过找最高的1的位置来确定答案。不能使用循环,所以我们采取二分的方式来操作。
int b16,b8,b4,b2,b1,b0;
int signX = x>>31;
x = ((~signX) & x) | (signX&(~x));
b16 = (!!(x>>16))<<4;
x=x>>b16;
b8 = (!!(x>>8))<<3;
x=x>>b8;
b4 = (!!(x>>4))<<2;
x=x>>b4;
b2 = (!!(x>>2))<<1;
x=x>>b2;
b1 = !!(x>>1);
x = x>>b1;
b0 = x;
return b0+b1+b2+b4+b8+b16+1;