关于有符号数与无符号数的建议
有符号数到无符号数的隐式类型转换导致了某些非直观的行为。
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;
}