C++数据类型(强制)转换详解

有时,编程的过程中需要将值从一种数据类型转换为另一种数据类型。C++ 提供了这样做的方法。

如果将一个浮点值分配给一个 int 整型变量,该变量会接收什么值?如果一个 int 整数乘以一个 float 浮点数,结果将会是什么数据类型?如果一个 double 浮点数除以一个 unsigned int 无符号整数会怎么样?是否有办法预测在这些情况下会发生什么?

答案是肯定的。当运算符的操作数具有不同的数据类型时,C++ 会自动将它们转换为相同的数据类型。当它这样做时,遵循一组规则。理解这些规则将有助于程序员防止一些细微的错误蔓延到自己的程序中。

就像军队的军官有军阶一样,数据类型也可以按等级排名。如果一个数字数据类型可以容纳的数字大于另一个数据类型,那么它的排名就高于后者。例如,float 类型就超越了 int 类型,而 double 类型又超越了 float 类型。表 1 列出了从高到低排列的数据类型。

表 1 数据类型排名
long double
double
float
unsigned long long int
long long int
unsigned long int
long int
unsigned int
int

表 1 中排名的一个例外是当 int 和 long int 的大小相同时。在这种情况下,unsigned int 将超越 long int,因为它可以保存更高的值。

当 C++ 使用运算符时,它会努力将操作数转换为相同的类型。这种隐式或自动的转换称为类型强制。当一个值被转换为更髙的数据类型时,称之为升级。反之,降级则意味着将其转换为更低的数据类型。

现在来看一看管理数学表达式评估的具体规则:
  • 规则 1:char、short 和 unsigned short 值自动升级为 int 值。细心的读者可能已经注意到,char、short 和 unsigned short 都未出现在表 1 中,这是因为无论何时在数学表达式中使用这些数据类型的值,它们都将自动升级为 int 类型。
  • 规则 2:当运算符使用不同数据类型的两个值时,较低排名的值将被升级为较高排名值的类型。在下面的表达式中,假设 years 是一个 int 变量,而 interestRate 是一个 double 变量:

years * interestRate

在乘法发生之前,years 中的值将升级为 double 类型。
  • 规则 3:当表达式的最终值分配给变量时,它将被转换为该变量的数据类型。在下面的语句中,假设 area 是一个 long int 长整型变量,而 length 和 width 都是 int 整型变量:

area = length * width;

因为存储在 length 和 width 中的值是相同的数据类型,所以它们都不会被转换为任何其他数据类型。但是,乘法的结果将被升级为 long int 类型,这样才可以存储到 area 中。

但是,如果接收值的变量的数据类型低于接收的值,那该怎么办呢?在这种情况下,值将被降级为变量的类型。如果变量的数据类型没有足够的存储空间来保存该值,则该值的一部分将丢失,并且该变量可能会收到不准确的结果。

我们知道,如果接收值的变量想要的是一个整数,而赋给它的值是一个浮点数,那么当转换为 int 并存储在变量中时,浮点值将被截断。这意味着小数点后的所有内容都将被丢弃。示例如下:

int x;
double y = 3.75;
x = y; // x被赋值为3,y仍然保留3.75

但是,重要的是要了解,当变量值的数据类型更改时,它不会影响变量本身。例如,来看下面的代码段。

int quantity1 = 6;
double quantity2 = 3.7;
double total;
total = quantity1 + quantity2;

在 C++ 执行上述加法之前,它会将一个 quantity1 值的副本移动到其工作空间中,并将其转换为 double 类型。然后把 6.0 和 3.7 相加,并且将结果值 9.7 存储到 total 中。但是,变量 quantity1 保持为 int,存储在存储器中的值保持不变,它仍然是整数 6。

类型强制转换

有时程序员想要自己更改值的数据类型,这可以通过使用类型强制转换表达式来完成。类型强制转换表达式允许手动升级或降级值。它的一般格式如下:

static_cast<DataType>(Value)

其中 Value 是要转换的变量或文字值,DataType 是要转换的目标数据类型。以下是使用类型转换表达式的代码示例:

double number = 3.7;
int val;
val = static_cast<int>(number);

上述代码定义了两个变量:double 类型的 number 和 int 类型的 val。第 3 个语句中的类型转换表达式返回 number 中的值的副本,转换为 int。当 double 或 float 类型的值转换为 int 时,小数部分被截断,因此该语句将 3 存储在 val 中。而 number 的值仍为 3.7,保持不变。

类型转换表达式在 C++ 不能自动执行所需转换的情况下很有用。

下面的程序显示了使用类型强制转换表达式来防止发生整除法的示例。
//This program uses a type cast to avoid an integer division.
#include <iostream>
using namespace std;

int main()
{
    intbooks, months;
    double booksPerMonth;
    // Get user inputs
    cout << "How many books do you plan to read? ";
    cin >> books;
    cout << "How many months will it take you to read them? ";
    cin >> months;
    // Compute and display books read per month
    booksPerMonth = static_cast<double>(books) / months;
    cout << "That is " << booksPerMonth << " books per month.\n";
    return 0;
}
程序输出结果:

How many books do you plan to read? 30
How many months will it take you to read them? 7
That is 4.28571 books per month.

其中,使用类型强制转换表达式的语句是:

booksPerMonth = static cast<double>(books) / months;

变量 books 是一个整数,但是它的值的副本在除法运算之前被转换为 double 类型。如果没有此类型转换表达式,则将执行整除法,导致错误的答案。值得一提的是,如果按以下语句改写此行,则整除法仍然会发生:

booksPerMonth = static_cast <double> (books / months);

因为括号内的操作在其他操作之前完成,所以除法运算符将对其两个整数操作数执行整除法,books / month 表达式的结果将是 4,然后将 4 转换为 double 类型的值 4.0,这就是将赋给 booksPerMonth 的值。

警告,为了防止发生整除法,在除法运算之前,其中一个操作数应该转换为一个 double 双精度值。这将强制 C++ 自动将其他操作数的值也转换为双精度值。

下面的程序显示了类型强制转换的另一种用法:
//This program prints a character from its ASCII code.
#include <iostream>
using namespace std;

int main()
{
    int number = 65;
    //Display the value of the number variable
    cout << number << endl;
    // Use a type cast to display the value of number
    // converted to the char data type
    cout << static_cast<char>(number) << endl;
    return 0;
}
程序输出结果:

65
A

现在来仔细看一看这个程序。首先,int 变量 number 的值被初始化为值 65,同时将 number 发送到 cout,导致显示 65。随后,类型强制转换表达式用于将 number 的值转换为 char 数据类型,再将其发送到 cout。我们知道,字符作为整数 ASCII 代码存储在内存中。因为数字 65 是字母 A 的 ASCII 码,所以最后的输出语句会显示字母 A。

注意,C++ 提供了若干种不同类型的强制转换表达式。static_cast 是最常用的类型强制转换表达式,所以这将是在本教程中主要使用的表达式。

C 风格和预标准 C++ 类型强制转换表达式

虽然 static_cast 是目前使用最多的类型强制转换表达式,但是 C++ 还支持两种较旧的形式,这也是程序员应该有所了解的,即 C 风格形式和预标准 C++ 形式。

C 风格的转换将要转换的数据类型放在括号中,位于值要转换的操作数的前面。因为类型转换运算符在操作数前面,所以这种类型转换表示法被称为前缀表示法,示例如下:

booksPerMonth = (double)books / months;

预标准 C++ 形式类型强制转换表达式也是将要转换的数据类型放在其值要转换的操作数之前,但它将括号放在操作数周围,而不是围绕数据类型。这种类型转换表示法被称为功能性表示法,示例如下:

booksPerMonth = double(books) / months;