C言語編 第4章 計算の仕方

先頭へ戻る

この章の概要

この章の概要です。


計算をさせる

プログラムを書いて、コンピュータに行わせる最も代表的な仕事は「計算」でしょう。計算を行うプログラムの書き方を知っておかないと、恐らくほとんどのプログラムは書けません。

まずは最も基本的な四則演算(足し算・引き算・掛け算・割り算)をしてみましょう。

一般的には、加算(足し算)には「+」、減算(引き算)には「-」、乗算(掛け算)には「×」、除算(割り算)には「÷」を使います。この中で、「×」と「÷」については、C言語では別の記号を使います。乗算は「*」、除算は「/」です

ここまでに登場した「+」「-」「*」「/」といった、計算に使う記号類を、C言語では演算子と呼びます。「演算」とは「計算」のことです。他にも演算子はいくつもあります。

実際にこれらの演算子を使ったプログラムは次のようになります。

#include <stdio.h>

int main(void)
{
    int sum = 10 + 20;
    int sub = 10 - 20;
    int mul = 10 * 20;
    int div = 10 / 20;

    printf( "sum: %d\n", sum );
    printf( "sub: %d\n", sub );
    printf( "mul: %d\n", mul );
    printf( "div: %d\n", div );

    return 0;
}

実行結果:

sum: 30
sub: -10
mul: 200
div: 0

ここで登場する演算子は、「オペランド」「演算子」「オペランド」という並びで記述して使います。オペランドというのは、演算子の効果の対象になる値や変数のことです。オペランドと演算子の間に開ける空白は、あってもなくても問題ありません。

計算結果を使って、変数を初期化できますし、代入もできます。また、次のプログラムのように、計算結果を変数に入れずに使うことも可能です。

#include <stdio.h>

int main(void)
{
    printf( "sum: %d\n", 10 + 20 );
    printf( "sub: %d\n", 10 - 20 );
    printf( "mul: %d\n", 10 * 20 );
    printf( "div: %d\n", 10 / 20 );

    return 0;
}

実行結果:

sum: 30
sub: -10
mul: 200
div: 0

この場合、計算結果が直接、printf関数の "%d" の部分にあてはめられます。

正確に言えば、計算結果が printf関数へ渡され、printf関数の内部の処理で使用されるという流れです。関数に値を渡すという考え方については、第9章で説明します。

除算の結果

先ほどのサンプルプログラムの実行結果の中で、除算の結果には注目しておくべきでしょう。「10 / 20」の結果が、「0.5」ではなく「0」となっています。printf関数の "%d" は整数で出力を行う指示なので、実行結果が整数になるのは当然なようではありますが、仮に指定を "%f" に変えたとしても、やはり結果は 0 になります。

実は、整数同士の除算では、結果の小数点以下が切り捨てられて、整数になります。「10 / 20」の数学的な結果は「0.5」ですが、小数点以下が切り捨てられて「0」になるという訳です。

ただし、整数同士の除算の結果が負数になり、余りが出るケース、例えば「-10 / 20」のような場合は、(C99 より前の規格では)計算結果が「0」か「-1」かのいずれかになります。どちらになるかは、コンパイラの実装次第です。つまり、0 に近い方に切り捨てるか、0 から遠い方に切り捨てるかが、明確に定められていない訳です。

C99 (結果が負数になる整数除算の結果)

C99 になって、整数の除算の結果が負数になる場合で、余りが出る場合の結果が明確に規定され、必ず、0 に近い方に切り捨てられることになりました。ですから、「-10 / 20」の結果は、必ず「0」です。

VisualStudio、clang は、規定通り、0 に近い方へ切り捨てられます。

ゼロ除算

除算に関しては注意しなければならないことがあります。C言語に限らない話ですが、多くのプログラミング言語では、何かを 0 で割る行為は禁止されています。例えば「10 / 0」のような演算の結果は、未定義です。

未定義というのは、「どうなるか決まっていない」ということです。ですから、「多分、結果は 0 になるだろう」といった、勝手な予測をしてはいけません。C言語には、未定義とされていることがいくつかありますが、こういったプログラムを書いてはいけません。

「10 / 0」のように、はっきりと「/ 0」と書く場合には気が付きやすいですが、変数に代入された 0 をうっかり使ってしまうことがあるので、注意が必要です。次のようなプログラムは正しくありません。

#include <stdio.h>

int main(void)
{
    int num = 0;
    int ans = 10 / num;

    printf( "ans: %d\n", ans );

    return 0;
}

実行結果:



この場合、変数num には 0 が入っているため、結局、「10 / 0」という計算を行っていることになります。

ゼロ除算の結果は未定義なので、このプログラムを実行すると何が起こるか分かりません。異常停止するかもしれませんし、おかしな結果が出力されるかもしれませんし、単に 0 になるだけかもしれません(異常な動作をすると分かっていても、学習のためには、試しに実行してみて欲しいと思います。どんなことが起こるのか理解しておかないと、実際にトラブルが起きたときにパニックになるでしょうから)


