C言語を学ぶ上で、プログラムに計算をさせるための「算術演算」は、最も基本的かつ重要な要素の一つです。

コンピューターの主要な役割が計算である以上、演算子を正しく使いこなすことは、効率的でバグのないプログラムを記述するための第一歩となります。

本記事では、C言語における主要な5種類の算術演算子の使い方はもちろん、演算の優先順位、型変換の仕組み、そして実務で陥りやすい注意点までを網羅的に詳しく解説していきます。

C言語における算術演算子の基礎知識

C言語の算術演算子とは、数値データに対して加算、減算、乗算、除算、剰余(余り)といった数学的な計算を行うための記号を指します。

これらの演算子は、主に変数や定数と組み合わせて使用され、計算結果を新しい値として返します。

C言語は、ハードウェアに近いレベルでの計算を高速に行うことができるため、数値計算のルールが厳密に定められています。

特に「整数同士の計算」と「浮動小数点数(実数)を含む計算」では、結果の型や精度が異なるという特性があります。

この特性を理解せずにコードを書くと、意図しない計算結果(論理エラー)を招く原因となるため注意が必要です。

基本となる5つの算術演算子

C言語で頻繁に使用される基本的な算術演算子は以下の5種類です。

これらは「2項演算子」と呼ばれ、2つの値(オペランド)を対象に計算を行います。

演算子名称役割
+加算演算子2つの値を足す
-減算演算子左の値から右の値を引く
*乗算演算子2つの値を掛ける
/除算演算子左の値から右の値を割る
%剰余演算子割り算の余りを求める

それでは、それぞれの演算子の具体的な挙動について詳しく見ていきましょう。

加算(+)、減算(-)、乗算(*)

加算、減算、乗算については、数学で使われる記号とほぼ同じ感覚で使用できます。

ただし、乗算には「×」ではなく「*(アスタリスク)」を使用する点に注意してください。

C言語
#include <stdio.h>

int main(void) {
    int a = 10;
    int b = 3;

    // 加算
    int sum = a + b;
    // 減算
    int diff = a - b;
    // 乗算
    int prod = a * b;

    printf("加算: %d + %d = %d\n", a, b, sum);
    printf("減算: %d - %d = %d\n", a, b, diff);
    printf("乗算: %d * %d = %d\n", a, b, prod);

    return 0;
}
実行結果
加算: 10 + 3 = 13
減算: 10 - 3 = 7
乗算: 10 * 3 = 30

除算(/):整数と浮動小数点の挙動の違い

除算演算子 / は、計算する値のデータ型によって結果が大きく変わるため、最も注意が必要な演算子です。

  1. 整数同士の除算:小数点以下が切り捨てられ、結果は「整数」になります。例えば、7 / 2 の結果は 3.5 ではなく 3 になります。
  2. 浮動小数点数を含む除算:少なくとも一方が浮動小数点数(doublefloat)であれば、結果は小数点以下を含む「実数」になります。
C言語
#include <stdio.h>

int main(void) {
    int i_a = 7;
    int i_b = 2;
    double d_a = 7.0;
    double d_b = 2.0;

    // 整数同士の割り算
    int res1 = i_a / i_b;
    // 浮動小数点数同士の割り算
    double res2 = d_a / d_b;
    // 混合した割り算
    double res3 = i_a / d_b;

    printf("整数同士: 7 / 2 = %d\n", res1);
    printf("実数同士: 7.0 / 2.0 = %.1f\n", res2);
    printf("混合: 7 / 2.0 = %.1f\n", res3);

    return 0;
}
実行結果
整数同士: 7 / 2 = 3
実数同士: 7.0 / 2.0 = 3.5
混合: 7 / 2.0 = 3.5

剰余(%):整数の余りを求める

剰余演算子 % は、割り算を行った際の「余り」を計算します。

この演算子は、整数型のデータにのみ適用可能であり、浮動小数点数に使用するとコンパイルエラーになります。

