本科目で学習対象とするプログラミング言語「C」について, とにかく,プログラミング作業を体験してみよう.
また,プログラミングに適したエディタ「vi」の操作方法を習得して行こう.
この授業では毎回,複数のファイルを作成する. ファイルを整理するために, ディレクトリ(フォルダ)を用意しておくとよいだろう. 端末(「ターミナル」等)の中で次のように操作しよう:
$ cd # ホームディレクトリへ移動 $ mkdir C # この授業用のディレクトリを作成
ここで,記号「$」はコマンドプロンプトを表している. つまり,「$」を入力する必要はない.
また,記号「#」から行末までは, コメント(説明文)なので入力不要.
以上のことは,今回だけ 1 度だけ実行しておけば OK.
次のことは,毎回の授業の最初に 1 度だけ,実行しよう:
$ cd C/ # この授業用のディレクトリへ移動 $ mkdir 0411 # その日の授業用のディレクトリを作成 $ cd 0411/ # 〃 へ移動
以上について,次回以降は特に説明しないかもしれないが, 毎回,忘れずに実行しよう.
テキストエディタはCプログラミングに不可欠な道具だ. 世の中には様々なエディタが存在するが, この授業では,Unix の伝統的なエディタ「vi」の 改良版「vim」(Vi IMproved)を推奨する. vi では,他のエディタと比べると,操作方法に多少のクセはあるものの, 慣れてしまえば,異常に高速な編集作業が可能になるだろう.
vi の操作方法について,自習用プログラムを利用して学習しよう:
$ vimtutor # 日本語版 # または $ LANG=C vimtutor # English version
一度にすべてを理解する必要はない. 毎日少しずつ,技をおぼえて行こう.
vi の高度な編集機能を使いこなせるようになれば, プログラムの編集作業を効率的に進められるようになる.
今後の作業を効率的に進めるため, vi の設定を整えておこう.
設定ファイルの編集開始:
$ vim ~/.vimrc もしくは $ vi ~/.vimrc
設定ファイルへ記入する最低限の内容:
syntax on set paste
この記入のためのキー操作:
[i] [s][y][n][t][a][x][Space][o][n][Enter] [s][e][t][Space][p][a][s][t][e][Enter] [Esc] [:][w][q][Enter]
まずは,超簡単なプログラム〜文字列の出力〜をイジり回しながら, Cプログラミングの作業手順をおぼえよう.
まず,テキストエディタを利用して, List 1 のような内容のファイル hello.c を作ろう:
$ vim hello.c
main() { printf("Hello, world.\n"); // 「Hello, world.」と表示 printf("こんにちは。\n"); // 「こんにちは。」と表示 }
なお,このファイル hello.c は, 画面にメッセージ「Hello, world.」等を出力する プログラムのソースファイルだ. ソースファイルの内容は,ソースコードと呼ばれる. なお,文字列の最後にある記号「\n」はそこで改行することを表している.
ソースファイルの名前について,自由に変えて良いが, その拡張子については,必ず「.c」とすること.
C言語のプログラムはソースファイルのままでは実行できない. Cコンパイラによって, コンピュータが実行可能な機械語のプログラムへ変換する必要がある:
$ cc hello.c # もしくは $ gcc hello.c
hello.c: In function ‘main’:
hello.c:3: warning: incompatible implicit declaration of built-in function ‘printf’
...
警告メッセージ(warning)が表示されているが, 現時点では,無視して良い.
とにかく,コンパイルが成功した場合には, 実行形式ファイル a.out が作成されているはずだ. 一応,ls コマンドで確認してみよう:
$ ls a.out hello.c
もしソースに文法的な間違いがあれば, コンパイル時にエラーメッセージが表示される. たとえば,3行目の末尾の「;」を付け忘れてしまった場合: (実際にソースを書き換えて試すこと.)
$ cc hello.c hello.c: In function `main': hello.c:5: parse error before `}'
これは,「5行目の } の前が変だ」という意味だ.
また,3 行目で,「printf」でなく「print」のように, スペルを間違えてしまった場合: (実際にソースを書き換えて試すこと.)
$ cc hello.c /tmp/ccUXoONx.o: In function `main': /tmp/ccUXoONx.o(.text+0x9): undefined reference to `print' collect2: ld returned 1 exit status
これは,「print なんて知らないよ」ということだ.
他にもわざと間違えてみて,どんなエラーメッセージが現れるか? 試してみると良い.
コンパイルに成功したら, このプログラムを次のようにして実行できる:
$ ./a.out Hello, world. こんにちは。
ファイル名の前の「./」は, 「現在のディレクトリにあるファイル」という意味を表している. 他のディレクトリにあるファイルを実行する場合には, 当然,ディレクトリ名も指定する必要がある. 例:
$ cd # 授業用ディレクトリ ~/C/0411/ からホームディレクトリ ~/ へ移動 $ ./a.out # ここには a.out は無いはずなので... ./a.out: コマンドが見つかりません. # まぁ,当然の結果だよね $ C/0411/a.out # ディレクトリを指定して実行 Hello, world. $ cd C/0411/ # ディレクトリを移動して... $ ./a.out # ここにある a.out を実行 Hello World. こんにちは。
実行形式には a.out 以外の名前を自由に付けられる. たとえば,次のようにしてコンパイルすると, プログラム名は hello になる:
$ cc hello.c -o hello $ ls a.out hello hello.c $ ./hello Hello World. こんにちは。
注意せよ.プログラムファイルには,絶対に, ソースファイルと同じ拡張子「.c」を付けないこと. せっかく苦労して作ったソースファイルが上書きされ, 消えてしまうかもしれない.
プログラムファイルの拡張子の例:
また,他のプログラム(Unix コマンド等)と同じファイル名にならないようにしよう. どちらが実行されるのか?混乱の元だ. たとえば,ありがちなのは, test という名前のプログラムを作ってしまうことだ. 同名のコマンドが既に存在しているので,トラブることが多い.
足し算 x + y の計算機プログラムを例として, プログラムの開発プロセスの雰囲気を感じてみよう. 単純なプログラムから開始し, ソースコードを書き換えながら, 徐々に発展させて行き, (ちょっとだけ)高機能なプログラムを完成しよう.
とりあえず, ソースファイル名を add.c として, 計算式「1 + 2 = 3」の表示プログラムから始めよう:
main() { printf("1 + 2 = 3\n"); }
$ cc add.c -o add $ ./add 1 + 2 = 3
おお,計算機が出来た!?
いや,まだ出来ていない. 人間が計算してあげるのでは, コンピュータを使う意味が無いし... 1 + 2 の計算しかできないし...
コンピュータに計算させよう. ソースファイル add.c の内容を次のように改造:
main() { printf("1 + 2 = %d\n", 1 + 2); }
記号「%d」の部分は, 式「1 + 2」の計算結果の数値「3」に置き換えられる. コンパイル,実行し,確認しよう.
同じ定数「1」とか「2」を複数回書いてしまっていると, 今後の改造を妨げる(何より面倒だし,間違いにもなり易い)ので, 変数を利用し,1回だけにまとめよう:
main() { int x = 1; int y = 2; int a; a = x + y; printf("%d + %d = %d\n", x, y, a); }
機能は変わらないのに,コードが長くなり, かえって面倒にしてしまっただけだって? しかし,後で数値を変えたくなったときの手間を考えよう. このコードの方がさっきより良い(best ではないが better)だろう.
プログラムの実行中に,計算式の数値を自由に変えられるようにしよう:
main() { int x, y, a; printf("整数値 x, y > "); scanf("%d %d", &x, &y); a = x + y; printf("%d + %d = %d\n", x, y, a); }
ここで,scanf("%d", &x) は, キーボード入力の数値を変数 x に格納する.
$ ./add 整数値 x, y > 1 2 1 + 2 = 3 $ ./add 整数値 x, y > 3 4 3 + 4 = 7
これでやっと,計算機らしくなった.
一応,「動くプログラム」ではあるが, 「正しいプログラム」ではない. たとえば,整数値を入力すべきときに, 実数や文字を入力してみると, このプログラムの不具合が発覚する. 試してみよう.
なお,不具合修正については, 現段階では難しすぎるので,気にしないでおこう.
何度も連続して計算できるようにしよう:
main() { int x, y, a; while (1) { printf("整数値 x, y > "); scanf("%d %d", &x, &y); a = x + y; printf("%d + %d = %d\n", x, y, a); } }
ここで,while (1) { ... } は, ブロック { ... } 内のコードを無限に繰り返す.
$ ./add 整数値 x, y > 1 2 1 + 2 = 3 整数値 x, y > 2 3 2 + 3 = 5 整数値 x, y > 3 4 3 + 4 = 7 ...
このプログラムを止めるには,[Ctrl]+[C] キーで強制終了しよう.
動作を逆に,コンピュータに問題を作らせ,ユーザが回答するようにしよう:
main()
{
int x, y, a;
int z;
while (1) {
x = rand()%10; // 0~9の乱数
y = rand()%10;
printf("%d + %d = ?? > ", x, y); // 問題を表示
scanf("%d", &z); // 答案を入力
a = x + y;
if (z == a) { // 判定
printf("◎ 正解!!\n");
} else {
printf("× 不正解\n");
}
}
}
コマンドラインで数値を指定できるようにしてみよう. つまり,プログラムの実行途中ではなく, 実行開始時に数値を指定できるようにする. なお,この改造では,ちょっと戻って,Step 3 のソースコードを元にする:
main(int argc, char *argv[]) { int x, y; int a; x = atoi(argv[1]); y = atoi(argv[2]); a = x + y; printf("%d + %d = %d\n", x, y, a); }
コマンドラインに指定されたデータは, 変数 argv[番号] にセットされる. ただし,そのデータは文字列(数字列)なので, 関数 atoi( ) で数値に変換した.
$ ./add 5 6 5 + 6 = 11 $ ./add 7 8 7 + 8 = 15
これは要するに,Unix コマンドを自分で作ったということか!?
実は,List 1 等は「動くプログラム」にしか過ぎない. 「正しいプログラム」としては,次のように書くべき:
#include <stdio.h> int main() { printf("Hello, world.\n"); ... return (0); }
しかし,この授業では,しばらくの間, List 1 のように省略しておく. くわしくは後日,説明予定.