整型提升+算术转换——“C”

news2024/12/23 17:57:48

各位CSDN的uu们你们好呀,今天小雅兰的内容是之前操作符那篇博客中没有讲完的内容,整型提升这个小知识点也非常重要,那现在,就让我们进入操作符的世界吧


隐式类型转换

算术转换

操作符的属性


 隐式类型转换

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

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

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

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

这一规则是由C语言的发明人丹尼斯·里奇与肯·汤普逊创设的:

"A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion."

这段话的大意是:表达式中可以使用整数的地方,就可以使用枚举类型,或有符号或无符号的字符、短整数、整数位域。如果一个int可以表示上述类型,则该值被转化为int类型的值;否则,该值被转化为unsigned int类型的值。这一过程被称作integral promotion。

整型提升的意义:

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

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

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

C语言标准中仅规定了:

char的长度 ≤ short int的长度 ≤ int的长度

这意味着short int与int的长度相等的可能。这种情形下,unsigned short就无法提升为int表示,只能提升为unsigned int。

char a;
char b;
char c;
c=a+b;

 b和a的值被提升为普通整型,然后再执行加法运算。

加法运算完成之后,结果将被截断,然后再存储于a中。

说了这么久整型提升,那么,究竟怎样进行整型提升呢?

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

int main()

{

   char a=3;

   //00000000000000000000000000000011——3的二进制

   //但是,这是一个char类型的变量,会发生截断

   //00000011——截断

   char b=127;

   //00000000000000000000000001111111——127的二进制

   //01111111——截断

   char c=a+b;

   //00000011

   //011111111

   //整型提升

   //00000000000000000000000000000011

   //00000000000000000000000001111111

   //相加

   //00000000000000000000000010000010

   //10000010——截断

   printf("%d\n",c);

   //但是以%d的形式打印,所以c又要发生整型提升

   //按照变量的数据类型的符号位来提升

   //11111111111111111111111110000010——补码

   //11111111111111111111111110000001——反码

   //10000000000000000000000001111110——原码

   //所以最后的结果是-126

   return 0;

}

//负数的整型提升

char c1 = -1;

//变量c1的二进制位(补码)中只有8个比特位:

//1111111

//因为 char 为有符号的 char

//所以整型提升的时候,高位补充符号位,即为1

//提升之后的结果是:

//11111111111111111111111111111111

//正数的整型提升

char c2 = 1;

//变量c2的二进制位(补码)中只有8个比特位:

//00000001

//因为 char 为有符号的 char

//所以整型提升的时候,高位补充符号位,即为0

//提升之后的结果是:

//00000000000000000000000000000001

//无符号整型提升,高位补0

下面,我们来看一下整型提升的具体例子

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;
}

 我们会发现,打印结果最后只有c

 因为:

  a,b要进行整型提升,但是c不需要整型提升

  a,b整型提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600的结果是假,但是c不发生整型提升,则表达式 c==0xb6000000 的结果是真

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

 c只要参与表达式运算,就会发生整型提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节

 表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节

 但是 sizeof(c) ,就是1个字节


算术转换

大多数 C 运算符执行类型转换以将表达式的操作数引入常见类型或将较短的值扩展到计算机运算中使用的整数大小。

C 运算符执行的转换取决于特定的运算符和操作数的类型。

但是,许多运算符对整型和浮点型的操作数执行相似的转换。

这些转换称为“算术转换”。

从操作数值到兼容类型的转换会导致不改变其值。

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。

下面的层次体系称为寻常算术转换。

long double

double

float

unsigned long int

long int

unsigned int

