CSAPP 第二章课后作业,主要是位运算

2.58

判断大小端存储

int is_little_endian() {
    union {
        int a;
        char b;
    } u;
    u.a = 1;
    if (u.b) return 1;
    else return 0;
}

2.59

生成一个数字,最低有效字节为 x 的最低有效字节,其余部分由 y 剩下字节组成

(x & 0xff) | (y & ~0xff)

2.60

将 x 中第 i 个字节置换成 b

int replace_byte(unsigned x, int i, char b) { // 将第 i 个字节置为 b
    /* char 可能是无符号,也可能是有符号 */
    i <<= 3;
    /* 将第 i 字节置 0 */
    x &= ~(0xff << i);
    /* 将 b 放到第 i 字节上 */
    x |= (unsigned char)b << i;
    return x;
}

2.61

!~x // x 全位等于 1 时真
!x // x 全位等于 0 时真
!~(x | ~0xff) // 最低有效字节全为 1 时真
!(x & (0xff << ((sizeof(x) - 1) << 3))) // 最高有效字节全为 1 时真

2.62

判断该机子右移是否为算术右移

int int_shifts_are_arithmetic() {
    int x = ~0; // 0xff...,0xff...算术右移还是0xff...
    x = !((x >> 1) ^ x); // 通过移动前后是否相同,判断是否为算术右移
    return x;
}

2.64

判断 x 是否奇数位上全是 1(x 限定为 32 位的 unsigned)

int any_odd_one(unsigned x) {
    return !!(x & 0x55555555); // 最低位从0开始
}

2.65

当 x 有奇数个 1 时返回真(x 限定为 32 位的 unsigned)

代码中算术运算、位运算和逻辑运算最多只能包含12个

int odd_ones(unsigned x) {
    x ^= (x >> 16);
    x ^= (x >> 8);
    x ^= (x >> 4);
    x ^= (x >> 2);
    x ^= (x >> 1);	
    return (x ^ 1) & 1;
}

2.66

生成一个掩码,取 x 的最高非零位(x 限定为 32 位的 unsigned)

代码中算术运算、位运算和逻辑运算最多只能包含5个

int leftmost_one(unsigned x) {
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    return x ^ (x >> 1);
}

2.67

实现一个函数,当在一个 int 是 32 位的机器上运行时为真

并指出 bad_int_size_is_32 错误之处

int bad_int_size_is_32() {
    int set_msb = 1 << 31;
    int beyond_msb = 1 << 32; // int 位移超过 31 位为未定义行为
    return set_msb && !beyond_msb;
}

int int_size_is_32() {
    int set_msb = 1 << 31;
    int beyond_msb = 2 << 32; 
    return set_msb && !beyond_msb;
}

int int_size_is_16() {
    int set_msb = 1 << 15;
    int beyond_msb = 2 << 15; 
    return set_msb && !beyond_msb;
}

2.68

生成一个掩码,将最低 n 位置为 1 。其中 1 ≤ n ≤ w 1≤n≤w 1nw。注意 n = w n=w n=w 的情况。

int lower_one_mask(int n) {
    return (2 << (n - 1)) - 1;
}

2.69

实现按字节的循环左移

unsigned rotate_left(unsigned x, int n) {
    int w = sizeof(x) << 3;
    int l = x << n;
    int r = x >> (w - n);
    return l | r;
}

2.70

当 x 可以被表示为 n 位补码时返回 1

// 题目含意是:n 位整型可以容纳 int 的 x 时返回 1
int fits_bits(int x, int n) {
    x >>= n - 1;
    return !x || !~x;
}

2.71

实现一个函数,抽取指定字节,将它符号扩展为 int。并指出 bad_xbyte 错误之处。

typedef unsigned packed_t;

int bad_xbyte(packed_t word, int bytenum) {
    /* unsigned 的右移是逻辑右移,成了 0 扩展 */
    return (word >> (bytenum << 3)) & 0xff;
}

int xbyte(packed_t word, int bytenum) {
    word = word << ((3 - bytenum) << 3); // 将保留字节移到最高位
    return word >> ((sizeof(int) << 3) - 8); // 符号扩展
}

2.72

实现一个函数,当 buf 缓冲区足够时,将 val 复制到 buf 中。并指出 bad_copy_int 错误之处。

/* 1. linux 中 sizeof 返回的是 unsigned
   2. 有符号与无符号运算转换为无符号 
   3. 有符号数与无符号数底层二进制序列相同,只是解释方式不同
   综上所述,maxbytes 不管是多少,都能复制进缓冲区
 */
void bad_copy_int(int val, void *buf, int maxbytes) {   
    if (maxbytes - sizeof(val) >= 0) {
        memcpy(buf, (void *) &val, sizeof(val));
    }
}

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

2.73

实现饱和加法,当正溢出时返回 Tmax,当负溢出时返回 Tmin

#define Tmax 0x7FFFFFFF
#define Tmin 0x80000000

/* 异号相加一定不会溢出
   同号相加才可能会溢出
     1. 当两个正数相加结果为负数时,出现正溢出
     2. 当两个负数相加结果为正数时,出现负溢出
     3. 当两个数相加符号位没改变时,没出现溢出
*/
int saturate_add(int x, int y) {
    int sx = x >> 31;
    int sy = y >> 31;
    int ss = (x + y) >> 31;
    /* 检测是否溢出 */
    int fl = !(sx ^ sy) && (sx ^ ss);
    /* INT_MAX 与 INT_MIN 差 1,正溢出偏置置 0,负溢出偏置置 1 */
    int offset = fl & sx;
    return INT_MAX + offset;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值