【C语言初阶】表达式求值(隐式类型转换,算术转换)

简介: 目录表达式求值一、先补充一点(为下文做准备)1.首先,要了解原码、反码、补码(简单说一下) 2.有符号(signed)与无符号(unsigned)的区别 二、隐式类型转换(整型提升)1.什么是整型提升?2.整型提升的意义3.有符号(signed)类型的整型提升3.无符号(unsigned)整形提升4.简而言之 5.例子 三、算术转换四、

目录

表达式求值

一、先补充一点(为下文做准备)

1.首先,要了解原码、反码、补码(简单说一下)

2.有符号(signed)与无符号(unsigned)的区别

二、隐式类型转换(整型提升)

1.什么是整型提升?

2.整型提升的意义

3.有符号(signed)类型的整型提升

3.无符号(unsigned)整形提升

4.简而言之

5.例子

三、算术转换

四、


表达式求值

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

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

一、先补充一点(为下文做准备)

1.首先,要了解原码、反码、补码(简单说一下)

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

整数在内存中存储的形式是补码的二进制。

整数的二进制表示:有3种(原码、反码、补码)

原码:直接根据数值写出的二进制序列就是原码(32位)

反码:原码的符号位不变,其他位按位取反就是反码

补码:反码加1,就是补码

对于正整数的原码、反码、补码都相同;负数是存放在二进制的补码中,负整数的原码、反码、补码都不相同。

例如:1(正整数的原码、反码、补码都相同)

原码:0000000000000000000000000000001反码:0000000000000000000000000000001补码:000000000000000000000000000000


最高位为0 ,也是符号位

例如:-1(负整数的原码、反码、补码都相同)

原码:10000000000000000000000000000001反码:11111111111111111111111111111110(按位取反,符号位不变)补码:11111111111111111111111111111111(反码加1)


最高位为1,也是符号位

2.有符号(signed)与无符号(unsigned)的区别

首先在计算机中,有符号数是可以用来区分数值的正负,而无符号数仅有正值,没有负值。

其次当一个数是无符号数时,它的最高位仅用来表示该数的大小。而当一个数是有符号数时,此时的最高位称为符号位

该符号位为1时表示该数为负值,为 0 时则表示为正值。

最后有符号数和无符号数两者表示的范围不同,即同样长度的字节,有符号数比无符号数的最大值出现缩水。

二者最明显的区别就是二者表示的范围不同:

无符号数中,所有的位都用于直接表示该值的大小。

有符号数中最高位用于表示正负,所以,当为正值时,该数的最大值就会变小。

二、隐式类型转换(整型提升)

1.什么是整型提升?

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

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

2.整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度。

一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

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

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

3.有符号(signed)类型的整型提升

简单说一下,char 虽然是字符类型,但字符类型储存的时候,储存的是字符的ASCII码值,ASCII值是整数,关于数据的储存后面会介绍,这里不多解释了。

(1)正数的整形提升

charb=1;
变量b的二进制位(补码)中只有8个比特位:00000001(补码截取8个bit)因为char为有符号的char所以整形提升的时候,高位补充符号位,即为0提升之后的结果是:00000000000000000000000000000001

char 在我当前的编译器 vs2022 是有符号型,不同的编译器会有不同的情况,因为 char 在C语言中未定义为有符号还是无符号,有符号还是无符号取决的编译器。

(2)负数的整型提升

chara=-1;
变量a的二进制位(补码)中只有8个比特位:1111111(补码截取8个bit))因为char为有符号的char所以整形提升的时候,高位补充符号位,即为1提升之后的结果是:11111111111111111111111111111111

3.无符号(unsigned)整形提升

chara=-1;
变量a的二进制位(补码)中只有8个比特位:1111111(补码截取8个bit))因为char为有符号的char所以整形提升的时候,高位补充0,即为0提升之后的结果是:00000000000000000000000011111111

4.简而言之

之上简而言之,有符号与无符号的整型提升的区别就是:

有符号的整型提升高位补 符号位

无符号的整型提升高位补 0

5.例子

先上代码

#include <stdio.h>intmain()
{
chara=3;
charb=127;
charc=a+b;
printf("%d\n", c);
return0;
}

结果是 -126

image.png

接下来我们来分析为什么是 -126

1、首先,我们先看a=3是一个整型,占 4 个字节,32 个bit位;b = 127 也是如此,占4个字节

3 和 127 的二进制原码的补码

3的补码:00000000000000000000000000000011127的补码:00000000000000000000000001111111(正数的原、反、补相同)

