C生万物 | 揭开【整型提升】神秘面纱

简介: 什么是整型提升?不同类型的数据在内存中究竟是怎样变化的,本文让我们一起揭开🔥整型提升🔥的神秘面纱

一、前言

有关 隐式类型转换中的整型提升,相信对于很多读者来说比较神秘,但若是了解了这一块的小知识便可以很好地解开你对很多类型转换的一些困惑,清楚数据在内存中是如何发生变化的:mag:
  • 首先,在C语言中,整型算术运算总是至少以缺省整型类型的精度来进行的

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

二、整型提升的意义所在

:dart:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

:dart:因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度

:dart:通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为intunsigned int,然后才能送入CPU去执行运算

通过上面的陈述,相信你对整型提升有了一个初步的概念,接下去我们来看看如何去进行一个整型提升👇

三、如何进行整型提升❓

  • [x] 整形提升是按照变量的数据类型的符号位来提升的

对于整型提升来说,整数和负数是不一样的,首先我们来看看正数的整型提升

//正数的整形提升
char c1 = 1;
  • 首先先来写出1的32个二进制位00000000000000000000000000000001,然后将其转换为原码的形式,但是通过上面的学习我们可以知道对正数的原、反、补码是相同的,因此这就是它在内存中的形式
  • 但是可以看到现在要将这个整数1给到一个char类型的变量c2,此时,此时就会发生一个【截断】的现象,因为一个字符型的数据在内存中只占1个字节,也就是8个比特位,所以在截断之后就只剩下00000001
  • 接下去若是要去使用这个c1的话就会进行一个整型提升,此时在八个比特位的首部填充24个符号位,以达到整型4个字节32个比特位在内存中的要求,那么此时提升之后的结果便是00000000000000000000000000000001可以看出又回到了原来的1,不过若是你不去使用这个c1的话是不需要进行整型提升的

接下去再来看看负数的整型提升

//负数的整形提升
char c2 = -1;
  • 那对于负数其实也是一样,-1 在内存中的32位二进制补码为11111111111111111111111111111111。同理,将其给到一个整型的变量之后就会发生截断即为11111111
  • 那这个时候再进行一个整型提升就和正数不一样了,因为负数的符号位为1,而整型提升在高位是要补充符号位,所以会在前头加上24个1,那其实也就变回了和之前-1的补码一般的样子,为32个1
好,说完了该如何去进行整型提升之后我们就可以去代码段中看看到底是如何进行的

四、实战演练🗡

1、深剖两数求和的内部运算

首先来看第一个,我们要去计算两个整数的和,但是呢却要放到char类型的变量中去,那会发生什么化学反应呢:crystal_ball:

int main(void)
{
    char a = 5;
    char b = 126;
    char c = a + b;
}
  • 根据上一小节的讲解,相信你已经知道编译器第一步会做什么了,首先的话就是分别写出这两个整数的32个比特位,接着转换为补码的形式,正数三者均一致。然后因为要给到一个char类型的变量,所以会进行一个【截断】
00000000000000000000000000000101 - 5
——> 00000101 - 5【截断】
00000000000000000000000001111110 - 126
——> 01111110 - 126【截断】
  • 接下去我们要开始去使用到这两个字符型的变量了,使用它们进行一个加法运算,那么此时就会发生一个[整型提升],在高位补充24个符号位之后就变成了下面这样,然后便可以对它们去进行一个运算了
//到了内存中开始计算 —— 整型提升(正数)
00000000000000000000000000000101 - 5
00000000000000000000000001111110 - 126
  • 那在运算出来之后呢在计算机中是一个补码的形式,输出来便得是一个原码的形式,由于正数三者是一致,所以不发生改变(==一强调这点是因为想让读者搞懂每一步==)其实这时可以看到运算出来的数字是正确的,5 + 126 = 131
  • 可是呢可以看到左边又是拿了一个char类型定义的变量在接受这个运算后的结果,因此便又会发生一个【截断】,就只剩下1个字节8个比特位
  • 那其实在这个地方如果我用一个整型变量去接收一下然后再用%d做一个打印,那么此时就会输出正确的内容131
