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

C语言【隐式类型转换】和【显式类型转换】,第1张

一、前言

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/2991785.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存