百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

【C/C++】中的隐式类型转换 ~ 关于整数除以整数,商仍为整数的问题

liebian365 2025-01-23 18:33 15 浏览 0 评论

在下述三种情况下,C/C++会进行隐式类型转换(implicit type cast):①变量初始化或者赋值时,值与变量的类型不同;②表达式中不同类型的变量/值进行运算时;③函数参数传递▲时。

知识产权协议

允许以教育/培训为目的向学生或受众进行免费引用,展示或者讲述,无须取得作者同意。

不允许以电子/纸质出版为目的进行摘抄或改编。

3.5.1赋值/变量初始化

我们通过对下述C++程序及其执行结果的分析来解释赋值/变量初始化时的隐式类型转换及其影响:

//Project - AssignType
#include <iostream>
using namespace std;
   
int main(){
    bool a = 3.0;            //转换为bool类型:0为false,非0为true
    float b = -99999.2301;   //double转float,精度降低,可能超出储值范围
    int c = b;               //float转int, 小数部分丢失,可能超出储值范围
    unsigned int d = c;      //int转unsigned int,负值被错误解释
    short e = d;             //unsigned int转short,可能超出储值范围
    double f = b;            //float转double,安全

    cout.setf(ios_base::fixed, ios_base::floatfield);
    cout << "a = " << a << "\nb = " << b << "\nc = " << c
         << "\nd = " << d << "\ne = " << e << "\nf = " << f;
    return 0;
    }

上述代码的执行结果为:

a = 1
b = -99999.226562
c = -99999
d = 4294867297
e = 31073
f = -99999.226562

第6行:任意值赋值给布尔型变量,按非零即真原则,0值转换为false, 非零转换为true。double类型的字面量3.0赋值给布尔类型的变量a,被转换成true。true输出给cout,结果为1。

第7行:double转换为float时,可能发生精度损失。因为double类型对象由64个比特组成,而float类型只有32个比特。如果double类型的值超过float的储值范围,转换结果将是不确定的。本行进行了此类转换,可以看到转换后的float类型变量b丢失了精度(值不等于-99999.2301)。如果把b的类型改为double,可以保持更高的精度。

第8行:float转换为int时,小数部分丢失。当储值范围超出时,结果不确定。输出结果证实,这种转换只保留了整数部分-99999。

第9行:int(有符号)转换为unsigned int时,如果原值为负数,则结果不确定,因为无符号整数只能存储非负值。输出结果中,可以看到c值-99999被错误转换成了4294867297。

第10行:unsigned int转成short(有符号),是从32位无符号整数到16位有符号整数的转换,如果超出储值范围,则结果不确定。输出结果中,可以看到无符号整数d值4294867297被错误转换成了有符号短整数31073。

第11行:float转换为double则是安全的,因为double的储值范围更大,精度也更高。输出结果证实,double类型变量f完美复制了float类型变量b的值。同理,short到int,int到long long也是安全的。

第13行:对cout输出浮点数的格式进行了设置:按定点小数(相对于科学计算法)输出,保留6位小数。具体细节请参考本书2.3.4节中的扩展阅读内容。

将储值范围和精度较低的对象赋值给储值范围更大,精度更高的对象是安全的,反之则不然。程序员应尽量避免将储值范围大/精度高的对象赋值给储值范围小/精度低的对象。如果因为某些原因不得不这么做,则需要反复确认两件事:①精度的损失在可接受范围内;②源对象的值不会超过目标对象的储值范围。

使用列表初始化(list initialization)【C++ 11】可以避免变量初始化过程中不恰当的隐式类型转换所带来的差错。如下述C++代码所示:

//Project - ListInit
#include <iostream>
using namespace std;
   
int main(){
    char c = 712;         //允许,但会溢出
    char d {66};          //允许,66在char的储值范围内
    char e {712};         //不允许,712超过char的储值范围
    unsigned int f {-1};  //不允许,unsigned int只能存非负整数
    int g {3.12};         //不允许,收窄会导致精度丢失
    
    return 0;
}

与传统的赋值初始化不同,列表初始化不允许收窄(narrowing),当被初始化的变量无法准确表达{ }内的字面量或者常量▲时,编译器会报错,拒绝编译。

第6行:char类型对象c只有8个比特的存储空间,由于是有符号字符,其储值范围为-128 ~ +127。显然,字面量712超过了c的储值范围。但由于这是传统的赋值初始化,编译器将放任这种情况的发生,至多给出一个警告。

第7行:字面量66在d的储值范围内,该行代码会被编译器所接受。

