第 3 章家庭作业:3.70~3.75

第 3 章家庭作业:3.70~3.75

家庭作业 3.70(★★★)考虑下面的联合声明:

union ele {
    struct {
        long *p;
        long y;
    } e1;
    struct {
        long x;
        union ele *next;
    } e2;
};

这个声明说明联合中可以嵌套结构。

下面的函数(省略了一些表达式)对一个链表进行操作,链表是以上述联合作为元素的:

void proc(union ele *up) {
    up->________ = *(________) - ________;
}

A. 下列字段的偏移量是多少(以字节为单位):

字段 偏移量(字节)
e1.p
e1.y
e2.x
e2.next

B. 这个结构总共需要多少个字节?

C. 编译器为 proc 产生下面的汇编代码:

void proc(union ele *up)
up in %rdi
proc:
    movq    8(%rdi), %rax
    movq    (%rax), %rdx
    movq    (%rdx), %rdx
    subq    8(%rax), %rdx
    movq    %rdx, (%rdi)
    ret

在这些信息的基础上,填写 proc 代码中缺失的表达式。提示:有些联合引用的解释可以有歧义,当你清楚引用指引到哪里的时候,就能够澄清这些歧义。只有一个答案,不需要进行强制类型转换,且不违反任何类型限制。

家庭作业 3.71(★)写一个函数 good_echo,它从标准输入读取一行,再把它写到标准输出。你的实现应该对任意长度的输入行都能工作。可以使用库函数 fgets,但是你必须确保即使当输入行要求比你已经为缓冲区分配的更多的空间时,你的函数也能正确地工作。你的代码还应该检查错误条件,要在遇到错误条件时返回。参考标准 I/O 函数的定义文档 [45, 61]。

家庭作业 3.72(★★)图 3-54a 给出了一个函数的代码,该函数类似于函数 vfunct(图 3-43a)。我们用 vfunct 来说明过帧指针在管理变长栈帧中的使用情况。这里的新函数 aframe 调用库函数 alloca 为局部数组 p 分配空间。alloca 类似于更常用的函数 malloc,区别在于它在运行时栈上分配空间。当正在执行的过程返回时,该空间会自动释放。

图 3-54b 给出了部分的汇编代码,建立帧指针,为局部变量 ip 分配空间。非常类似于 vframe 对应的代码。在此使用与练习题 3.49 中同样的表示法:栈指针在第 4 行设置为值 s1,在第 7 行设置为值 s2。数组 p 的起始地址在第 9 行被设置为值 p。s2 和 p 之间可能有额外的空间 e2,数组 p 结尾和 s1 之间可能有额外的空间 e1

A. 用数学语言解释计算 s2 的逻辑。

B. 用数学语言解释计算 p 的逻辑。

C. 确定使 e1 的值最小和最大的 n 和 s1 的值。

D. 这段代码为 s2 和 p 的值保证了怎样的对齐属性?

#include <alloca.h>

long aframe(long n, long idx, long *q) {
    long i;
    long **p = alloca(n * sizeof(long *));
    p[0] = &i;
    for (i = 1; i < n; i++)
        p[i] = q;
    return *p[idx];
}

a)C 代码

long aframe(long n, long idx, long *q)
n in %rdi, idx in %rsi, q in %rdx

1  aframe:
2      pushq   %rbp
3      movq    %rsp, %rbp
4      subq    $16, %rsp               Allocate space for i (%rsp = s1)
5      leaq    30(,%rdi,8), %rax
6      andq    $-16, %rax
7      subq    %rax, %rsp              Allocate space for array p (%rsp = s2)
8      leaq    15(%rsp), %r8
9      andq    $-16, %r8               Set %r8 to &p[0]
       ...

b)部分生成的汇编代码

图 3-54 家庭作业 3.72 的代码。该函数类似于图 3-43 中的函数

家庭作业 3.73(★)用汇编代码写出匹配图 3-51 中函数 find_range 行为的函数。你的代码必须只包含一个浮点比较指令,并用条件分支指令来生成正确的结果。在 232 种可能的参数值上测试你的代码。网络旁注 ASM:EASM 描述了如何在 C 程序中嵌入汇编代码。

家庭作业 3.74(★★)用汇编代码写出匹配图 3-51 中函数 find_range 行为的函数。你的代码必须只包含一个浮点比较指令,并用条件传送指令来生成正确的结果。你可能会想要使用指令 cmovp(如果设置了偶校验位传送)。在 232 种可能的参数值上测试你的代码。网络旁注 ASM:EASM 描述了如何在 C 程序中嵌入汇编代码。

家庭作业 3.75(★)ISO C99 包括了支持复数的扩展。任何浮点类型都可以用关键字 complex 修饰。这里有一些使用复数数据的示例函数,调用了一些关联的库函数:

#include <complex.h>

double c_imag(double complex x) {
    return cimag(x);
}

double c_real(double complex x) {
    return creal(x);
}

double complex c_sub(double complex x, double complex y) {
    return x - y;
}

编译时,GCC 为这些函数产生如下代码:

double c_imag(double complex x)
c_imag:
    movapd  %xmm1, %xmm0
    ret

double c_real(double complex x)
c_real:
    rep; ret

double complex c_sub(double complex x, double complex y)
c_sub:
    subsd   %xmm2, %xmm0
    subsd   %xmm3, %xmm1
    ret

根据这些例子,回答下列问题:

A. 如何向函数传递复数参数?

B. 如何从函数返回复数值?