剰余演算は、「ある数値が偶数か奇数かを判定する(2で割った余りが0か1か)」や「特定の周期で処理を繰り返す」といったロジックで非常によく使われます。

C言語
#include <stdio.h>

int main(void) {
    int a = 10;
    int b = 3;

    int rem = a % b; // 10を3で割った余り

    printf("%d を %d で割った余りは %d です\n", a, b, rem);

    // 偶数・奇数の判定例
    int num = 15;
    if (num % 2 == 0) {
        printf("%d は偶数です\n", num);
    } else {
        printf("%d は奇数です\n", num);
    }

    return 0;
}
実行結果
10 を 3 で割った余りは 1 です
15 は奇数です

代入演算子との組み合わせ(複合代入演算子)

算術演算の結果をそのまま元の変数に代入したい場合、複合代入演算子を使用するとコードを簡潔に記述できます。

複合代入演算子意味
a += ba = a + b と同じ
a -= ba = a - b と同じ
a *= ba = a * b と同じ
a /= ba = a / b と同じ
a %= ba = a % b と同じ

これらは単なる短縮記法ではなく、読みやすさ(可読性)を向上させ、変数名の打ち間違いを減らす効果もあります。

インクリメントとデクリメント

C言語には、変数の値を1だけ増減させるための特殊な演算子が存在します。

  • インクリメント演算子(++):値を1増やす
  • デクリメント演算子(–):値を1減らす

これらには、変数の前に置く「前置(++a)」と、後ろに置く「後置(a++)」の2種類があり、式の評価タイミングが異なります。

前置と後置の決定的な違い

前置と後置の違いは、「値を更新してから式を評価するか、評価してから値を更新するか」にあります。

  • 前置(++a):先に値を1増やし、その増やした後の値を式全体の結果として使用します。
  • 後置(a++):現在の値を式全体の結果として使用した後、変数の値を1増やします。
C言語
#include <stdio.h>

int main(void) {
    int x = 5;
    int y = 5;

    printf("初期値: x = %d, y = %d\n", x, y);

    // 後置インクリメントの使用
    int res_post = x++; 
    // 前置インクリメントの使用
    int res_pre = ++y;

    printf("後置の結果: res_post = %d (実行後のx = %d)\n", res_post, x);
    printf("前置の結果: res_pre = %d (実行後のy = %d)\n", res_pre, y);

    return 0;
}
実行結果
初期値: x = 5, y = 5
後置の結果: res_post = 5 (実行後のx = 6)
前置の結果: res_pre = 6 (実行後のy = 6)

この違いは、ループ処理や複雑な条件式の中で使う際に、意図しない挙動(オフバイワン・エラーなど)の原因となるため、確実に理解しておく必要があります。

基本的には、for 文の更新式のように単独で使用する場合はどちらでも構いませんが、他の処理と組み合わせる場合は注意しましょう。

演算子の優先順位と結合規則

複数の演算子が1つの式に含まれる場合、どの演算から先に実行されるかは「優先順位」によって決まります。

また、同じ優先順位の演算子が並んだ場合の計算順序を「結合規則」と呼びます。

優先順位の主なルール

