入出力と変数

演算子まとめ(教科書4.2節)

前回の課題で出てきたように、C言語では/(除算)を整数値に使用した場合の計算結果は整数値となることに注意。例えば、次の計算式の値はいずれも「1」となる。

3 / 3;
4 / 3;
5 / 3;

4.0 / 3; や 4 / 3.0; のようにどちらか一方でも実数型になっていると計算結果も実数型になる(小数点以下まで計算する)。ただし、その計算結果を整数型の変数に代入すると小数点以下が切り捨てられてしまうことに注意(C言語のコンパイラはエラーや警告を出すことなく素通ししてしまう)。

整数値同士の除算に対して「余り」を計算するには、剰余演算子%を使う。

練習:次の式の値を考えてみよう。また、計算結果を表示するプログラムを作成して確認してみよう。
3 % 3;
4 % 3;
5 % 3;

値の符号を反転させる(正の値を負に、負の値を正にする)には-(マイナス記号)を使う。普通に数値に対して-を付ける書き方は数学と同じなので特に混乱することはないだろう。

-12 * 3;
12 * -3;
- (4 - 6);

これらの算数的な計算をする演算子を算術演算子と呼ぶ。演算子には優先順位があって、*, /, %は+, -よりも先に計算される(符号としての-は算術演算子よりも優先順位が高い)。これも数学と同じ。

練習:次の式の値を考えてみよう。また、計算結果を表示するプログラムを作成して確認してみよう。
4 + 5 * 6 / 2;
4 + 5 / 2 * 6;

同じ優先順位の演算子の場合、左から右に計算(評価)していく。評価の優先順位を明示的に指定するには()を使う。(と)は対応が取れている必要がある(開いたら必ず閉じる)。()の中に()を書くこともできる。

練習:次の式の値を考えてみよう。また、計算結果を表示するプログラムを作成して確認してみよう。
(4 + 5) * (6 / 2);
(4 + 5 * 6) / 2;
(4 + 5) / (2 * 6);

変数に対して算術演算子を使う場合は、まず変数の値が取り出されて、その値に対して計算が行われる、と考えればよい。ある式を計算する(評価する、あるいは実行する)時点での変数の値が使われる、というのがポイント。

これまでのプログラムでは順次実行のみなので混乱はないが、後に扱う繰り返しが含まれていると複雑になってくる。

練習:次のプログラムの実行結果を予想しよう。また実行して予想が正しいか確認しよう。

/*****
    calcExample.c
    変数の計算の練習
    M.Minakuchi
*****/

#include <stdio.h>

int main() {
  int value;
  int answer;

  value = 2;
  answer = value * 3 + 4;
  printf("answer = %d\n", answer);

  value = 4;
  answer = value * 3 + 4;
  printf("answer = %d\n", answer);

  return 0;
}

代入文と変数

上のプログラムを見て、おや?と思う人もいるかもしれない。

まず、同じ変数valueとanswerに対してそれぞれ2回代入している点。C言語では変数に対して何度でも値を代入可能である。値が変わりうるから「変数 variable」なのである。実行している文に応じて変数の値が決まる点に注意(プログラムでは基本的に上から下に1文ずつ実行する、ということを意識してほしい)。

また、前回次のような話をした。

変数の使い方のポイント:
・変数をケチらない。1つの目的ごとに変数を用意する。
・変数名は、変数の意味を端的に表す名前を付ける。

この原則からすると前半の計算の部分と後半の計算の部分とで同じvalueやanswerを使うべきではないと思うかもしれない。同様に前回の課題で、四則演算の計算結果ごとに変数を用意したプログラムを書いた人も少なくないだろう。

さて、「1つの目的ごとに」という意味をよく考えてみよう。四則演算の計算それぞれを分けて、和、差、積、商と考えれば目的が異なっているのだから、それぞれの計算結果ごとに変数を用意するべきである。一方、「なんらかの計算結果」と考えれば、計算内容は異なっているが目的は同じことになる。上のプログラムで変数名をvalue(値)、answer(答)としているのはそういう考え方だからである。

