C言語の基本|算術演算子の使い方

足し算から余り、符号反転まで。C言語の算術演算子を正しく使い、計算の落とし穴を防ごう。

C言語で数値を扱うプログラムを作るとき、基本になるのが計算です。

商品の合計金額を求めたり、残りの個数を計算したり、長方形の面積を求めたりする処理は、すべて数値の計算によって作られています。その計算を表す記号が算術演算子です。

算術演算子を使うと、次のような処理を書けるようになります。

  • 2つの値を足して合計を求める
  • ある値から別の値を引いて差を求める
  • 単価と個数を掛けて合計金額を求める
  • 値を割って1人分や1個分を求める
  • 割り算の余りを求める
  • 数値の符号を反転する

算術演算子の使い方自体は、それほど難しくありません。ただし、C言語の計算には注意したい点があります。

特に重要なのが、次の3つです。

  • 整数同士の割り算では小数部分が残らない
  • 0で割ることはできない
  • 演算子には計算される順序がある

このような決まりを知らないまま計算すると、想像していた結果と違う値になることがあります。

ここでは、2つの値を使う2項演算子と、1つの値に作用する単項演算子を中心に、算術演算子の使い方と注意点を順番に確認していきましょう。

算術演算子は2種類に分けられる

算術演算子は、演算に必要なオペランドの数によって、大きく2種類に分けられます。

オペランドとは、演算子によって操作される値や変数のことです。

種類オペランドの数主な用途
2項演算子2つa + b加算、減算、乗算、除算、剰余
単項演算子1つ-a符号の反転など

たとえば、次の式ではaとbの2つがオペランドです。

a + b

+は2つの値を使って加算するため、2項演算子に分類されます。

一方、次の式で操作される値はaだけです。

-a

この-は、aの符号を反転する単項演算子です。

同じ-という記号でも、オペランドの数によって役割が変わります。

図1:2項演算子と単項演算子の違い

この図から分かること

2項演算子は、a + bのように2つのオペランドを使います。加算、減算、乗算、除算、剰余に使う演算子は、すべて2項演算子です。

単項演算子は、-aのように1つのオペランドだけを操作します。

同じ記号でも、a - bの-は減算を行う2項演算子、-aの-は符号を反転する単項演算子です。式の中でどのように使われているかを見て判断します。

2項の算術演算子

2項の算術演算子は、2つの値を使って計算します。

C言語で使われる基本的な2項の算術演算子は、次の5種類です。

演算子演算式の例意味
+加算a + baとbを足す
-減算a - baからbを引く
*乗算a * baとbを掛ける
/除算a / baをbで割る
%剰余a % baをbで割った余りを求める

+、-、*、/は、整数と小数の計算に使えます。

一方、%は整数型の値に対して使用する演算子です。double型などの浮動小数点数には使用できません。

+ で値を加算する

+は、2つの値を足す加算演算子です。

int total = a + b;

この式では、aとbを加算し、その結果で変数totalを初期化しています。

たとえば、aが12、bが5の場合は、次の結果になります。

a + b
12 + 5
17

加算は、次のような場面で使われます。

  • 複数の商品の合計金額を求める
  • テストの合計点を求める
  • 移動した距離の合計を求める
  • 経過時間を合計する
  • 現在の値に一定の値を加える

int型の整数だけでなく、double型の小数も加算できます。

double total = price + tax;

priceが1200.5、taxが120.0なら、計算結果は1320.5です。

- で値を減算する

-は、左側の値から右側の値を引く減算演算子です。

int rest = stock - sold;

stockが30、soldが8の場合、計算結果は22です。

stock - sold
30 - 8
22

減算は、次のような処理で使われます。

  • 在庫数から販売数を引いて残りを求める
  • 2つの時刻や距離の差を求める
  • 目標値まであといくつ必要か計算する
  • 変更前と変更後の差を求める

-は左側と右側の順序によって結果が変わります。

10 - 4は6
4 - 10は-6

どちらの値からどちらの値を引くのかを確認して使いましょう。

* で値を乗算する

*は、2つの値を掛ける乗算演算子です。

int amount = price * quantity;

priceが500、quantityが4の場合、合計金額は2000です。

price × quantity
500 × 4
2000

C言語では、乗算に×ではなく*を使用します。

