C语言【隐式类型转换】和【显式类型转换】

一、前言

C语言是一种强类型语言,当使用一种类型代替另外一种类型进行操作时或者存在两个不同类型的对象进行操作时,首先就得进行类型的转换然后再说其他。而类型转换的方式一般可分为隐式类型转换(也称:自动类型转换)和显示类型转换(也称:强制类型转换),两者有着本质上的区别。

隐式类型转换是由编译器自动进行的,不需要人为的干预,而且我们也观察不到类型是如何进行转换的,所以被称为:“ 隐式 ”。而显式类型转换是由程序员明确指定的,所以才被称为:“ 强制类型转换 ”。

二、隐式类型转换

有些表达式的操作数在求值的过程中可能需要转换为其他的类型,而这种你不知晓的类型上的转换通常分为两种情况:整形提升和运算转换。

在讲整形提升和算数转换之前,还得再补充一个知识点:截断操作。何为截断操作呢?下面举个例子:char a = 3;。大家有没有想过字面常量3是如何存储到char变量a中去的呀?注意这里的字面常量3本质上是一个整数,而一个整数所占的内存空间是32个二进制位,所以这时的3在内存存放的补码如下图所示。但是你想啊,字符变量a只能存放1个字节(即:8个二进制位),怎么可能放得3呢。所以这个时候就要发生截断了,截断的规则:挑低位的字节数,放置到需要截断存储的变量中去,如下图所示。

请添加图片描述

2.1 整形提升

C语言的整形算数运算总是至少以默认的整形类型(int型)的精度来进行,也就是说参与运算的操作数最小也不能小于4个字节的精度,如若精度小于4个字节该操作数就必须提升成整形的精度。所以为了获得这个精度,表达式中字符型(char)和短整型(short)操作数在使用之前就应该被转换为普通整形,这种转换被称为:整形提升。

那大家有没有想过为什么最小是以整形的精度来进行运算呢?其实表达式的整形运算是在CPU内的整形运算器(ALU)执行,而该运算器操作对象的字节长度一般就是int型的字节长度。因此CPU是无法实现直接对2个char类型的操作数的运算,而是通过先转换为CPU内整形操作数的标准长度然后再进行加法运算的。

整形提升前提条件:只有当表达式中出现长度可能小于int型的整形值时,才须要对该值进行整形提升转换为int或unsigned int型,然后再送入CPU去执行运算。

整形提升的规则:对于有符号的整形变量来说,整型提升是在高位补变量的符号位;而对于无符号的整形变量来说,整形提升是直接高位补0来实现的。
  
整形提升的意义
  表达式的整形运算要在cpu的相应运算器件内执行,cpu内整形运算器(ALU)的操作数的字节长度一般就是 int字节长度,同时也是cpu的通用寄存器的长度。
  因此,即使两个 char类型的相加,在cpu执行时实际上也要先转换为cpu内整形操作数的标准长度
通用cpu是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以表达式中各种长度可能小于int长度的整形值,都必须先转换为 int 或 unsigned int,然后才能送入cpu去执行运算。

2.1.1 例题1

请添加图片描述
  问上面这个程序最终打印结果为什么是-126,是怎么算出来的呢?下面我们来细致的分析一下,如下图所示。首先char型变量a和b中分别放入截断后的3和127,然后将a+b整形提升运算后的结果进行截断操作放入char型变量c中,最后printf打印%d整形值时对变量c中的值进行按符号位的整形提升,得出的结果当然是-126啦。
请添加图片描述
没错结果是 -126

怎么算的呢?

来看,我们一步步的算

char a = 3;
这里3是一个整数,整数类型所占的内存空间是32比特位,所以3在内存中的二进制序列为00000000 00000000 0000000 00000011
但是这里用来存放常量3的变量a是char类型,其所占内存空间为8比特位,这时候c语言就会将其截断,截断的规则为将最低为的一个字节截断存储在a里面。
这时候a里面存的就是00000011

