C言語の基本|キャスト演算子の使い方

キャスト演算子は、計算を始める前に型を切り替えるスイッチ。変換する位置を理解すれば、小数部分を正しく残せるようになります。

C言語で割り算をしたとき、小数になるはずの計算結果が整数になってしまうことがあります。

たとえば、11を4で割った結果は2.75です。しかし、int型の値同士で11 / 4を計算すると、小数部分が切り捨てられて2になります。その結果をdouble型の変数へ代入しても、失われた0.75は元に戻りません。

このような場面で役立つのがキャスト演算子です。

キャスト演算子を使うと、値や変数を指定した型へ明示的に変換できます。整数除算が行われる前に、一方の値をdouble型へ変換すれば、小数部分を含む浮動小数点除算に切り替えられます。

ただし、キャスト演算子は記述する位置が大切です。計算前の値に対してキャストする場合と、計算後の結果に対してキャストする場合では、得られる結果が変わります。

また、キャスト演算子を使えば、ポインタ型を別のポインタ型へ変換することもできます。便利である一方、扱いを間違えると未定義動作につながる可能性があるため、変換する型とキャストする位置を丁寧に確認する必要があります。

キャスト演算子の書き方

キャスト演算子は、次の形式で記述します。

(型) 式

型には変換後の型名を指定します。式には、型を変換したい値、変数、または計算式を指定します。

代表的な記述例は次のとおりです。

(double)a
(int)x
(unsigned int)count

それぞれの意味を表にすると、次のようになります。

記述変換の内容
(double)aaの値をdouble型へ変換する
(int)xxの値をint型へ変換する
(unsigned int)countcountの値をunsigned int型へ変換する

キャスト演算子を使っても、元の変数の型そのものが変更されるわけではありません。

aがint型の場合に(double)aと記述すると、その式の中ではaの値がdouble型として扱われます。しかし、aの宣言がint型からdouble型へ書き換わるわけではありません。

整数除算で小数部分が失われる理由

C言語では、演算に使われる値の型によって計算方法が決まります。

aとbがどちらもint型の場合、a / bは整数除算になります。計算結果に小数部分があっても、その部分は切り捨てられます。

たとえば、aが11、bが4の場合は、次のように計算されます。

11 / 4
↓
int型同士の整数除算
↓
2

計算結果の2をdouble型の変数へ代入すると、2.0へ変換されます。しかし、整数除算の時点で失われた0.75は戻りません。

小数部分を残したい場合は、割り算が行われる前に一方の値をdouble型へキャストします。

result = (double)a / b;

この式では、aが11.0として扱われます。もう一方のbも計算時にdouble型へ変換されるため、浮動小数点除算が行われます。

11.0 / 4.0
↓
2.75

主な書き方の違いは、次のとおりです。

書き方計算時の型aが11、bが4の場合
result = a / b;int型とint型2.0
result = (double)a / b;double型とint型2.75
result = a / (double)b;int型とdouble型2.75
result = (double)a / (double)b;double型とdouble型2.75

通常は、aまたはbのどちらか一方をdouble型へキャストすれば十分です。

一方がdouble型になると、もう一方もdouble型へ暗黙的に変換されます。そのため、両方をキャストしなくても浮動小数点除算を行えます。

図1:整数除算と浮動小数点除算の違い

この図から分かること

int型同士で割り算を行うと、結果の小数部分は計算時に切り捨てられます。その後でdouble型へ変換しても、失われた小数部分は復元できません。

割り算を始める前に一方の値をdouble型へキャストすると、もう一方もdouble型として扱われます。その結果、整数除算ではなく浮動小数点除算が行われ、2.75を得られます。

キャストする位置による違い

キャスト演算子を使用するときに注意したいのが、キャストを記述する位置です。

次の2つの式はよく似ていますが、計算の順序が異なります。

value1 = (double)a / b;
value2 = (double)(a / b);

value1では、aをdouble型へ変換してから割り算を行います。

aをdouble型へ変換
↓
double型を含む割り算を行う
↓
小数部分が残る

value2では、丸括弧内のa / bが先に計算されます。aとbがどちらもint型なら、この段階で整数除算が行われます。

a / bをint型で計算
↓
小数部分が切り捨てられる
↓
計算結果をdouble型へ変換

