C言語のきほん|キャスト演算子

キャストは“計算の型”を操るスイッチ!どこを double にするかで、答えが変わる理由が見えてくるよ。

C言語で計算していると、「えっ、2.5になるはずなのに 2.0 になった…」みたいなことが起きます。
原因の多くは、計算が int のまま行われていたことです。

そこで使うのが キャスト演算子
キャストは「この値は、この型として扱ってね」と明示して、意図した型で演算させるための道具です。

ただしキャストは強力なので、使いどころを間違えると、値が失われたり、最悪クラッシュや未定義動作につながることもあります。便利だけど、丁寧に使うのがコツです。

キャスト演算子の書き方(文法)

キャスト演算子はこう書きます。

(型) 式
  • (型) は、変換したい型名を丸括弧で囲みます
  • 式 は、変換対象の値や変数です

例:

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

キャストで何が嬉しいの?

一番わかりやすい効果:整数の割り算を浮動小数点にできる

int 同士の割り算は、小数点以下が切り捨てられます。
だから「割り算の前」にどちらかを double にしておくと、計算全体が double 側に寄って小数が出ます。

書き方中で起きること結果のイメージ
x = a / b;int / int の割り算2.0 になりがち
x = (double)a / b;double / int で計算2.5 になりやすい
x = (double)a / (double)b;両方doubleもちろん 2.5

通常は片方だけキャストすれば十分です。片方が double になれば、暗黙の型変換で相手も double として扱われるからです。

落とし穴1:キャストする場所を間違える(演算の順序)

キャストは「どこを先に計算するか」で結果が変わります。ここが最大の罠です。

たとえば a=5, b=2 なら本当は 2.5 が欲しいのに…

書き方先に起きることx
x = (double)a / b;a を double 化してから割る2.5
x = (double)(a / b);先に a/b を int で計算2.0(手遅れ)

つまり、キャストは「計算の前」に効かせないと意味が薄くなります。

シンプルな確認プログラム

キャストの位置で結果が変わるのを、短いコードで見てみます。

ファイル名:6_9_1.c

#include <stdio.h>

int main(void)
{
    int total = 5;
    int people = 2;

    double x1;
    double x2;

    x1 = (double)total / people;     /* 先にdoubleにして割る */
    x2 = (double)(total / people);   /* 先にintで割ってからdouble化 */

    printf("正しいキャスト: %.1f\n", x1);
    printf("手遅れキャスト: %.1f\n", x2);

    printf("メッセージ: キャストは計算の前に効かせるのがコツだよ。\n");
    return 0;
}

落とし穴2:キャストは強力すぎる(危ない変換もできてしまう)

キャストは「型を合わせる」だけじゃなく、かなり強引なことも可能です。
たとえばポインタ型を無理やり変えるようなキャストは、扱いを間違えると未定義動作の原因になります。

文書の例にあるように、int のアドレスを char のポインタとして扱うと、int を 1バイト単位で覗くような形になり、意図しない読み取りになりやすいです。

ここはポインタの学習が進んだ段階で改めて深掘りするとして、今の段階ではこの一言を覚えておくと安心です。

  • 値のキャスト(int ↔ double など)はよく使う
  • ポインタのキャストは危険度が高いので慎重に

実践問題

次のプログラムの計算式を、キャスト演算子を使って修正し、実行結果が小数を含む値になるようにしてください。

  • 改造のポイントは「割り算が整数のまま終わらないようにする」ことです
  • どこをキャストすればいいか考えてみてね
#include <stdio.h>

int main(void)
{
    double avg;
    int sum = 17;
    int count = 4;

    avg = sum / count;   /* ここを修正 */
    printf("平均は%.2fです。\n", avg);

    return 0;
}

期待する実行結果の例

平均は4.25です。

解答例

ファイル名:6_9_2.c

#include <stdio.h>

int main(void)
{
    double avg;
    int sum = 17;
    int count = 4;

    avg = (double)sum / count;  /* sumをdoubleにしてから割る */
    printf("平均は%.2fです。\n", avg);

    return 0;
}

解説

sum と count がどちらも int だと、sum / count は整数除算になります。
そこで sum を double にキャストすると、式全体が double の割り算になり、4.25 を得られます。

ちなみに、

avg = (double)(sum / count);

は「先に整数除算してから double にする」形なので、4.00 になってしまいます。キャストの位置は本当に大事です。

実践問題

元の pr6_6_2.c は V = 4/3 π r³ の 4/3 が整数除算になるのが落とし穴でした。同じ狙いで半径を変えた類似問題を作ります。

半径 r の球の体積を求めて表示してください。

  • 体積の公式は V = 4/3 π r³
  • r³ は r × r × r として計算してください
  • 4/3 が整数除算にならないように注意してください
  • PI は 3.14159 を使います
#include <stdio.h>

int main(void)
{
    int r = 3;                 /* 半径 */
    double v;                  /* 体積 */
    const double PI = 3.14159; /* π */

    v = _______________________;

    printf("半径%dの球の体積は%fです。\n", r, v);
    return 0;
}

期待する実行結果の例

半径3の球の体積は113.097240です。

解答例(キャストあり)

ファイル名:6_9_3.c

#include <stdio.h>

int main(void)
{
    int r = 3;
    double v;
    const double PI = 3.14159;

    v = (4.0 / 3.0) * PI * (r * r * r);

    printf("半径%dの球の体積は%fです。\n", r, v);
    return 0;
}

解説

4.0 / 3.0 のように、最初から浮動小数点にしておくと安全です。
ここはキャストで

  • (double)4 / 3
  • 4 / (double)3

のようにしてもOKですが、定数なら 4.0 と書く方が読みやすいことが多いです。

チャレンジ問題:連立方程式

次の連立方程式の解 x と y を求めるプログラムを作成してください。

ax + by = c
dx + ey = f

条件

  • 係数 a〜f は整数
  • 解は一意に存在するものとします

ヒント:クラメルの公式を使うと、次のように求められます。

D  = a*e - b*d
Dx = c*e - b*f
Dy = a*f - c*d

x = Dx / D
y = Dy / D

D が整数でも、割り算結果は小数になることがあるので、計算の型に注意してね。

解答例

ファイル名:6_9_4.c

#include <stdio.h>

int main(void)
{
    int a, b, c, d, e, f;
    int D, Dx, Dy;
    double x, y;

    printf("a, b, c, d, e, f を入力してください。\n");
    scanf("%d %d %d %d %d %d", &a, &b, &c, &d, &e, &f);

    D  = a * e - b * d;
    Dx = c * e - b * f;
    Dy = a * f - c * d;

    x = (double)Dx / D;
    y = (double)Dy / D;

    printf("x = %f, y = %f\n", x, y);
    return 0;
}

解説

Dx と D が int なので、そのまま Dx / D をすると整数除算になってしまいます。
そこで (double)Dx にしてから割ることで、浮動小数点の割り算になります。