int

 如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

  • 如果任一操作数是 long double 类型,则将另一个操作数转换为 long double 类型。
  • 如果上述一个条件不满足,且其中一个操作数的类型为 double,则另一个操作数被转换为 double 类型。
  • 如果上述两个条件不满足,且其中一个操作数的类型为 float,则另一个操作数被转换为 float 类型。
  • 如果未满足上述三个条件(所有操作数都不是浮点型),则对操作数执行整型转换,如下所示:
  • 如果任一操作数是 unsigned long 类型,则将另一个操作数转换为 unsigned long 类型。
  • 如果上述一个条件不满足,且其中一个操作数的类型为 long 类型,另一个操作数的类型为 unsigned int,则这两个操作数都被转换为 unsigned long 类型。
  • 如果上述两个条件不满足,且其中一个操作数的类型为 long,则另一个操作数被转换为 long 类型。
  • 如果未满足上述三个条件,并且任一操作数是 unsigned int类型,则将另一个操作数转换为 unsigned int 类型。
  • 如果未满足上述任何条件,则将两个操作数转换为 int 类型。

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

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

操作符的属性 

复杂表达式的求值有三个影响的因素。

1. 操作符的优先级

2. 操作符的结合性

3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?

   取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

下面,我们来看看操作符的优先级,这是一张表:

 

 

 在了解了操作符的优先级之后,有些人就可能会突发奇想,写出一些稀奇古怪的表达式,写出来不仅是坑自己,也是坑队友!!!

猪队友表达式1:

a*b + c*d + e*f

对于这行代码,如果仅由优先级决定这个表达式的求值顺序,那么所有三个乘法运算将在所有加法运算之前进行。事实上,这个顺序不是必需的。实际上只要保证每个乘法运算在它相邻的的加法运算之前执行即可。

这个执行顺序可能是这样的:

  • a*b
  • c*d
  • a*b + c*d
  • e*f
  • a*b + c*d + e*f

还有另外一种可能是这样: 

  • a*b
  • c*d
  • e*f
  • a*b + c*d
  • a*b + c*d + e*f

这就是一个典型的问题表达式,写出来就是猪队友!!!

猪队友表达式2:

c + --c;  

操作符的优先级规则只要求自减运算在加法运算之前进行,但我们并没有办法得知加法操作符的左操作数是在右操作数之前还是之后进行求值。它在这个表达式中将存在区别,因为自减操作符具有副作用,--c在c之前或之后执行,表达式的结果在两种情况下将会不同。

猪队友表达式3:

int main()

{

   int i = 10;

   i = i-- - --i * ( i = -3 ) * i++ + ++i;

   printf("i = %d\n", i);

   return 0;

}

这也是一个猪队友表达式,试想:这样的表达式,写出来就是为祸人间!!!

《C和指针》的作者Kenneth A.Reek老师专门研究了这串代码,并在各种不同的编译器下运行出了各种不同的结果。

 猪队友表达式4:

int fun()

{  

  static int count = 1;  

  return ++count;

}

int main()

{  

  int answer;  

  answer = fun() - fun() * fun();  

  printf( "%d\n", answer);//输出多少?

  return 0;

}

虽然在大多数的编译器上求得结果都是相同的。

但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法, 再算减法。

函数的调用先后顺序无法通过操作符的优先级确定。

就是说:调用fun函数要调用3次,第一次fun函数的调用结果为2,第二次fun函数的调用结果为3,第三次fun函数的调用结果为4,但是顺序是不确定的。

  • 可能是2-3*4
  • 可能是3-2*4
  • 可能是4-2*3

 猪队友表达式5:

#include<stdio.h>

int main()

{

  int i = 1;

  int ret = (++i) + (++i) + (++i);

  printf("%d\n", ret);

  printf("%d\n", i);

  return 0;

}

尝试在linux 环境gcc编译器,VS2022环境下都执行,看结果。

 

 VS2022环境的结果:

 这段代码中的第一个 + 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第 三个前置 ++ 的先后顺序。

总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。


好啦,小雅兰今天的内容就到这里啦,又是一篇拖了好久的博客,小雅兰以后的时间可能就没那么多了,今天,开学考试所有的科目就都已经结束,从明天开始,就要正式上课啦,星期一第一节课就是电路分析,这是破大防了!!!小雅兰以后也会一直努力学C语言和数据结构的!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/340058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