キャストする位置による違いを表にすると、次のようになります。

書き方最初に行われる処理aが11、bが4の場合
value = (double)a / b;aをdouble型へ変換してから割る2.75
value = (double)(a / b);a / bをint型で計算してから変換する2.0

キャストを割り算に反映させたい場合は、整数除算が始まる前の値に対してキャストすることが大切です。

キャストする位置による結果を比較するプログラム

ファイル名:6_9_1.c

#include <stdio.h>

int main(void)
{
    int length = 11;
    int pieces = 4;
    double before_division;
    double after_division;

    before_division = (double)length / pieces;
    after_division = (double)(length / pieces);

    printf("割る前にキャストした結果: %.2f\n", before_division);
    printf("割った後にキャストした結果: %.2f\n", after_division);
    printf("キャストは整数除算より前に行います。\n");

    return 0;
}
実行結果の例
割る前にキャストした結果: 2.75
割った後にキャストした結果: 2.00
キャストは整数除算より前に行います。

before_divisionの計算では、lengthをdouble型へ変換してからpiecesで割っています。

before_division = (double)length / pieces;

lengthは11.0として扱われます。piecesも計算時に4.0へ変換されるため、11.0 / 4.0という浮動小数点除算が行われます。その結果は2.75です。

after_divisionの計算では、丸括弧内のlength / piecesが先に処理されます。

after_division = (double)(length / pieces);

lengthとpiecesはどちらもint型なので、11 / 4の結果は2です。その2をdouble型へ変換するため、最終的な結果は2.0になります。

図2:キャストする位置と計算順序

この図から分かること

(double)length / piecesでは、lengthをdouble型へ変換した後で割り算が行われます。そのため、2.75という小数を含む結果が得られます。

(double)(length / pieces)では、丸括弧内の整数除算が先に行われます。整数除算によって2になった後でdouble型へ変換されるため、結果は2.0です。

丸括弧で囲んだ式全体をキャストする場合は、その丸括弧内でどのような計算が行われるのかを確認する必要があります。

強引なキャストに注意する

キャスト演算子は便利ですが、変換後の値が必ず安全になるわけではありません。

数値型同士のキャストでは、int型の値をdouble型へ変換する処理がよく使われます。一方、double型の値をint型へ変換すると、小数部分が失われます。

たとえば、次の変換では26.8の小数部分が取り除かれ、26になります。

int number = (int)26.8;

キャストによる主な変換と注意点は、次のとおりです。

変換主な用途や注意点
int型からdouble型整数除算を浮動小数点除算へ切り替えるときに使う
double型からint型小数部分が失われる
ポインタ型から別のポインタ型メモリー上のデータの見方が変わるため慎重に扱う

特に注意が必要なのが、ポインタ型のキャストです。

たとえば、int型の変数を指すポインタをchar型のポインタへ変換すると、int型のデータを1バイト単位で確認できるようになります。

int *number_pointer;
char *byte_pointer;

byte_pointer = (char *)number_pointer;

この変換は、int型の数値をchar型の数値へ変換しているわけではありません。同じメモリー領域を、別の型のポインタから見ようとしています。

char型のポインタからオブジェクトの内部表現を確認することはできますが、読み取れる内容はコンピューターのバイト順序やint型の大きさに影響されます。

さらに、本来とは異なる型のポインタへ強引に変換し、そのポインタからデータを読み書きすると、メモリーの境界や型の解釈に関する問題が発生することがあります。誤った使い方は、クラッシュや未定義動作の原因になります。

初心者のうちは、次の点を覚えておきましょう。

  • int型とdouble型のような数値型同士のキャストは計算でよく使う
  • double型からint型へ変換すると小数部分が失われる
  • ポインタ型のキャストではメモリー上のデータの見方が変わる
  • コンパイラの警告を消すことだけを目的にキャストしない
  • 変換したポインタを安全に参照できるとは限らない

実践問題:平均値を小数で求める

5回分の合計が23であるとき、1回当たりの平均値を求めます。

次のプログラムはコンパイルできますが、実行すると平均が4.00と表示されます。計算式をキャスト演算子で修正し、4.60と表示されるようにしてみましょう。

修正するときは、total / countが整数除算になる前に、どちらか一方をdouble型へ変換します。

整数除算のまま平均値を計算するプログラム