第8 ~ 10行的变量不能准确表达{ }内的字面量,编译器将报错拒绝。

3.5.2表达式

在表达式a/b中,a,b称为操作数(operand),/称为操作符(operator)。当算术运算发生时,如果参与运算的操作数具有不同的类型,则编译器会生成额外的代码将其转变成相同的类型后再进行运算。因为,绝大多数的CPU指令集均只支持同类型对象之间的算术运算,比如两个有符号32位整数之间的运算。显然,算术运算的结果类型也受上述操作数类型转换的影响。

下述C++代码以除法运算为例,研究算术运算过程中的类型转换:

//Project - DivisionOperator
#include <iostream>
using namespace std;
   
int main(){
    int i = 3;                     //有符号整数
   
    auto a = 10 / i;               //整数 / 整数 = 整数
    cout << "10/3 = " << a << ", type = " << typeid(a).name() << endl;
   
    auto b = double(10.0) / i;     //浮点数 / 整数 = 浮点数
    cout << "10.0/3 = " << b << ", type = " << typeid(b).name() << endl;
   
    auto c = i / double(10.0);     //整数 / 浮点数 = 浮点数
    cout << "3/10.0 = " << c << ", type = " << typeid(c).name() << endl;
   
    auto d = double(10.0) / 3.0f;  //双精度浮点数 / 单精度浮点数 = 双精度浮点数
    cout << "10.0/3.0f = " << d << ", type = " << typeid(d).name() << endl;
   
    auto e = (unsigned int)10 / i; //无符号整数 / 有符号整数 = 无符号整数
    cout << "unsigned 10/3 = " << e << ", type = " << typeid(e).name() << endl;
   
    auto f = 10 / (unsigned int)3; //有符号整数 / 无符号整数 = 无符号整数
    cout << "10/unsigned 3 = " << f << ", type = " << typeid(f).name() << endl;
   
    return 0;
}

上述代码的执行结果为:

10/3 = 3, type = i
10.0/3 = 3.33333, type = d
3/10.0 = 0.3, type = d
10.0/3.0f = 3.33333, type = d
unsigned 10/3 = 3, type = j
10/unsigned 3 = 3, type = j


第6行:为了便于解释,此处显式定义了一个值为3的整数i,根据前述章节的描述,它事实上是一个有符号整数(signed int)。接下来的代码里,我们使用auto【C++ 11】来推断商的结果类型,并使用typeid()操作符及其name成员函数▲将商的类型打印出来。

第8 ~ 9行:输出结果证实,整数/整数的结果类型为整数,商的小数部分被舍弃。请学习过Python语言的读者注意,这与Python语言不同。

第11 ~ 12行:输出结果证实,浮点数/整数的结果类型为浮点数,此处的浮点数类型为double。请读者注意,字面量10.0的类型也是double。作者在这里有意写成double(10.0),通过一个显式的double类型构造函数▲将10.0“转换”成一个double,是因为担心读者无法正确识别10.0字面量的类型而产生疑惑。

第14 ~ 15行:输出结果证实,整数/浮点数的结果类型为浮点数。

第17 ~ 18行:输出结果证实,双精度浮点数/单精度浮点数的结果类型为双精度浮点数。3.0f中的后缀f表明该字面量为float类型。

第20 ~ 24行:输出结果证实,有符号整数与无符号整数进行除法运算的结果为无符号整数。输出结果type = j中的j指无符号整数。

当表达式中两种不同类型的对象进行算术运算时,编译器总是将较小的类型转换为较大的类型再进行计算。第11行中,一个double除以一个int,编译器会先将整数i转换成double,再进行除法运算。两个double相除,其结果自然是double。

需要注意的是,这种形式的隐式类型转换只是创建一个被转换对象的副本,不会改变被转换对象自身。比如第11行的i被转成double,编译器只是创建了一个double类型的用完即弃的临时对象,其值与i相同。第11行代码执行前后,对象i不会有任何变化。


本案例节选自作者编写的教材及配套实验指导书。

《C++编程基础及应用》(高等教育出版社,出版过程中)

《Python编程基础及应用》,高等教育出版社

《Python编程基础及应用实验教程》,高等教育出版社

高校教师同行如果期望索取样书,教学支持资料,加群,请私信作者,联系时请提供学校及个人姓名为盼,各高校在读学生勿扰为谢。

青少年读者们如果期望系统性地学习Python及C/C++程序设计语言,欢迎尝试下述今日头条(西瓜)免费视频课程。

C/C++从入门到放弃(重庆大学现场版)

Python编程基础及应用(重庆大学现场版)



相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: