モバでプログラム

最近ページ作ってないなぁ、 という事でページを書く事にする。 決して現実逃避では無い(笑)
なお、徹夜明けの眠い状態で眠らない為に書いているので、 なぜかナチュラルハイ状態なので文脈が壊れてる部分もあるかも。
という事でプログラムする為の環境を整えよう、 というのがこのページの主題。 まあモバやる人なら誰でもこの程度は出来るかもしれないけど、 少しでも敷居が低くなれば幸いです、はい。

準備編

まずはモバのインストールをしなきゃいけないけど、 これはパス。 で、次にやるのがエディタとコンパイラの設定だやね。 っていうか言語はどうする? という気もするけどcにする。 エディタは独断でnemacsに決定。 コンパイラはgccのミニパッケージでいいでしょう。 ついでにmakeもミニパッケージでいれる。 gmakeでもいいし、普通のでもいい。 これらはMGLIBにあります。 ただし、urlは覚えてないのでリンクは無し。 ポケビのトップからたどれるリンク集にリンクがあるので、 そこで落として下さい。 また、kosmosさんの所でもhello worldまでがまとめてあるので、 参考にするといいでしょう。こちらもトップからたどってください。
あらすじだけまとめておくと、 MGLIBからバイナリパッケージをダウンロードして、 ルートになって
# pkg_add ファイルネーム
ファイルネームの所はnemacs-2.2.3.tgzとかだと思う。 まあこんな感じでやって下さい。 ASCII本についてる奴はgccだけはライブラリがたりないので、 使えないらしい。 他の物はASCII本についてるのでいいでしょう。 MGLIBからおとしてもいいです。
で、nemacs、make、gccが入ったとする。 ついでにmglもいれよう。 これもMGLIBにある、と思うけど。あったかなぁ。 なかったら佐野さんの所だけど。 これまたトップからたどってくれや。

うむ、結論としては、このページを読んでえられる事は何も無いな(笑)
まあいい、適当にすすめる。 それで、ここまでで、 hello worldなプログラムを作る準備が出来た訳だ。 まあkosmosさんの所見れば全部書いてあるうえに、 むこうの方が親切だろう。 まあいい。 で、一応作ってみる。
適当なディレクトリでも作って(つくらなくてもいいが)、 その中にhello.cとかいうファイルを作る。 作り方は、
$ nemacs hello.c
とかやるとエディタでhello.cが新規作成出来るので、 そこに適当にいろいろうちこむ。たとえばこんなかんじ。

#include <stdio.h>

int main(){
	printf("hello world\n");
	return 0;
}
かなり適当。 まあどうでもいい。 こんな感じでうちこんでくれや。 で、こいつを保存する。 C-xC-sで保存。なお、C-xはCtrlを押しながらx、という意味。 ここらへんはemacs系のページでも見ればいいが、 別にそんな知識なくても問題無い。 ちなみに終了はC-xC-c、だったかなぁ。 手が勝手に動くからよく覚えてない。 でも、このページではおそらく終了はしないです。 nemacsは重いからなるだけ終了させないようにするのが基本です。 もちろんjvimでもそうでしょうが。で、テキストうったら保存する訳です。 C-xC-s、と入力する訳です。 その後は、わりと基本ですが、これを実行する方法は2通りあります。
  1. 一旦nemacsをサスペンドする。
  2. M-!使う
