Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3785414
  • 博文数量: 880
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 6155
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-11 09:12
个人简介

To be a better coder

文章分类

全部博文(880)

文章存档

2022年(5)

2021年(60)

2020年(175)

2019年(207)

2018年(210)

2017年(142)

2016年(81)

分类: C/C++

2018-10-30 15:28:47

原文地址:C的“类型提升” 作者:lvyilong316

写在前面:本来因为一个朋友问我为什么可以给unsigned int赋值负数,我打算写一篇关于解释unsigned的文章。但是写的过程中发现有很多地方需要涉及其他的知识点,特别是关于整型提升和算数转换。所以就翻了一下之前看过的书,做了一个总结,感觉自己又学到了不少。由于我不是写教科书,有些用语和描述难免不够准确。另外,由于本人能力有限,有错之处在所难免,希望各位看到此文的朋友如果发现有什么错误请留言指正,或发邮件交流。

(电子邮箱:                  ——转载请注明出处,lvyilong316         

1.  C的“类型提升”

1.1 什么是整形提升

charshort int或者int位段(无论signedunsigned)以及枚举类型,可以使用在需要int或者unsigned int的表达式中。如果int可以完整的表示源类型的所有值,那么该源类型的值就转换int, 否则转换为unsigned int,这被称为整形提升。——《C专家编程

注意:“整形提升”是先提升到int,而不管源数据是signed还是unsigned,而不是char 提升到intunsigned char提升到unsigned int

1.2 什么是类型提升

就是整型提升和float提升为double的总称。

1.3 什么时候需要类型提升

这个问题比较关键,从整形提升的概念可以看出,在1】“charshort int或者int位段(无论signedunsigned)以及枚举类型出现在可以使用int或者unsigned int表达式中”的时候,就会发生整形提升。与此类似,我们可以得到floatdouble的提升情况。但是ANSI C对类型提升有了弱化。ANSI C标准说明如下:

char c1,c2;

c1=c1+c2;

类型提升规则要求把每个变量提升为int的长度,然后两个int值执行加法运算,然后在对运算结果进行裁剪。如果两个char的加法运算结果不会发生溢出,那么实际执行时只要产生char类型的运算结果,可以省略类型提升。同样道理也适用于floatdouble的提升。

2另一个会发生隐式类型转换的地方就是参数传递。 
 K&R C中,由于函数的参数也是表达式,所以也会发生类型提升。 
 ANSI C中,如果使用了适当的函数原型,类型提升便不会发生,否则也会发生。 在被调用函数内部,提升后的参数被裁剪为原先声明的大小。

这就是为什么单个的printf()格式字符串%d能适用于几个不同类型,shortcharint,而不论实际传递的是上述类型的哪一个。函数从堆栈中取出的参数总是int类型,并在printf[1]或其他或其他被调用的函数里按统一格式处理。所以如果使用printf来打印比int长的类型,如long long,除非使用long long格式化限定符%ld,否则无法获取正确的值。因为在缺少更多信息的情况下,printf假定他处理的数据是int类型。

[1]即使一个原型位于printf()能及的范围内,注意它的原型以省略号结尾:

int printf(const char* format,...);

表示它是一个接收可变参数的函数,参数的信息(除第一个外)均未给出,此时一般的参数提升始终会发生。

总结:判断是否发生类型提升记住上述的【1】【2】即可。

1.4 类型提升实例分析

  (1) printf(%d,sizeof(A))//输出4

分析:sizeof()是一个表达式,字符Achar类型提升到int,所以打印出是4int的大小),而不是1char的大小)。

  (2)char a,b;

printf ( " the size of the result of a+b :%d " ,sizeof( a+b) );  //输出4

* (3)char a;

printf ( "%d\n" ,sizeof(a)); //输出1

这个题要引起注意,为什么这里没有发生类型提升呢?我的理解是这样的:当传递的是字符常量的时候,sizeof是根据数值推断的,而当表达式使用到的时候,就会进行进行类型提升(回忆整型提升定义,这里可以用int值代替)。但当参数是变量时,此时sizeof(a)实际等价于sizeof(char),并没有用到数,所以输出1

1.5 如何提升

   我们把提升分为两个阶段——扩展和解释。

1.5.1 扩展

要想进行类型提升,很明显首先需要进行位扩展,比如char1字节)提升到int4字节)要在高位扩展3个字节。那么是如何扩展呢?答案是:有符号数按照有符号数的扩展规则(高位补符号位)扩展,无符号数按照无符号数的扩展规则(高位补0)扩展

我们先看一个例子:

int main()

{

 unsigned int a=0x12345678;

 unsigned char b=(unsigned int)a;

 char *c=(char*)&a;

 printf("%04x  %04x\n",b,*c ); 

 return 0;

}

/*

 *case1:unsigned int a=0x12345678;

 *output:0078  0078

 *

 *case2:unsigned int a=0x123456a8;

 *output:0078 ffffffa8;

 */

分析:

printf("%04x %04x\n",b,*c ); //实参为char,则要进行整形提升

*c是有符号的,做有符号扩展,高位补符号位。在case1中(78)符号位为0,因此提升后为:0x00000078,按宽度至少4位输出即0078。而在case2中(a8)符号位为1,因此提升后为:0xffffffa8,由于全是有效位,则忽略按4位宽度,输出:ffffffa8。 b提升成int,因为b是无符号的,所以做无符号扩展,高位补0

1.5.2 解释

   单单扩展完并没有完成任务,因为这涉及到程序该如何理解这些二进制位。这里我们再回到最开始的“整型提升”的定义——如果int可以完整的表示源类型的所有值,那么该源类型的值就转换int, 否则转换为unsigned int所以得到结论,优先解释为int。我们先看一个例子:

        char a = -1;

    unsigned char b = 1;

printf("%d", a > b);//输出0

分析:这个过程是这样的,首先对表达式a>b进行整型提升,a-1,内存表示为0xff,由于a是有符号数,所以扩展为0xffffffffb1,内存表示为0x01,由于b是无符号的,所以扩展为0x00000001。接下来是解释,编译器都将其解释为int(注意,这里同为int,所以不发生算数转换),所以0xffffffff解释为-10x00000001解释为1.因为-1<1;所以a>b不成立,输出0

这个例子要和下面这个例子区别:

    int a = -1;

    unsigned int b = 1;

printf("%d", a > b);//输出1

这个没有发生类型提升(变量已经是int),而是算数转换,我们后面专题再介绍。

那什么时候解释为unsigned int呢?答案是有unsigned int参与的时候,比如下面这个例子:

    char a= -1;

    unsigned int b = -1;

printf("%d\n",a==b);//输出1

其实这个应该算是算数转换,我们可以把它看做一次整形提升+一次算数转换,首先将char提升为intintunsigned int的比较和运算就要涉及到算数转换了。所以我们可以简单地记住,整形提升都解释为int

阅读(3391) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~