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

この章の概要

この章の概要です。

計算をさせる

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

まずは最も基本的な四則演算をしてみましょう。 四則演算というのは、プログラムの世界とは関係のない一般用語です。 意味としては、足し算・引き算・掛け算・割り算の総称です。

一般的に、加算(足し算)の際には「+」という記号を使います。 同様に、減算(引き算)では「-」が、乗算(掛け算)では「×」が、除算(割り算)では「÷」を使います。 この中で「×」と「÷」については、半角文字が存在しないため、プログラム中では別の記号を使う必要があります。 「×」は「*」に、「÷」は「/」に置き換えます

ここまでに登場した「+」「-」「*」「/」といった記号を、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 の部分にあてはめられます (正確に言えば、結果が関数へ渡されていることになります。この辺りは第9章で説明します)。

除算について

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

整数同士の除算においては、結果も整数になります。 「10 / 20」の数学的な結果は「0.5」ですが、小数点以下が捨てられて「0」になります。
この辺りの規則は少し複雑なのですが、 整数の除算の結果が、正の数になるのであれば、単に小数点以下が切り捨てられます
一方、結果が負数になる場合、例えば「-10 / 20」では、数学的な結果は「-0.5」ですが、 (C99より前の規格の)C言語では、「0」か「-1」になります(コンパイラによって、いずれかが選択されます)。 つまり、0 に近い方に切り捨てるか、0 から遠い方に切り捨てるかが、明確に定められていない訳です。

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

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

VisualC++ 2013/2015/2017、clang 3.7 は、規定通り、0 に近い方へ切り捨てられます。


もう1つ、除算に関して注意点があります。 C言語に限らず、多くのプログラミング言語では、何かを 0 で割る行為は禁止されています。 例えば「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 になるだけかも知れません。 (異常動作すると分かっていても、学習のためには試しに実行してみて欲しいところです。 どんなことが起こるのか理解しておかないと、実際にトラブルが起きたときにパニックになるでしょうから)

「0で割る」ことによって起こるエラーをゼロ除算エラーなどと言います。 これはコンパイルしたときにはエラーにならず、実行するとエラーになるという困ったものです。 エラーの種類としては、実行したときのエラー(実行時エラー(ランタイムエラー))よりも、 コンパイル時のエラー(コンパイルエラー)の方が望ましいものです。 実行時エラーは、その原因を追究するのが難しいのです。 ゼロ除算エラーを防ぐためには、第11章で見るような分岐処理を使って回避したり、 第28章のアサートで検出したりします。

少し複雑な計算

演算子を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

最近のプログラミングの世界では、読み易さは重要視されます。 自分が読みづらいと感じるようなら、他の人が見ても読みづらい可能性は高いでしょうから、改善策を探すことを心がけるべきです。 1つの方法としては、一旦、変数に結果を入れてやることです。

#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

このように、ソースコードを、同じ結果を保ったまま書き換えることも練習になります。 こちらの方が読みやすいかどうかは、結局のところ、個人の感性になります。重要なのは、改善の工夫をしようとする心がけです。 ちなみに、上のプログラムの変数を宣言している3行で、「=」の位置を揃えているのも、読み易さを向上させる小さな工夫の1つです。

剰余の求め方

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

#include <stdio.h>

int main(void)
{
	int div = 10 / 3;
	int mod = 10 % 3;

	printf( "div: %d\n", div );
	printf( "mod: %d\n", mod );

	return 0;
}

実行結果:

div: 3
mod: 1

剰余を求める場合、整数同士の演算でなければなりません。 例えば、「10.0 / 3」のような式は正しくなく、コンパイルエラーになります。 なお、「%」の演算の優先順位は「*」や「/」と同じで、「+」や「-」よりも優先されます

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


練習問題

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

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

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

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


解答ページはこちら

参考リンク

更新履歴

'2017/3/25 VisualC++ 2017 に対応。

'2016/10/15 clang の対応バージョンを 3.7 に更新。

'2015/10/12 clang の対応バージョンを 3.4 に更新。

'2015/9/5 VisualC++ 2012 の対応終了。

'2015/8/18 VisualC++ 2010 の対応終了。

'2015/8/15 VisualC++ 2015 に対応。

'2015/7/11 除算の説明を別個の項に分離して、詳細化した。

'2011/2/5 単に「小数」と表記していた箇所を、「実数」や「浮動小数点数」に改めた。

'2009/3/5 「この章の概要」を追加。

'2008/11/4 新規作成。



前の章へ

次の章へ

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

Programming Place Plus のトップページへ