深入了解计算机系统课后习题


关于有符号数与无符号数的建议


有符号数到无符号数的隐式类型转换导致了某些非直观的行为。

float sum_elements(float a[], unsigned length) {
		int i;
		float result=0;
		
		for(i=0;i<=length-1;i++)
			result += a[i];
		return result;	
}

当参数length等于0时,运行这段代码应该返回0.0,但实际上,运行时会遇到一个内存错误。为什么会发生这样的错误?

在这里插入图片描述
从调试可以看出,当为unsigned类型 的length等于0时,length-1会把结果隐式转换为无符号数,-1的无符号32位补码是一个很大的正整数,这样形成非法访问内存,数组越界了。造成的结果会意想不到。

修改:

float sum_elements(float a[], int length) {
		int i;
		float result=0;
		
		for(i=0;i<=length-1;i++)
			result += a[i];
		return result;	
}

在这里插入图片描述


现在有一个任务,写一个函数用来判定一个字符串是否比另一个更长,前提是要用字符串库函数strlen。它的声明如下:

size_t strlen(const char* s);

int strlonger(char* s,char* t){
	return strlen(s)-strlen(t) >0;
}

注:在头文件stdio.h中,size_t 为unsigned int 类型。

1、在什么情况下,这个函数会产生不正确的结果?

当字符串t 的长度大于字符串长度s 时,相差的结果在无符号32位表示下是一个很大的正整数。理应结果返回0,可返回1.

在这里插入图片描述

修改:相差变为比较

int strlonger(char* s,char* t){
		if(strlen(s)strlen(t))  return 1;
		else return 0;
}

一个字中的字节从0(最低有效字节)编号到3(最高有效字节)。函数会抽取指定的字节,再把它符号扩展为一个32位int。

int xbyte(unsigned x, int bytenum) {
	return (x>>(bytenum<<3))&0xff;
}

这段代码错在哪里?
给出函数的正确实现,只能使用左右移位和补码减法。

这段代码错误:在进行一个字节截取后,由unsigned类型转换成int进行符号扩展,在一个字节中最高位为1时代表为负数,可进行转换后并非得到我们想要的那个负数,而是在8字节前添上24个0的结果。

例如:0x00 00 00 FF 截取其中一个字节并进行符号扩展后理应返回结果为
0xFF FF FF FF(-1) 但返回结果却是:0x00 00 00 FF

修改如下:


int xbyte(unsigned x, int bytenum) {
	return 
 (x >> (bytenum << 3) & 0x7f)- (x >> (bytenum << 3) & 0x80);
}

例如:
假如w位为3
-3的补码: [101]B=1-2w-1=1-4=-3
假如w位为4
-7的补码: [1001]B=1-2w-1=1-8=-7


给你一个任务,写一个函数,将整数val复制到缓冲区buf中,但是只有当缓冲区有足够可用的空间时,才执行复制。

void copy_int(int val, void* buf, int maxbytes) {
	if (maxbytes - sizeof(val) >= 0) {
		memcpy(buf, (void*)&val, sizeof(val));
	}
}

仔细地测试了这段代码后发现,哪怕maxbytes很小的时候,他也能把值复制到缓冲区。

A、解释下为什么代码中的条件测试总是成功。提示:sizeof运算符返回类型为size_t ?

因为sizeof返回的类型为无符号整型,在进行计算后的结果也为无符号整型。
在这里插入图片描述

修改:

void copy_int(int val, void* buf, int maxbytes) {
	if (maxbytes >sizeof(val) ) {
		memcpy(buf, (void*)&val, sizeof(val));
	}
}

在这里插入图片描述

一个缓冲区用maxbytes肯定是无符号整型的,当一个较小的无符号数减去一个较大的无符号数就会形成溢出,形成一个很大的正数。故我们可以用比较。


写出具有以下原型的函数代码:

//Addition that saturates to Tmin or TMax
int saturating_add(int x,int y);

同正常的补码加法溢出的方式不同,当正溢出时,饱和加法返回TMax,负溢出时,返回TMin。饱和运算常常用在执行数字信号处理的程序当中。

你的函数应该遵循位级整数编码规则

int saturating_add(int x, int y) {
	int sum = x + y;
	unsigned char x_ =(unsigned) x >> 31;
	unsigned char y_ = (unsigned)y >> 31;
	unsigned char sum_ = (unsigned)sum >> 31;
	if ((x_ ^ y_ )== 0 && (x_ ^ y_ ^ sum_) == 1)
		return INT_MAX;
	else if ((x_ ^ y_) == 0 && (x_ ^ y_ ^ sum_ )== 0)
		return INT_MIN;
	else
		return sum;
}

2.62 编写一个函数int_shifts_are_arithmetic() ,在对int类型的数使用算术右移的机器上运行时这个函数生成1,而对其他情况下生成0,你的代码应该可以运行在任何机器上。

int int_shift_are_arithmetic() {
	int x = INT_MIN;
	unsigned x_ = x;
	int n = sizeof(int) * 8 - 1;
	return x >> n == -1 ? 1 : 0;
} 

在这里插入图片描述
在对负数进行右移时,一般采取算术右移。INT_MIN(即除了最高位为1,其余各位都为0)在w位机器上进行w-1次的算术右移会得到w位全为1的情况。


任何奇数位上存1都返回1

/*Return 1 when any odd bit of x equals 1; 0 otherwise.*/
int arr_odd_one(unsigned int x) {
	while (x) {
		x = x >> 1;
		if (x & 1) return 1;
		x = x >> 1;
	}
	return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值