C言語:順次実行

今回は、プログラムの処理の流れの重要な要素である順次実行について確認しよう。

HandyGraphic

C言語には標準のグラフィクスライブラリ(図形を描くためのプログラム)は無い。そこで本講義では、本学コンピュータ理工学部の荻原剛志教授によって作成されたHandyGraphicというライブラリを使う。HandyGraphicのインストール方法はこちらのページの通り。公式説明書はこちら(インストーラディスクイメージにも入っている)。

またまたやってみよう

  1. 前回同様、作業用のディレクトリに次のソースファイルを作成する。名前は任意であるが、とりあえずhandy.cとでもしておこう。作業用ディレクトリは前回作ったものでも構わないが、今後ファイルが大量になることを考えるとサブディレクトリを作って整理した方がよいかもしれない。
    例によって名前の部分は自分の名前に置き換えるように。コメントの中なので少々はこの通りでなくても構わない。それぞれの行がどのような意味を持つのか意識しながら入力してみよう。
/*****
    handy.c
    A simple sample of Handy Graphic
    Mitsuru Minakuchi
*****/

#include <stdio.h>
#include <stdlib.h>
#include <handy.h>

int main() {
    HgOpen(600, 400);
    HgCircle(300, 150, 120);
    HgGetChar();
    exit(0);
}
  1. 保存してコンパイルする。HandyGraphicを使ったプログラムをコンパイルするコマンドは、gccの代わりにhgccを使う。

hgcc handy.c

  1. コンパイルエラーが出たら1.に戻って間違いを修正する。コンパイルが通ったらa.out(あるいは-oオプションで実行ファイルの名前を指定したらそのファイル)が存在することを確認して、実行させてみる。次のようなウィンドウが表示されたらOK(pidの後の数字は異なっていてもよい)。何かキーを押すと終了する。


HandyGrpahicの関数 その1

では新しい部分を順に見ていこう。

まず

#include <handy.h>

これはHandyGraphicの関数を使う準備として必要。handy.hにはHandyGraphicの関数に関する情報が書かれているので、それを読み込むことをプリプロセッサに指示している。

コンパイル時にgccでなくhgccを使うのも、HandyGraphicのライブラリ(および他に必要になるライブラリ)を参照する必要があるためである。

handy.cで出てきたHgで始まる行は、いずれもHandyGraphicで提供される関数を呼び出している。HandyGraphicの関数であることが分かりやすいように、いずれもHgで始まる名前に統一している。どのような関数があるかはHandyGraphic説明書の最後のページにまとめてある。

HgOpen(600, 400);

はウィンドウを開くための関数である。HandyGraphic説明書の3ページ目には次のような説明がある:

int HgOepn(double w, double h)
引数: w,h: 幅と高さ
返り値: 0:正常、-1:異常

このような説明書の書き方に注意!! この通りプログラムに書けば良いのではない。関数は次の3つの構成要素がある:

[返値の型] [関数名]([引数列])
返値の型:関数によって返される値の型(種類)
関数名:関数の名前
引数列:関数に渡す値の型と個数

返値は「かえりち」、引数は「ひきすう」と読む。

今は値の型(上記の例ではintやdouble)については置いておこう。また、返値もHandyGraphicではあまり重要ではないので無視しておこう。

関数が返値を返しても必要がなければ無視することができる。もし必要ならば、値を変数に格納するとか、条件判定に使う、などの処理を行う。これらについてはいずれ扱う。

ポイントは、

である。

「ことがある」と言っているのは、関数によっては引数を受け取らないものもあったり、引数によって挙動が変わるかどうかは関数の内容次第であるから。

HgOpen関数の場合、1つめの引数はウィンドウの幅、2つめの引数はウィンドウの高さを指定しているので、上記のプログラム例では幅600ピクセル、高さ400ピクセル、となる。

次の

HgCircle(300, 150, 120);

も同様に説明書を見ると次のようになっている:

int HgCircle(double x, double y, double r)
引数: x,y:円の中心 r:半径
返り値: 0:正常、-1:異常

練習:HgOpen関数、HgCircle関数の引数の値を変更してみてコンパイル&実行し、どのように変化するか確認してみよう。

HandyGraphicではウィンドウの左下が座標の原点(0,0)で、右がx軸の正方向、上がy軸の正方向になっている。

グラフィクスライブラリによっては、左上が原点で、右がx軸の正方向、下がy軸の正方向となっているものもある。これは歴史的な経緯によるものである(昔は左上が原点なのが主流であった)。

HgGetChar();

については説明書には次にように書いてある:

int HgGetChar(void)
返り値: 0以上:入力された文字、-1:異常

引数のvoidというのは「何も与えない」という意味である。実際のプログラムではHgGetChar();のように、()のみ書くようにする。HgGetChar関数は1文字分キーボードからの入力を待ち、何かキーが押されたらその文字コードを返す関数であるが、このプログラム例では単にプログラムを終了させてしまわないように「待ち状態」を作るために使っている。

プログラムの実行順序

handy.cではmain関数の中に4行のプログラム(コードと呼ぶことにしよう)が書かれている。直感的に分かるように、これらは上から下に順番に実行されている。つまり、まずウィンドウを開き、円を描いて、キー入力を待ち、プログラムを終了する、という手順である。もしこの順序で実行されなかったらおかしなことになるだろう。

このように、プログラムでは出てきた順に実行されるのが原則となる。これを「順次実行」と呼ぶ。

C言語ではmain関数が実行の入り口となり、順番に実行されていく。関数を呼び出した場合は一旦寄り道をすると考えればよい。関数の中で順次実行され、関数を終了すると呼び出し元に戻ってくる。実行しているコードは常に1箇所しかなく、同時に複数のコードが実行されることはない。

