详解【C语言】类型转换--整型提升,算术

简介: 详解【C语言】类型转换--整型提升,算术

一,整型提升

首先我们来看整型提升的概念 :

  C语言中整型运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换成普通整型,这种转换称为整型提升。

  这句话的意思就是,表达式中的字节数少于整型的类型在使用前,会被自动转换成整型类型来进行使用,举个例子:

char a = 5;
char b = 127;
char c = a + b;

  因为char类型的大小为1byte,小于整型int的4byte,所以char类型会被转换成int类型来进行操作,这就叫做整型提升

  接下来我们了解一下整型提升的意义 :

  表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器ALU操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加, 在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

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

  我们会发现需要进行整型提升的只有char类型和short类型。接下来我们举详细例子探究里面细节 :

  首先我们得知道一些知识点 :

   1. 数据在进行存储时,都是以二进制的补码形式存储的,有符号类型的原码,反码,补码是一样。无符号类型的原码,反码,补码可能不同。具体为:反码为将原码的符号位不变,有效位0 1 交换,补码就是在反码加上一个1

   2. 在进行整型提升时候,遵守以下两个规则 :

      2.1. 如果是无符号数,则高位直接补0;

      2.2. 如果是有符号数,则高位全补符号位。

   来看这段代码 :

#include <stdio.h>
  int main()
  {
    char a = 5;
    char b = 127;
    char c = a + b;
    printf("%d",c);
    return 0;
  }

这个结果应该是什么呢?

既然a和b都会转换成int类型,那么c就应该是132.

这个结果对吗? 运行以后发现输出了 -124,,这是因为什么呢?接下来我们从细细道来 :

    a(char)的二进制为 :0000 0101

    b(char)的二进制为 :0111 1111

  1. 将a转换成int类型后,得到 :0000 0000 0000 0000 0000 0000 0000 0101
  2. 将b转换成int类型后,得到 :0000 0000 0000 0000 0000 0000 0111 1111
  3. 相加以后得到 : 0000 0000 0000 0000 0000 0000 1000 0100
  4. 这里有一个值得注意的点:当将相加的值赋值给c时,直接截取最右边的八位,丢弃左边的二十四位。得到c(char) :1000 0100,整型提升后,( 如果是有符号数,则高位全补符号位;)
    得到 :c(int):1111 1111 1111 1111 1111 1111 1000 0100
  5. 由于c的符号位为1,会被视作无符号类型,因为无符号类型以补码形式存储,但是我们需要的是原码,所以此时应当进行转换,反码为 :1111 1111 1111 1111 1111 1111 1000 0100
    原码为:1000 000 000 000 000 000 0111 1100
  6. 所以此时c(int) = -124因此打印的是-124;

  小于int大小的类型会进行整型提升,那大于int类型的会怎么操作呢?接下来我们看看算术转换


二,算术转换

他的定义是 :

  如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换成另一个操作数的类型,否则操作就无法进行。下面的层次体系成为寻常算术转换。

这段话的意思就是不同类型进行操作时需要把其中一个操作数的转换成另一个操作数的类型,这里的规则是 :

  i n t − > u n s i g n e d i n t − > l o n g i n t − > u n s i g n e d l o n g i n t − > f l o a t − > d o u b l e − > l o n g d o u b l e int -> unsigned int -> long int -> unsigned long int -> float -> double -> long doubleint>unsignedint>longint>unsignedlongint>float>double>longdouble

按照上面的顺序,左边的类型转换成右边的类型,举个例子 :

  这里int类型和double类型进行运算,输出的是一个double类型的数,可以侧面反映出,int被转换成了double类型进行运算,而不是double转换成了int。

三,问题表达式

我们来看一串代码

#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
  int a = 4;
  a = a + ++a;
  printf("%d", a);
  return 0;
}

可以看到结果为10,不出意外的话,这个结果肯定能让很多人感到意外。我们通过汇编来看看怎么实现的:

  1. 00007FF7120E252B mov dword ptr [rbp+4],4 这串代码的意思是将4放在[rbp+4]里面
  2. 00007FF7120E2532 mov eax,dword ptr [rbp+4]将[rbp+4]这个地址赋给eax寄存器
  3. 00007FF7120E2535 inc eax inc是加1指令,也就是将eax里的值加1,此时的操作就是 ++a
  4. 00007FF7120E2537 mov dword ptr [rbp+4],eax 再将eax还给[rbp+4],此时a = 5
  5. 00007FF7120E253A mov eax,dword ptr [rbp+4]再将[rbp+4]赋给eax寄存器
  6. 00007FF7120E253D mov ecx,dword ptr [rbp+4]再将[rbp+4]赋给ecx寄存器
  7. 00007FF7120E2540 add ecx,eax 将eax和ecx相加得10存放在ecx里面。
  8. 00007FF7120E2542 mov eax,ecx 将ecx赋给eax。
  9. 00007FF7120E2544 mov dword ptr [rbp+4],eax 将eax赋给[rbp+4],[rbp+4]地址是a的地址,也就是说,此时a = 10这就是为什么结果是10.

为什么不能是9呢?

当然可以,我们换了一种编译器运行发现,结果为9

下面的代码是《c和指针》里面出现的,这串代码令人头大,作者经过不同编译器运行发现,不同的编译器运行结果都不一样。

  所以我们在平时写代码时候,要杜绝这种问题代码的出现,让代码没有二义性。


     😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。😄

相关文章
|
26天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
34 10
|
1月前
|
存储 C语言
C语言:设置地址为 0x67a9 的整型变量的值为 0xaa66
在C语言中,可以通过指针操作来实现对特定地址的访问和赋值。要将地址为 0x67a9 的整型变量值设为 0xaa66,可以先定义一个指向该地址的指针,并通过该指针对该内存位置进行赋值操作。需要注意的是,直接操作内存地址具有一定风险,必须确保地址合法且可写。代码示例应考虑字节序及内存对齐问题。
|
2月前
|
存储 C语言
【C语言基础考研向】04整型进制转换
本文介绍了计算机中整型常量的不同进制表示,包括二进制、八进制、十六进制和十进制,并解释了它们之间的转换方法。以一个32位整型数为例,展示了其在不同进制下的表示形式及计算方法,特别指出在内存观察中常用十六进制,同时提到了小端存储方式对数据的影响。
|
5月前
|
存储 C语言
C语言整型详解
C语言整型详解
|
5月前
|
存储 编译器 C语言
【C语言】:整型提升,算术转换与大小端的介绍
【C语言】:整型提升,算术转换与大小端的介绍
38 0
|
5月前
|
C语言
C语言---单身狗(1)---在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字
C语言---单身狗(1)---在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字
|
5月前
|
存储 编译器 C语言
C语言学习记录——数据的存储(数据类型、类型的基本归类、整型在内存中的存储、大小端介绍、浮点型在内存中的存储)二
C语言学习记录——数据的存储(数据类型、类型的基本归类、整型在内存中的存储、大小端介绍、浮点型在内存中的存储)二
34 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
33 3
|
6天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6
|
19天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。