乗算は、次のような計算でよく使われます。

  • 単価と個数から合計金額を求める
  • 幅と高さから面積を求める
  • 速度と時間から距離を求める
  • 同じ値を何倍かに増やす

小数を含む計算にも使用できます。

double area = width * height;

/ で値を除算する

/は、左側の値を右側の値で割る除算演算子です。

int quotient = a / b;

除算は整数と小数の両方に使用できます。ただし、使用する値の型によって、計算結果が変わります。

整数同士の割り算では小数部分が残らない

C言語では、整数同士を割ると結果も整数になります。

たとえば、次の計算を考えてみましょう。

int a = 11;
int b = 4;
int quotient = a / b;

数学では、11 ÷ 4の答えは2.75です。しかし、aとbはどちらもint型なので、計算結果の小数部分は残りません。

11 / 4の数学的な結果:2.75
int型同士の計算結果:2

正の整数同士の割り算では、小数部分が切り捨てられたように見えます。

変数quotientに格納される値は2です。

小数の計算結果を得る方法

割り算の結果を小数で得たい場合は、少なくとも一方をdouble型として計算させます。

double quotient = (double)a / b;

(double)aは、aの値を計算時にdouble型へ変換します。この変換をキャストといいます。

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

(double)a / b
11.0 / 4
2.75

aがdouble型になることで、bもdouble型に合わせて計算され、小数を含む結果が得られます。

結果を保存する変数もdouble型にしておきます。

double quotient = (double)a / b;

次のように、計算後の整数をdouble型へ変換しても、小数部分は戻りません。

double quotient = (double)(a / b);

この書き方では、a / bが整数同士で先に計算されます。11 / 4の結果が2になったあとで、2が2.0へ変換されます。

小数まで求めたい場合は、割り算を行う前に一方をdouble型へ変換することがポイントです。

図2:整数の割り算と小数の割り算の違い

この図から分かること

aとbがどちらもint型の場合、a / bの結果も整数になります。11 / 4を計算すると、小数部分は残らず、結果は2です。

(double)a / bでは、割り算を行う前にaをdouble型へ変換します。そのため、11.0 / 4として計算され、2.75という小数を含む結果を得られます。

結果をdouble型の変数へ入れるだけではなく、割り算を行う値の一方を先にdouble型へ変換することが大切です。

%で割り算の余りを求める

%は、整数の割り算で生じる余りを求める剰余演算子です。

int remainder = a % b;

aが11、bが4の場合は、次のようになります。

11 ÷ 4
商:2
余り:3

したがって、11 % 4の結果は3です。

int remainder = 11 % 4;

%は、int型などの整数型に対して使用します。double型などの浮動小数点型には使用できません。

次のような計算はできません。

double remainder = 11.5 % 4.0;  /* 使用できない */

余りを使って偶数と奇数を調べる

整数を2で割った余りを調べると、偶数か奇数かを判断できます。

number % 2

偶数は2で割り切れるため、余りが0になります。

8 % 2の結果:0
8は偶数

奇数を2で割ると、余りが0ではありません。

9 % 2の結果:1
9は奇数

偶数かどうかを確認する条件は、次のように表せます。

number % 2 == 0

この条件が真であればnumberは偶数です。

剰余演算子は、次のような周期的な処理にも利用できます。

  • 2回ごとに処理を切り替える
  • 7日ごとに同じ曜日へ戻す
  • 60秒ごとに分を繰り上げる
  • 一定の回数ごとに同じ処理を繰り返す

単項の算術演算子

単項の算術演算子は、1つのオペランドに対して作用します。

算術演算に関係する主な単項演算子は、単項-と単項+です。

演算子演算式の例意味
-符号反転-aaの符号を反転する
+値をそのまま扱う+aaの値をそのまま使用する

単項-で符号を反転する

単項-は、値の符号を反転します。

int result = -a;

aが10の場合、-aの結果は-10です。

10から-10へ符号を反転

aが-6の場合、-aの結果は6になります。

-6から6へ符号を反転

コードでは、次のように使用できます。

int a = 10;
int b = -6;

int result1 = -a;
int result2 = -b;

result1には-10、result2には6が格納されます。

単項-は新しい値を作りますが、元の変数の値そのものを書き換えるわけではありません。

int a = 10;
int result = -a;

この場合、resultは-10になりますが、aは10のままです。

単項+で値をそのまま扱う

単項+は、オペランドの値をそのまま扱います。