少し複雑な計算

演算子は、1つの文の中で複数回使っても構いません。例えば「10 * 2 + 5」のような式は許されています。このような場合、数学と同じで、乗算や除算の方が、加算や減算よりも優先されます。また、やはり数学と同じく、( ) で囲めば、その部分の計算を優先させられます

#include <stdio.h>

int main(void)
{
    int ans1 = 10 * 2 + 5;
    int ans2 = 10 * (2 + 5);

    printf( "ans1: %d\n", ans1 );
    printf( "ans2: %d\n", ans2 );

    return 0;
}

実行結果:

ans1: 25
ans2: 70

( ) で囲った部分が先に計算されています。( ) は複数使うこともできますし、( ) の中にさらに ( ) があっても構いませんが、{ } や [ ] のようないわゆる中括弧や大括弧は使えません

なお、1つの式の中に優先順位が同じ部分が複数ある場合、例えば「10 * 5 + 10 / 5」のような場合に、「10 * 5」と「10 / 5」のどちらが先に計算されるかは決まっていません。左から右という規則はC言語にはないので注意しましょう。

どちらから計算されても同じように思えるかもしれませんが、違いが生まれてしまうケースがあるので、順番に規則が無いことは覚えておいた方がよいでしょう。

printf などの関数でも ( ) を使っているので、ややこしく感じるかもしれません。これはある意味で正しい感覚で、実際、( ) が大量に出てくると見づらくなります。

#include <stdio.h>

int main(void)
{
	printf( "ans: %d\n", (10 + 5) / (10 - (3 + 2)) );

	return 0;
}

実行結果:

ans: 3

プログラミングの世界では、読み易さは重要視されます。自分が読みづらいと感じるようなら、他の人が見ても読みづらい可能性は高いでしょうから、改善策を探すことを心がけるべきです。例えば、いったん、変数に計算の途中経過を入れてやるという方法が考えられます。

#include <stdio.h>

int main(void)
{
    int num1 = 10 + 5;
    int num2 = 10 - (3 + 2);
    int ans  = num1 / num2;

    printf( "ans: %d\n", ans );

    return 0;
}

実行結果:

ans: 3

無闇に変数の個数を増やすと、かえって分かりづらくなる可能性もありますが、変数には名前を付けられるので、うまくすれば、読みやすいプログラムにできます。

また、定義する変数が増えると、メモリの使用量が増えたり、実行速度に影響を与えたりする可能性があります。ただ、ほとんどの場合、大きな問題になることはありません。

剰余の求め方

四則演算のついでに、除算の余り(剰余)の求め方にも触れておきます。C言語では剰余を簡単に求められるように、専用の演算子「%」が用意されていますから、これを使うだけです。

「%」のオペランドはいずれも、整数でなければなりません。例えば、「10.0 / 3」のような式は正しくなく、コンパイルエラーになります。なお、「%」の演算の優先順位は「*」や「/」と同じで、「+」や「-」よりも優先されます

結果の符号については、左側のオペランドと同じになります。

また、剰余の場合も、0 で割る行為の結果は未定義なので注意してください

#include <stdio.h>

int main(void)
{
    printf( "%d\n", 10 % 3 );
    printf( "%d\n", 10 % -3 );
    printf( "%d\n", -10 % 3 );
    printf( "%d\n", -10 % -3 );

    return 0;
}

実行結果:

1
1
-1
-1


練習問題

問題① 4分31秒を、秒に変換した結果を出力するプログラムを作ってください。

問題② 502秒を、分と秒に変換した結果を出力するプログラムを作ってください。

問題③ 3÷2の結果を、1のような整数ではなく、1.5のような浮動小数点数で出力できるようにプログラムを作ってください。

問題④ 567という整数の10の位だけ(つまり6)を取り出して出力するにはどうすればいいでしょう。

問題⑤ 次のプログラムには問題があります。指摘してください。

#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 10;
    int c = 20;

    printf( "%d * %d = %d\n", a, b, a * b );
    printf( "%d / %d = %d\n", c, a, c / a );
    printf( "%d + %d = %d\n", b, c, b + c );

    return 0;
}


解答ページはこちら

参考リンク



更新履歴

'2018/6/15 第8章から練習問題②を移動してきて、練習問題⑤とした。

'2018/5/3 「剰余の求め方」の項を修正。結果の符号について追記し、サンプルプログラムもそれに合わせて変更した。

'2018/4/2 「VisualC++」という表現を「VisualStudio」に統一。

'2018/1/29 全面的に文章を見直し、修正を行った。

'2018/1/5 Xcode 8.3.3 を clang 5.0.0 に置き換え。

≪さらに古い更新履歴を展開する≫



前の章へ (第3章 変数)

次の章へ (第5章 コメントの書き方)

C言語編のトップページへ

Programming Place Plus のトップページへ


はてなブックマーク Pocket に保存 Twitter でツイート Twitter をフォロー
Facebook でシェア Google+ で共有 LINE で送る rss1.0 取得ボタン RSS
管理者情報 プライバシーポリシー