char b = 127;
同理127的二进制序列00000000 00000000 00000000 01111111
存到b里面的为01111111

char c = a + b;
那么a和b如何相加的呢?
这里a和b都是char类型,它的大小没有达到整形的大小,计算机为了提升计算精度这时候会整形提升,如何提升的?
整形提升是按照变量数据类型的符号位来进行提升的

a 为 00000011
提升后为 00000000 00000000 00000000 00000011
b 为 01111111
提升后为 00000000 00000000 00000000 01111111
注意:并不是将高位补0就完事了,这里补0是因为它们的符号位都为0也就是都为正所以才补0,如果为负符号位就为1,那么就补1.
这个时候就将a和b直接相加为00000000 00000000 00000000 10000010
然后因为c是char类型所以又要截断
这时候c里面就存的 10000010

printf(“%d”, c);
注意这里打印c的时候它的转换说明为% d,意思就是打印一个整形,但是c是char类型,这时候我们又要整形提升了,这里他的符号位为1那么就高位补1 为:
11111111 11111111 11111111 10000010
再注意我们内存中存的是补码,打印的是源码,所以还要补码转源码
先转为反码为 11111111 11111111 11111111 10000001
然后再转为源码为 10000000 00000000 00000000 01111110
最后打印为 -126

2.1.2 例题2

请添加图片描述
  程序如上图所示,问为什么只有c是成功打印的,而a和b却是不行的呢?先拿char型变量a来说吧,首先我们知道a截断存储了十六进制数0xb6,内存补码为:10110110。但当执行到if语句的时候,a需要进行关系运算时需要进行有符号的整形提升,所以整型提升时应该在高位补符号位,结果为:11111111 11111111 11111111 10110110,与0xb6的二进制序列:00000000 00000000 00000000 10110110当然不一样呀,所以if(a == 0xb6)判断的结果自然为假并不会打印a了呀。同理于short类型的变量b也是如此并不会被打印,而int类型的变量c由于其并不用进行整形提升,所以判断结果为真打印了c。

2.1.3 例题3

请添加图片描述
  很多人会觉得这道题的结果是两个1,但值得注意的是上面这个例子中 sizeof(+a) 计算的是+a这个表达式值的类型所占的内存空间的大小,而+a表达式的值是a进行整形提升后的结果,所以sizeof的结果为4个字节。

2.2 算术转换

我们刚刚讨论的是类型小于整形的情况,而算数转换是用来处理这些大于等于整形的情况。如果某个操作符的各个操作数属于不同的类型,那么计算是无法进行下去的,除非将这些操作数全都转化为同一类型。寻常算数转换的方向:
  int→unsigned int→long int→unsigned long int→float→double→long double

举个易错的例子:
请添加图片描述
肯定有人会说打印结果必然是hehe,因为 sizeof(i) 的结果是4嘛,必然大于-1的。可真的是这样吗?

请添加图片描述
  为什么会这样呢? 这是因为在执行 if(i < sizeof(i))判断的时候隐蔽的发生了算数转换,将i的类型从int型转换成了 unsigned int 型,我们知道int型 -1的补码在 unsigned int 下是一个非常大的整数必然远大于 sizeof(i) 计算出来的4,所以打印结果为haha。那为什么i会发生算数转换呢? 这是因为 sizeof() 表达式的返回值的类型其实是unsigned int型的。

三、显式类型转换

显示类型转换又被称为强制类型转换,是由我们所决定该转换成什么样的类型,通常在转换时会存在存储精度的损失,所以在使用时需要谨慎。强制类型转换规则: (强制转换的类型)表达式

举个列子:
请添加图片描述
  你会发现强制类型转换 (char)a; 其实就像之前的截断操作一样,取后面的低8位然后存储到char类型的b中去,然后在printf打印的时候再按位整形提升,结果自然为-46。

[1] https://blog.csdn.net/m0_66769266/article/details/124842985
[2] https://blog.csdn.net/wangshangming0/article/details/124952587

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值