CSAPP读书笔记与习题作业练习-第3章

疑问

疑问一 练习题3.26中为什么通过循环+异或可以判断二进制数中1的个数的奇偶性(已解决)

问题:练习题3.26中通过让val不断右移1位,然后用右移的值异或val之前的值直到右移结果为0。这时最低位代表了二进制数val中1的个数的奇偶性。

回答:该运算本质上是在最低位对所有位进行异或运算,如果有奇数个1,异或的结果就是1,反之为0.

习题

练习题3.1

操作数
%rax 0x100
0x104 0xAB
$0x108 0x13
(%rax) 0xFF
4(%rax) 0xAB
9(%rax,%rdx) 0x11
260(%rcx,%rdx) 0x13
0xFC(,%rcx,4) 0xFF
(%rax,%rdx,4) 0x11
更正:$0x108是立即数,值为0x108

练习题3.2

movl %eax,(%rsp)
movq (%rax),%dx
movb $0xFF,%bl
movw (%rsp,%rdx,4),%dl
movq (%rdx),%rax
movw %dx,(%rax)
更正:
要注意目标操作数的空间大小,所以:
操作数(%rax),%dx的指令应为movw
操作数(%rsp,%rdx,4),%dl的指令应为movb

练习题3.3

movb $0xF,(%ebx) 错误:源操作数只有4bit,不到byte
movl %rax,(%rsp) 错误:源操作数为64bit寄存器,但mov指令只移动了32bit
movw (%rax),4(%rsp) 错误:不能直接从内存移动到内存
movb %al,%sl 错误:
movq %rax,$0x123 错误:目的操作数不能是立即数
movl %eax,%rdx 错误:
movb %si,8(%rbp) 错误:
更正:
movb $0xF,(%ebx) 错误:不能使用%ebx作为地址寄存器
movl %rax,(%rsp) 错误:指令后缀和寄存器id不匹配
movw (%rax),4(%rsp) 错误:不能直接从内存移动到内存
movb %al,%sl 错误:没有叫做%sl的寄存器
movq %rax,$0x123 错误:目的操作数不能是立即数
movl %eax,%rdx 错误:目的操作数大小不正确
movb %si,8(%rbp) 错误:指令后缀和寄存器id不匹配(%si是16bit寄存器)

练习题3.4

src_t dest_t 指令
long long movq (%rdi),%rax
movq %rax,(%rsi)
char int movsbl (%rdi),%eax
movl %eax,(%rsi)
char unsigned movsbl (%rdi),%eax
movl %eax,(%rsi)
unsigned char long movzbl (%rdi),%eax
movq %rax,(%rsi)
int char movb (%rdi) ,%al
movb %al,(%rsi)
unsigned unsigned char movb (%rdi) ,%al
movb %al,(%rsi)
char short movsbw (%rdi) ,%ax
movw %ax,(%rsi)
更正:对于高位转低位,应该存高位个字节,读低位寄存器。
所以int->char和unsigned->unsigned char的第一条指令都是movl (%rdi),%eax

练习题3.5

void decode1(long *xp, long *yp, long *zp) {
   
	long x = *xp;
	long y = *yp;
	long z = *zp;
	*yp = x;
	*zp = y;
	*xp = z;
}

练习题3.6

表达式 结果
leaq 6(%rax),%rdx x+6
leaq (%rax,%rcx) x+y
leaq (%rax,%rcx,4) x+4y
leaq 7(%rax,%rax,8) 7+9x
leaq 0xA(,%rcx,4) 10+4y
leaq 9(%rax,%rcx,2) 9+x+2y

练习题3.7

long scale2(long x,long y,long z){
   
	/**
	 * t=x+4x=5x
	 * t=t+2y=5x+2y
	 * t=t+8z=5x+2y+8z
	 */
	long t = 5*x+2*y+8*z;
	return t;
}

练习题3.8

指令 目的
addq %rcx,(%rax) 0x100 0x100
subq %rdx,8(%rax) 0x108 0xA8
imulq $16,(%rax,%rdx,8) 0x118 0x110
incq 16(%rax) 0x110 0x14
decq %rcx %rcx 0x0
subq %rdx,%rax %rax 0xFD

练习题3.9

salq 0x4 %rax
sarl %esi %rax
更正:
salq $4 %rax
sarl %cl %rax
移位的源操作数只能是立即数或者cl寄存器

