C++:类型转换和类型安全
简介
本文档确定了一些常见的类型转换问题,并介绍了如何在 C++ 代码中避免它们。
在编写 C++ 程序时,请务必确保它是类型安全的。 这意味着每个变量、函数自变量和函数返回值将存储一个可接受的类型的数据,并意味着涉及不同类型的值的操作“有意义”且不会导致数据丢失、位模式解释不正确或内存损坏。 从不显式或隐式地将值从一种类型转换为另一种类型的程序在定义上是类型安全的。 但是,有时需要进行类型转换,甚至需要进行不安全的类型转换。 例如,你可能必须将浮点运算的结果存储在类型的变量中 int ,或者可能需要将中的值传递 unsigned int 给采用的函数 signed int 。 这两个示例列举了不安全的转换,因为它们可能会导致数据丢失或值的重新解释。
当编译器检测到不安全的转换时,它会发出错误或警告。 如果发出错误,则编译会停止;如果发出警告,则编译可以继续,但会指示代码中可能存在错误。 但是,即使您编译程序后没有收到警告,它仍然可能包含导致生成错误结果的隐式类型转换。 类型错误也可能由代码中的显式转换或强制转换引入。
隐式类型转换
如果表达式包含不同内置类型的操作数,并且不存在显式强制转换,则编译器将使用内置的 标准转换 来转换其中一个操作数,使这些类型匹配。 编译器将尝试按一个明确定义的顺序进行转换,直到有一个转换成功。 如果所选转换为升级,则编译器不会发出警告。 如果转换是收缩转换,则编译器会发出有关数据可能丢失的警告。 尽管是否真的发生数据丢失取决于涉及的实际值,但我们建议您将此警告视为错误。 如果涉及到用户定义的类型,则编译器将尝试使用您在类定义中指定的转换。 如果找不到可接受的转换,则编译器会发出错误,不会编译程序。
扩大转换(提升)
在扩大转换中,较小的变量中的值将赋给较大的变量,同时不会丢失数据。 因为扩大转换始终是安全的,所以编译器会以静默方式执行这些转换,而不会发出警告。 以下转换是扩大转换。
收缩转换(强制)
编译器隐式执行收缩转换,但会发出有关数据丢失可能的警告。 请重视这些警告。 如果您确定数据丢失不会发生(因为较大的变量中的值始终适合较小的变量),则添加显式强制转换,使编译器不再发出警告。 如果不确定转换是安全的,请在代码中添加一些运行时检查以处理可能的数据丢失,使程序不会产生不正确的结果。
任何从浮点类型到整型的转换都是收缩转换,因为浮点值的小数部分将会丢弃和丢失。
以下代码示例演示了一些隐式收缩转换以及编译器为其发出的警告。
int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
wchar_t wch = 'A'; //OK
char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
// to 'char', possible loss of data
unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
// 'int' to 'unsigned char'
int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
// 'int', possible loss of data
int k = 7.7; // warning C424