入出力と変数

演算子まとめ(教科書2.3.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>
#include <stdlib.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);

  exit(0);
}

代入文と変数

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

まず、同じ変数valueとanswerに対してそれぞれ2回代入している点。C言語では変数に対して何度でも値を代入可能である。値が変わりうるから「変数 variable」なのである。どこを実行中かに応じて変数の値が決まる点に注意。

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

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

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

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

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

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

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

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

変数 = 値;
左辺の変数の値を、右辺の値(あるいは式)とする

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

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;

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

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

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

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

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

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

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

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

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

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

++と--は、変数の前に書くことも、後ろに書くこともできる(例えば++value;)が、前に書くか後ろに書くかで少々意味合いが異なってくる(詳しくは教科書8.1.1節に書いてある)。実行後の変数の値自体は同じになるが。C言語のパズル的な問題ではよく出てくるが、ややこしくなるだけなので、後ろに書く方だけ使うようにしておくとよいだろう。

入出力と変数を使った計算の練習

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

実行例(下線部は入力した値の例。入力メッセージはこの通りでなくてもよい。):
$ hgcc -o scanfCircle scanfCircle.c
$ ./scanfCircle
input x: 250
input y: 200
input r: 100

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

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

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

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

本日の課題

2つの座標値(x0, y0)と(x1, y1)をユーザに入力させ、600x600の大きさのウィンドウを開き、入力された座標値を用いて下の図になるように長方形とひし形を描くプログラムboxes.cを作成せよ。様々な値を入力して正しく描けることを確認すること。
提出期限:6/12(木) 24:00

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

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

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