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 1≤n≤w。注意 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;
}