ファイル名:6_9_practice1.c

#include <stdio.h>

int main(void)
{
    double average;
    int total = 23;
    int count = 5;

    average = total / count;

    printf("平均は%.2fです。\n", average);

    return 0;
}
実行結果の例
平均は4.00です。

totalとcountはどちらもint型です。そのため、total / countでは23 / 5という整数除算が行われます。

整数除算の結果は4です。その4がdouble型へ変換されてaverageに代入されるため、表示結果は4.00になります。

この問題で期待する実行結果は次のとおりです。

平均は4.60です。
キャストして平均値を小数まで求めるプログラム

ファイル名:6_9_2.c

#include <stdio.h>

int main(void)
{
    double average;
    int total = 23;
    int count = 5;

    average = (double)total / count;

    printf("平均は%.2fです。\n", average);

    return 0;
}
実行結果の例
平均は4.60です。

修正後の式では、totalをdouble型へキャストしています。

average = (double)total / count;

totalが23.0として扱われるため、countもdouble型へ暗黙的に変換されます。計算内容は23.0 / 5.0に相当し、結果は4.6になります。

次のように記述すると、期待した結果にはなりません。

average = (double)(total / count);

この書き方では、丸括弧内のtotal / countが先に整数除算として処理されます。23 / 5の結果は4なので、その後でdouble型へ変換しても4.0にしかなりません。

実践問題:球の体積を求める

半径4の球の体積を計算します。

球の体積は、次の公式で求められます。

V = 4 / 3 × π × r × r × r

このプログラムでは、円周率を表すPIに3.14159を使用します。

次の点を意識して計算式を確認しましょう。

  • 半径の3乗はradius × radius × radiusで計算する
  • 4 / 3が整数除算にならないようにする
  • 割り算の前に4または3をdouble型へ変換する
  • 計算結果はdouble型のvolumeへ代入する
整数除算を使って球の体積を計算するプログラム

ファイル名:6_9_practice2.c

#include <stdio.h>

int main(void)
{
    int radius = 4;
    double volume;
    const double PI = 3.14159;

    volume = (4 / 3) * PI
           * (radius * radius * radius);

    printf("半径%dの球の体積は%fです。\n", radius, volume);

    return 0;
}
実行結果の例
半径4の球の体積は201.061760です。

4と3はどちらもint型の整数定数です。そのため、4 / 3の計算結果は1になります。

プログラム内では、実質的に次の計算が行われています。

1 × 3.14159 × 4 × 4 × 4

本来の4 / 3は約1.333333ですが、整数除算によって小数部分が切り捨てられています。そのため、計算された体積も本来より小さくなります。

期待する実行結果は次のとおりです。

半径4の球の体積は268.082347です。
キャストして球の体積を正しく求めるプログラム

ファイル名:6_9_3.c

#include <stdio.h>

int main(void)
{
    int radius = 4;
    double volume;
    const double PI = 3.14159;

    volume = ((double)4 / 3) * PI
           * (radius * radius * radius);

    printf("半径%dの球の体積は%fです。\n", radius, volume);

    return 0;
}
実行結果の例
半径4の球の体積は268.082347です。

(double)4によって、整数定数の4がdouble型の4.0へ変換されます。そのため、4.0 / 3.0に相当する浮動小数点除算が行われます。

3はint型ですが、もう一方の4がdouble型なので、計算時には3もdouble型へ暗黙的に変換されます。

この部分は、次のように3をキャストしても同じ結果になります。

4 / (double)3

4と3は変数ではなく定数なので、最初から浮動小数点定数として記述する方法もあります。

4.0 / 3.0

定数の型を最初から明確にできる場合は、4.0 / 3.0と書くと計算の意図が読み取りやすくなります。変数をdouble型へ一時的に変換したい場合は、キャスト演算子を使う方法が適しています。

図3:球の体積計算とキャストの位置

この図から分かること

4 / 3をint型同士で計算すると、結果は1になります。そのため、球の体積も本来より小さな値になります。

(double)4 / 3のように割り算の前でキャストすると、4 / 3を浮動小数点除算として処理できます。

4.0 / 3.0のように、定数を最初から浮動小数点形式で記述しても同じ結果になります。変数にはキャスト演算子、定数には小数点を付けた表記を使うと、計算の意図を分かりやすく表現できます。