このような実行形態をシングルスレッドと呼ぶ。複数のコードが同時に実行されるマルチスレッドという手法もあるが、高度かつ複雑なのでこの演習では扱わない(発展プログラミング演習IIでは簡単に扱う予定)。

いろんな図形を描いてみよう

円を描く以外にもいろいろな図形を描く関数がHandyGraphicには用意されている。代表的な関数を以下にまとめておく。他にもあるので説明書も見るとよいだろう。

以下の関数仕様では返値の説明は省略する。

int HgLine(double x0, double y0, double x1, double y1)
線分を描く
引数: x0,y0:線分の始点 x1,y1:線分の終点

int HgBox(double x, double y, double w, double h)
長方形を描く
引数: x,y:左下隅の座標 w,h:幅と高さ

int HgArc(double x, double y, double r, double a0, double a1)
扇形を描く
引数: x,y:円の中心 r:半径 a0:始点角度(ラジアン) a1:終点角度(ラジアン)

180°がラジアンではπである。x度ならx*π/180でラジアン単位に変換できる。

int HgText(double x, double y, const char *str, ...)
文字列を描く
引数: x,y:左下隅の座標 str:書式文字列(printf関数と同様の指定が可能)

書式文字列については「変数」の回で説明する。とりあえず""で囲まれた文字列を表示する。

練習:これらの関数を使っていろいろ絵を描いてみよう。特に意味のある絵でなくてもよい。

図形の見栄えを設定する

ここまで紹介した関数は黒い線で描くだけであった。線の太さを変えたり色をつけたりするには次の関数を使う。

int HgSetWidth(double t)
線の太さを指定する(初期状態では1ピクセル)
引数: t:線の太さ(ピクセル単位)

HgSetColor(hgcolor clr)
図形の線の色を指定する。色の指定は下記参照。
引数: clr:色

色は、次の18種類が定義されている。マクロ名とかかれている大文字と_(アンダースコア)の文字列を引数として与えればよい。

マクロ名 マクロ名 マクロ名
HG_WHITE HG_RED シアン HG_CYAN
HG_BLACK HG_GREEN オレンジ HG_ORANGE
灰色 HG_GRAY HG_BLUE ピンク HG_PINK
淡灰色 HG_LGRAY HG_YELLOW マゼンタ HG_MAGENTA
濃灰色 HG_DGRAY HG_PURPLE HG_BROWN

表にない色を使いたい場合はHgGray関数やHgRGB関数で色を作ることができる。詳しくは説明書を見よ。

これらの線の太さと色の指定は図形を描く前に行う。一旦指定すると、別の指定を行うまで有効である。図形を描くためのペン(太さや色)を選んでから、図形を描き、次のペンに持ち変えるまでは同じペンを使う、とイメージすればよい。

例えば次のプログラムを実行してみよう。

/****
    widthAndColor.c
    test of line width and color
    Mitsuru Minakuchi
*****/

#include <stdio.h>
#include <stdlib.h>
#include <handy.h>

int main() {
    HgOpen(600, 400);

    HgBox(10, 150, 100, 100);  // the left side square
    
    HgSetWidth(4);
    HgBox(130, 150, 100, 100);  // 2nd square
    HgBox(250, 150, 100, 100);  // 3rd square
    
    HgSetWidth(1);
    HgBox(370, 150, 100, 100);  // 4th square
    HgBox(490, 150, 100, 100);  // the right side square
    
    HgGetChar();
    exit(0);
}

次のような実行結果になる。線の太さの設定(HgSetWidth関数)がどの四角に影響しているか確認せよ。

練習:実行結果が次の図になるようにwidthAndColor.cに線色の指定(HgSetColor関数)を追加せよ。例えば赤色に設定するにはHgSetColor(HG_RED);とすればよい。

図形を塗りつぶす

これまで紹介した図形を描く関数は線だけで描いていた。HandyGraphicでは、中を塗りつぶした図形を描くには別の関数を使う必要がある。代表的な関数を以下に挙げる。

HgCircleFill(double x, double y, double r, int stroke)
塗りつぶした円を描く
引数: x,y:円の中心 r:半径 stroke:縁を描くかどうか、0の場合描かない、0以外の場合描く

HgFanFill(double x, double y, double r, double a0, double a1, int stroke)
塗りつぶした扇形を描く
引数: x,y:円の中心 r:半径 a0,a1:始点角度、終点角度 stroke:縁を描くかどうか

HgBoxFill(double x, double y, double w, double h, int stroke)
塗りつぶした長方形を描く
引数: x,y:左下隅の座標 w,h:幅と高さ stroke:縁を描くかどうか

HandyGraphicでは塗りつぶし図形の関数名にはFillが付いている。また、縁を描くかどうかを指示する引数が追加されている。

縁の線の太さや色を設定するには、線で図形を描く場合と同様に、HgSetWidth関数とHgSetColor関数を使う。塗りつぶしの色を設定するには、HgSetPaintColor関数を使う。色の指定方法はHgSetColor関数と同様である。

関数名がHgSetFillColorでないのは作者の意向だから仕方ない。

HgSetPaintColor(hgcolor clr)
塗りつぶし色を設定する
引数: clr:色

練習:次のような表示になるようにwidthAndColor.cを修正せよ。

本日の課題

次の図を描くプログラムfourCircles.cを作成せよ。円の重なり方も一致させること!!
・ウィンドウの大きさは400x400ピクセル
・円の中心はそれぞれ(120, 120)、(280, 120)、(120, 280)、(280, 280)
・円の大きさはいずれも100ピクセル
・円の色は青、緑、赤、黄
・円の縁は黒で1ピクセルの太さ(初期状態のままなので設定する必要なし)

ヒント:どの円が一番下で、どのような順番で重なっているかを考えよう

提出期限:5/26 13:15