両方基本なので覚えておきましょう。 まずはnemacsをサスペンド、から。 サスペンドはC-zです。 nemacsに限らずjvimでもそうです。 C-zするとシェルに戻るので、 そこで
$ gcc hello.c
$ ./a.out
とかやると、
hello world
$
とかいう状態になると思います。 まずはgcc hello.c、というのがコンパイルで、 ./a.outというのが実行です。 まあいい。
ここまで出来ると、 そこらへんにあるC言語入門、 とかいう本の内容がいかせます。 まあ図書館にでもいって、よさそうなの借りてきて、 ひたすら練習、とかすると、C言語の勉強が出来て情報2種とかの勉強も出来ます。 あ、サスペンドしたまま戻る方法を書いてませんねぇ。
$ fg
で戻れると思います。 本当は、
$ jobs
とやると
[1]+ Stopped nemacs (wd: ~/src)
とか表示されると思うので、 この数字を、fg、というコマンドの後につける、つまり
$ fg 1
とやると確実です。 まあここらへんはunix特有の知識ですが。 で、ここまででコード書いて、 編集して、 それを保存して、コンパイルして実行、 まで出来るようになった訳です。 あ、最初に指定したファイルしかまだ編集出来ないですね。 nemacsが立ち上がってる状態で、C-xC-fと入力すると、 画面の下の方(ミニバッファ領域という)に、
Find file: ~/src/
とか(少し内容は違うかも)出ると思うので、
Find file: ~/src/test1.c
とかいう風にtest1.cと入力してリターン押すと、 test1.cというファイルが作られます。 あとはhello.cと同じように書いて、保存して、C-zでサスペンドしてコンパイルして、 実行、となる訳です。

そうだ、M-!の方も解説しておこう。 nemacs起動中にEsc押してから!(つまりShift+1)押すと(以下M-!と略記)、 ミニバッファ領域に
Shell command:
とか出ると思います。 ここで、
Shell command: gcc hello.c;./a.out
と入力してリターン押すと、 neamcsを終了せずにコンパイルと実行が出来ます。 こっちの方が楽ですが、 こっちだと履歴が使えないので使い訳がじゅーよーです。

sinが使えない!

時はすすんで、基本的なプログラムくらいは組めるようになってきました。(という事にしておく)
で、C言語の本とかみて、
#include <stdio.h>

int main(){
	printf("sin(25)=%f\n",sin(25));
}
とか書いたファイルをコンパイルしてリンクしたら なんかおこられたよん、 という事になる訳です。 で、Winな自称プログラマにきいてもなんだかよくわらない、 とかいわれるし、どうしよう? という事になるかもしれない訳です(なるか?)
ここらへんもC言語の話なはずですが、 世のC言語の本ではリンクの事なんてあんまり真面目に書いてないので、 ちょいと軽く解説。 まず
#include <math.h>
が無いのが本当は論外なのですが、 何故かこれ無しでも動くコンパイラとかもあるらしいので。 まず、数学関係の関数を使うには、 stdio.hの他にmath.hというのをインクルードしなくてはいけません。 で、includeすればオッケーか、 というと全然そんな事無いです。 include、というのはようするにそこで指定したファイルの中をコピーペーストする事と一緒です。 という訳で、math.h、 というのは/usr/include/math.hあたりにあるので、 こいつを見てみると、、、 たくさんだらだらあるので却下。 これがスラスラ読める人は、 こんなページを読んではいけません(笑)
とにかく基本的にはプロトタイプ宣言だけが書いてあるような物です。 つまり引数と返値だけです。 つまり中身はここには無い訳です。 なので当然これだけでは動きません。 中身はどこにあるのか。 /usr/lib/libm.aにあります。 libm.a、という名前はどっから出てくるのか? というのは何かしらべるコマンドがありますが、 忘れました。 っていうかモバにいれてないし。 まあ基本的なのしか普通は使わないので、 だいたい覚えているからいらないんですよね。 母艦には調べる方法を書いたメモがあるんですが。 まあいいでしょう。
で、名前としては、lib、 は基本的に全てのライブラリについているので、 残りの文字に着目します。 mathは、頭文字でlibm、となる訳です。.aも全部につくので、 無事libm.aとなる訳です。 なんでこんな話しをするかというと、 このlibm.aとリンクしないとダメだからです。
$ gcc -lm test1.c
とかやるとlibm.aとリンクされる訳です。 gccのオプションで-lの後にライブラリ名をつける訳です。 この場合はm。つまりlib*.aの*の部分を指定する訳です。 libmgl.aとリンクするなら、
$ gcc -lmgl test1.c
とかやる訳です。 まあいい。 という訳で、-lmでリンクしないといけない訳です。 これで数学関数が使えるようになったので、 実験のデータ整理とかがCで出来る訳です。 まあデータ整理にC使うのもどうかと思いますが、 それしか知らないならしょうがない。
なお、コンパイルとリンクは分ける事が出来ます。
gcc -c test1.c
とやると、test1.o、とかいうファイルが出来ると思います。 これに、
gcc -lm test1.o
とかやるとlibm.aとリンクされてa.outというのが出来ます。

