C言語では、変数などの記憶域を 希望のデータで設定する場合、
変数などの記憶域を表現する型と、
設定するデータの型を
合わせなければならない場合がありました。
注意すべきは、次の3箇所になるでしょう。
=演算子を使った変更で、=演算子の左辺と右辺の型 |
=初期化指定を使った設定で、対応する記憶域と初期化したい値表現の型 |
関数呼び出しで、仮引数の型と実引数の型 |
変更対象の記憶域の型と設定するデータの型 において、型が合わなくても、問題ないケースは、次の通りなどです。
変更対象の記憶域の型 | 設定するデータの型 |
---|---|
int | char |
double | int |
void * | 任意のポインタ |
これらの型が合わなくても、情報を失うような記憶域の設定になりません。
double d = 123 ; // 整数を実数変数へ記憶
これは「問題がおきないケース」として、C言語の仕様で操作が可能になるよう決められれたからです。
しかし、この逆の場合は、情報を失って問題が起きることがありえます。例えば次の場合です。
int i;
double d = 1.23;
i = d ;
この場合、 i がint型なので、1.23の0.23の情報を失って i に記憶されるのが 1 だけになります。
そして、このようなプログラムのミスに気づくように、このような箇所で警告を出すようにコンパイラが作られて
いるのです。
特にポインタ操作にいたっては、間違えた場合、変更ができてしまうと危険なプログラムになります。
それを、以下の例で示します。
char a[5];
int * p = a; //コンパイルエラー(設定によってエラーでなく警告?)
*p = 48 ;
この場合、pの型は(int*)型で、本来は(char *)型のaを設定する表現で、
コンパイルエラーの指摘がでます。
しかし、仮にでききてしまうと、pが指し示す型はint型なので、
48は、a[0]だけを変更するのではなく、aのアドレスから始まるint型領域を、
int型記憶方法に従って変更することになります。
仮にintが4byte整数とすると、a[0], a[1], a[2], a[4] の4byteが変更されることになります。
このようなプログラムのミスに気づくように、ポインタ型が合わない箇所ではエラーまたは警告を出すようにコンパイラが作られて
いるのです。
型の違いにより情報を失うことを利用したい場合や、宣言時の記憶域をその型以外で扱いたい場合もあります。
そのような場合は、キャスト演算子のカッコを使います。つまり、 ( ) は、
カッコの中の演算を先行させる優先順最高位としての利用以外で他に、単項演算子として、
単純な型を、 ( と ) の中で指定した型に変換する
使い方があるのです。
( 型の名前 ) 型変換したい式
という表現の式で、 型変換したい式 を、型の名前へ、強制的に変換したデータに
します。単項演算子なので、オペランドは右にあるデータです。
(変換できるのは、単純な型のみで、構造体では使えません。
なお、構造体を指し示すポインタは、実質的にアドレスという単純な値なので可能です。)
int i;
double d = 4.23;
i = (int) d ;
この場合、d の記憶内容の、4.23 の値が、 (int) のキャスト演算子で、整数の 4 を演算結果として出します。
それを、iに記憶させるだけなので、コンパイラの警告はなくなります。
もう一つの例を以下に示します。
int func(double d) /* 2で割った整数を返す関数 */
{
return (int)(d / 2);
}
この場合、 (int) のキャスト演算子がないと、
一般的なコンパイラは警告を出します。
戻り値が整数で、情報を失う可能性がある箇所です。
整数に切り捨てて処理させたい場合は、このようにキャスト演算子で、
強制的に変換して、この失う処理を行なわせたい旨をコンパイラに知らせるわけです。
なお、(d / 2)のカッコが無いと、/の割り算より、キャスト演算の優先順位が高いので、
違う結果になることがあると注意ください。
以下の場合もキャスト演算を使わないと、警告が出るコンパイラが あります。
char c;
int d = 128;
c = (char) d ;
この場合、d の記憶内容の、128 の値が、 (char) のキャスト演算子で、強制的に変換して
います。この場合の例で情報の損失が起きませんが、違う値になります。
128をバイナリで表現すると、10000000ですが、
これがcharになると、1byteの最上ビットの符号ビットが1なので、負の値ということになります。
2の補数で記憶されているとすると、この値は-128になってしまいます。
プログラマが、この結果を意図するなら、このようなキャストを使って
警告を無くせばよいことになります。
(この例でキャスト演算を使わなくても
、標準の指定で警告を出さないコンパイラが多いでしょう。しかし、注意してプログラムを作るべき箇所です。)