以一个四位数字表示,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位的无符号数。还是溢出。