MGL使ってみたり〜

まあやっとモバらしい話題になる訳です。 MGLです。時代はMGLな訳です。 何故ならダメソフト作るにはグラフィックスは必須だから(謎)
という事で時代はmglです。 サンプル読んでドキュメント読むと大抵の事はわかってしまいますが、 まあ解説しておきます。
まずはmglのインストールです。 ここまできていると、 もうmglのインストールなんてお手の物だと思います。 じゃあ、基本をおさえますか、という事で簡単な奴を組みます。 ファイル名はtest1.cです(こればっか)
/* test1.c */
#include "mgl.h"

int main(){
	open_graph();
	clear_screen();
	draw_string(20,20,"hello");
	refresh();
	get_key(-1);
	close_graph();
	return 0;
}
動くのかなぁ、このプログラム。 テストしてないのでしりません(爆)
おいといて、 中身の解説はそのまんまなので、どうでもよさそうですが、 一応解説。open_graph()はmgl使う時のお約束です。 で、clear_graph()で画面をクリアします。 それで、draw_stringはまあそのまんま。 文字列書きます。 refresh()は実は意味無しです。 ただ、X上でのエミュレーターではこれが無いと再描画されません。 なので、描画したい時はrefresh()をはさんでおきましょう。 get_key(-1)は、なにかキーの入力があるまでずっと待ち続ける、 という事です。まあ様子を見たい時にこのコードをはさみます。
で、こいつをコンパイルします。 これが結構面倒。
$gcc -I/usr/local/lib/mgl -L/usr/local/lib/mgl -lmgl test1.c
とかやる訳です。 長いですね。 ここらへんからMakefileが欲しくなります。 Makefileは基本を理解しておけば、 後は適当にやれば平気です。 トラブるとやや困りますが、 困ったらMakefileを勉強すればいいでしょう(ほんとか?)
という事でMakefileの書きから。
CFLAGS =  -I/usr/local/lib/mgl
LDFLAGS = -L/usr/local/lib/mgl -lmgl

all:test1

test1 : test1.c
	gcc $(CFLAGS) -o test1 test1.o $(LDFLAGS)
基本はこれです。 これだとa.outのかわりにtest1、という実行ファイルができます。 何をやってるか、 という事はおいておいて、 CFLAGSとLDFLAGSという名前は同じ名前を使ってください。 このMakefileだけなら別に違う名前使っても平気ですが、 暗黙のルールとか使う時に便利なので。
で、CFLAGSはインクルードファイルのパス指定、 LDFLAGSはリンクするファイルのパスとリンクするファイル自体も指定(-lmglの所)です。 まあうつせばいいので、問題無いでしょう。 ポイントは
test1 : test1.c
	gcc $(CFLAGS) -o test1 test1.o $(LDFLAGS)
だけです。 test1、というのを作るのに必要なファイルが:より後に書いてあります。 で、その下にはTabをはさんでtest1の作り方が書いてあります。 Makefile、 というのは、test1の時間とtest1.cの時間をみて、 test1の方が古い時だけmakeします。 あやしい時は、*.oとかtest1を消してしまえば問題無しです(爆)
おいといて、all、というのは特殊です。 何も指定せずにmake、とやるとallがmakeされる事になってます。
all :test1
とかいてあると、 all、を作るにはtest1が必要だ、 という事で、 test1をさがすとtest1が無いので、 test1のルールをみると、先程の記述にいきつく訳です。 test1.oが無いぞ、とか-oってオプションは何? とかいろいろでてくるので、 まあこまかい事は気にせずに上記のひながたをパクりましょう。 もうちょっとパクリやすい形にしておくと、
CFLAGS =  -I/usr/local/lib/mgl
LDFLAGS = -L/usr/local/lib/mgl -lmgl

TARGET=test1
OBJ = \
	test1.o


all:$(TARGET)

$(TARGET) : $(OBJ)
	gcc $(CFLAGS) -o $(TARGET) $(OBJ) $(LDFLAGS)