00000000000000000000000010000011 - 131
10000011 - 131【截断】

可是呢,我就是不用整形去接收,就是玩:satisfied:用字符型去接受,然后再用%d去打印(“主要还是为了加深知识点的灵活运用”)

printf("c的整数打印形式为:%d\n", c);
  • 那在若是在这个时候又要去进行打印的话,又要放到内存里面去运算了,调用这个printf()库函数其实也算是一个运算,也要放到内存里面去,然后这个变量c又不是整型,所以此时呢就又会发生一个[整型提升]
  • 此时就需要去补充10000011前面的24个符号位了
//整型提升(负数)
11111111111111111111111110000011 - 补码
11111111111111111111111110000010 - 反码
10000000000000000000000001111101 - 原码
  • 然后变要将这个32个二进制位以十进制的形式打印出来,可是计算机中的运算是采取补码的形式,打印输出的话就要采取补码的形式了,所以此时就需要将这个补码转化为原码了,可以看到这是一个负数的补码,所以转化为原码的时候要小心了,需要将补码-1然后再除符号位外均做取反
  • 此时再去转化为十进制的形式输出便是-125,我们来看看结果【全体起立:cop:】

在这里插入图片描述

2、三种不同数据类型的整型提升

接下去再来看看第二个栗子🌰
  • 上面呢我们只说到了字符类型的整型提升,下面呢我们再来看看短整型,它们都是属于整型数据类型的一种
  • 可以看到定义了三个变量,分别是字符型、短整型、整型,然后初始化了一个十六进制的数据,那我们可以将其转换为二进制的形式便为10110110,那其实到这里我就已经可以看出答案是多少了,只有最后一个if语句会进去,其余的都不成立
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;
}

在这里插入图片描述

  • 好,来解释一下为什么我一眼就可以看出最后的结果是多少,并不是因为我知道结果,而是我看到了这个十六进制的b,因为它的二进制为1011,可以看到首尾是为0,那么当这个变量a参与运算的时候就会发生一个[整型提升],在上面我说到过对于负数的整型提升和正数不一样,填充符号位后均为1,那么再转化为原码从计算机输出之后就一定不会是原来的值了,会发生一个改变👈
  • 对于char ashort b它们均不是一个整型int类型的数据,所以都会发生一个[整型提升],不过int c它就是一个整型的变量,所以是不会发生变化的
通过这个例子相信你对整型提升一定有了更加深刻的理解

3、整型提升与sizeof

最后一个小案例我们和sizeof做一个结合,顺便再回顾一下前面的知识点:scroll:

int main()
{
    char c = 1;
    printf("%u\n", sizeof(c));
    printf("%u\n", sizeof(c + 1));
    printf("%u\n", sizeof(+c));
    printf("%u\n", sizeof(-c));

    return 0;
}

在这里插入图片描述

  • 通过运行结果可以看到,有三个结果发生了整型提升,首先对于sizeof(c)很明确,计算的就是char这个数据类型的字节长度,也就是1,可以对于下三个结果为什么会发生整型提升呢?我们来分析一下:mag:
  • [x] 对于c + 1来说它是一个表达式,上面说到过若是一个char类型或者是short类型定义的变量参与了运算,那在内存中就会发生一个整型提升,那如果这是一个表达式的话也就相当于是参与了运算,整型提升后变为4个字节,具体细节不做展开
  • [x] 那对于+c-c来说就是我们前面说到过的单目操作符中的+-操作符,和一个操作数结合也可以说它是一个表达式,那么同理也会进行一个整型提升

但是我再将它们变个形却又不会发生【整型提升】了,一起来看看👇

