转换(一)
C语言中有时需要将一种类型的值转换成另一种类型,实现这种转换存在两种方式:隐式转换(implicit conversion)和显式转换(explicit conversion)。隐式转换,也称自动类型转换(automatic type conversion),这种转换由编译器自动完成,不需要编程人员干预。显式转换,也称强制类型转换(type casting),这种转换由编程人员设置特定表达式的数据类型来实现。
int i; float f = 3.14f; i = f; //隐式转换。 i = (int)f; //显式转换。
除非另有明确说明,否则操作数的值转换为兼容类型不会改变操作数的值和操作数的表示。
一、算术操作数
1、整数转换等级
每个整数类型都有一个整数转换等级(integer conversion rank),规则定义如下:
-- 任意两个有符号整数类型都不能具有相同的转换等级,即使它们具有相同的表示。
-- 有符号整数类型精度越高,转换等级越高。
例如:signed char类型精度为7,int类型精度为31(假设int类型占4个字节。),因此int类型转换等级高于signed char类型。
-- 标准有符号整数类型转换等级由低到高的顺序:signed char < short < int < long < long long。
-- 位精确有符号整数类型的转换等级高于具有较小宽度的标准整数类型或者位精确整数类型。
例如:_BitInt(16)类型宽度为16,signed char类型宽度为8,_BitInt(8)类型宽度为8,因此_BitInt(16)类型转换等级高于signed char类型和_BitInt(8)类型。
-- 无符号整数类型的转换等级与对应有符号整数类型的转换等级相同。
例如:unsigned int类型与signed int类型的转换等级相同。
-- 宽度相同的情况下,标准整数类型的转换等级高于扩展整数类型和位精确整数类型的转换等级。
例如:signed char类型宽度为8,_BitInt(8)类型宽度为8,因此signed char类型转换等级高于_BitInt(8)类型。
-- char类型、signed char类型、unsigned char类型转换等级相同。
-- bool类型的转换等级低于所有其它标准整数类型的转换等级。
-- 枚举类型的转换等级与兼容整数类型的转换等级相同。
enum color {red, green, blue = -10, white}; enum shape {club, diamond, heart, spade};
使用GCC编译时,枚举类型enum color与signed类型兼容,所以枚举类型enum color与signed类型转换等级相同;枚举类型enum shape与unsigned类型兼容,所以枚举类型enum shape与unsigned类型转换等级相同。
-- 相同精度的扩展有符号整数类型的转换等级由实现定义,但仍须遵守整数转换等级的其它规则。
-- 对于所有整数类型,如果T1的转换等级高于T2,T2的转换等级高于T3,那么T1的转换等级高于T3。
2、整数提升
一个表达式只要可以使用int类型或者unsigned int类型,就可以使用以下内容:
-- 整数类型(int类型和unsigned int类型除外)对象或者表达式,其整数转换等级低于或者等于int和unsigned int类型。
-- bool、int、signed int或者unsigned int类型位字段。
位精确整数类型的位字段值转换成相应的位精确整数类型,例如:位字段_BitInt(32) : 10转换成_BitInt(32)类型。在原始类型不是位精确整数类型的情况下,如果int类型能够表示原始类型的所有值(对于位字段会受宽度限制。),则原始类型值将转换为int类型;否则将转换为unsigned int类型,这称为整数提升(integer promotions)。所有其它类型不受整数提升影响。整数提升会保留值和符号。普通char("plain" char)类型值是否保留负值将由实现定义。
整数提升的特例:二元运算符的_BitInt类型操作数并不会总提升至int类型或者unsigned int类型;作为替代方式,低整数转换等级的操作数会转换成高整数转换等级操作数的类型,运算结果类型是高整数转换等级操作数的类型。
_BitInt(3) bi1 = 1; _BitInt(4) bi2 = 2; _BitInt(36) bi3 = 3; signed char ch = 'a'; // 操作数bi1转换成_BitInt(4)类型。 // 表达式bi1 + bi2的结果类型是_BitInt(4)类型。 bi1 + bi2; // 操作数bi1会转换成int类型。 // 操作数ch会转换成int类型。 // 表达式bi1 + ch的结果类型是int类型。 bi1 + ch; // 操作数ch会转换成int类型; // 如果int类型占32个位,操作数ch会转换成_BitInt(36)类型。 // 表达式bi3 + ch的结果类型是_BitInt(36)类型。 bi3 + ch;
(注:整数提升中涉及位精确整数类型的部分是ISO/IEC 9899:2024标准新增内容。)
整数提升仅适用于以下情况:
① 常用算术转换(usual arithmetic conversions)
signed char ch1 = 126; signed char ch2 = 127; signed i; i = ch1 + ch2; //ch1、ch2会提升为int类型。
② 某些实参表达式(argument expressions)
函数原型声明符中的省略号会导致参数类型转换在最后一个声明形参(如果存在。)后停止;每个尾随参数(trailing argument)都会执行整数提升,具有float类型的尾随参数(trailing argument)会提升为double类型;这称为默认参数提升(default argument promotions)。
int func(unsigned char ch, ...); ... unsigned char ch1 = 'A'; unsigned char ch2 = 'B'; unsigned char ch3 = 'C'; func(ch1, ch2, ch3); //ch1不会整数提升,ch2和ch3会整数提升。
③ 一元运算符+、-、~的操作数
signed char ch;
+ch;
④ 移位运算符的操作数。
unsigned char ch = 5;
ch << 2;
3、整数类型间的相互转换
标量值转换成bool类型时,如果标量值是0(对于算术类型)、空指针(对于指针类型)或者标量类型是nullptr_t类型,转换结果是false;否则转换结果是true。由于非数值(NaN)不等于0,因此当非数值转换成bool类型时,转换结果将等于true。
bool a, b, c; a = 0; //转换结果是false。 b = 5; //转换结果是true。 c = 0.0/0.0; //0.0/0.0结果是非数值,赋值后c的值为true。
整数类型值转换成bool类型外的其它整数类型时,如果该整数类型值可以用新类型表示,则保持不变;否则,如果新类型是无符号整数类型,则通过反复加减模(对于无符号整数类型,模 = 该类型可表示的最大值 + 1。)直至值在新类型的表示范围内(注:该规则描述的是数学值的运算,而不是给定类型表达式的值。);否则,如果新类型是有符号整数类型,并且值无法使用新类型表示,要么结果由实现定义,要么生成实现定义的信号。
unsigned char uc; uc = 257; //uc值为1,但编译时会给出警告。 signed char sc; sc = 257; //结果取决于实现。 unsigned int ui; uc = 25; ui = uc*uc; //ui值为625。
变量uc类型是unsigned char类型,由于整数提升,uc会提升至int类型,表达式uc*uc的值可以超出unsigned char类型能够表示的范围。