opencv显示图像

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

带头节点的单链表的就地逆置

目录 1.题目背景 2.思路方法分析 2.1采用循环实现的就地逆置法 2.2 递归方式实现的逆置 3.金句省身 1.题目背景 本题要求编写函数实现带头结点的单链线性表的就地逆置操作函数。L是一个带头结点的单链表&#xff0c;函数ListReverse_L(LinkList &L)要求在不新开辟节点的…

使用自己的数据集,测试mmrotate新网络rotated_rtmdet,旋转目标检测

1.安装 &#xff01;&#xff01;&#xff01;&#xff01;一定不要安装mmrotate 1.版本需求 mmcv 2.0.0rc2 mmdet 3.0.0rc5 mmengine 0.5.0 不用安装mmcv-full 下载mmrotate 1.x 源码 &#xff08;不要下载默认的master&#xff0c;因为新的网络只在1.x版本中&#xff09; …

【前端】Vue项目:旅游App-(23)detail:房东介绍、热门评论、预定须知组件

文章目录目标过程与代码房东介绍landlord热门评论HotComment预定须知Book效果总代码修改或添加的文件detail.vuedetail-book.vuedetail-hotComment.vuedetail-landlord.vue参考本项目博客总结&#xff1a;【前端】Vue项目&#xff1a;旅游App-博客总结 目标 根据detail页面获…

我用python/C++调用ChatGPT自制了一个聊天问答机器人

目录1 ChatGPT完整版2 Python/C调用ChatGPT2.1 获取API秘钥2.2 测试API功能2.3 设计简单UI3 聊天问答1 ChatGPT完整版 2015年&#xff0c;OpenAI由马斯克、美国创业孵化器Y Combinator总裁阿尔特曼、全球在线支付平台PayPal联合创始人彼得蒂尔等硅谷科技大亨创立&#xff0c;公…

【脚本开发】运维人员必备技能图谱

脚本&#xff08;Script&#xff09;语言是一种动态的、解释性的语言&#xff0c;依据一定的格式编写的可执行文件&#xff0c;又称作宏或批处理文件。脚本语言具有小巧便捷、快速开发的特点&#xff1b;常见的脚本语言有Windows批处理脚本bat、Linux脚本语言shell以及python、…

Spring缓存Demo

Spring中的缓存用法:有说错请指正 启动类加EnableCache 注解很多,这里举例几个实用的 第一组 value和key都没什么特别的含义,随你自己取,注意key里面是包了一层的 Cacheable(value"user",key "findUsers") 第一次查询的时候,会查数据库,然后将返回结果…

【GlobalMapper精品教程】045:空间分析工具(2)——相交

