本文档包含内容有:
- 数据类型转换中的隐式类型转换存在的风险;
整型提升存在的风险
标准算数转换存在的风险 - 数据类型转换中存在的数据类型范围溢出风险;
- 数据类型转换中存在的数据精度问题(数据截断)。
隐式类型转换(implicit type conversion)
隐式类型转换(implicit type conversion)包括整型提升(integer promotion)和标准算数转换(usual arithmetic conversions)
遵循较大范围优先规则
1. 整型提升(integer promotion)
C语言规定,表达式中各种小于int长度的整型值(signed/unsigned character/short integer),都必须先转换为int或unsigned int,才能送入CPU取执行运算。因为CPU中的整型运算器(ALU)操作数长度是int字节长度,使用的通用寄存器长度也是。
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.”
验证
验证是否存在整型提升
#include <stdio.h>
int main(int argc, char const *argv[])
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
/*
最后输出结果为c
因为整型提升:a/b整型提升为int类型
*/
return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[])
{
char a = 1;
printf("%u", sizeof(a));
printf("\n");
printf("%u", sizeof(+a)); // 单操作符+,一个主要作用就是“整型提升”
/*
输出:
1
4
*/
return 0;
}
2. 标准算数转换(usual arithmetic conversions)
当两个不同的类型操作数进行算数运算时,它们会先转换为一种类型,然后进行计算。转换规则通常是**“较大范围”的类型优先**即,将小范围转换成大范围。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = -5;
unsigned int b = 10;
int result = a + b; // a会被转成无符号整数,最终的运算会按照无符号整数的规则进行
printf("result: %d\n", result); // 5
return 0;
}
这里为什么输出结果是正确的,原因在于:printf的%d格式符,结果被解释为有符号整数,即 ( 11111111111111111111111111111011 ) 2 + ( 10 ) 10 (1111 1111 1111 1111 1111 1111 1111 1011)_{2}+(10)_{10} (11111111111111111111111111111011)2+(10)10 解释为有符号整数,这个二进制超出unsigned integer表示范围,解释为负数。
-5的原码 | -5的反码 | -5的补码 |
---|---|---|
0x0000 0005 | 0xFFFF FFFA | 0xFFFF FFFB |
实例1: 有符号和无符号混合运算问题
当不同数据类型进行运算时,编译器会根据转换规则进行隐式类型转换(如:整数和浮点数运算),以确保所有操作数的类型一致。
问题描述:
这里涉及到的是符号扩展问题,即当进行类型转换或位操作时,编译器将原数据的符号位(最高位)扩展到更高位。
#include <stdio.h>
int main()
{
int a = -1; // 有符号整数 0xFFFF FFFF
unsigned int b = 1; // 无符号整数
if (a > b)
{
printf("a > b\n");
}
else
{
printf("a <= b\n");
}
/*
输出结果:a > b
分析:在a,b进行比较时,a发生了隐式类型转换,转换为unsigned integer
-1的计算机表示采用补码(整数采用补码表示):
0x1000 0001 // 原码
0xFFFF FFFE // 反码
0xFFFF FFFF // 补码
因此a整型 隐式转换之后的结果为:4,294,967,295
*/
return 0;
}
解决方案:
#include <stdio.h>
int main()
{
int a = -1; // 有符号整数 0xFFFF FFFF
unsigned int b = 1; // 无符号整数
if (a > (int)b) // 解决方案
{
printf("a > b\n");
}
else
{
printf("a <= b\n");
}
/*
输出结果:a > b
*/
return 0;
}
实例2
和实例1对隐式类型转换的解决方法一致
C++隐式类型转换问题
数据类型范围溢出(overflow)
超出了所使用的数据类型所表示的范围,则会出现溢出现象。
整型溢出
#include <stdio.h>
#include <limits.h>
#include <cmath>
int main(int argc, char const *argv[])
{
int max_int = INT_MAX;
int result = max_int + 2;
printf("int max value: %d\n", max_int);
printf("result: %d\n", result);
/*
输出结果:
int max value: 2147483647
result: -2147483647
*/
return 0;
}
浮点数溢出
#include <stdio.h>
#include <float.h>
#include <math.h>
int main(int argc, char const *argv[])
{
float large_float = FLT_MAX;
float overflow_result = large_float * 2.0;
printf("large_float: %e\n", large_float);
printf("overflow result: %e\n", overflow_result);
if (overflow_result == INFINITY)
{
printf("result is Infinity\n");
}
/*
输出结果:
large_float: 3.402823e+038
overflow result: 1.#INF00e+000
result is Infinity
*/
return 0;
}
实例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
long long_value = 2147483648;
int int_value = (int)long_value;
printf("%d\n", int_value);
return 0;
/*
输出:
-2147483648
*/
}
数据截断(data truncation)
数据截断导致精度的损失:当高精度数据类型转成低精度数据类型时,会出现精度损失。例如:小数部分会直接截断
#include <stdio.h>
int main(int argc, char const *argv[])
{
float hv = 123.456;
int lv = (int)hv;
printf("%d\n", lv); // 123
return 0;
}
reference
C++/C implicit conversion
integer promotion