跳到主要内容

2018-2019学年下学期期中

2018-2019学年下学期期中试卷(含答案)

一、选择题(20 分)

  1. 代码和数据被加载到( ),处理器就开始执行 main 程序中的机器语言指令。

    A. 主存

    B. 寄存器

    C. 程序计数器

    D. 磁盘

    答案:

    A


  2. 当调用 malloc 这样的 C 标准库函数时,( )可以在运行时动态的扩展和收缩。

    A. 共享库

    B. 栈

    C. 堆

    D. 虚拟存储器

    答案:

    C


  3. CPU 中有一个程序计数器(又称指令计数器)。它用于存储( )。

    A. 保存将要提取的下一条指令的地址

    B. 保存当前 CPU 所要访问的内存单元地址

    C. 暂时存放 ALU 运算结果的信息

    D. 保存当前正在执行的一条指令

    答案:

    A


  4. 当一个有符号数和一个无符号数执行运算后的结果是( )。

    A. 有符号数

    B. 无符号数

    C. 非法运算

    D. 不一定

    答案:

    B


  5. 变量 xfd 分别为 intfloatdouble 类型的,除了 fd 不能等于无穷大和 NaN 以外值可以任意选取,下面表达式为真的是( )。

    A. x == (int)(float)x

    B. d == (double)(float)d

    C. x == (int)(double)x

    D. (f+d)-f == d

    答案:

    C


  6. 执行如下指令:

    testq %rax, %rax
    jne 3d<function+0x3d>

    %rax 为( )时,会引起程序跳转。

    A. 1

    B. 0

    C. %rax 任意值

    D. %rax 没有合适值能满足指令跳转

    答案:

    A


  7. 使用小端法的机器中,int x = 0x123456; 语句的高地址字节是( )。

    A. 0x12

    B. 0x00

    C. 0x34

    D. 0x56

    答案:

    B


  8. 在 X86-64 Linux 系统中,( )占用内存中字节最多。

    A. int *c

    B. char a[7]

    C. short b[3]

    D. float d

    答案:

    A


  9. %rsp 的值为 0x108popq %rax 指令后 %rsp 的值为( )。

    A. 0x108

    B. 0x100

    C. 0x110

    D. 0x104

    答案:

    C


  10. 下面指令中错误的是( )。

    A. movl 8(%rdi),%eax

    B. movq 0(%rax,%rcx,4),%rdx

    C. movq %rax,8(%rsp)

    D. movl 4(%rdx),(%rax)

    答案:

    D


二、(6 分)

假设一种整数为 8 位字长,根据第一列的描述,填写其对应的值。

描述值(十进制表示)
Umax
Tmin
(unsigned)((int)-7)
(((unsigned)0x81) << 1) & 0xff
12 && 4
(!0x15) > 16
答案:
描述值(十进制表示)
Umax255
Tmin-128
(unsigned)((int)-7)249
(((unsigned)0x81) << 1) & 0xff2
12 && 41
(!0x15) > 160

三、(20 分)

考虑基于 IEEE 浮点格式标准的 8 位浮点表示(符号位 1 位,阶码 3 位,尾数 4 位)。

该 8 位浮点数数值为 V=(1)S×1.M×2EV=(-1)^S\times1.M\times2^E,其中 EE 是偏置后的阶码值,MM 是尾数值,SS 为符号位。

给出以下十进制值,请写出其对应的浮点数二进制表示,并请给出其舍入值(采用 round-to-even 舍入规则)。

描述浮点数位模式ME实际值
0-0
0 100 0101
最小的非规格化数(负)
最大的规格化数(正)
11
0 101 0110
答案:
描述浮点数位模式ME实际值
0-01 000 0000002-20.0-0.0
0 100 010121/1621/161121/821/8
最小的非规格化数(负)1 000 111115/1615/162-215/64-15/64
最大的规格化数(正)0 110 111131/1631/163331/231/2
110 011 000011001.01.0
0 101 011011/811/8225.55.5

四、(12 分)

ab 为整型变量,将左侧的每个描述与右侧的一行代码匹配。

描述匹配代码
1. ~a
2. a
3. a & b
4. a * 7
5. a / 4
6. (a < 0) ? 1 : -1

可选代码:

A. ~(~a | (b ^ (MIN_INT + MAX_INT)))

B. ((a ^ b) & ~b) | (~(a ^ b) & b)

C. 1 + (a << 3) + ~a

D. (a << 4) + (a << 2) + (a << 1)

E. ((a < 0) ? (a + 3) : a) >> 2

F. a ^ (MIN_INT + MAX_INT)

G. ~((a | (~a + 1)) >> 31) & 1

H. ~((a >> 31) << 1)

I. a >> 2

答案:
  1. ~a:F
  2. a:B
  3. a & b:A
  4. a * 7:C
  5. a / 4:E
  6. (a < 0) ? 1 : -1:H

五、(12 分)

考虑如下 C 函数,请根据对应的汇编代码,填写 C 代码中缺失的表达式。

int looper(int n, int *a) {
int i;
int x = ______(1)________;
for (i = ______(2)________;
______(3)________;
i++) {
if (______(4)________)
x = ______(5)________;
______(6)________;
}
return x;
}

其对应的汇编代码如下:

int looper(int n, int *a)
n in %edi, a in %rsi
looper:
xorl %eax,%eax
xorl %ecx,%ecx
jmp .L25
.L27:
movl (%rsi,%rcx,4),%edx
cmpl %eax,%edx
jle .L28
movl %edx,%eax
.L28:
incl %eax
incl %ecx
.L25:
cmpl %edi,%ecx
jl .L27
ret
答案:
int looper(int n, int *a) {
int i;
int x = 0;
for (i = 0;
i < n;
i++) {
if (a[i] > x)
x = a[i];
x++;
}
return x;
}

六、(8 分)

考虑如下 C 程序。

#include <stdio.h>

/* Read a string from stdin into buf */
long evil_read_string() {
long buf[2];
scanf("%s", buf);
return buf[0];
}

int main() {
printf("0x%x\n", evil_read_string());
}
  • scanf("%s", buf):从标准输入流(stdin)中读入字符串,并存储在 buf 开始的存储器中(字符串以 '\0' 结束,不检查 buf 存储器的长度)。
  • printf("0x%x", i):以 0x 开始打印输出整型变量 i 的十六进制值。
  • Linux/x86-64 机器采用小端法。
  • 部分字符的 ASCII 码如下:
字符ASCII字符ASCII字符ASCII
'd'0x64'i'0x69'e'0x65
'v'0x76'.'0x2e's'0x73
'r'0x72'l'0x6c'\0'0x00

A. 上述程序运行在 Linux/x86-64 机器,输入字符串 dr.evils.lives,针对如下所示的栈结构,请填写 buf[0]buf[1] 中存储的数据(以十六进制表示)。

|<---------------- buf[0] ---------------->|<---------------- buf[1] ---------------->|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

B. 程序的输出结果是什么?

0x___________
答案:
|<---------------- buf[0] ---------------->|<---------------- buf[1] ---------------->|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|64 |72 |2e |65 |76 |69 |6c |73 |2e |6c |69 |76 |65 |73 |00 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

buf[0] = 0x736c6976652e7264

程序的输出结果是:

0x736c6976652e7264

七、(12 分)

考虑 x86-64 机型下定义的如下结构:

struct my_struct {
int *p;
struct {
int x;
short y;
} s;
struct my_struct *next;
};
  1. (4 分)试确定下列字段的偏移量是多少?(以字节为单位)

    p: ______

    s.x: ______

    s.y: ______

    next: ______

  2. (2 分)这个结构总共需要的字节数为 ______。

  3. (6 分)下面的过程是对这个结构进行操作的 C 代码及其编译后的汇编代码,试填写过程中缺失部分:

    void my_struct_init(struct my_struct *sp) {
    sp->s.x = ______(6)______;
    sp->p = ______(7)______;
    sp->next = ______(8)______;
    }

    其汇编代码如下:

    void my_struct_init(struct my_struct *sp)
    sp in %rdi
    my_struct_init:
    movl 12(%rdi),%eax
    movl %eax,8(%rdi)
    leaq 8(%rdi),%rax
    movq %rax,(%rdi)
    movq %rdi,16(%rdi)
    ret
答案:
  1. 字段偏移量:

    p: 0

    s.x: 8

    s.y: 12

    next: 16

  2. 这个结构总共需要的字节数为 24。

  3. C 代码:

    void my_struct_init(struct my_struct *sp) {
    sp->s.x = sp->s.y;
    sp->p = &(sp->s.x);
    sp->next = sp;
    }

八、(10 points)

某程序中包含 switch 语句,采用跳转表实现。其跳转表如下:

0x4004b7: jmpq *0x400600(,%rax,8)

Using GDB, we extract the 8-entry jump table:

0x400600: 0x00000000004004d1 0x00000000004004c8
0x400610: 0x00000000004004c8 0x00000000004004be
0x400620: 0x00000000004004c1 0x00000000004004d7
0x400630: 0x00000000004004c8 0x00000000004004be

包含 switch 语句的 C 语言程序的反汇编代码如下:

# on entry: %rdi = x, %rsi = y, %rdx = z
0x4004b0: cmp $0x7,%edx
0x4004b3: ja 0x4004c8
0x4004b5: mov %edx,%eax
0x4004b7: jmpq *0x400600(,%rax,8)
0x4004be: mov %edi,%eax
0x4004c0: retq
0x4004c1: mov $0x3,%eax
0x4004c6: jmp 0x4004da
0x4004c8: mov %esi,%eax
0x4004ca: nopw 0x0(%rax,%rax,1)
0x4004d0: retq
0x4004d1: mov %edi,%eax
0x4004d3: and $0x19,%eax
0x4004d6: retq
0x4004d7: lea (%rdi,%rdi,1),%eax
0x4004da: add %esi,%eax
0x4004dc: retq

请分析反汇编程序以及跳转表,填写下列 C 程序中的缺失部分:

int test(int x, int y, int z)
{
int result = 3;
switch (z)
{
case __(1)__:
_____(2)_____;
break;
case __(3)__:
case __(4)__:
result = _____(5)_____;
break;
case __(6)__:
result = _____(7)_____;
case __(8)__:
result = _____(9)_____;
break;
default:
result = _____(10)_____;
}
return result;
}
答案:
int test(int x, int y, int z)
{
int result = 3;
switch (z)
{
case 0:
result = x & 25;
break;
case 3:
case 7:
result = x;
break;
case 5:
result = 2 * x;
case 4:
result = result + y;
break;
default:
result = y;
}
return result;
}