前言
我选择的书籍是大名鼎鼎的《深入理解计算机系统》,作为本次项目实训的出题参考教程。
CS:APP 这本书自带很多 Lab,但是对于我来说这些实验的难度有点高,所以我首先自己做了一下 CS:APP Lab,然后参考其中出题的方式,自己出了一些题目,降低难度,以供同学使用。本文首先介绍实验一相关的题目,主体实验程序将在项目完成后,补充在本文末尾。
介绍
本实验包含 7 道与位级层面整数运算及操作的题目,使用 C++ 写成。bits.cc 文件中包含这 7 道题的空函数体,学生需要通过有限的 C++ 算术或逻辑操作符修改这些函数体,以实现函数功能。因为需要在树莓派上运行,实验将提供基于 Linux 环境本地检查功能,以方便学生进行检查。
本实验及后续所有实验需要学生有 C++ 和 Linux 基础,其余涉及的 GNC GCC、Makefile 等技术不做要求。
具体内容
本实验假设学生的机器有如下特征:
- 使用 32 位二进制补码存储数据
- 右移操作为算术右移
- 使用小端法存储数据
题目一,使用位级运算符号 NOT ~ 和 OR | 实现 x & y,本题目为 CS:APP Data Lab 中 bitXor 的简化版。
/*
* 1. bitAnd
* 使用位级运算符号 NOT ~ 和 OR | 实现 x & y
* 测试点样例:bitAnd(0x69, 0x55) = 0x41
* 可用操作符:~ |
* 难度:1
*/
int bitAnd(int x, int y) {
return 2;
}
题目二,返回当前值的相反数,本题为 CS:APP Data Lab 原题。
/*
* 2. negate
* 返回当前值的相反数
* 测试点样例:negate(1) = -1
* 可用操作符:! ~ & ^ | + << >>
* 难度:1
*/
int negate(int x) {
return 2;
}
题目三,无符号整数乘法,这道题是从 CS:APP 书上模拟出来的,只要认真看了课本应该没有什么难度。
/*
* 3. multi14sub3
* 给一个无符号整数 x,返回 x * 14 + 3
* 测试点样例:multi14sub3(1) = 17
* 可用操作符:! ~ & ^ | + << >>
* 难度:1
*/
unsigned int multi14plus3(unsigned int x) {
return 2;
}
题目四,从整数中取出字节,这道题目是 0-index 的。
/*
* 4. getBytes
* 从整数 x 中取出第 n 个字节
* 测试点样例:getBytes(0x01234567, 0) = 0x67
* getBytes(0x284A4C32, 2) = 0x4A
* 可用操作符:! ~ & ^ | + << >>
* 难度:2
*/
unsigned char getBytes(int x, int n) {
return 2;
}
题目五,判断当前值是否为 32 位补码整数最大值,即 2^31-1 = 2147483647,本题为 CS:APP Data Lab 原题。
/*
* 5. isTmax
* 当 x 为 32 位整数最大值时返回 1,否则返回 0
* 测试点样例:isTmax(-1) = 0
* isTmax(0x7FFFFFFF) = 1
* 可用操作符:! ~ & ^ | +
* 难度:2
*/
int isTmax(int x) {
return 2;
}
题目六,判断当前整数的所有奇数位比特是否为 1,本题目为 CS:APP Data Lab 中 allOddBits 的修改版。
/*
* 6. allEvenBits
* 当所有奇数位比特为 1 时返回 1,否则返回 0
* 测试点样例:allEvenBits(0xFFFFFFFA) = 0
* allEvenBits(0xFFFFFFF4) = 1
* 可用操作符:! ~ & ^ | + << >>
* 难度:2
*/
int allEvenBits(int x) {
return 2;
}
题目七,判断是否 x ≥ y,本题目为 CS:APP Data Lab 中 isLessOrEqual 的修改版。
/*
* 7. isGreaterOrEqual
* 当 x >= y 时返回 1,否则返回 0
* 测试点样例:isGreaterOrEqual(5, 4) = 1
* isGreaterOrEqual(-1, 3) = 0
* 可用操作符:! ~ & ^ | + << >>
* 难度:3
*/
int isGreaterOrEqual(int x, int y) {
return 2;
}
文件内容
- bits.cc:学生需要修改的文件
- bits.h:头文件
- driver.cc:测试驱动文件
- driver.h:测试驱动的头文件
- Makefile:简化编译过程的文件
- README:实验指导书实验一部分节选
测试文件
为了方便学生检查自己的答案是否正确,而且无需地了解 GNC gcc 和 Makefile 技术,我编写了测试驱动程序,并准备了一系列的测试点,帮助学生实现这个目标。学生在修改 bits.cc 后,可以通过简单的 Make 命令即可完成编译并运行,以检查自己的答案是否正确。
unix> make
g++ -O -Wall -m32 -c -o bits.o bits.cc
g++ -O -Wall -m32 -c -o driver.o driver.cc
g++ -O -Wall -m32 -lstdc++ -o lab bits.o driver.o
rm -f *.o
在实现的过程中,我从《GNU gcc 嵌入式系统开发》中简单地学习了 GNC gcc 和 Makefile 技术,并以此编写了 Makefile 文件,如下:
#
# Makefile 可用于编译实验一的文件
#
CC = g++
CPPFLAGS = -O -Wall -m32
LIBS = -lstdc++
lab: bits.o driver.o
$(CC) $(CPPFLAGS) $(LIBS) -o lab bits.o driver.o
rm -f *.o
driver: driver.cc driver.h
$(CC) $(CPPFLAGS) -o driver.o driver.cc driver.o
bits: bits.cc bits.h
$(CC) $(CPPFLAGS) -o bits.o bits.cc bits.h
clean:
rm -f *.o lab
我首先将 bits.cc 和 bits.h 进行编译得到 bits.o,后对测试驱动文件进行编译得到 driver.o,最后将两个可重定向目标文件 bits.o 和 driver.o 与 C++ 官方 iostream 函数库通过参数 -lstdc++ 进行链接得到可执行文件 lab,最后删除 bits.o 和 driver.o。
参数 -O 表示对目标文件的进行优化,改善程序的执行性能,-Wall 表示的是 Warning ALL 即显示所有告警信息,-m32 表示编译为 32 位文件,这里是为了满足机器为 32 位机器的前提条件。
最开始使用的是 gcc 进行编译,后来因为库文件找不到,最后换成了 g++。
测试方式
每次学生编写完文件后都要进行编译,编译完成后即可执行。
unix> ./lab
You have passed bitAnd!
You have passed negate!
You have passed multi14plus3!
ERROR: Test getBytes(2837247[0x2b4aff],0[0x0]) failed...
...Gives 2[0x2]. Should be 255[0xff].
执行 lab 文件后,通过全部测试点的题目会显示你已经通过,例如通过第一题为「You have passed bitAnd!」,而未通过的题目会报错,并显示未通过的测试点,如目前显示的「ERROR: Test getBytes(2837247[0x2b4aff],0[0x0]) failed…Gives 2[0x2]. Should be 255[0xff].」,意为使用参数 0x2b4aff 和 0 对函数 getBytes(int, int) 进行测试,学生给出的结果是 2,而正确答案应该为 0xff。
若完成所有题目,系统会给出提示,但并不代表答案完全正确,因为测试程序并未检查学生给出的答案中是否包含不允许使用的符号,需要进一步进行检查。
实验答案
我本人自己做过一部分 CS:APP Data Lab,因此也大致知道我所出题目的解法,答案将会给到指导老师,这里就不给出来了。