GlobalMapper提供的空间分析(操作)的方法有:交集、并集、单并集、差异、对称差集、相交、重叠、接触、包含、等于、内部、分离等,本文主要讲述相交工具的使用。 文章目录 一、实验数据二、符号化设置三、相交运算四、结果展示五、心灵感悟一、实验数据 加载配套实验数据(…

Hadoop安装 --- 简易安装Hadoop

目录 1、使用xftp工具 在opt目录下创建install和soft文件 2、使用xftp工具 将压缩包上传到install文件 3、编写shell脚本 3.1、创建目录来放shell脚本 3.2、创建autoinsatll.sh文件并修改权限 3.3、编写autoinsatll.sh 文件 刷新资源 运行文件 格式化 启动所有进程 Ha…

ChatGPT到底有多牛?博主带你亲测

文章目录论文项目代码算法学习情感职业回答知乎ChatGpt网页版与客户端版个人评价论文 问他毕设框架&#xff1a; 让他帮我写一段毕设背景部分&#xff1a; 项目代码 我让他帮我用Django写一个demo网站&#xff1a; 算法 matlab写遗传算法&#xff1a; 问一个数据结构&…

Java是如何创建线程的(二)从glibc到kernel thread

Java是如何创建线程的&#xff08;二&#xff09;从glibc到kernel thread 背景 上一节我们讨论了java线程是如何创建的&#xff0c;看了看从java代码层面到jvm层面的源码里都干了什么。 整个流程还是比较复杂的&#xff0c;我将上一节总结的调用时序图贴在下面&#xff0c;方…

Flutter Widget - Container 容器

Container 属性 child 容器包含的子组件color 容器背景色padding 内边距margin 外边距decoration 定义容器形状、颜色alignment 子组件在容器内的对齐方式constraints 定义宽高width 宽(可选)height 高(可选)transform 变换transformAlignment 变换原点的相对位置foregroundDe…

【Unity3D 常用插件】Haste插件

一&#xff0c;Haste介绍 Haste插件是一款针对 Unity 3D 的 Everthing软件&#xff0c;可以实现基于名称快速定位对象的功能。Unity 3D 编辑器也自带了搜索功能&#xff0c;但是在 project视图 和 Hierarchy视图 中的对象需要分别查找&#xff0c;不支持模糊匹配。Haste插件就…

MySQL-InnoDB数据页结构浅析

在MySQL-InnoDB行格式浅析中&#xff0c;们简单提了一下 页 的概念&#xff0c;它是 InnoDB 管理存储空间的基本单位&#xff0c;一个页的大小一般是 16KB 。 InnoDB 为了不同的目的而设计了许多种不同类型的 页&#xff1a; 存放表空间头部信息的页存放 Insert Buffer信息的…

Maven专题总结

1. 什么是Maven Maven 是一个项目管理工具&#xff0c;它包含了一个项目对象模型 (POM&#xff1a; Project Object Model)&#xff0c;一组标准集合&#xff0c;一个项目生命周期(Project Lifecycle)&#xff0c;一个依赖管理系统(Dependency Management System)&#xff0c;和…

3分钟,学会了一个调试CSS的小妙招

Ⅰ. 作用 用于调试CSS , 比控制台添更加方便&#xff0c;不需要寻找 &#xff1b;边添加样式&#xff0c;边可以查看效果&#xff0c;适合初学者对CSS 的理解和学习&#xff1b; Ⅱ. 快速实现&#xff08;两边&#xff09; ① 显示这个样式眶 给 head 和 style 标签添加一个…

Power BI 筛选器函数---Window实例详解

一、Window函数 语法&#xff1a; Window ( <起始位置>,<起始位置类型>,<结束位置>,<结束位置类型>, [<关系>], [<OrderBy>],[空白],[PartitionBy] ) 含义&#xff1a; 对指定分区&#xff08;PartitioinBy)中的行&#xff08;关系表&…

getchar()的用法

getchar的功能 它的作用是从stdin流中读入一个字符&#xff0c;也就是说&#xff0c;如果stdin有数据的话不用输入它就可以直接读取了&#xff0c;第一次getchar()时&#xff0c;确实需要人工的输入&#xff0c;但是如果你输了多个字符&#xff0c;以后的getchar()再执行时就会…

python+django高校师生健康信息管理系统pycharm

管理员功能模块 4.1登录页面 管理员登录&#xff0c;通过填写注册时输入的用户名、密码、角色进行登录&#xff0c;如图所示。 4.2系统首页 管理员登录进入师生健康信息管理系统可以查看个人中心、学生管理、教师管理、数据收集管理、问卷分类管理、疫情问卷管理、问卷调查管理…

大数据框架之Hadoop:HDFS(一)HDFS概述

1.1HDFS产出背景及定义 HDFS 产生背景 随着数据量越来越大&#xff0c;在一个操作系统存不下所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;迫切需要一种系统来管理多台机器上的文件&#xff0c;这就是分布式文件…