【csapp】【微软面试题】有符号数到无符号数隐式转换

来源:互联网 发布:日本身份证 知乎 编辑:程序博客网 时间:2024/06/05 14:38

大部分程序对无符号数类型的使用可能不是很频繁(实际上,仅C/C++等少数语言支持unsigned类型)。但是,unsigned到signed的隐式强制转换常常会导致程序错误与漏洞,比较著名的一个案例就是:函数getpeername的安全漏洞。


一、从一道微软面试题说起

微软面试题: 
<span style="font-size:18px;">unsigned int i=3;cout<<i*-1;                                                                                                         </span>
问输出是多少?
如果忽略了有符号数到无符号数的隐式转换,答案就是-3,但是微软面试题没那么简单。
运行一下上面的程序,发现结果是4294967293。其实是因为i是unsigned int类型,而-1是int类型,一个无符号,一个有符号,编译时会发生强制类型转换,统一将有符号数转化为无符号数,然后再进行运算。

解析:

首先,要明确三点:
1、unsigned int和int类型的数据大小均为4字节,即32位。(无论是32位字长机器还是64位字长机器)
2、机器一般都是用补码表示有符号数。
3、有符号数到无符号数的转换规则:位值保持不变,只改变解释这些位值的方式。
回到本题中,根据上面三点,下面分几步分析:

#1:-1的机器码表示
根据第1点,-1的机器码为:0xFFFFFFFF,即 1111...1111(32个1)

    知识补充:为什么 1111...1111 (32个1)表示-1?

    根据上面第2点,用补码表示有符合数。1111...1111 (32个1)是补码,它计算得到-1的过程如下:
    1*(-2^31)+1*2^30+1*2^29+...+1*2^1+1*2^0=-1

    注意,因为是补码,所以规定最高位的权重是负的,其他位的权重是正的,这样计算出来的数就表示有符号数。

    int可以表示的范围是 -2^31~2^31-1,十六进制0x80000000~0x7fffffff,十进制-2147483648~2147483647

#2:-1转化为无符号数是多少?

根据第3点,-1转化为无符号数时,保持位值不变,即转化后机器码仍然是0xFFFFFFFF,但是此时它表示无符号数,各个位置的权重均是正的,因此转化结果是1*2^31+1*2^30+...+1*2^0=4294967295

#3:运算

4294967295*3=12884901885,而unsigned int的表示范围为0~2^32-112884901885超出了该范围。

C语言中规定这种情况要采取模运算,即将无符号乘积模2^32(取余)

12884901885 mod 2^32 = 4294967293,相当于取乘积的低32位,将其写成十六进制形式会更加明显:0x2FFFFFFFD mod 0xFFFFFFFF = 0xFFFFFFFD



下面再举《深入理解计算机系统》中的两个例子

二、计算数组中元素的和,length为0引起的程序错误

代码:
<span style="font-size:18px;">float array_sum(float a[],unsigned length){int i;float sum=0;for(i=0;i<=length-1;i++)sum+=a[i];return sum;}</span>

注意函数中参数length被声明为unsigned类型,数组长度作为无符号数传递,看起来是很理所当然的做法,但是它却会导致意外的结果。
当我们调用array_sum(a,0)时,length=0,在计算length-1时,因为length是unsigned,所以(0-1)的结果也应该是unsigned,因此结果不是-1,而是Umax(Umax是unsigned int可以表示的最大数2^32-1)。这将导致运行错误,因为访问了数组的非法元素。
可以将i<=length-1改为i<length,或者length声明为int类型


三、用字符串库函数strlen判断字符串str1是否比str2长

代码:
<span style="font-size:18px;">/*strlen函数原型*/size_t strlen(const char *s);/*长度比较函数*/int strlonger(char *str1,char *str2){return strlen(str1)-strlen(str2)>0;}</span>

注意,strlen函数原型的返回类型是size_t,在stdio.h头文件中该类型是被定义成unsigned int类型,是无符号类型。
因此,strlen(str1)-strlen(str2)的结果恒大于0,无论str1比str2长还是短,结果均返回1。
可以将return strlen(str1)-strlen(str2)>0 改为return strlen(str1)>strlen(str2);


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果限制访问密码忘了怎么办 东奥会计书盗版怎么办 合同封印少盖一页怎么办 玩单机游戏屏幕输入不支持怎么办 汽车不小心陷进泥潭怎么办 招商银行信用卡激活电话换了怎么办 冲鼻子是耳朵进水了怎么办 不小心点了赞怎么办 小车间太热了怎么办 在车间上班好热怎么办 英雄杀四星李逵不拉仇恨怎么办 手表玻璃里面有雾水怎么办 dw手表玻璃碎了怎么办 手表玻璃面花了怎么办 有个窝囊的父母怎么办 苹果7p玩游戏卡怎么办 三星玩游戏很卡怎么办 三星打游戏很卡怎么办 香水喷到眼睛里怎么办 萍果平板锁机怎么办? 苹果6开机卡死怎么办 辐射4发夹用完了怎么办 辐射4多的武器怎么办 大姨妈恶心想吐怎么办 玩完游戏想吐怎么办 玩完了海盗船想吐怎么办 戴眼镜恶心想吐怎么办 玩电脑恶心想吐怎么办 玩游戏玩的头疼怎么办 玩游戏头疼想吐怎么办 游戏玩久了头疼怎么办 有3d眩晕症怎么办 玩游戏晕3d怎么办 梦幻西游亏的钱怎么办 普惠卡销户了钱存进去了怎么办 梦幻西游现金变储备了怎么办 孩子挣了钱存不下怎么办 电脑显示副本不是正版怎么办 斗战神师徒一个人删除角色怎么办 起业kx5防盗器不响怎么办 灌浆记录仪存盘满了怎么办