练习题3.10

long arith2(long x,long y,long z){
   
	long t1=x|y;
	long t2=t1>>8;
	long t3=~t2;
	long t4=z-t3;
	return t4;
}

更正:t2=t1>>3

练习题3.11

A.将%rdx赋值为0
B.movq $0 %rdx
C.
xorq %rdx,%rdx(3字节)
movq $0 %rdx(7字节)
xorl %edx,%edx(2字节)
movl $0 %edx(5字节)
后两者利用了更新低4字节的指令会将高位字节置0的特性。

练习题3.12

movq %rdx, %r8
movq %rdi,%rax
xorq %rdx %rdx
divq %rsi
movq %rax,(%r8)
movq %rdx,(%rcx)
ret

练习题3.13

A.
data_t :int COMP:<
B.
data_t :short COMP:>=
C.
data_t :unsigned char COMP:<=
D.
data_t :long COMP:!=
data_t :unsigned long COMP:!=
data_t :某种指针 COMP:!=

练习题3.14

练习题3.15

A.je 4003fe
B.je 400425
C.400543
400545
D.400560

练习题3.16

A.
汇编代码:

# void cond(long a,long * p)
# a in %rdi, p in %rsi
cond:
	testq	%rsi,%rsi   将p的值与0进行比较
	je		.L1			p为0时跳到L1
	cmpq	%rdi,(%rsi) 比较p指向的值和a
	jge		.L1			当p指向的值大于等于a时跳到L1
	movq	%rdi,(%rsi) 将a的值写入p指向的位置
.L1:
	rep; ret			Return

所以可以写出c代码如下:

void cond(long a,long * p) {
   
	if (p==0)
		goto L1;
	if (*p>=a)
		goto L1;
	*p = a;
L1:
	return;
} 

B.因为一个if语句中有两个判断条件,所以要有两个条件分支。

练习题3.17

A.

long gotodiff_se(long x,long y) {
   
	long result;
	if (x < y) 
		goto x_lt_y;
	ge_cnt++;
	result = x - y;
	return result;
x_lt_y:
	lt_cnt++;
	result = y - x;
	return result;
}

B.运用该规则可以更直观地使用原来的判断条件。

练习题3.18

long test(long x,long y,long z){
   
	long val = x + y + z;
	if (x < -3) {
   
		if (y>=z) {
   
			val = y*z;
		} else {
   
			val = y*x;
		}
	} else if (x > 2)
		val = x*z;
	return val;
}

练习题3.19

A. T M P = 2 ( T r a n − T O K ) = 30 T_{MP}=2(T_{ran}-T_{OK})=30 TMP=2(TranTOK)=30
B. T O K + T M P = 46 T_{OK}+T_{MP}=46 TOK+TMP=46

练习题3.20

更正:补码除法用算术右移实现时要注意加上偏置量(1<<k)-1。
A.#define OP /
B.

arith:
	leaq	7(%rdi),%rax	将x+7写入%rax
	testq	%rdi,%rdi		测试x与0的关系
	cmovns	%rdi,%rax		当x为非负数时将x写入%rax
	sarq	$3, %rax		将%rax的数算术右移3位
	ret						返回

练习题3.21

long test(long x,long y){
   
	long val = 8*x;
	if (y > 0) {
   
		if (x >= y) {
   
			val = x & y;
		} else {
   
			val = y - x;
		}
	} else if (y <= -2)
		val = x + y;
	return val;
}

练习题3.22

A.n=12,n!=479001600
B.n=20,n!=2432902008176640000

练习题3.23

A.%rax存x,%rcx存y,%rdx存n
B.通过加载有效地址指令
C.

dw_loop:
	movq	%rdi,%rax			将x设为返回值
	movq	%rdi,%rcx			将%rcx的值置为x
	imulq	%rdi,%rcx			#y=x*x
	leaq	(%rdi,%rdi),%rdx	#n=2x
.L2:
	leaq	$1(%rcx,%rax),%rax	#计算x+=y和*p++
	subq	$1,%rdx				n--
	testq	$rdx,$rdx			比较n和0的关系
	jg		.L2					n>0时跳到L2
	rep;ret						return 

更正:
B.编译器认为指针p总是指向x

练习题3.24

long loop_while(long a,long b){
   
	long result = 1;
	while(a < b) {
   
		result = result * (a + b);
		a= a + 1;
	}
	return result;
}

