C言語:順次実行

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

HandyGraphic

C言語には標準のグラフィクスライブラリ(図形を描くためのプログラム)は無い。そこで本演習では、本学コンピュータ理工学部の荻原剛志教授によって作成されたHandyGraphicというライブラリを使う。HandyGraphicのインストール方法はこちらのページの通り。インストールパッケージはmoodleで配布する。あるいは公式ページからダウンロードする。
今回はHandyGraphicの基本機能のみを紹介するが、詳しい説明は公式ページ中のユーザーズガイドに書かれている。ユーザーズガイドはHandyGraphicに慣れてきたら必要に応じて参照するためにダウンロードしておくとよいだろう。

またまたやってみよう

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

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

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

hgcc -o handy handy.c

  1. コンパイルエラーが出たら1.に戻って間違いを修正する。コンパイルが通ったらhandyができことを確認して、実行させてみる。次のようなウィンドウが表示されたらOK。何かキーを押すと終了する。


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 HgOpen(double w, double h)
引数: w,h: 幅と高さ
返り値: 0:正常、-1:異常

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

[返値の型] [関数名]([引数列])
返値の型:関数によって返される値の型(種類)
関数名:関数の名前
引数列:関数に渡す値の型と値(複数の引数がある場合は,で区切る)

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

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

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

ポイントは、

である。

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

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

ピクセル(画素)は、画面上の大きさの単位。画面は縦横方向にピクセルと呼ばれる小さな長方形(現在ではほとんどが正方形)に区切られており、この大きさが1ピクセル。

次の

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文字分キーボードからの入力を待ち、何かキーが押されたらその文字コードを返す関数であるが、このプログラム例では単にプログラムを終了させてしまわないように「待ち状態」を作るために使っている。

HgClose();

はウィンドウを閉じる関数である。開いたものは最後にちゃんと閉じておこう。

int HgClose(void)
返り値: 0:正常、-1:異常

プログラムの実行順序

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

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

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

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

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

円を描く以外にもいろいろな図形を描く関数が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:色

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

マクロ名 マクロ名 マクロ名 マクロ名
HG_WHITE HG_RED シアン HG_CYAN 空色 HG_SKYBLUE
HG_BLACK HG_GREEN オレンジ HG_ORANGE 濃赤色 HG_DRED
灰色 HG_GRAY HG_BLUE ピンク HG_PINK 濃緑色 HG_DGREEN
淡灰色 HG_LGRAY HG_YELLOW マゼンタ HG_MAGENTA 濃靑色 HG_DBLUE
濃灰色 HG_DGRAY HG_PURPLE HG_BROWN 透明※ HG_CLEAR

※透明色の使い方についてはユーザーズガイド9.9節に書いてある。少々難しい。

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

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

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

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

#include <stdio.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();
    HgClose();
    return 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:縁を描くかどうか、0の場合描かない、0以外の場合描く

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

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

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

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

確認課題

次のような表示になるように(左から順に赤、赤、青、青、黒)widthAndColor.cを修正せよ。

出来上がったら確認してもらうこと。

本日の提出課題

いずれの課題もソースコードをmoodleで提出すること!!

提出課題1. 実行すると次のようにメッセージをターミナルに表示するプログラムgreeting.cを作成せよ。ただし、名前(Mitsuru Minakuchi)、故郷(Kyoto)、趣味(sleeping)は自分の内容に変えること(趣味は必ず変えること)

提出期限:次の土曜日の24:00まで

% gcc -o greeting greeting.c % ./greeting Hello, I am Mitsuru Minakuchi. I am a student of Kyoto Sangyo University. My hometown is Kyoto. My hobby is sleeping. %

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

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

提出期限:次回授業開始時まで


次回の予習

教科書3章に目を通しておこう。少々難しい内容も含まれているので、飛ばし読みで構わない。