算術演算子の優先順位は、概ね数学のルールに従います。

  1. 括弧 () 内の計算
  2. 単項演算子(++, --, 正負の符号 + -
  3. 乗算 *、除算 /、剰余 %
  4. 加算 +、減算 -

例えば、10 + 2 * 5 は、数学と同様に掛け算が優先され、結果は 20 になります。

もし足し算を先にしたい場合は、(10 + 2) * 5 と記述します。

結合規則

乗算と除算のように優先順位が同じ演算子が並んだ場合、C言語の算術演算子は通常「左から右」へと計算が進みます。

これを「左結合」と呼びます。

C言語
int result = 100 / 5 * 2;
// (100 / 5) が先に計算され 20 になり、20 * 2 で結果は 40 となる

複雑な式を書く際は、優先順位を暗記して頼るよりも、括弧を用いて明示的に計算順序を指定する方が、自分以外のプログラマーにとっても読みやすく、ミスを防ぐことができます。

型変換(キャスト)と算術演算の関係

C言語では、異なるデータ型同士で演算を行うと、自動的に精度の高い(表現範囲の広い)型へ変換される「暗黙の型変換(型昇格)」が行われます。

暗黙の型変換のルール

例えば、int(整数)と double(浮動小数点数)を計算すると、intdouble に変換されてから計算が行われ、結果も double になります。

C言語
int n = 5;
double d = 2.0;
double result = n / d; // 5.0 / 2.0 として計算される

明示的な型変換(キャスト)

整数同士の割り算で、小数点以下の結果を得たい場合は、キャスト演算子を使って強制的に型を変換する必要があります。

C言語
#include <stdio.h>

int main(void) {
    int a = 5;
    int b = 2;

    // キャストなし
    double res1 = a / b;
    // キャストあり
    double res2 = (double)a / b;

    printf("キャストなし: %f\n", res1);
    printf("キャストあり: %f\n", res2);

    return 0;
}
実行結果
キャストなし: 2.000000
キャストあり: 2.500000

res1 では、代入先の変数が double であっても、右辺の a / b が「整数同士の演算」として評価されるため、計算が終わった瞬間に小数点以下が失われています。

res2 のように、片方の変数の前に (double) と記述することで、計算前に型を変換し、正しい実数結果を得ることができます。

算術演算における注意点とエラー回避

プログラムを実行中に強制終了させたり、予期せぬ挙動を引き起こしたりする「演算の罠」がいくつか存在します。

ゼロ除算の危険性

数学と同様、「0で割る(ゼロ除算)」は許されません

C言語で整数を0で割ろうとすると、実行時にプログラムがクラッシュ(異常終了)します。

C言語
int x = 10;
int y = 0;
int z = x / y; // ここで実行時エラーが発生する可能性がある

除算や剰余演算を行う際は、分母となる変数が 0 でないことを if 文などで事前にチェックすることが、堅牢なプログラムを作成する鉄則です。

オーバーフローとアンダーフロー

各データ型には、保持できる値の範囲が決まっています。

例えば、一般的な環境の int 型(32bit)は約21億までの値を扱えますが、これを超える計算を行うと、オーバーフローが発生します。

オーバーフローが発生すると、値が突然負の最小値にループしたり、全く予期しない値になったりします(未定義動作の原因にもなります)。

C言語
#include <stdio.h>
#include <limits.h>

int main(void) {
    int max = INT_MAX; // int型の最大値
    printf("最大値: %d\n", max);
    
    int overflow = max + 1;
    printf("オーバーフロー後: %d\n", overflow);

    return 0;
}

実行結果(環境依存の一例):

実行結果
最大値: 2147483647
オーバーフロー後: -2147483648

非常に大きな数値を扱う場合は、long long 型を使用したり、計算の順序を工夫して中間結果が大きくならないようにする配慮が必要です。

浮動小数点数の精度誤差

浮動小数点数(double, float)の計算では、丸め誤差が発生することがあります。

これは、コンピューターが内部で数値を2進数として扱っているため、10進数の「0.1」などを正確に表現できないことが原因です。

そのため、実数同士の比較に == を使うのは危険です。

算術演算の結果が「厳密にその値になる」とは限らないことを念頭に置く必要があります。

まとめ

C言語の算術演算子は、一見シンプルですが、その背後には型システムやメモリの仕組みが深く関わっています。

  • 5種類の基本演算子+, -, *, /, %)を使い分ける。
  • 整数の除算は小数点以下が切り捨てられる点に注意する。
  • インクリメント/デクリメントの前置・後置の違いを理解する。
  • 計算順序が複雑なときは括弧 () を積極的に活用する。
  • ゼロ除算やオーバーフローといった実行時エラーへの対策を怠らない。

これらのポイントを抑えることで、計算精度の高い、安定したプログラムを構築できるようになります。

特にキャストを用いた型制御とゼロ除算のチェックは、実務レベルのプログラミングにおいても非常に重要なテクニックですので、ぜひこの機会にマスターしてください。