どちらが良いかは一概には言えない。コンピュータの性能が低く、メモリを節約しなければならなかった時代であれば、無駄に変数を沢山使うことは良くないとされていた。しかし、現在のように少々メモリを潤沢に使っても問題の無い環境であれば、人間にとってプログラムがいかに理解しやすいか、を最優先にするのがよい。とは言うものの、これらの例においてはどちらが理解しやすいかも考え方次第である。詰まるところはプログラムを書く人が決めればよい。

現在でも組み込み用のプログラム(例えば炊飯器の制御用マイコン)など、メモリの無駄遣いができない環境は存在する。世の中、常にローエンドなものがあることは意識しておかなければならない。

これまでのプログラムでは順次実行、すなわちプログラムは上から下に一本道で実行されるもののみであったが、「繰り返し」を使うプログラムでは同じ式を何度も実行することになる。その際に、ある変数の値が変化していくのが普通となるので、ここで説明しているような変数に何度も値を代入できるということをしっかり理解して欲しい。

代入文の文法は次のように書ける:

変数 = 値;
右辺の値を左辺の変数に代入する(格納する)。右辺が式の場合は先に右辺を計算し、その結果を変数に代入する。左辺には変数1個しか書けないことに注意。

数学の=(等号)とは意味が異なる点に注意。

C言語では、左辺と右辺の値が等しいか判別する演算子には==を使う(次回説明する)。少々ややこしくて慣れるまで混乱するかもしれないが、そう決まっているのだから仕方がない。プログラム言語の歴史的には、代入演算子を:=と書いたりLET x = 1のように書く言語が初期の頃はあった。代入演算子は頻繁に使うので、C言語では簡単に書ける=を採用したのだろう。

代入文は右辺の式の値が計算(式を評価)されてから、その値が左辺の変数に代入される。なので、例えば次のような式も可能となる:

value = value + 1;

練習:この式の意味を考えてみよう。

練習:次の式を順に実行したとき、valueの値はどのように変化していくか考えてみよう。また、それぞれの式の後でvalueの値を表示するプログラムを作成して確認してみよう。

int value = 4;
value = value + 1;
value = value - 2;
value = value * 3 + 2;
value = (value + 1) / 4;

このような、ある変数の現在の値を使って計算した結果を次の値とする代入式は一般的によく使う。単に何かの値を加える、引く、掛ける、割る計算は多く使われるので、次のような演算子が用意されている:

+= 左辺の変数の値を、右辺の値だけ増やす(加える)

-= 左辺の変数の値を、右辺の値だけ減らす(引く)

*= 左辺の変数の値を、右辺の値だけ掛ける

/= 左辺の変数の値を、右辺の値だけ割る

value += 2;  // value = value + 2;と同じ意味
value -= 2;  // value = value - 2;と同じ意味
value *= 2;  // value = value * 2;と同じ意味
value /= 2;  // value = value / 2;と同じ意味

さらに、ある変数の値を1増やす、あるいは1減らす、という計算もよく使われるので、次のような演算子が用意されている:

++ 変数の値を1増やす(インクリメント increment 演算子)

-- 変数の値を1減らす(デクリメント decrement 演算子)

value++;  // value = value + 1; あるいは value += 1; と同じ意味
value--;  // value = value - 1; あるいは value -= 1; と同じ意味

++と--は、変数の前に書くことも、後ろに書くこともできる(例えば++value;)が、前に書くか後ろに書くかで少々意味合いが異なってくる(詳しくは教科書p.86〜88に書いてある)。実行後の変数の値自体は同じになるが、計算式の途中で使うとすごくややこしいことになる。C言語のパズル的な問題ではよく出てくるが、実際のプログラムでは間違いの元になるだけので注意しよう。お薦めとしては、計算式の途中では使わないことである。

確認課題 入出力と変数を使った計算の練習

以下、様々な値を入力して正しく実行されることを確認すること。ただし、整数型で扱えない値(整数以外の値や大きすぎる/小さすぎる値)を入力した場合は考慮しなくてよい。出来上がったら教員に確認を受けること。