char c = 1;
printf("%u\n", sizeof(c + 1));
printf("%u\n", sizeof(+c));

printf("-------------------\n");
printf("%u\n", sizeof(c = c + 1));
printf("%u\n", sizeof(++c));

在这里插入图片描述

  • 可以看到,若是将c + 1改换成了c = c + 1,就不会发生整型提升了,这是为什么呢?因为对于c + 1这个表达式来说确实会发生整型提升,但是呢我又将这个表达式计算后的结果放到c里面去,还记得我在讲述【sizeof()】的时候说到它里面的表达式是不会运算的吗,所以整个表达式的结果其实就是sizeof(c)的结果,和上面所列出的第一个是一样的
  • 再来看看这个++c,那又有同学会产生疑惑,为何+c会发生整型提升,但是++c却不会呢,其实对于++c来说就等价于c = c + 1,那其没有发生整型提升的原因相信你已经清楚了
以上就是有关【整型提升】要介绍的所有内容,看完这些相信你对计算机内部隐式类型转换一定有了一个深刻的了解😀

五、总结与提炼

好,我们来总结一下本文所学习的有关 整型提升的内容
  • 首先在开篇讲到了在C语言中为了获得缺省整型类型的精度,对表达式中的字符型和短整型会采取一个整型提升
  • 接着我们谈到了整型提升的意义所在是为了让CPU中的【算术逻辑运算单元ALU】获得一个整型操作数的标准长度,以此来达到更好的计算
  • 了解了什么是整型提升之后,然后便讲到了如何去进行一个整型提升,也就是当这个char类型或short类型的变量参与运算的时候,便会将其放入内存中,此时也就会发生一个整型提升
  • 最后,在我们知道了何时会发生整型提升时,便去具体的场景中观察它的过程,在第一个实例中带大家一步步地理解了两数进行相加在内存中会发生一个怎样的数据转变;在第二个实例中又再度看了short和char类型与int类型在运算时会发生的不同变化;最后一个实例,又和先前讲到过的sizeof操作符做了一个结合,更加深度地理解了[整型提升]的作用,揭开了它的神秘面纱

文章到这里就结束了,近期遇到了有关【整型提升】的内容,特此总结做个分享
2023年1月3日记4

在这里插入图片描述

相关文章
|
5月前
|
编译器
C生万物 | 从浅入深理解指针【第二部分】(一)
C生万物 | 从浅入深理解指针【第二部分】 前言: 如果没有看过第一部分的话,推荐先看第一部分,然后再来看第二部分~~
C生万物 | 从浅入深理解指针【第二部分】(二)
C生万物 | 从浅入深理解指针【第二部分】(二)
C生万物 | 从浅入深理解指针【最后部分】(二)
C生万物 | 从浅入深理解指针【最后部分】(二)
|
5月前
|
存储 C语言
【C生万物】数组
【C生万物】数组
|
5月前
|
存储 C语言 C++
C生万物 | 从浅入深理解指针【第一部分】(一)
C生万物 | 从浅入深理解指针【第一部分】
|
5月前
|
机器学习/深度学习 安全 程序员
C生万物 | 从浅入深理解指针【第一部分】(二)
C生万物 | 从浅入深理解指针【第一部分】(二)
|
5月前
|
C语言 C++
C生万物 | 从浅入深理解指针【最后部分】(一)
C生万物 | 从浅入深理解指针【最后部分】(一)
|
10月前
|
存储 编译器 C语言
C生万物 | 指针进阶 · 炼狱篇-3
C生万物 | 指针进阶 · 炼狱篇
60 0
|
10月前
|
存储 编译器 C语言
C生万物 | 指针进阶 · 炼狱篇-1
C生万物 | 指针进阶 · 炼狱篇
79 0
|
10月前
|
算法 安全 Java
C生万物 | 指针进阶 · 提升篇-1
C生万物 | 指针进阶 · 提升篇
48 0
下一篇
无影云桌面