c++隐式类型转换_c++整数除法

c++隐式类型转换_c++整数除法在下述三种情况下,C/C++会进行隐式类型转换:1变量初始化或者赋值时,值与变量的类型不同;允许以教育/培训为目的向学生或受众进行免费引用,展示

在下述三种情况下,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编程基础及应用实验教程》,高等教育出版社

c++隐式类型转换_c++整数除法

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

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

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

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

c++隐式类型转换_c++整数除法

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/11997.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注