隐式转换
当表达式在期望不同类型值的上下文中使用时,可能会发生转换
int n = 1L; // expression 1L has type long, int is expected n = 2.1; // expression 2.1 has type double, int is expected char *p = malloc(10); // expression malloc(10) has type void*, char* is expected
转换发生在以下情况:
目录 |
[编辑] 如同赋值的转换
- 在赋值运算符中,右操作数的值被转换为左操作数的非限定类型。
- 在标量初始化中,初始化器表达式的值被转换为被初始化对象的非限定类型。
- 在函数调用表达式中,对于带有原型的函数,每个实参表达式的值被转换为对应形参的非限定声明类型。
- 在return 语句中,
return
操作数的值被转换为具有函数返回类型的对象。
请注意,实际赋值,除了转换之外,还会从浮点类型中移除额外的范围和精度,并禁止重叠;这些特性不适用于如同赋值的转换。
[编辑] 默认实参提升
在函数调用表达式中,当调用以下函数时:
每个整型实参都会进行整型提升(见下文),并且每个float类型的实参都会隐式转换为double类型。
int add_nums(int count, ...); int sum = add_nums(2, 'c', true); // add_nums is called with three ints: (2, 99, 1)
请注意,float complex和float imaginary在此上下文中不会提升为double complex和double imaginary。 |
(C99 起) |
[编辑] 常用算术转换
以下算术运算符的实参会进行隐式转换,以获取执行计算的通用实数类型:
1) 如果一个操作数是十进制浮点类型,则另一个操作数不得是标准浮点、
复数或虚数类型。
|
(自 C23 起) |
- 整型或实浮点类型转换为 long double
(C99 起) |
- 整型或实浮点类型转换为 double
(C99 起) |
- 整型转换为 float(唯一可能的实数类型是 float,保持不变)
(C99 起) |
- 如果类型相同,则该类型为通用类型。
- 否则,类型不同
- 如果类型具有相同的有符号性(都带符号或都不带符号),则类型具有较低转换等级1的操作数隐式转换为另一种类型。
- 否则,操作数具有不同的有符号性
- 如果无符号类型具有大于或等于带符号类型等级的转换等级,则带符号类型的操作数隐式转换为无符号类型。
- 否则,无符号类型的转换等级小于带符号类型
- 如果带符号类型可以表示无符号类型的所有值,则无符号类型的操作数隐式转换为带符号类型。
- 否则,两个操作数都隐式转换为带符号操作数类型的无符号对应类型。
- 1. 有关排序规则,请参阅下面的“整型提升”。
- 2. 有关“隐式转换语义”下的“整型转换”,请参阅下文。
1.f + 20000001; // int is converted to float, giving 20000000.00 // addition and then rounding to float gives 20000000.00 (char)'a' + 1L; // first, char 'a', which is 97, is promoted to int // different types: int and long // same signedness: both signed // different rank: long is of greater rank than int // therefore, int 97 is converted to long 97 // the result is 97 + 1 = 98 of type signed long 2u - 10; // different types: unsigned int and signed int // different signedness // same rank // therefore, signed int 10 is converted to unsigned int 10 // since the arithmetic operation is performed for unsigned integers // (see "Arithmetic operators" topic), the calculation performed is (2 - 10) // modulo (2 raised to n), where n is the number of value bits of unsigned int // if unsigned int is 32-bit long and there is no padding bits in its object // representation, then the result is (-8) modulo (2 raised to 32) = 4294967288 // of type unsigned int 5UL - 2ULL; // different types: unsigned long and unsigned long long // same signedness // different rank: rank of unsigned long long is greater // therefore, unsigned long 5 is converted to unsigned long long 5 // since the arithmetic operation is performed for unsigned integers // (see "Arithmetic operators" topic), // if unsigned long long is 64-bit long, then // the result is (5 - 2) modulo (2 raised to 64) = 3 of type // unsigned long long 0UL - 1LL; // different types: unsigned long and signed long long // different signedness // different rank: rank of signed long long is greater. // if ULONG_MAX > LLONG_MAX, then signed long long cannot represent all // unsigned long therefore, this is the last case: both operands are converted // to unsigned long long unsigned long 0 is converted to unsigned long long 0 // long long 1 is converted to unsigned long long 1 since the arithmetic // operation is performed for unsigned integers // (see "Arithmetic operators" topic), // if unsigned long long is 64-bit long, then // the calculation is (0 - 1) modulo (2 raised to 64) // thus, the result is 18446744073709551615 (ULLONG_MAX) of type // unsigned long long
结果类型确定如下:
double complex z = 1 + 2*I; double f = 3.0; z + f; // z remains as-is, f is converted to double, the result is double complex |
(C99 起) |
与往常一样,浮点运算符的结果可能具有比其类型指示的更大的范围和精度(参见 FLT_EVAL_METHOD)。
注意:实数和虚数操作数不会隐式转换为复数,因为这样做需要额外的计算,并且在涉及无穷大、NaN 和带符号零的某些情况下会产生不希望的结果。例如,如果实数转换为复数,2.0×(3.0+i∞) 将计算为 (2.0+i0.0)×(3.0+i∞) ⇒ (2.0×3.0–0.0×∞) + i(2.0×∞+0.0×3.0) ⇒ NaN+i∞,而不是正确的 6.0+i∞。如果虚数转换为复数,i2.0×(∞+i3.0) 将计算为 (0.0+i2.0) × (∞+i3.0) ⇒ (0.0×∞ – 2.0×3.0) + i(0.0×3.0 + 2.0×∞) ⇒ NaN + i∞,而不是 –6.0 + i∞。 |
(C99 起) |
注意:无论常用算术转换如何,根据as-if 规则,计算始终可以在比这些规则指定的更窄的类型中执行。
[编辑] 值转换
[编辑] 左值转换
任何非数组类型的左值表达式,在以下情况之外的任何上下文中:
会发生左值转换:类型保持不变,但会失去const/volatile/restrict限定符和atomic属性(如果有)。值保持不变,但失去其左值属性(可能无法再取地址)。
如果左值具有不完整类型,则行为未定义。
如果左值指定了一个自动存储期对象,其地址从未被获取,并且该对象未被初始化(未用初始化器声明,且在使用前未进行赋值),则行为未定义。
此转换模拟从对象位置加载对象值的内存操作。
volatile int n = 1; int x = n; // lvalue conversion on n reads the value of n volatile int* p = &n; // no lvalue conversion: does not read the value of n
[编辑] 数组到指针转换
- 作为取地址运算符的操作数
- 作为 sizeof 的操作数
- 作为 typeof 和 typeof_unqual 的操作数 (C23 起)
- 作为用于数组初始化的字符串字面量
会转换为其第一个元素的非左值指针。
如果数组声明为 register,则行为未定义。
int a[3], b[3][4]; int* p = a; // conversion to &a[0] int (*q)[4] = b; // conversion to &b[0]
[编辑] 函数到指针转换
任何函数指示符表达式,在以下情况之外的任何上下文中:
- 作为取地址运算符的操作数
- 作为 sizeof 的操作数
- 作为 typeof 和 typeof_unqual 的操作数 (C23 起)
会转换为指向该表达式指定的函数的非左值指针。
int f(int); int (*p)(int) = f; // conversion to &f (***p)(1); // repeated dereference to f and conversion back to &f
[编辑] 隐式转换语义
隐式转换,无论是如同赋值还是常用算术转换,都包含两个阶段:
[编辑] 兼容类型
将任何类型的值转换为任何兼容类型始终是空操作,并且不改变表示形式。
uint8_t (*a)[10]; // if uint8_t is a typedef to unsigned char unsigned char (*b)[] = a; // then these pointer types are compatible
[编辑] 整型提升
整型提升是任何整型值(其等级小于或等于 int 的等级)或类型为 _Bool(直至 C23)bool(C23 起)、int、signed int、unsigned int 的位域,隐式转换为int或unsigned int类型的值。
如果int可以表示原始类型的整个值范围(或原始位域的值范围),则该值被转换为int类型。否则,该值被转换为unsigned int。
来自位精确整型位域的值会转换为对应的位精确整型。否则,位精确整型不受整型提升规则的约束。 |
(自 C23 起) |
整型提升会保留值,包括符号。
int main(void) { void f(); // old-style function declaration // since C23, void f(...) has the same behavior wrt promotions char x = 'a'; // integer conversion from int to char f(x); // integer promotion from char back to int } void f(x) int x; {} // the function expects int
上面的等级是每个整型类型的属性,定义如下:
8) 位精确有符号整型类型的等级应大于任何宽度较小的标准整型类型或任何宽度较小的位精确整型类型。
9) 任何位精确整型类型相对于相同宽度的扩展整型类型的相对等级是实现定义的。
|
(自 C23 起) |
注意:整型提升仅在以下情况下应用:
- 作为常用算术转换的一部分(见上文)
- 作为默认实参提升的一部分(见上文)
- 作为一元算术运算符 + 和 - 的操作数
- 作为一元位运算符 ~ 的操作数
- 作为移位运算符 << 和 >> 的两个操作数
布尔转换任何标量类型的值都可以隐式转换为 _Bool(直至 C23)bool(C23 起)。 与值为零的整型常量表达式比较相等(直至 C23)为零(对于算术类型)、空(对于指针类型)或具有 nullptr_t 类型的(C23 起)的值转换为 0(直至 C23)false(C23 起),所有其他值转换为 1(直至 C23)true(C23 起)。 bool b1 = 0.5; // b1 == 1 (0.5 converted to int would be zero) bool b2 = 2.0*_Imaginary_I; // b2 == 1 (but converted to int would be zero) bool b3 = 0.0 + 3.0*I; // b3 == 1 (but converted to int would be zero) bool b4 = 0.0/0.0; // b4 == 1 (NaN does not compare equal to zero) bool b5 = nullptr; // b5 == 0 (since C23: nullptr is converted to false) |
(C99 起) |
[编辑] 整型转换
任何整型类型的值都可以隐式转换为任何其他整型类型。除了上面提升和布尔转换所涵盖的情况,规则如下:
- 如果目标类型可以表示该值,则该值不变。
- 否则,如果目标类型是无符号类型,则值 2b
(其中 b 是目标类型中值位的数量)会反复从源值中减去或添加到源值,直到结果符合目标类型。换句话说,无符号整型实现模运算。 - 否则,如果目标类型是带符号类型,则行为是实现定义的(可能包括引发信号)。
char x = 'a'; // int -> char, result unchanged unsigned char n = -123456; // target is unsigned, result is 192 (that is, -123456+483*256) signed char m = 123456; // target is signed, result is implementation-defined assert(sizeof(int) > -1); // assert fails: // operator > requests conversion of -1 to size_t, // target is unsigned, result is SIZE_MAX
[编辑] 实浮点-整型转换
任何实浮点类型的有限值都可以隐式转换为任何整型类型。除了上面布尔转换所涵盖的情况,规则如下:
- 小数部分被丢弃(向零截断)。
- 如果结果值可以由目标类型表示,则使用该值。
- 否则,行为未定义。
int n = 3.14; // n == 3 int x = 1e10; // undefined behavior for 32-bit int
任何整型类型的值都可以隐式转换为任何实浮点类型。
- 如果该值可以被目标类型精确表示,则它不变。
- 如果该值可以被表示,但不能被精确表示,则结果是实现定义的,选择最近的更高值或最近的更低值,尽管如果支持 IEEE 算术,则四舍五入到最近。在这种情况下是否引发 FE_INEXACT 是未指定的。
- 如果该值无法表示,则行为未定义,尽管如果支持 IEEE 算术,则会引发 FE_INVALID 并且结果值是未指定的。
此转换的结果可能具有比其目标类型指示的更大的范围和精度(参见 FLT_EVAL_METHOD)。
如果浮点到整型转换中需要控制 FE_INEXACT,可以使用 rint 和 nearbyint。
double d = 10; // d = 10.00 float f = 20000001; // f = 20000000.00 (FE_INEXACT) float x = 1+(long long)FLT_MAX; // undefined behavior
[编辑] 实浮点转换
任何实浮点类型的值都可以隐式转换为任何其他实浮点类型。
- 如果该值可以被目标类型精确表示,则它不变。
- 如果该值可以被表示,但不能被精确表示,则结果是最近的更高值或最近的更低值(换句话说,舍入方向是实现定义的),尽管如果支持 IEEE 算术,则四舍五入到最近。
- 如果该值无法表示,则行为未定义。
本节不完整
原因:如果需要适当带符号的无穷大,请检查 IEEE。
此转换的结果可能具有比其目标类型指示的更大的范围和精度(参见 FLT_EVAL_METHOD)。
double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625 float f = d; // f = 0.100000001490116119384765625 float x = 2*(double)FLT_MAX; // undefined
复数类型转换任何复数类型的值都可以隐式转换为任何其他复数类型。实部和虚部各自遵循实浮点类型的转换规则。 虚数类型转换任何虚数类型的值都可以隐式转换为任何其他虚数类型。虚部遵循实浮点类型的转换规则。 double imaginary d = 0.1*_Imaginary_I; float imaginary f = d; // f is 0.100000001490116119384765625*I 实数-复数转换任何实浮点类型的值都可以隐式转换为任何复数类型。
任何复数类型的值都可以隐式转换为任何实浮点类型。
注意:在复数到实数转换中,虚部的 NaN 不会传播到实数结果。 double complex z = 0.5 + 3*I; float f = z; // the imaginary part is discarded, f is set to 0.5 z = f; // sets z to 0.5 + 0*I 实部-虚部转换任何虚数类型的值都可以隐式转换为任何实数类型(整数或浮点数)。结果始终是正零(或无符号零),除非目标类型是_Bool(C23 之前)bool(C23 及之后),在这种情况下,应用布尔转换规则。 任何实数类型的值都可以隐式转换为任何虚数类型。结果始终是正虚零。 double imaginary z = 3*I; bool b = z; // Boolean conversion: sets b to true float f = z; // Real-imaginary conversion: sets f to 0.0 z = 3.14; // Imaginary-real conversion: sets z to 0*_Imaginary_I 复数-虚部转换任何虚数类型的值都可以隐式转换为任何复数类型。
任何复数类型的值都可以隐式转换为任何虚数类型
double imaginary z = I * (3*I); // the complex result -3.0+0i loses real part // sets z to 0*_Imaginary_I |
(C99 起) |
[编辑] 指针转换
void 类型的指针可以与任何指向对象类型的指针进行隐式转换,其语义如下:
- 如果一个指向对象的指针转换为指向 void 的指针再转换回来,其值与原始指针比较相等。
- 不提供其他保证
int* p = malloc(10 * sizeof(int)); // malloc returns void*
指向非限定类型的指针可以隐式转换为该类型的限定版本指针(换句话说,可以添加 const、volatile 和 restrict 限定符)。原始指针和结果比较相等。
int n; const int* p = &n; // &n has type int*
任何值为 0 的整数常量表达式以及值为零并强制转换为 void* 类型的整数指针表达式,都可以隐式转换为任何指针类型(包括指向对象和指向函数的指针)。结果是其类型的空指针值,保证与该类型的任何非空指针值比较不相等。这个整数或 void* 表达式被称为空指针常量,标准库通过宏 NULL 提供了这个常量的一个定义。
int* p = 0; double* q = NULL;
[编辑] 注意
尽管任何算术运算符中的有符号整数溢出是未定义行为,但整数转换中有符号整数溢出仅仅是未指定行为。
另一方面,尽管任何算术运算符中(和整数转换中)的无符号整数溢出是定义良好的操作并遵循模算术规则,但浮点数到整数转换中的无符号整数溢出是未定义行为:可以转换为无符号整数的实浮点类型的值是开区间 (-1; Unnn_MAX+1) 中的值。
unsigned int n = -1.0; // undefined behavior
指针和整数之间的转换(从指针到 _Bool(C23 之前)bool(C23 及之后) 除外,以及 (C99 及之后)从值为零的整数常量表达式到指针除外),指向对象指针之间的转换(to 或 from 是指向 void 的指针除外)以及指向函数指针之间的转换(当函数具有兼容类型时除外)从不隐式进行,需要强制转换运算符。
指向函数的指针与指向对象的指针(包括 void*)或整数之间没有转换(隐式或显式)。
[编辑] 参考
- C23 标准 (ISO/IEC 9899:2024)
- 6.3 转换 (p: 待定)
- C17 标准 (ISO/IEC 9899:2018)
- 6.3 转换 (p: 37-41)
- C11 标准 (ISO/IEC 9899:2011)
- 6.3 转换 (p: 50-56)
- C99 标准 (ISO/IEC 9899:1999)
- 6.3 转换 (p: 42-48)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 3.2 转换
[编辑] 另请参阅
C++ 文档 关于 隐式转换
|