欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【C语言】整型溢出和整型提升

程序员文章站 2022-07-02 20:34:45
什么是整型溢出: c语言的整型问题相信大家并不陌生了。对于整型溢出,分为无符号整型溢出和有符号整型溢出。 对于unsigned整型溢出,c的规范是有定义的—&mda...
什么是整型溢出:

c语言的整型问题相信大家并不陌生了。对于整型溢出,分为无符号整型溢出和有符号整型溢出。

对于unsigned整型溢出,c的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。例如:

unsigned char x = 0xff; printf("%d\n", ++x);

上面的代码会输出:0 (因为0xff + 1是256,与2^8求模后就是0)

对于signed整型的溢出,c的规范定义是“undefined behavior”,也就是说,编译器爱怎么实现就怎么实现。对于大多数编译器来说,算得啥就是啥。比如:

signed char x =0x7f; //注:0xff就是-1了,因为最高位是1也就是负数了

printf("%d\n", ++x);

上面的代码会输出:-128,因为0x7f + 0x01得到0x80,也就是二进制的1000 0000,符号位为1,负数,后面为全0,就是负的最小数,即-128。

另外,千万别以为signed整型溢出就是负数,这个是不定的。比如:

signed char x = 0x7f; signed char y = 0x05; signed char r = x * y; printf("%d\n", r);

上面的代码会输出:123

相信对于这些大家不会陌生了。、

什么是整型提升:

多数c程序员以为,整型间的基本操作都是安全的。事实上,整型间基本操作也容易出现问题,例如下面的代码:

int main(int argc, char** argv) {

long i = -1;

if (i < sizeof(i)) {

printf("ok\n");

}

else {

printf("error\n");

}

return 0;

}

上述代码中,变量i被转换为无符号整型。这样一来,它的值不再是-1,而是size_t的最大值。变量i的类型之所以被转换,是因为sizeof操作符的返回类型是无符号的。

先看下列代码:

#include

int array[] = {1, 2, 3, 4, 5, 6, 7};

#define total_elements (sizeof(array) / sizeof(array[0]))

int main()

{

int i = -1;

int x;

if(i <= total_elements - 2) {

x = array[i + 1];

printf("x = %d.\n", x);

}

printf("now i = %d.\n", total_elements);

return 0;

}

执行结果:

randy@ubuntu:~/c_language$ ./a.out

now i = 7.

是不是很奇怪?为什么没有打出line13的x = ?。

是这样的。这个小例子有三点值得注意:

1.sizeof()是运算符,返回类型是无符号的,即非负数。

2.if语句在singned int和unsigned int之间进行判断语句,根据c语言的整型提升规则,如果原始类型的所有值都可用int类型表示,则其值将被转换为int类型;否则将被转换为unsigned int类型。因此此时的i将会被升级成无符号类型。

3.i = -1被升级为无符号型,值究竟是多少?这要用到整型转换规则:k&r上这样解释,将任何整数转换为某种指定的无符号数类型数的方法是:以该无符号数类型能够表示的最大值加1为摸,找出与此整数同余的最小的非负值。听着很拗口,其实说白了,只要知道原整数的二进制表达方法,再用要即将转换的类型去解析,就得到升级后的值了。 比如-1,负数在计算机里用补码表示,0xffffffff,那升级成无符号型之后值就是0xffffffff,显然比total_elements(7)大。

k&r c中关于整型提升(integral promotion)的定义为:

"a character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. if an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. this process is called integral promotion."

上面的定义归纳下来就是以下两个原则:

1). 只要一个表达式中用到了整型值,那么类型为char、short int或整型位域(这几者带符号或无符号均可)的变量,以及枚举类型的对象,都可以被放在这个整型变量的位置。

2). 如果1)中的变量的原始类型值域可以被int表示,那么原值被转换为int;否则的话,转为unsigned int。

以上两者作为一个整体,被成为整型提升(integral promotion)

整型提升的概念容易与普通算术类型转换产生混淆。这两者的区别之一在于后者是在操作数之间类型不一致的情况下发生,最终将操作数转换为同一类型。而在算术运算这种情景下,即使操作数具有相同的类型,仍有可能发生整型提升。

例如:

char a, b, c;

c = a + b;

在上述过程中,尽管两个运算符"+"和"="的操作数全为char型,但在中间计算过程中存在着整型提升:对于表达式a+b ,a、b都是char型,因此被提升至int型后,执行“+”运算,计算结果(int型)再赋值给c(char型),又执行了隐式的类型转换。

sizeof(a+b)------>值为4

还有个小问题提醒下大家:

整型提升只会发生在算术运算时,sizeof ('a') 应该没有整型提升,而是在c中'a'为int型,sizeof ('a')=4;而在c++中sizeof ('a')=1。