数据溢出问题
#include <stdio.h>/*
数据溢出
*/int main()
{char i; // 数据表示范围[-128,127] 0xf0 ~ 0x7ffor(i=0;i<130;i++) // {printf("%d ",i);}return 0;
}/*
编译运行上面的程序,你会发现程序陷入了死循环,一直在不断打印。当我们给一个变量赋一个超出其能表示范围的数时,就会发生
数据溢出,如上面的示例代码所示,导致程序运行出现异常。对于有符号数,当发生数据溢出时,由于C语言的语法宽松性,不对数据类型做安全性检查,因此也不会触发异常,但是会产生一个
未定义行为。什么是未定义行为呢?通俗点理解,就是遇到这种情况时,C语言标准也没有规定该如何操作,各家编译器在处理这种情况时也就没有了参考标准,各自按照自己的方式处理,编译器都不算错误。这也导致了当有符号数发生溢出时,运行结果是不确定的,在不同的编译器环境下编译运行,结果可能不一样。
*/
#include <stdio.h>/*
数据溢出(无符号)
*/int main()
{unsigned char a=255; // unsigned char的数据表示范围为[0,255]printf("%u\n",a);a++;printf("%u\n",a);return 0;
}
一般来讲,无符号数溢出时会进行取模运算,继续“周期轮回”。
例如,一个unsigned char类型的数据,它能表示的数据范围为[0,255],当其循环到255最大值时继续加1,这个数就变成了0,开始新的一轮循环,周而复始。
#include <stdio.h>/*
数据溢出
*/int main()
{signed char a=127; // unsigned char的数据表示范围为[0,255]printf("%d\n",a);a++;printf("%d\n",a);return 0;
}
/*
大家可以尝试在不同的编译器环境下编译运行上面的程序,你会发现大部分结果都是-128,也就是说大部分编译器都默认采用了与无符号数一样的轮回处理
*/
int main()
{unsigned char a=0; // a的取值[0,255]printf("a=%d\n",a);a--; // 周期轮回,a变成255printf("a=%d\n",a);return 0;
}
int print_star( unsigned int len)
{int i=1;while(len-- >=0){if(i++%20==0)printf("\n");printf("*");}printf("\n");return 0;
}
以上程序的设计存在问题,如果我们在调用该函数时给它传递一个实参-1,你会发现该程序将陷入死循环。这是因为 l e n len len是unsigned int类型,unsigned int类型在递减的时候存在数据溢出的问题,当减到-1时数据发生溢出, l e n len len的值变成 2 32 − 1 {2}^{32}-1 232−1,所以在 l e n len len递减的情况下, l e n ≥ 0 len\ge0 len≥0
为了防止这种问题发生,我们有两种解决方法:可以将函数的参数类型设置为signed int,或者在print_star()函数中对传进来的实
参进行判断(如代码中注销的示例代码),预防由于隐式类型自动转换而使代码的程序逻辑发生不确定的变化。
如何防范数据溢出?
对于有符号数的相加,方法其实很简单,我们先看看两个有符号数相加的情况。如果两个正数相加的和小于0,说明运算过程中发生了数据溢出。同理,如果两个负数相加的和大于0,也说明数据发生了溢出。
#include <stdio.h>/*
数据溢出
*/int main()
{char a =127;char b=30;char c=a+b;if(c<0){printf("data overflow!!\n");}else{printf("%d\n",c);}return 0;
}
对于无符号数的相加,如果两个数的和小于其中任何一个加数,此时我们也可以判断数据在计算过程中发生了溢出现象。
#include <stdio.h>/*
数据溢出
*/int main()
{unsigned char a =255;unsigned char b=30;unsigned char c=a+b;if(c<a || c<b){printf("data overflow!!\n");}else{printf("%d\n",c);}return 0;
}