暗黙の型変換
式が異なる型の値が期待されるコンテキストで使用される場合、変換が発生することがあります。
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)
|
|
(C99以降) |
[編集] 通常の算術変換
以下の算術演算子のオペランドは、計算が実行される型である共通実数型を取得するために、暗黙の型変換を受けます。
- 二項算術演算子
*,/,%,+,- - 比較演算子
<,>,<=,>=,==,!= - 二項ビット演算子
&,^,|, - 条件演算子
?:
|
1) 一方のオペランドが10進浮動小数点型の場合、もう一方のオペランドは標準浮動小数点型、複素数型、または虚数型であってはなりません。
複素数型、または虚数型であってはなりません。
|
(C23以降) |
long double、long double complex、またはlong double imaginary(C99以降) の場合、もう一方のオペランドは以下のように暗黙的に変換されます。- 整数型または実数浮動小数点型から
long doubleへ
- 整数型または実数浮動小数点型から
|
(C99以降) |
double、double complex、またはdouble imaginary(C99以降) の場合、もう一方のオペランドは以下のように暗黙的に変換されます。- 整数型または実数浮動小数点型から
doubleへ
- 整数型または実数浮動小数点型から
|
(C99以降) |
float、float complex、またはfloat imaginary(C99以降) の場合、もう一方のオペランドは以下のように暗黙的に変換されます。- 整数型から
floatへ(可能な実数型は float のみで、これはそのままです)
- 整数型から
|
(C99以降) |
- 型が同じ場合、その型が共通の型となります。
- それ以外の場合、型は異なります。
- 型が同じ符号性(両方とも符号付きまたは両方とも符号なし)の場合、変換ランク1 が小さい方の型のオペランドが、もう一方の型に暗黙的に変換されます2。
- それ以外の場合、オペランドは異なる符号性を持っています。
- 符号なし型が、符号付き型のランク以上である場合、符号付き型のオペランドは符号なし型に暗黙的に変換されます。
- それ以外の場合、符号なし型が符号付き型よりランクが小さい場合
- 符号付き型が符号なし型すべての値を表現できる場合、符号なし型のオペランドは符号付き型に暗黙的に変換されます。
- それ以外の場合、両方のオペランドは、符号付きオペランドの型の符号なし型に対応するものに暗黙的に変換されます。
- 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ルール」の下で、これらの規則で指定されたよりも狭い型で行われる場合があります。
[編集] 値の変換
[編集] lvalue変換
配列型以外の任意の型の任意のlvalue式は、以下の場合を除き、
- アドレス演算子(許可されている場合)のオペランドとして
- 前置/後置インクリメントおよびデクリメント演算子のオペランドとして。
- メンバアクセス(ドット)演算子の左辺オペランドとして。
- 代入演算子および複合代入演算子の左辺オペランドとして。
sizeofのオペランドとして
lvalue変換を受けます。型はそのままですが、const/volatile/restrict 修飾子および atomic プロパティ(もしあれば)を失います。値はそのままですが、lvalueプロパティを失います(アドレスを取得できなくなります)。
lvalueが不完全型の場合、動作は未定義です。
lvalueが自動記憶域期間のオブジェクトを指定し、そのアドレスが一度も取得されておらず、かつそのオブジェクトが初期化されていない場合(初期化子で宣言されておらず、使用前に代入が行われていない場合)、動作は未定義です。
この変換は、その位置からのオブジェクトの値のメモリロードをモデル化します。
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
[編集] 配列からポインタへの変換
任意のlvalue式で、配列型を持つものは、以下の場合を除き、
- アドレス演算子のオペランドとして
sizeofのオペランドとしてtypeofおよびtypeof_unqualのオペランドとして (C23以降)- 配列初期化の文字列リテラルとして
最初の要素へのポインタの非lvalueへの変換を受けます。
配列が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以降)
指定された関数へのポインタの非lvalueへの変換を受けます。
int f(int); int (*p)(int) = f; // conversion to &f (***p)(1); // repeated dereference to f and conversion back to &f
[編集] 暗黙の型変換の意味論
暗黙の型変換は、「代入としての変換」または「通常の算術変換」のいずれであっても、2つの段階で構成されます。
[編集] 互換性のある型
任意の型の値を互換性のある型に変換することは常に no-op であり、表現を変更しません。
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
上記のランクは、すべての整数型のプロパティであり、以下のように定義されます。
signed char < short < int < long int < long long int__int64 のランク < long long int のランクですが、規則 (1) により long long のランク < __int128 のランクになります)。char のランクは、signed char および unsigned char のランクと等しくなります。_Bool(C23まで) / bool(C23以降) のランクは、他のすべての標準整数型のランクよりも小さくなります。|
8) ビット精緻符号付き整数型のランクは、幅が狭い任意の標準整数型またはビット精緻整数型よりも大きくなければなりません。
9) 同じ幅のビット精緻整数型と拡張整数型との相対ランクは、実装定義です。
|
(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 算術がサポートされている場合、丸めは最も近い値になります。
- 値が表現できない場合、動作は未定義です。
このセクションは未完成です
理由:必要に応じて適切に符号付けされた無限大をチェックします。
この変換の結果は、ターゲット型が示すよりも大きな範囲と精度を持つ場合があります(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 実数と虚数の変換任意の虚数型の値は、任意の(整数または浮動小数点)実数型に暗黙的に変換できます。結果は常に正の(または符号なし)ゼロになります。ただし、ターゲット型が 任意の整数型または浮動小数点型の値は、任意の虚数型に暗黙的に変換できます。結果は常に正の虚数ゼロになります。 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以降) およびゼロの値を持つ整数定数式からポインタへの変換、void* へのポインタへの変換を除く)、オブジェクトへのポインタ間の変換(いずれかが void へのポインタである場合を除く)、および関数へのポインタ間の変換(関数が互換性のある型を持つ場合を除く)は、決して暗黙的ではなく、キャスト演算子が必要です。
関数へのポインタとオブジェクトへのポインタ(void* を含む)または整数との間の変換(暗黙的または明示的)はありません。
[編集] 参照
- C23標準 (ISO/IEC 9899:2024)
- 6.3 Conversions (p: TBD)
- C17標準 (ISO/IEC 9899:2018)
- 6.3 Conversions (p: 37-41)
- C11標準 (ISO/IEC 9899:2011)
- 6.3 Conversions (p: 50-56)
- C99標準 (ISO/IEC 9899:1999)
- 6.3 Conversions (p: 42-48)
- C89/C90標準 (ISO/IEC 9899:1990)
- 3.2 Conversions
[編集] 関連項目
| C++ ドキュメント (暗黙の型変換について)
|