int类型是 4 个字节,32 个bit位,而char类型只能储存 1 个字节,也就是 8 个 bit 位,所以 char 会发生截断,即选择 32 位中的最低位放入 char 中,也就是后八位

所以 3 和 127 在char类型中只能储存 8 个 bit,如下(补码)

3:00000011127: 01111111

2、接下来a和b是如何相加的呢??

a和b自身的大小都是8个比特位(char类型),没有达到一个整型的大小(4字节)。在计算时为了能够提升计算精度,要将a和b整型提升。

整型提升是按照变量的数据类型的符号位来进行提升的。

我们来看 a 和 b,a 和 b 现在是有符号的字符型变量,最高位0是它的符号位。高位通通补0,补全32个比特位。

a整型提升前:00000011//最高位的0是它的符号位a整型提升后:00000000000000000000000000000011b整型提升前:01111111//最高位的0是它的符号位b整型提升后:00000000000000000000000001111111

整型提升后 a 和 b 进行相加,结果为

a+b:00000000000000000000000010000010

现在要将结果放入 c 中,而c又是 char 型,又要发生截断

c:100000010

3、接下来如何打印呢??

此时还不能直接打印输出,因为 printf 函数中是以 %d 的形式进行打印。又要对 c 进行整型提升。


c 的类型是有符号字符型,最高位 1 为他的符号位。高位通通补1,补全32个比特位。

c整型提升前:10000010c整型提升后:11111111111111111111111110000010

此时 c 得到的是补码,还要反推原码才能打印

c的补码:11111111111111111111111110000010c的反码:11111111111111111111111110000001c的原码:10000000000000000000000001111110

接下来就可以打印 c 了,结果是 -126

三、算术转换

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

 

longdoubledoublefloatunsignedlongintlongintunsignedintint

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。(与整型提升有点像,但不相同,整型提升是不足 4 个字节才会发生)

简单来说就是:从下往上转换

例如:

#include<stdio.h> intmain()
{
inta=3;
floatb=3.5;
floatc=a+b;
//算术转换,int-->float //结果为 6.5return0;
}

如图:

image.png

警告:但是算术转换要合理,要不然会有一些潜在的问题

floatf=3.14;
intnum=f;//隐式转换,会有精度丢失

结果为 num = = 3

四、

文章到这就结束了,希望对你有帮助,觉得文章不错就点个赞吧。

文章有什么问题可以留言,感谢支持!!

image.png

相关文章
|
9天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
30 10
|
5月前
|
C语言
C语言初阶:如何判断是否为素数并且输出
C语言初阶:如何判断是否为素数并且输出
42 0
|
2月前
|
程序员 C语言
【C语言基础考研向】06运算符与表达式
本文介绍了C语言中的运算符分类、算术运算符及表达式、关系运算符与表达式以及运算符优先级等内容。首先概述了13种运算符类型,接着详细说明了算术运算符的优先级与使用规则,以及关系运算符和表达式的真假值表示,并给出了C语言运算符优先级表。最后附有课后习题帮助巩固理解。
98 10
|
2月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
283 8
|
6月前
|
C语言
C语言中的关系运算符和关系表达式
C语言中的关系运算符和关系表达式
56 0
|
6月前
|
C语言
C语言中的条件运算符和条件表达式详解
C语言中的条件运算符和条件表达式详解
500 0
|
2月前
|
C语言
C语言程序设计核心详解 第二章:数据与数据类型 4种常量详解 常见表达式详解
本文详细介绍了C语言中的数据与数据类型,包括常量、变量、表达式和函数等内容。常量分为整型、实型、字符型和字符串常量,其中整型常量有十进制、八进制和十六进制三种形式;实型常量包括小数和指数形式;字符型常量涵盖常规字符、转义字符及八进制、十六进制形式;字符串常量由双引号括起。变量遵循先定义后使用的规则,并需遵守命名规范。函数分为标准函数和自定义函数,如`sqrt()`和`abs()`。表达式涉及算术、赋值、自增自减和逗号运算符等,需注意运算符的优先级和结合性。文章还介绍了强制类型转换及隐式转换的概念。
|
3月前
|
C语言
C语言------运算符与表达式
这篇文章是C语言运算符与表达式的实训教程,通过多个示例程序展示了如何使用算术运算符、关系运算符、逻辑运算符以及条件语句来解决实际问题,并介绍了如何通过函数库简化复杂数学运算。
C语言------运算符与表达式
|
4月前
|
编译器 C语言
【C语言初阶】指针篇—下
【C语言初阶】指针篇—下
|
4月前
|
存储 C语言
【C语言初阶】指针篇—上
【C语言初阶】指针篇—上