で、TARGETとOBJだけ書きかえてください。 OBJには、 そのプログラムで使う全てのオブジェクトファイルを書いておけばいいです。 たとえば、
OBJ	= \
	getdata.o \
	makelist.o \
	viewlist.o
とかいう風に複数のファイルを記述します。 使いかたは、
$ make
でおっけー。 まあこれで基本は終わりです。 次にいろいろとmglで遊びましょう。

ファイルを分けたいぞ

ファイルをわけないとやっていけません。 理由は簡単で、いちいひ全部のファイルコンパイルするのは無理だからです。 何故無理かというとモバがへぼへぼぴーだからです。 ですが、むしろこれを勉強の機会、 とみるのがよいでしょう。 自己暗示です、はい。
でファイルを分けたい訳です。 まあこんなのプログラマにとっては基本なんですが、 何故か本とかにはのってないので、 どうにかして我流で学ぶ所らしいです。 うぐぅ。 という事でファイルを分けます。 何も考えずに分けます。 externとかは使いません。これは個人の趣味なんで、 extern使ってもいいんですが。 という訳でまずはすげー簡単な奴を二つにわけます。
#include <stdio.h>

int sum(int a,int b){
	return a+b;
}
int times(int a,int b){
	return a*b;
}
int minus(int a,int b){
	return a-b;
}
int pmt(int a,int b,int c,int d){
	return times(sum(a,b),minus(c,d));
}

int main(){
	printf("%d\n",pmt(5,3,6,2));
	return 1;
}
はい、まじやる気ないプログラムです。 sumなのになんでminus?という名前づけのおかしさとかもつっこんではいけません。 まあ意味の無いプログラムですが、 これを2つのファイルにわけます。
まずはtest2.cというファイル
/* test2.c*/
int sum(int a,int b){
	return a+b;
}
int times(int a,int b){
	return a*b;
}
int minus(int a,int b){
	return a-b;
}
int pmt(int a,int b,int c,int d){
	return times(sum(a,b),minus(c,d));
}
と、test1.cというファイル
/* test1.c*/
#include <stdio.h>

int sum(int a,int b);

int times(int a,int b);

int minus(int a,int b);
int pmt(int a,int b,int c,int d);


int main(){
	printf("a%d\n",pmt(5,3,6,2));
	return 1;
}

これです。 test1.cにはプロトタイプ宣言がダラダラとならぶ事に注意。 で、Makefileは、
CFLAGS =  -I/usr/local/lib/mgl
LDFLAGS = -L/usr/local/lib/mgl -lmgl

TARGET=test1
OBJ = \
	test1.o \
	test2.o


all:$(TARGET)

$(TARGET) : $(OBJ)
	gcc $(CFLAGS) -o $(TARGET) $(OBJ) $(LDFLAGS)

mglとのリンクなんていらないじゃん、 とかいうつっこみは却下。
で、これでmake、 とかやるとtest1という実行ファイルができます。 これは、複数のファイルでtest2.cは使えます。 sumとかはいろんなファイルで使いたい訳です(この場はありがたみが低いですが)
次にこいつをヘッダファイルにわけます。 こうすると、いちいちプロトタイプかかずにインクルードするだけでおっけーなので。 という訳でtest2.hという名前のファイルをつくります。
/* test2.h*/
int sum(int a,int b);

int times(int a,int b);

int minus(int a,int b);
int pmt(int a,int b,int c,int d);
でtest1.cの中身がこうかわります。
#include <stdio.h>
#include "test2.h"

int main(){
	printf("a%d\n",pmt(5,3,6,2));
	return 1;
}
うむ、ふたつにわかれた。 ここで注意したいのは、 test2.hの変更をmakeは感じない、って事です。 ちゃんと直してもいいんですが、 ヘッダファイルはあんまり変更しないので、 これでいいでしょう。 ヘッダファイルだけ変更した時はtest2.oとかを消してからmakeすればいい訳です。