int result = +a;

aが10なら、+aの結果も10です。

単項+を付けても、値の符号は変わりません。通常の計算では省略できるため、単項-ほど使用する機会は多くありません。

値を正の向きとして表現していることを明示したい場合などに使われることがあります。

2項- と単項- の見分け方

-は、2項演算子と単項演算子の両方で使われます。

a - b

この-には、aとbの2つのオペランドがあります。そのため、aからbを引く2項の減算演算子です。

-a

こちらの-が操作するオペランドはaだけです。そのため、aの符号を反転する単項演算子です。

-の種類処理
a - b2項演算子aからbを引く
-a単項演算子aの符号を反転する

記号だけを見るのではなく、-の前後にいくつのオペランドがあるかを見ると判断しやすくなります。

0で割らないように注意する

除算演算子/と剰余演算子%では、右側の値を0にできません。

次の計算では、bが0の場合に問題が起こります。

a / b
a % b

整数を0で割る処理や、0で割った余りを求める処理は、C言語では未定義の動作になります。プログラムが異常終了する可能性もあり、正しい計算結果は得られません。

キーボードからbを入力する場合は、計算する前に0でないことを確認します。

if (b != 0) {
    printf("a / b = %d\n", a / b);
    printf("a %% b = %d\n", a % b);
} else {
    printf("0では割り算と余りの計算ができません\n");
}

b != 0は、bが0ではないという条件です。

この条件が真の場合だけ、a / bとa % bを計算します。bが0の場合は計算を行わず、メッセージを表示します。

演算子の優先順位に注意する

1つの式に複数の算術演算子がある場合、すべてが左から順番に計算されるわけではありません。

算術演算子には優先順位があります。この記事で扱う演算子の主な順序は、次のとおりです。

優先順位演算子内容
高い( )括弧の中を先に計算する
高い単項+、単項-符号を扱う
中間*、/、%乗算、除算、剰余
低い+、-加算、減算

たとえば、次の式を考えてみましょう。

int result1 = 6 + 3 * 4;

*は+よりも優先順位が高いため、3 * 4が先に計算されます。

6 + 3 * 4
6 + 12
18

加算を先に行いたい場合は、括弧を使います。

int result2 = (6 + 3) * 4;

括弧の中が先に計算されるため、結果は36です。

(6 + 3) * 4
9 * 4
36

括弧の有無によって、結果が大きく変わります。

計算の順番結果
6 + 3 * 43 * 4を先に計算する18
(6 + 3) * 46 + 3を先に計算する36

式が複雑になったときは、括弧を使って意図する計算順序を明確にすると読みやすくなります。

算術演算子をプログラムで確認しよう

次のプログラムでは、キーボードから2つの整数を入力し、加算、減算、乗算、除算、剰余を行います。

除算と剰余については、0による計算を防ぐ処理を入れています。整数の割り算と小数の割り算の違い、単項演算子による符号の扱いも確認できます。

2つの整数を入力して算術演算の結果を表示するプログラム

ファイル名:6_2_2.c

#include <stdio.h>

int main(void)
{
    int number1;
    int number2;

    printf("2つの整数を入力してください\n");

    printf("1つ目の整数 > ");
    scanf("%d", &number1);

    printf("2つ目の整数 > ");
    scanf("%d", &number2);

    printf("\n2項演算子による計算結果\n");
    printf("number1 + number2 = %d\n", number1 + number2);
    printf("number1 - number2 = %d\n", number1 - number2);
    printf("number1 * number2 = %d\n", number1 * number2);

    /* 0による除算と剰余を防ぐ */
    if (number2 != 0) {
        printf("number1 / number2 = %d\n", number1 / number2);
        printf("number1 %% number2 = %d\n", number1 % number2);

        /* double型へ変換して小数の結果を表示する */
        printf("小数で割った結果 = %.2f\n",
               (double)number1 / number2);
    } else {
        printf("2つ目の整数が0なので、除算と剰余は計算できません\n");
    }

    printf("\n単項演算子による計算結果\n");
    printf("-number1 = %d\n", -number1);
    printf("+number2 = %d\n", +number2);

    return 0;
}
実行結果の例

number1に18、number2に5を入力した場合の実行結果です。

2つの整数を入力してください
1つ目の整数 > 18
2つ目の整数 > 5

