您的位置:首页 > 编程语言 > C语言/C++

C语言数据类型重新认识

2014-08-12 19:23 260 查看
注:本篇文章是对《深入理解操作系统》这本书的笔记。

无符号运算:

以一个四位数字表示,x=9,y=12;分别是1001 和1100,它们的和是21,相加后5位表示为10101,但是如果丢弃最高位,我们就得到了0101也就是十进制的5,他就和21mod15的值一样了。是否溢出是看和的最高位w+1是否被置1,如果等于0,和原来4位一样丢弃也没有影响。如果溢出相当于从和减去16.

int  uadd_ok(unsigned char x,unsigned chary)

{

unsigned char s=x+y;

printf("%d  ",s);

printf("%d",s>=x) ;

}


对于一个w位的数判断是否溢出的方法,

设s=x+y;

0<=x<=2^w-1;

0<=y<2^w-1;

所以0<=s<=2^(w+1)-2;

我们要判断s是否等于x+y,因为如果正常s=x+y>=x,如果s确实溢出了,s=x+y-2^w,假设y<2^w,我们就有y-2^w<0,因此s=x+(y-2^w)<x.,即s<x或者s<y的时候就可以了.

补码加法:

给定:-2^w-1<=x,y<=2^w-1之内 整数值,他们的和就在范围-2^w<=x+y<=2^w-2之内。要准确表示可能需要w+1位,我们通表示截断到w位,来避免数据大小的扩张,然而结果却不像模数加法那样熟悉。

还是以4位数的为例子。

一共有四种情况:

x y x+y 截断后结果 情况

-8[1000] -5[1011] -13[10011] 3[0011] 1

-8[1000] -8[1000] -16[10000] 0[0000] 1

-8[1000] 5[0101] -3[11101] -3[1101] 2

2[0010] 5[0101] 7[00111] 7[0111] 3

5[0101] 5[0101] 10[01010] -6[1010] 4

当w=4的补码加法,运算数的范围是-8~7,当x+y<-8的时候,补码加法会负溢出,导致和增加了16,当-8
<x+y<8的时候,加法就产生x+y。当x+y>=8的时候,会正溢出,使得结果减少了16。

判断是否溢出,无符号整数。

//有符号型判断。

int tadd_ok(char x,char y)

{

//这是答案 答案的数据类型为整形。

char sum=x+y;

intneg_over=x<0&&y<0&&sum>0=0;

intpos_over=x>=0 && y>=0 && sum<0;

return!neg_over && !pos_over;

}

int tadd_ok_2(char x,char y)

{

//答案说这个不行,我试了下可以的,答案的类型是整形

//阿贝尔群知识不懂

char sum=x+y;

return (sum-x==y)&&(sum-y==x);

}

void inplace(int *x,int*y)//交换两个变量的值,不用临时变量

{

*y=*x^*y;

*x=*x^*y;

*y=*x^*y;

}//不可自己交换自己否则得0

void reverse_array(int a[],int cnt)

{

intfirst,last;

for(first=0,last=cnt-1;first<=last;first++,last--)

{

voidinplace(&a[first],&a[last]);

}

}

/*上面这个函数包含元素个数为偶数个的时候可以,奇数不可以,奇数时候最后一次交换中间值本身,inplace函数中运算会的到0,所以把first<=last改成first<last.*/

//检验乘法是否溢出

int tmult_ok(int x,int y)

{

intp=x*y;

return!x || p/x==y;

}


//在int tadd_ok_2(char x,char y)不成立的前提下无法理解用除法判断乘法溢出,并且无法看懂解释:

1:说明x和y的乘积x*y可以写成x*y=p+t*2^w;,其中t不等于0,当且仅当p的计算溢出。

2.说明p可以写成这样形式:p=x*q+r|r|<|x|;

3.说明q=y当且仅当r=t=0;

//这个函数对于数据类型为int 的32位情况,使用64位精度数据类型long long 而不使用除法。

int tmult_ok_2(int x,int y)

{

longlong pll =(long long )x*y;

returnpll==(int)pll;

}


乘法溢出漏洞案例:

Sun Microsystems公司的XDR。

代码

void *copy_elements(void *ele_src[],intele_cnt,size_t ele_size)

{

void *result=malloc(ele_cnt *ele_size);

if(result==NULL)

return NULL;

void *next=result;

int i;

for(i=0;i<ele_cnt;i++)

{

memcpy(next,ele_src[i],ele_size);

next+=ele_size;

}

return result;

}


函数copy_elements的设计上将ele_cnt个数据结构复制到申请得到的数据缓冲区内,每个函数结构包含了ele_size个字节,需要的字节是通过ele_cnt*ele_size得到的。

如果怀有恶意的人把参数ele_cnt等于1048577(2^20+1)ele_size等于(2^12)4096,来调用这个函数,然后乘法上会溢出,导致只分配4096个字节而不是装下这些数据所需要的

4294971392个字节,从循环开始的会试图复制所有字节,超越已经分配的缓冲区的界限,因而破坏了其他的数据结构,导致程序崩溃或者行为异常。

修复代码:

long long unsignedrequired_size=ele_cnt*(long long unsigned)ele_size;

size_t request_size=(size_t)required_size;

if(request_size!=required_size)

return NULL;


修复代码的后面三句至关重要,删除后没有作用,malloc调用的时候会把longlong
unsigned 当成一个32位的无符号数。还是溢出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: