CSAPP 实验I Data Lab
更新历史
- 23.03.10:初稿
系列
- CSAPP - 笔记汇总
- I Data Lab - 位操作,数据表示
- II Bomb Lab - 汇编,栈帧与 gdb
- III Attack Lab - 漏洞是如何被攻击的
- IV Cache Lab - 实现一个缓存系统
- V Shell Lab - 实现一个Shell
任务目标
DataLab实验,需要我们完善函数,条件是使用限定的操作符,并且操作符的数量也有限制,“=”不做数量限制。
相关内容
自己定义的int整型的赋值范围是0到0xFF,8bits
机器是32位
只能使用一元操作符
!
~
,二元操作符&
|
+
<<
>>
不可以调用其他函数
测试命令
make clean
:清除生成,每次修改bits.c
文件都要清除重新生成make
:生成文件./btest -f funname -1 v
:测试单独的函数,带参数-T 100
:修改限制时间./dlc bits.c
:测试编码是否符合规则,符合规则无输出,参数-e
,输出所有信息./ishow 22
:输出int值的二进制,数值./fshow 22
:输出float的二进制,数值,已经非规范值
题目及解法
bitXor
题目要求:返回两个int的异或值
允许操作:
~ &
操作数量:14
分值:1
int bitXor(int x, int y) { |
tmin
题目要求:求补码的最小值
允许操作:
! ~ & ^ | + << >>
操作数量:4
分值:1
32位机器码的补码最小值是最高位为1,其他位0,所以用1左移31位形成。
int tmin(void) { |
isTmax
题目要求:如果x是补码最大值返回1,否则返回0
允许操作:
! ~ & ^ | +
操作数量:10
分值:1
补码的最大值是最高位0,其他位1。加1后是最小值,Tmin和Tmax每位都相反,Tmin^Tmax是全1,可以使用这个值进行返回。需要注意补码的特殊值Tmin,Tmax,还需要注意-1,32位全1 1111
,因为-1 ^ 0 = 1,需要排除这种情况
flag_2 表示x是否等于 -1
如果x==-1, y=0,y^0 == 0 flag_2为0
如果x!=-1,y!=0, y^0 != 0,flag_2不为0,flag的值是0或1,所以需要取非两次,把非零值转换为1
flag_1 表示(x+1)^x,有两种情况结果为全1,x是
Tmax
和-1
,我们只需要全为1的情况,所以先取反~,换成全0,再取非!, 转为1。返回值为两个标志相与,用flag_2排除掉flag_1中-1的这种情况,剩下的就是Tmax
int isTmax(int x) { |
allOddBits
题目要求:如果每一个偶数位都是1返回1,否则返回0
允许操作:
! ~ & ^ | + << >>
操作数量:12
分值:2
先从32比特中只保留所有偶数位的值,其他位赋值0,再和0xAAAAAAAA
作对比相等返回1,因为不能用等号,所以用异或^代替判断,如果相等全为0,取非!值为1,否则为0。
移位和加法让变量y等于0xAAAAAAAA。
x与y相与,只留下x的偶数位的值
用异或判断x与y是不是相等
int allOddBits(int x) { |
negate
题目要求:返回-x
允许操作:
! ~ & ^ | + << >>
操作数量:5
分值:2
这个比较简单,根据二进制补码x与-x就是按位取反再加1。
int negate(int x) { |
isAsciiDigit
题目要求:判断x是不是ASCII码的0-9, 相当于判断x的值是不是0x30到0x39。
允许操作:
! ~ & ^ | + << >>
操作数量:15
分值:3
先观察0-9的补码形式
0x30 110000
0x37 110111
0x38 111000
0x39 111001
首先需要满足除后6位外,高位全为0,满足第一个条件后0-7满足前三位为110
,剩下的直接对比是不是等于8或9。不能使用等号所以用异或判断相等
flag_1 右移6位后,如果高位全为0,就是0,取非!为1,不然就是0
flag_2 右移3位后,满足第一个条件的除了低3位其他全为0,与
110
比较相等就是0到7中的值flag_3, flag_4,直接判断是不是等于8,9。
int isAsciiDigit(int x) { |
conditional
题目要求:x不为0,返回y, x等于0,返回z。
允许操作:
! ~ & ^ | + << >>
操作数量:16
分值:3
先找返回值,x等于0,return (x & y) ^ (~x & z),x不等于0的时候,结果很多,这时候把x处理一下变为全1,返回值就相等了。
对于x不等于0的情况,换全1比较麻烦,对x取非!,把x简化成0或1。
把x扩展到全1和全0,用移位加也能做出来而且满足操作数量的限制,继续做后面的题目
logicalNeg
想到了一个快速扩展的方法,0或1,取反加1,就是全0或者全1。
int conditional(int x, int y, int z) { |
isLessOrEqual
题目要求:x<=y,返回1,否则返回0。
允许操作:
! ~ & ^ | + << >>
操作数量:24
分值:3
找出所以返回1的情况,一共两种情况,相等,小于。相等的时候比较好做,用异或判断相等,小于的情况比较复杂。
等于:flag_3表示相等的情况 异或判断
小于:有两种情况
需要注意减法同号一点不会溢出
flag_5:x负数,y正数。
flag_4:x,y同号,x < y,转换为 y-x>0。
用flag_5 表示x负数,y正数。
用右移31位看符号看判断正负
用flag_6 表示x正数,y负数。
用sum=y - x,flag_4 表示符号。
int isLessOrEqual(int x, int y) { |
logicalNeg
题目要求:实现取非!的功能
允许操作:
~ & ^ | + << >>
操作数量:12
分值:4
思路:取非结果只有0和1,把全0或全1加1,就是1或0,所以就需要把0转换为全1,非0转换为全0,然后加1,就是返回值。默认返回0,找出返回1的情况,也就是全1。只有0和Tmin的负值和自己符号相同,其他的符号都相反
取负值,对符号位扩展,然后自己的符号位和负值符号位相加,
0和Tmin,结果为0。
其他结果为全1。
flag_1:~sum,0和Tmin结果为全1, 其他为全0。
flag_2:~Tmin 01111 ~0全1
flag_3:flag_1 中排除到flag_2的Tmin情况
Tmin
0111
0为1111
其余为0000
取反,只保留符号位, Tmin
1111
0为0000
其余为1111
加1后,0为
0001
, 其余都统一为0000
int logicalNeg(int x) { |
howManyBits
题目要求:返回表示x的补码最短长度
注意-1,用一位
允许操作:
! ~ & ^ | + << >>
操作数量:90
分值:4
思路:用上面做的函数 conditional 做两个if功能, 一次返回分半查找的剩余一半函数 ,一次计算分半如果选择前一半,bit数要加上一半的长度,选择后面长度加0不做处理。
用0xffff0000 留下x的前16位,如果不等于0 说明位数大于16 等于0 说明位数小于16 在后16位内再进行分半查找
正负数的处理重点
都处理成正数,方便右移添0。加上符号位就是有效位数。
正数:找出最高位的1。
负数:找出最高位的0,
Tmin也符合
负数高位连续的1都是无用数据,只需要找到高位连续的1后的第一个0在哪里,做法是取反,如
1110010
,有用的数据是10010
,全部取反后0001101
, 有效位数是4位,加上符号位。flag_nage: x是负数 值为1
重点代码,
if函数功能
x = (extend_f_n & (~x)) ^ ((~extend_f_n) & x);
:如果标记为全1,选择的~x, 如果为全0,选择的x。异或的两边有一个一直为全0,结果一定是另一个。一个二分法
x_32 = x & bit_32;
:取x的前16位x_32 = !x_32;
:取非,如果前16位全0,值就是1,否则是0x_32 = ~x_32 + 1;
前16位全0,扩充到全1,否则扩充到全0flag_32 = ~x_32;
这里是为了减少一个op值,重复使用代码x_16 = (flag_32 & (x >> 16)) ^ ((x_32 & (x & (~bit_32))));
x_32如果全0,说明前16位,有1,那就把x右移16位,覆盖掉后面,继续对这16位进行二分法。这里不用考虑右移补1,因为都是正数最高位都是0
。并且总数加16。
如果全1,说明前16位都是0,要从后16位开始二分法,不用移位,x_16要等于x的后16位。总数加0。sum = sum + (flag_32 & 16);
:
int howManyBits(int x) { |
floatScale2
题目要求:无符号数保存的float值,返回乘2
允许操作:
所有的int,无符号操作符,if,while
操作数量:30
分值:4
思路:
0返回0
exp全1: 为无穷 直接返回
exp全0:
非规格化值:小数部分没有隐藏位
f不是全0 直接加左移一位 最后补上符号。
非规格化到规格化的过渡
f是全0 不做处理 返回原值 ,就是第一种情况exp 不全1全0 直接加1 相当于乘2
unsigned floatScale2(unsigned uf) { |
floatFloat2Int
题目要求:float转int值
允许操作:
所有的int,无符号操作符,if,while
操作数量:30
分值:4
思路:确定指数的范围
exp < 127 指数小于0时,是小数,返回0。
exp > 158 指数大于31 越界
exp 全0 返回0
exp 全1 返回0x80000000u
exp 其他情况下 对f部分补上隐藏位1 再右移exp-127位,最后补上符号位
int floatFloat2Int(unsigned uf) { |
floatPower2
题目要求:返回float值 2的x次方,x为32位int
允许操作:
所有的int,无符号操作符,if,while
操作数量:30
分值:4
思路:确定指数的范围
x为-127到128之间。
时间超时 10s内跑不完所有测试数据 可能是电脑问题
unsigned floatPower2(int x) { |