2項演算子による計算結果
number1 + number2 = 23
number1 - number2 = 13
number1 * number2 = 90
number1 / number2 = 3
number1 % number2 = 3
小数で割った結果 = 3.60

単項演算子による計算結果
-number1 = -18
+number2 = 5

加算・減算・乗算の処理

最初の3つのprintfでは、加算、減算、乗算の結果を表示しています。

printf("number1 + number2 = %d\n", number1 + number2);
printf("number1 - number2 = %d\n", number1 - number2);
printf("number1 * number2 = %d\n", number1 * number2);

number1が18、number2が5なので、それぞれの計算結果は次のようになります。

計算結果
number1 + number218 + 523
number1 - number218 - 513
number1 * number218 × 590

printfの%dは、計算結果を10進整数として表示します。

除算と剰余を行う前の確認

除算と剰余は、if文の中で実行しています。

if (number2 != 0) {
    printf("number1 / number2 = %d\n", number1 / number2);
    printf("number1 %% number2 = %d\n", number1 % number2);
} else {
    printf("2つ目の整数が0なので、除算と剰余は計算できません\n");
}

条件number2 != 0が真なら、number2は0ではありません。そのため、除算と剰余を安全に計算できます。

number2が0の場合は条件が偽になり、else側のメッセージが表示されます。問題のある計算そのものを実行しないことが重要です。

図3:0による除算と剰余を防ぐ流れ

この図から分かること

除算や剰余を実行する前に、右側のオペランドが0ではないことを確認する必要があります。

number2 != 0が真の場合だけ、number1 / number2とnumber1 % number2を計算します。number2が0の場合は計算を行わず、else側でメッセージを表示します。

入力値によって問題が起こる可能性がある場合は、計算前に条件を確認して、危険な演算を実行しないようにすることが大切です。

%を画面に表示するときは%%と書く

printfの書式文字列では、%は変換指定の始まりを表します。

たとえば、%dはint型の値を10進整数として表示する変換指定です。そのため、%という記号自体を画面に表示したい場合は、%%と記述します。

printf("number1 %% number2 = %d\n", number1 % number2);

このprintfには、2種類の%が使われています。

  • %%は画面に%を表示する
  • %dはnumber1 % number2の計算結果を整数として表示する

実行すると、次のように表示されます。

number1 % number2 = 3

書式文字列の中では%%と書きますが、剰余の計算では通常どおり%を1つ使用します。

整数の割り算と小数の割り算の違い

プログラムでは、同じnumber1とnumber2を使って、整数と小数の2種類の割り算を行っています。

整数の割り算は、次の部分です。

printf("number1 / number2 = %d\n", number1 / number2);

number1とnumber2はどちらもint型なので、18 / 5の小数部分は残りません。表示される結果は3です。

小数の割り算は、次の部分です。

printf("小数で割った結果 = %.2f\n",
       (double)number1 / number2);

(double)number1によって、number1をdouble型へ変換してから割り算を行います。

整数の割り算:18 / 5 = 3
小数の割り算:18.0 / 5 = 3.6

%.2fを使っているため、実行結果では小数点以下2桁の3.60と表示されます。

単項演算子の結果

最後に、単項-と単項+の結果を表示しています。

printf("-number1 = %d\n", -number1);
printf("+number2 = %d\n", +number2);

number1の値は18なので、-number1の結果は-18です。

number2の値は5なので、+number2の結果は5のままです。

この処理によってnumber1やnumber2に格納されている値が書き換えられるわけではありません。単項演算子によって作られた値をprintfで表示しています。

複雑な計算では括弧を活用する

算術演算子には優先順位があるため、式を見れば計算順序を判断できます。しかし、演算子が増えるほど、読み手が計算順序を確認する負担も大きくなります。

たとえば、次の式では乗算が先です。

total = base + price * quantity;

price * quantityを先に計算し、その結果にbaseを加算します。

baseとpriceを先に足したい場合は、括弧を付けます。

total = (base + price) * quantity;

括弧を付けると、意図した計算順序がはっきりします。

  • *、/、%は、2項の+、-より先に計算される
  • 計算順序を変えたい場合は括弧を使う
  • 複雑な式では、読みやすくするためにも括弧を活用する

算術演算子の優先順位だけに頼らず、どの計算を先に行うのかが明確に伝わる式を書くことが、計算間違いを防ぐポイントです。