练习题3.25

long loop_while2(long a,long b){
   
	long result = b;
	while(b > 0) {
   
		result = result * a;
		b = b - a;
	}
	return result;
}

练习题3.26

A.jump to middle
B.

long fun_a(unsigned long x){
   
	long val = 0;
	while(x != 0) {
   
		val = val ^ x;
		x>>1;
	}
	return val & 0x1;
}

C.
当x小于32位时生成和x位数相同的全1数,超过32位时生成0xFFFFFFFF
更正:该运算用于校验x的二进制数中1的个数奇偶性

练习题3.27

long fact_for_goto(long n){
   
	long i = 2;
	long result = 1;
	if (i > n)
		goto done;
loop:
	result *= i;
	i++;
	if (i <= n)
		goto loop;
done:
	return result;
}

练习题3.28

A.

long fun_b(unsigned long x){
   
	long val = 0;
	long i;
	for(i=64;i!=0;i--) {
   
		long y = x;
		y &= 0x1;
		val = (2 * val)| y;
		x>>=1;
	}
	return val;
}

B.因为初始测试一定满足条件?
C.
更正:

long fun_b(unsigned long x){
   
	long val = 0;
	long i;
	for(i=64;i!=0;i--) {
   
		val = (val<<1) | (x&0x1);
		x>>=1;
	}
	return val;
}

该段代码的功能是获得和x在二进制序列上呈镜像关系的数字

练习题3.29

A.因为for改为while时会把第三个一般是控制循环的表达式放到循环体的结尾,而continue会跳过这个表达式,相当于这个表达式失效了
B.continue应该被替换为跳转到循环体结尾的上一语句之前的goto语句。

练习题3.30

A.标号为-1到7
B.case 2和4、case 0和7
更正:A.标号为-1,0,1,2,4,5,7

练习题3.31

void switcher(long a,long b,long c,long* dest){
   
	long val;
	switch(a){
   
		case 5:
			c = b ^ 15;
		case 0:
			val = c + 112;
			break;
		case 2:
		case 7:
			val = (b+c)*4;//(b+c)<<2
			break;
		default:
			val = b ;
	}
	*dest = val;
}

练习题3.32

|指令|||状态值(指令执行前)||||||
-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
标号|PC|指令|%rdi|%rsi|%rax|%rsp|* %rsp|描述
M1|0x400560|callq|10|-|-|0x7fffffffe820|-|调用first(10)
F1|0x400548|lea|10|-|-|0x7fffffffe818|0x400565|计算x+1
F2|0x40054c|sub|10|11|-|0x7fffffffe818|0x400565|计算x-1
F3|0x400550|callq|9|11|-|0x7fffffffe818|0x400565|调用last(x-1,x+1)
L1|0x400540|mov|9|11|-|0x7fffffffe810|0x400555|u=x-1
L2|0x400543|imul|9|11|9|0x7fffffffe810|0x400555|u=u*(x+1)
L3|0x400547|ret|9|11|99|0x7fffffffe810|0x400555|从last(x-1,x+1)返回99
F4|0x400555|repz retq|9|11|99|0x7fffffffe818|0x400565|从first(x)返回99
M2|0x400565|mov|9|11|99|0x7fffffffe820|-|%rdx=99

练习题3.33

1.(int a,char b,int *u, char *v)
2.(int a,byte b,int *u, byte *v)
更正:
1.(int a,short b,long *u, char *v)
2.(int b,short a,long *v, char *u)
要注意movb只是传送低8位,不代表b的类型是char,而是说明v的类型是char指针。b的类型应该通过函数的返回值6确定为2字节大小的类型。

练习题3.34

A.a0-a5
B.a6、a7
C.寄存器不够用

练习题3.35

A.x
B.

void rfun(unsigned long x){
   
	if (x == 0)
		return 0;
	unsigned long nx = x>>2;
	long rv = rfun(nx);
	return rv + x;
}

练习题3.36

数组 元素大小 整个数组的大小 起始地址 元素i
S 2 14 x S x_S xS x S + 2 i x_S+2i xS+2i
T 8 24 x T x_T xT x T + 8 i x_T+8i xT+8i
U 8 48 x U x_U xU x U + 8 i x_U+8i xU+8i
V 4 32 x V x_V x
  • 21
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八云黧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值