[toc]

整型提升

要想知道什么是整型提升,那就要从表达式求值说起

表达式求值

表 达 式 求 值 的 顺 序 一 部 分 是 由 操 作 符 的 优 先 级 和 结 合 性 决 定

同 样,有 些 表 达 式 的 操 作 数 在 求 值 的 过 程 中 可 能 需 要 转 换 为 其 他 类 型

什么是表达式? 举个最简单的例子

1
2
3
4
5
6
7
8
int main()
{
int a=3;
int b=5;
int c = a + b * 7;
//c的式子即为表达式
return 0;
}

而C的整个式子就是一个表达式求值

在这之中,参与计算的两个变量以及结果变量都是int类型,并不需要进行整型提升

在我们日常编写代码的时候,编译器经常会有隐式类型转换


隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转化为普通整型,这种转换称为整型提升

1
2
float f =3.14
int num =f;

在上面这个代码中就有隐式类型转换,由float型转换为int型

但是会有精度的丢失

此时num为3


我们知道,不同数据类型的数据大小如下

数据类型及大小

char 字符数据类型 4byte
short 短整型 2byte
int 整型 4byte
long 长整型 4(32), 8(64)
long long 长长整型 8byte
float 单精度浮点型(实型) 4byte
double 双精度浮点型(实型) 8byte

可以看到,short类型和char类型的字节数都是小于int类型的

当我们将一个int类型的数据存放到char中时,会发生截断

截断&提升

把四个字节放在1个字节的内容中,截断,只保留一个字节

把数字5放入char类型中,5的4个字节的内容会被截断

示例

1
2
3
char a = 3;
//00000000000000000000000000000011 为3的二进制
//00000011 -a

在char类型a中,只存放了3的后8位(1个字节)的内容

这就是截断的表现形式


在char类型数据的表达式求值中,就会发生整型提升

规则如下:

把最高位视为符号位进行提升,在a和b的8位码前加24个0或1

代码示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
int main()
{
char a = 3;
//00000000000000000000000000000011 为3的二进制
//00000011 -a
char b = 127;
//00000000000000000000000001111111 为127的二进制
//01111111 -b
char c = a + b;
//把最高位视为符号位进行提升
//在a和b的8位码前加24个0
//00000000000000000000000000000011
//00000000000000000000000001111111
//00000000000000000000000010000010

//10000010 为char c
//11111111111111111111111110000010 -补码
//11111111111111111111111110000001 -反码
//10000000000000000000000001111110 -原码
// -126
//发现a和b都是char类型的,都没有达到一个int的大小
//这里就会发生整型提升
printf("%d\n", c);//%d是有符号数,打印原码
return 0;
}

编译器中,负数是以补码的形式存放的

但在使用的时候,要先转换成原码再计算其数值

代码示例2

再来看一个比较大小的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;

if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");

return 0;
}

猜猜打印结果是什么?

是这个吗?

1
2
3
a
b
c

错!

image-20211117204908103

运行后,我们会发现编译器只打印了c这个字符

因为if只会比较整型

而char和short类型都不足4个字节,在比较的时候需要进行整型提升

提升之后的结果与原数据不同

而int c本身就是整型,无须进行整型提升

1
int c = 0xb6000000;

所以代码只打印了字符c


代码示例3

再来看下面的这个代码,打印的结果是几呢?

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
short s = 5;
printf("%u\n", sizeof(s = a + 3));

return 0;
}

结果为2

因为最后的s是短整型,推断出来的长度为2


代码示例4 -sizeof

1
2
3
4
5
6
7
8
9
10
int main()
{
char c=1;
printf("%u\n",sizeof(c));//1
printf("%u\n",sizeof(+c));//4
printf("%u\n",sizeof(-c));//4
printf("%u\n",sizeof(!c));//4

return 0;
}

当+c和-c参与计算的时候,就会进行整型提升

这里涉及到了另外一个重要的知识点,sizeof括号中表达式的问题

下篇博客会详细介绍!


算术转换

说完整型提升,接下来就是和隐式类型转换很像的算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另外一个操作数的类型,否则操作就无法进行。

下面的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

具体是怎么转换的呢?

在进行运算的时候,如果某个操作数的类型在上表中排名较低,那么首先要转换为另外一个操作数的类型后,再执行运算。

简单地说,就是从下往上转换

注意:算术转换要合理,不然会有潜在的问题


代码示例1

下面的是一个简单算术转换的示例

在int型和float类型一起计算的时候

int类型会转换为float类型进行计算

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h> 

int main()
{
int a=3;
float f =5.5;
float ra =a+f;
//算术转换,int-->float

return 0;
}

表达式属性

这里可以引出另外一个知识点,表达式的属性

表达式的两个属性:

  • 值属性
  • 类型属性

如以下代码中

  • a+b的结果30就是这个表达式的指属性

  • 类型属性是int

1
2
3
4
5
6
7
8
9
int main()
{
int a =10;
int b=20;
a+b;//表达式2个属性:值属性,类型属性
//30 值属性
//int 类型属性
return 0;
}

结语

本篇博客到这里就结束了

如果对你有帮助的话,还请点个赞再走吧!

这对我非常重要!