C
C 语法

Implicit conversions

隐式转换

当预期在不同类型的值的上下文中使用表达式时,可能会发生转换

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

转换发生在以下情况:

按照分配进行转换

  • 在赋值运算符中,右侧操作数的值被转换为左侧操作数的非限定类型。

请注意,除转换外,实际分配也会从浮点类型中移除额外的范围和精度,并禁止重叠; 这些特性不适用于转换,就像通过分配一样。

默认参数优化

在调用时的函数调用表达式中。

1)没有原型的功能

2)可变参数函数,其中参数表达式是与省略号参数匹配的尾随参数之一

整数类型的每个参数都经过整数提升(参见下文),并且每个类型的参数都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 complexfloat imaginary没有提升到double complexdouble imaginary在这种情况下。

通常的算术转换

以下算术运算符的参数经过隐式转换,以获得公共实数类型,这是执行计算的类型:

  • 二进制算术*,/,%,+, -

1)如果一个操作数是long doublelong double complexlong double imaginary,另一个操作数被隐式转换如下:

  • 整数或实际浮动类型 long double

2)否则,如果一个操作数是doubledouble complexdouble imaginary,另一个操作数被隐式转换如下:

  • 整数或实际浮动类型 double

3)否则,如果一个操作数是floatfloat complexfloat imaginary,另一个操作数被隐式转换如下:

  • 整数类型为float(唯一可能的实际类型是float,保持原样)

4)否则,两个操作数都是整数。在这种情况下,首先,两个操作数都进行整数升级(见下文)。然后

  • 如果促销后的类型相同,则该类型是常见类型

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 is promoted back to int. // this is signed + signed case, different rank // int is converted to long, the result is 98 signed long 2u - 10; // signed / unsigned, same rank // 10 is converted to unsigned, unsigned math is modulo UINT_MAX+1 // assuming 32 bit ints, result is 4294967288 of type unsigned int (aka UINT_MAX-7) 0UL - 1LL; // signed/unsigned diff rank, rank of signed is greater. // If sizeof(long) == sizeof(long long), signed cannot represent all unsigned // this is the last case: both operands are converted to unsigned long long // 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

与往常一样,浮点运算符的结果可能比其类型所指示的范围和精度更高(请参阅参考资料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∞。

注意:无论通常的算术转换如何,在as-if规则下,通过这些规则,计算总是可以以窄于指定符的类型执行。

价值转化

左值转换

任何非数组类型的任何左值表达式,当在除。以外的任何上下文中使用时。

  • 作为操作符的操作数(如果允许)

进行左值转换:类型保持不变,但会丢失 const / volatile / restrict-qualifiers 和原子属性(如果有的话)。价值保持不变,但失去了左值属性(地址可能不再被采用)。

如果左值具有不完整类型,则行为未定义。

如果左值指定一个自动存储持续时间的对象,其地址从未被采用,并且该对象未初始化(未使用初始化器声明并且在使用之前未对其进行分配),则行为是未定义的。

此转换将模拟对象的值从其位置的内存负载。

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

数组到指针的转换

数组类型的任何左值表达式,当在除。以外的任何上下文中使用时。

  • 作为操作符地址的操作数

经历转换到非左值指针到它的第一个元素。

如果数组被声明为注册,则行为是未定义的。

int a[3], b[3][4]; int* p = a; // conversion to &a[0] int (*q)[4] = b; // conversion to &b[0]

函数指针转换

任何函数指示符表达式,当在除。以外的任何上下文中使用时。

  • 作为操作符地址的操作数

经过转换到非左值指针指向由表达式指定的函数。

int f(int int (*p)(int) = f; // conversion to &f (***p)(1 // repeated dereference to f and conversion back to &f

隐式转换语义

隐式转换,无论是通过赋值还是通常的算术转换,都由两个阶段组成:

1)价值转化(如果适用)

2)下面列出的转换之一(如果它可以产生目标类型)

兼容的类型

将任何类型的值转换为任何兼容类型始终是不可操作的,并且不会更改表示形式。

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,整型,符号intunsigned int类型的位域的,以类型的值intunsigned int

如果int可以表示原始类型的整个值范围(或原始位域的值的范围),则将该值转换为类型int。否则,该值将转换为unsigned int

整数升级保持价值,包括符号:

int main(void) { void f( // old-style function declaration 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

上面的 rank 是每个整数类型的属性,定义如下:

1)所有有符号整数类型的行列是不同的,并且随着它们的精度而增加:被签名的char的等级<短的等级<int的等级<long int的等级<long long 的等级 int

2)所有有符号整数类型的等级等于对应无符号整数类型的等级

3)任何标准整数类型的等级大于任何相同大小的扩展整数类型的等级(即,__int64 <等级长long int,但 long long 等级<由于规则而等于__int128等级(1))

4)char 的等级等于 signed char 的等级和 unsigned char 的等级