確認課題1.
3つの整数を入力させ、1つめと2つめの値を足した結果に3つめの値を掛けた値を答として表示するプログラムthreeCalc.cを作成せよ。

実行例(下線部は入力した値の例。入力および出力メッセージはこの通りでなくてもよい。以下同様。)
% gcc -o threeCalc threeCalc.c
% ./threeCalc
1st number: 1
2nd number: 2
3rd number: 3
answer = 9
% ./threeCalc
1st number: 2
2nd number: 3
3rd number: 4
answer = 20
%

確認課題2.
3つの整数を入力させ、3つの値の平均値を答として表示するプログラムaverage.cを作成せよ。ただし、答は小数点以下を切り捨てでよい(整数型で計算すれば自然とそうなる)。

実行例
% gcc -o average average.c
% ./average
1st number: 1
2nd number: 2
3rd number: 3
average = 2
% ./average
1st number: 2
2nd number: 3
3rd number: 5
average = 3
%

確認課題3.
中心座標(x座標とy座標)と半径の値をユーザに入力させ、400x400の大きさのウィンドウを開き、入力された中心座標と半径となる円を描くプログラムscanfCircle.cをHandyGraphicを使って作成せよ。様々な値を入力して正しく描けることを確認すること。

実行例:
% hgcc -o scanfCircle scanfCircle.c
% ./scanfCircle
input x: 250
input y: 200
input r: 100

確認課題4.
中心座標(x座標とy座標)と半径の値をユーザに入力させ、400x400の大きさのウィンドウを開き、入力された中心座標と半径となる円と、その円に外接する正方形を描くプログラムscanfCircleAndSquare.cをHandyGraphicを使って作成せよ。また、正方形の左下の座標値をターミナルに表示(printf関数で標準出力に出力)せよ。様々な値を入力して正しく描けることを確認すること。
(練習問題3でできたプログラムをコピーして追加修正すると良い。)

実行例(下線部は入力した値の例。入力および出力メッセージはこの通りでなくてもよい。):
% hgcc -o scanfCircleAndSquare scanfCircleAndSquare.c
% ./scanfCircleAndSquare
中心のx座標: 180
中心のy座標: 230
半径: 80
正方形の左下の座標は(100, 150)

本日の提出課題

提出課題1.
中心座標(x座標とy座標)と半径の値をユーザに入力させ、400x400の大きさのウィンドウを開き、入力された中心座標と半径となる円と、その円に外接する正方形と、その正方形の対角線を描くプログラムscanfFigures.cをHandyGraphicを使って作成せよ。また、正方形の左下の座標値をターミナルに表示(printf関数で標準出力に出力)せよ。様々な値を入力して正しく描けることを確認すること(固定値で描いてはならない)
(確認課題4でできたプログラムをコピーして追加修正すると良い。)
提出期限:次の土曜日の24:00まで

実行例(下線部は入力した値の例。入力および出力メッセージはこの通りでなくてもよい。):
% hgcc -o scanfFigures scanfFigures.c
% ./scanfFigures
中心のx座標: 180
中心のy座標: 230
半径: 80
正方形の左下の座標は(100, 150) 

提出課題2. 2つの座標値(x0, y0)と(x1, y1)(ただし、x0<x1かつy0<y1とする)をユーザに入力させ、600x600の大きさのウィンドウを開き、入力された座標値を用いて下の図になるように長方形とひし形を描くプログラムboxes.cを作成せよ。様々な値を入力して正しく描けることを確認すること(x0<x1かつy0<y1でない場合は、プログラムの書き方によっては正しく描かれないが、気にしなくてもよい)。
提出期限:次回授業開始時まで

実行例(下線部は入力した値の例。入力メッセージはこの通りでなくてもよい。):
% hgcc -o boxes boxes.c
% ./boxes
input x0: 220
input y0: 200
input x1: 450
input y1: 320

ヒント:描画に必要となる座標値と長方形の大きさの計算方法を考えよう。ひし形は4本の線で描くしかない。

次回の準備

「条件分岐」の最初のプログラムtreasure.cを入力して作成しておくこと。