#型変換
その名の通り型を変換すること。char -> intになど。
JISでは、型変換に関して以下を規定している。
いくつかの演算子は、オペランドの値をある型から他の型へ自動的に型変換する。
これを暗黙の型変換とよび、その結果に対する要求を規定。
キャスト演算:明示的な型変換の結果に対する要求を規定。
#算術オペランド
型の説明をする。
##論理型、文字型、整数型
型 char、符号付整数型、符号なし整数型、浮動小数点型を総称して基本型(basic type)と呼ぶ
型 char, signed char, unsigned charを総称して、文字型(character type)と呼ぶ。
整数型、浮動小数点数型を総称して実数型(real type)と呼ぶ。
charは、基本型であり、文字型であり、整数型でもある。
###全ての整数型は、整数変換の順位を持つ。
2つの符号付き整数型は、同じ表現を持つ場合でも、同じ順位を持ってはいけない。
符号付整数型は、より小さい精度の符号付整数型より高い順位を持たなければならない。(int < long < long long)
#暗黙の型変換
符号付き整数型と符号無し整数型を混在できるのは、暗黙の型変換が行われているからです。
#include <stdio.h>
int main(void)
{
int snum = -5;
unsigned int unum = 10;
int ans = snum + unum; // int = int + unsigned int
printf( "%d\n", ans );
return 0;
}
//実行結果:5
int型の snum と、unsigned int型の unum を加算演算子で加算しています。このように、型が異なるとき、型変換が行われます。この場面では、通常の算術型変換というルールによって、snum が unsigned int型に変換されてから加算処理が行われます。
加算処理を unsigned int型で行ったため、その結果も unsigned int型です。結果を int型の変数 ans の初期値にしていますが、ここでも暗黙の型変換が起きています。今度は、unsigned int型を int型に型変換しているわけです。
変数を初期化する際の「=」は、代入を意味する代入演算子ではありませんが、代入と同じルールで型変換されることになっています。
つまり、計算時と代入時(初期化などを含む代入演算子の使用時)に暗黙の型変換が起こる。
計算時は、順位に従って行われる。順位は、c言語の規定で定義されている。
代入時は、左辺の型に合わせて代入される。
右辺のデータ型のサイズが大きいときは、データが損失して代入される。
損失とは、収まりきらない上位ビットの切り捨てである。
#整数拡張(integer promotion)
int より小さな整数型は、演算時に整数拡張される。元の型のすべての値を intで 表現できる場合、小さな型の値を int に変換する。それ以外の場合、unsigned int に変換する。
たとえば、short型が 16bit、int型が 32bit、2の補数表現を使う環境で、整数拡張前の型が signed short型だったとします。
signed short型の表現範囲は -32768~32767、int型の表現範囲は -2147483648~2147483647 なので、signed short型で表現できるすべての値が、int型で表現できます。よって、整数拡張後の型は int型が選択されます。
一方、short型と int型がともに 32bit、2の補数表現を使う環境で、整数拡張前の型が unsigned short型だったとします。unsigned short型の表現範囲は 0~4294967295、int型の表現範囲は -2147483648~2147483647 なので、unsigned short型で表現できるすべての値を int型で表現することができません。
よって、__整数拡張後の型は unsigned int型__が選択されます。
整数拡張は通常の算術型変換の一部として、引数式や、単項 +、-、~演算子, シフト演算子のオペランドに対して適用される。以下に示すコードは、整数拡張の適用例を示している。
char a, b;
a = a + b; /* 右辺、a + bは char + charは、intよりも小さい
よって変数a と bの整数拡張が行われる(演算時のみint型に拡張)
左辺はchar型なので、intの演算結果は、
charに格納されて、収まりきららない上位ビットは切り捨てられる。
*/
計算時のみ、変数a と b がそれぞれ int に整数拡張されることを要求する。
#整数拡張を行う理由
整数拡張が行われるのは、計算途中の値がオーバーフローして算術エラーが起こることを防ぐためである。
###通常の算術型変換
二項演算子の2つのオペランドや条件演算子 (?:) の第二、第三オペランドは共通の型の値として扱われる。通常の算術型変換は、ここで使われる共通の型を導出するための規則である。型変換は2つのオペランドの型が異なる場合に行われ、一方もしくは両方のオペランドが変換される。算術オペランドをとる演算子の多くは、通常の算術型変換にしたがって型変換を行う。
両方のオペランドに整数拡張が適用され、その後で以下の規則が適用される。
()内は適合例
1 両方のオペランドが同じ型をもつ場合、更なる型変換は行わない。(int と int)
2 そうでない場合、両方のオペランドが符号付き整数型をもつ、又は両方のオペランドが符号無し整数型をもつならば、整数変換の順位の低い方の型を、高い方の型に変換する。
(int & long int -> long int & long int)
3 そうでない場合、符号無し整数型を持つオペランドが、他方のオペランドの整数変換の順位より高い又は等しい順位をもつならば、符号付き整数型をもつオペランドを、符号無し整数型をもつオペランドの型に変換する。
(unsigned int & signed int -> unsigned int & unsigned int)
4 そうでない場合、符号付き整数型をもつオペランドの型が、符号無し整数型をもつオペランドの型のすべての値を表現できるならば、符号無し整数型をもつオペランドを、符号付き整数型をもつオペランドの型に変換する。
(signed long int > unsigned int -> signed long int & signed long int)
5 そうでない場合、両方のオペランドを、符号付き整数型をもつオペランドの型に対応する符号無し整数型に変換する。
(signed long int < unsigned long long int -> unsigned long long int & unsigned long long int)
以下では、8ビット char、32ビット int、64ビット long long の処理系でコードがコンパイルされると仮定する。
signed char sc = SCHAR_MAX;
unsigned char uc = UCHAR_MAX;
signed long long sll = sc + uc;
この例では、signed char sc と unsigned char uc はどちらも整数拡張される。元の型のすべての値が int で表現できるため、整数拡張の過程で両方の値は自動的に int 型に変換される。通常の算術型変換では、もし両方のオペランドの型が等しくなければさらに変換が行われる可能性がある。この例では、実際の加算演算は32ビット int の値に対して行われる。
演算結果の値は signed long long 型整数に格納されるが、演算自体はそれに影響されず、32ビット int での演算となることに注意。
加算結果の32ビット値は単純に符号拡張され64ビットの値として signed long long 型整数に格納される。
signed char の精度が7ビットであり、unsigned char の精度が8ビットであると仮定した場合、この演算はまったく安全に行える。しかし、もしコンパイラが signed char と unsigned char をそれぞれ 31ビットと32ビットの精度で表現した場合、変数 uc は signed int ではなく、unsigned int に変換する必要がある。通常の算術型変換の結果、signed int は符号無し型に変換され、加算は2つの unsigned int 型の値に対して行われる。また、uc は UCHAR_MAX に等しく、これは UINT_MAX に等しくなるため、この例の加算はオーバーフローを引き起こす結果となる。結果の値はゼロ拡張され、sll に割り当てられた64ビットの領域に格納される。
引用・参考元
https://programming-place.net/ppp/contents/c/021.html#integral_promotion
https://www.jisc.go.jp/app/jis/general/GnrJISNumberNameSearchList?show&jisStdNo=X3010
http://wisdom.sakura.ne.jp/programming/c/c13.html