で、 簡単な例だと解説しづらいのですが、 sum.hを2個所のヘッダファイル内にインクルードしたい、 となる事があります。 データ構造を使いたい時ですが、 まあ例はちょっと簡単なのは作るの面倒なのでいいません。 とにかく、 sum.hをminus.hとtimes.hでインクルードしたい、 と思う時がある訳です。 で、pmt.hがminus.hとtimes.hという二つのファイルをインクルードしたくなるとすると、 minus.hの中にあるsum.hとtimes.hの中のsum.hが多重定義になってエラーになってしまいます。 で、こいつを避ける為に普通、ヘッダファイルには細工をします。 基本的には、

#ifndef _ヘッダファイル名_H_
#define _ヘッダファイル名_H_

ヘッダファイルの中身

#endif

とやります。 これは、"_ヘッダファイル名_H_"が定義されてない時だけコンパイルされる訳で、 一回目にコンパイルする時に2行目で"_ヘッダファイル名_H_"が定義されるので、 2回目以降はコンパイルされない訳です。 まあ困った時がきたらこんな話を思いだせば十分でしょう。 具体的は、sum.hというファイルは
#ifndef _SUM_H_
#define _SUM_H_

int sum(int a,int b);

#endif
とか書く訳です。 まあおいといて、これでファイルが分けられるようになったので、 何でも作れるはずです。

staticとかstaticじゃないとか

最初に言いだしたのは誰だったのかしら?
という事でstatic、という事も解説しておきます。 これは第2弾を書いて、 この解説が無い事に気付いたからです。
C言語は、ファイル毎に、外部に公開する関数、変数と公開しない関数、 変数を作る事が出来ます。
例えば、先程の例で、test2.h、 という中には、
/* test2.h*/
int sum(int a,int b);

int times(int a,int b);

int minus(int a,int b);
int pmt(int a,int b,int c,int d);
という定義がありましたが、 mainで使うのはpmtだけです。 という訳で、pmtだけを外部に公開する事にする、 とすると、この中にはint pmt(int a,int b,int c,int d);の定義だけで十分な訳です。 という訳でこれだけにすればいいか、 というと、少し問題があります。
何が問題か、 というと、たまたまtimes、という名前の関数を他のファイルでも作りたくなったとします。 でも公開はされてませんが、すでに名前空間としてtimesは消費されているので、 リンクの時に衝突してしまいます。
そこで登場するのがstaticです。 timesはtest2.cの中でしか使わないのですから、 この中だけで有効ならいい訳です。 という事で、test2.cの中で、timesの定義を
static int times(int a,int b);
とかして、実装を、
static int times(int a,int b){
  return a*b;
}
とすればいい訳です。 こうすると、test2.cの中のだけでしかこのsumは存在しなくなり、 よそのファイルでsumとか定義しても平気になります。
また、global変数も同じです。 たとえば、test2.cの最初の方で
static char buf[256];
と書くと、 このbuf、という変数はtest2.cの中でしか使えません。 なので、このモジュール内のみで使いたい文字列等はこうやって定義しておきます。 公開されてない部分が汚い、 というなら、公開されてる部分に手を入れないで直す事が出来るので、 被害は少ないです。 ヘッダファイルにグローバル変数が並んでると、 それを直すのは難しいです。 まあグローバル変数ガンガン使うのがC言語らしさだ、 というむきもあるかもしれませんが。
まあとにかく、ヘッダにはなるべく公開しないで、 必要な関数(この公開されてる関数をインターフェースと呼ぶ)だけを書いておく、が基本な訳です。
そうやって、test2というモジュールとみなす、 というのがC言語の普通のやり方です。 ただ、これは複数実体が持てない、 という欠点があります。 複数の実体が必要なモジュールを作りたい場合は、 いい方法は無いので引数で構造体のポインタわたしたりしなくちゃいけません。

終わりに

この解説、非常にわかりにくいなぁ。 しかもどっかにもっとマシな解説がいかにもありそうだ。 まあいいや。 これでいろんなソフトが作れるはずです。 mglの解説とかはちょっと短かったと思う。 ただ、 基本は線を書く、文字を書く、 テキストスクリーンに文字を書く、線を書く、画像を表示する、 の5種類なので、 ドキュメントみながらでも結構余裕だとは思います。 もうちょっとサンプルが示せるといいんですが。 まあいいでしょう。