5)_Bool 的排名小于任何其他标准整数类型的排名

6)任何枚举类型的等级等于其兼容整数类型的等级

7)排名是传递性的:如果T1的等级<T2的等级和T2的等级<T3的等级,那么T1的等级<T3的等级

8)上面未涉及的扩展整数类型的相对排序的任何方面都是实现定义的

注意:整数升级只适用。

  • 作为通常算术转换的一部分(见上文)

布尔转换

任何标量类型的值都可以隐式转换为_Bool。将等于零的值转换为​0​,将其他所有值转换为1

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)

整数转换

任何整数类型的值都可以隐式转换为任何其他整数类型。除了上述促销和布尔转换所涵盖的地方外,规则如下:

  • 如果目标类型可以表示源类型的整个值范围,则值不变

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

Real floating-integer conversions

任何实数浮点类型的有限值都可以隐式转换为任何整数类型。除了上述布尔转换覆盖的地方,规则是:

  • 小数部分被丢弃(截至零)。

int n = 3.14; // n == 3 int x = 1e10; // undefined behavior for 32-bit int

任何整数类型的值都可以隐式转换为任何实数浮点类型。

  • 如果该值可以完全由目标类型表示,则该值不变

这种转换的结果可能比其目标类型指示的范围和精度更高(请参阅FLT_EVAL_METHOD

如果FE_INEXACT在浮点到整数转换中需要控制,rintnearbyint可以使用。

double d = 10; // d = 10.00 float f = 20000001; // f = 20000000.00 (FE_INEXACT) float x = 1+(long long)FLT_MAX; // undefined behavior

实际浮点转换

任何实际浮动类型的值都可以隐式转换为任何其他实际浮动类型。

  • 如果该值可以完全由目标类型表示,则不会改变

这种转换的结果可能比其目标类型指示的范围和精度更高(请参阅FLT_EVAL_METHOD

double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625 float f = d; // f = 0.100000001490116119384765625 float x = 2*(double)FLT_MAX; // undefined

复杂的类型转换

任何复杂类型的值都可以隐式转换为任何其他复杂类型。实部和虚部分别遵循实际浮动类型的转换规则。

double complex d = 0.1 + 0.1*I; float complex f = d; // f is (0.100000001490116119384765625, 0.100000001490116119384765625)

虚构类型转换

任何虚构类型的值都可以隐式转换为任何其他虚构类型。虚部遵循实际浮动类型的转换规则。

double imaginary d = 0.1*_Imaginary_I; float imaginary f = d; // f is 0.100000001490116119384765625*I

真正复杂的转换

任何实际浮动类型的值都可以隐式转换为任何复杂类型。

  • 结果的实际部分取决于实际浮动类型的转换规则

任何复杂类型的值都可以隐式转换为任何实际浮动类型。

  • 实部按照真实浮动类型的规则进行转换

注意:在复数到实数转换中,虚数部分的 NaN 不会传播到实际结果。

实际的虚拟转换

任何虚数类型的值都可以隐式转换为任何实数类型(整型或浮点型)。除目标类型为_Bool 之外,结果始终为正值(或无符号)零,在这种情况下,布尔转换规则适用。

任何实数类型的值都可以隐式转换为任何虚数类型。结果总是一个正的虚构的零。

复数 - 虚数转换

任何虚构类型的值都可以隐式转换为任何复杂类型。

  • 结果的实际部分是正的零

任何复杂类型的值都可以隐式转换为任何虚构类型。

  • 真正的部分被丢弃

double imaginary z = I * (3*I // the complex result -3.0+0i loses real part, gives zero

指针转换

一个指针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*转换为0的整型指针表达式都可以隐式转换为任何指针类型(既指向对象又指向函数的指针)。结果是它的类型的空指针值,保证比较不等于该类型的任何非空指针值。这个整数或 void *表达式被称为空指针常量,标准库提供这个常量的一个定义作为宏NULL

int* p = 0; double* q = NULL;

笔记

尽管任何算术运算符中的有符号整数溢出都是未定义行为,但在整数转换中溢出有符号整数类型仅仅是未指定的行为。

另一方面,尽管任何算术运算符(和整数转换)中的无符号整数溢出是一个定义明确的操作,并遵循模算术规则,但浮点到整数转换中的无符号整数溢出是未定义的行为:可以转换为无符号整数的实际浮动类型的值是来自打开间隔(-1; Unnn_MAX + 1)的值。

unsigned int n = -1.0; // undefined behavior

指针和整数之间的转换(除了指向_Bool 的指针和整数常量表达式,值为零到指针)之间,指向对象的指针(除非指向 void 的指针或指向 void 的指针之外)以及指向函数的指针之间的转换函数具有兼容类型)从不隐含,并且需要一个演员操作符。

在指向函数的指针和指向对象的指针(包括 void *)或整数之间没有转换(隐式或显式)。

参考

  • C11 standard (ISO/IEC 9899:2011):