名稱空間
變體
操作

隱式轉換

來自 cppreference.com
< c‎ | 語言

當表示式在期望不同型別值的上下文中使用時,可能會發生轉換

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 運算元的值被轉換為具有函式返回型別的物件。

請注意,實際賦值,除了轉換之外,還會從浮點型別中移除額外的範圍和精度,並禁止重疊;這些特性不適用於如同賦值的轉換。

[編輯] 預設實參提升

函式呼叫表示式中,當呼叫以下函式時:

1) 沒有原型的函式 (直至 C23)
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

(C99 起)

[編輯] 常用算術轉換

以下算術運算子的實參會進行隱式轉換,以獲取執行計算的通用實數型別

1) 如果一個運算元是十進位制浮點型別,則另一個運算元不得是標準浮點、

複數或虛數型別。

  • 首先,如果任一運算元的型別是 _Decimal128,則另一個運算元被轉換為 _Decimal128。
  • 否則,如果任一運算元的型別是 _Decimal64,則另一個運算元被轉換為 _Decimal64。
  • 否則,如果任一運算元的型別是 _Decimal32,則另一個運算元被轉換為 _Decimal32。
(自 C23 起)
2) 否則,如果一個運算元是 long doublelong double complexlong double imaginary (C99 起),則另一個運算元隱式轉換為如下:
  • 整型或實浮點型別轉換為 long double
  • 複數型別轉換為 long double complex
  • 虛數型別轉換為 long double imaginary
(C99 起)
3) 否則,如果一個運算元是 doubledouble complexdouble imaginary (C99 起),則另一個運算元隱式轉換為如下:
  • 整型或實浮點型別轉換為 double
(C99 起)
4) 否則,如果一個運算元是 floatfloat complexfloat imaginary (C99 起),則另一個運算元隱式轉換為如下:
  • 整型轉換為 float(唯一可能的實數型別是 float,保持不變)
(C99 起)
5) 否則,兩個運算元都是整型。兩個運算元都進行整型提升(見下文);然後,在整型提升之後,適用以下情況之一:
  • 如果型別相同,則該型別為通用型別。
  • 否則,型別不同
    • 如果型別具有相同的有符號性(都帶符號或都不帶符號),則型別具有較低轉換等級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

結果型別確定如下:

  • 如果兩個運算元都是複數,則結果型別是複數。
  • 如果兩個運算元都是虛數,則結果型別是虛數。
  • 如果兩個運算元都是實數,則結果型別是實數。
  • 如果兩個浮點運算元具有不同的型別域(複數 vs 實數,複數 vs 虛數,或虛數 vs 實數),則結果型別是複數。
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

[編輯] 陣列到指標轉換

任何左值表示式陣列型別,在以下情況之外的任何上下文中:

會轉換為其第一個元素的非左值指標。

如果陣列宣告為 register,則行為未定義。

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(直至 C23)bool(C23 起)intsigned intunsigned int位域,隱式轉換為intunsigned 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

上面的等級是每個整型型別的屬性,定義如下:

1) 所有帶符號整型型別的等級都不同,並隨其精度而增加:signed char的等級 < short的等級 < int的等級 < long int的等級 < long long int的等級
2) 所有帶符號整型型別的等級等於相應的無符號整型型別的等級
3) 任何標準整型型別的等級大於任何相同大小的擴充套件整型型別或位精確整型型別(C23 起)的等級(即,__int64的等級 < long long int的等級,但由於規則(1)long long的等級 < __int128的等級)
4) char 的等級等於 signed charunsigned char 的等級
5) _Bool(直至 C23)bool(C23 起) 的等級小於任何其他標準整型型別
6) 任何列舉型別的等級等於其相容整型型別的等級
7) 等級是可傳遞的:如果 T1 的等級 < T2 的等級 且 T2 的等級 < T3 的等級,則 T1 的等級 < T3 的等級
8) 位精確有符號整型型別的等級應大於任何寬度較小的標準整型型別或任何寬度較小的位精確整型型別。
9) 任何位精確整型型別相對於相同寬度的擴充套件整型型別的相對等級是實現定義的。
(自 C23 起)
10) 任何未涵蓋的擴充套件整型型別相對等級的方面是實現定義的。

注意:整型提升僅在以下情況下應用:

  • 作為常用算術轉換的一部分(見上文)
  • 作為預設實參提升的一部分(見上文)
  • 作為一元算術運算子 +- 的運算元
  • 作為一元位運算子 ~ 的運算元
  • 作為移位運算子 <<>> 的兩個運算元

布林轉換

任何標量型別的值都可以隱式轉換為 _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,可以使用 rintnearbyint

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

[編輯] 實浮點轉換

任何實浮點型別的值都可以隱式轉換為任何其他實浮點型別。

  • 如果該值可以被目標型別精確表示,則它不變。
  • 如果該值可以被表示,但不能被精確表示,則結果是最近的更高值或最近的更低值(換句話說,舍入方向是實現定義的),儘管如果支援 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 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

實數-複數轉換

任何實浮點型別的值都可以隱式轉換為任何複數型別。

  • 結果的實部由實浮點型別的轉換規則確定。
  • 結果的虛部是正零(或非 IEEE 系統上的無符號零)。

任何複數型別的值都可以隱式轉換為任何實浮點型別。

  • 實部按照實浮點型別的規則進行轉換。
  • 虛部被丟棄。

注意:在複數到實數轉換中,虛部的 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*

指向非限定型別的指標可以隱式轉換為該型別的限定版本指標(換句話說,可以新增 constvolatilerestrict 限定符)。原始指標和結果比較相等。

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++ 文件 關於 隱式轉換