端末は, プログラムを実行したり, コンパイルをしたりするなどして, コマンドを実行し結果を表示する機能を提供する. xterm kterm, gnome-terminal (GNOME端末), konsole などの X Window System 上で使用できる端末がある.
GNOME端末は, GNOMEデスクトップ上で右クリックして, 「端末を開く」ことで実行できる.
端末は入力や表示の機能を持ち, 入力されたコマンド(文字列)はシェル[23.4.1]と呼ばれる別のプログラムが解釈して, プログラムの実行を制御する.
プログラムで日本語の入出力を行う場合, 端末の文字コードをプログラムが入出力に用いる文字コード(EUC-JP)に合わせる必要がある.
GNOME端末では, メニューバー:「端末」「文字コードの設定」から, 「EUC-JP」を選ぶ.
konsoleでは, 予め konsole を起動して encoding を eucjp にし, その設定をdefault としてsaveしておく.
例えば次のようなプログラムを書くことができる.
#include <stdio.h> int main(void) { char str[] = "漢字文字列"; char *ptr = "漢字ポインター"; /* 漢字のコメント */ printf("%s\n", str); printf("%s\n", ptr); printf("%s\n", "漢字ポインター"); printf("漢字\n"); return 0; }
コマンド | 機能 | 本来の機能 | 詳細 | 例 |
ls | ファイルリストの表示 | [24.2.1] | ls |
|
mv | ファイル名の変更 | ファイルの移動 | [24.2.2] | mv from.c to.c |
cp | ファイルのコピー | [24.2.3] | cp from.c to.c |
|
cat | ファイル内容の表示 | ファイルの結合 | [24.2.4] | cat file.c |
コマンド名とファイル名や ファイル名とファイル名の間にはスペースが必要 であることに注意. 完全な説明はマニュアル[24.1.1]を参照のこと.
先頭に「.」(ドット)のつくファイル名を持つファイルは, 通常のファイルと は区別して, 各種設定用のファイルとして用いられる. 「.login」,「.emacs」 等がその例である. また, 「.」で始まる名前のディレクトリーも同じ目的で 利用される.
ls コマンドをそのまま用いた場合(ls
),「.」で始まるファイル名やディ
レクトリー名は,表示されない. -a
オプションを与えると表示される
ようになる(ls -l
) [24.2.1].
ディレクトリとファイルの構造を視覚的に表すと 例えば次のようになってい る.
--+-bin | +-usr--+-include--+--stdio.h | | | | | +--math.h | | | | | +--stdlib.h | | | | | | +-lib | | | +-local | | | | +-home-+-user1-+-file1 | | | | +-file2 | | | +-dir---+-file3 | | | | | +-file4 | | | +-dir2--+-file5 | | | +-file6 | | +-user2 | ... +-usern |
一番上のディレクトリの中には, bin, usr, home等のディレクトリがあ る. 更に bin の中には include, lib, local 等のディレクトリがあり, そ の中に幾つかのファイルやディレクトリがある. 各ユーザーのためのディレ クトリ (ホームディレクトリ) は home 以下に置かれることが多い.
/
で区切って表現する.
/
で表し, そこからの見てどこにあるかを指定す
る. 例えば次のようになる.
ディレクトリ dir |
/home/user1/dir |
ファイル file1 |
/home/user1/file1 |
ファイル file3 |
/home/user1/dir/file1 |
/
の代わりに現在居るディレクトリ (カレントディレクトリ)
を 「.」 (ドット1つ) で表す. そして, カレントディレクトリからの相
対的な場所を指定する. 例えば, カレントディレクトリが
/home/user1
だとすると, 次のようになる.
ディレクトリ usr1 |
./ |
ディレクトリ dir |
./dir |
ファイル file1 |
./file1 |
ファイル file3 |
./dir/file1 |
dir
, file1
, dir/file1
となる.
一つ上のディレクトリ (親ディレクトリ) は 「..」 (ドット2つ) で表す.
例えば, カレントディレクトリが/home/user1/dir
だとすると, 次の
ようになる.
ディレクトリ user1 |
.. |
ディレクトリ dir2 |
../dir2 |
ファイル file1 |
../file1 |
ファイル file5 |
../dir2/file5 |
主要なディレクトリ操作のためのコマンド.
コマンド名 | 機能 | 詳細 | 例 |
cd | 現在のディレクトリの変更 | [24.3.1] | cd dirname |
pwd | 現在のディレクトリの表示 | [24.3.2] | pwd |
mkdir | ディレクトリの作成 | [24.3.3] | mkdir dirname |
rmdir | ディレクトリの削除 | [24.3.4] | rmdir dirname |
/
」で表す.
一番上のディレクトリ(ルートディレクトリ)に移動するには,
$ cd /
..
」 で表す.
一つ上のディレクトリーに移動するには,
$ cd ..
.
」 で表す.
/
」から見た 絶対的なファイル名やディレクトリー名は,
一番前に「/
」 をつける.
シェルは, 端末からコマンドの入力などを行ない, それに応じてプログラムの実行の制御を行う. シェルを実現するプログラムとしては, sh, bash, csh, tcsh, ... 等がある. csh や tcsh のような C シェルには良くない点があるので, Borne シェルである sh や bash を覚えた方がいい26.2.
更にシェルには, コマンド行編集や環境変数の設定の機能がある. コマンドラインの入力行の編集を行なうために便利な機能が用意されている場合がある. 実際のキー操作はシェルに完全に依存する. 例えば次のような機能がある.
tcsh が実行されている時に bash へ変更するには.
tcsh % exec bash bash $又は, (EDITOR変更の後)
$ chshを実行する.
特殊な端末からの入力には次のものがある. それぞれがどのキー入力に割り当 てられているかは設定に依存する. 現在の設定は
$ stty -aにより知ることができる.
stty all
の場合もある. 例えば, 端末か
ら「入力の終了」を入力するには, stty コマンドにより 「eof =^D
」
と得られたら 「Control + D」 を押す.
使用例:
$ cat > filename.dat
により端末から filename.dat に入力を
行なえる. 入力を終了するときは「Control + D」 を押す.
UNIX では, Standard Input (stdin), Standasrd Output (stdout), Standard Error (stderr) という仮想的なファイルが自動的にオープンされている. そ れぞれ 0, 1, 2という番号(ファイルディスクリプタ)で参照される. 特に指定 を行なっていないときには, stdin は端末からの入力, stdout と stderr は 端末への出力に対応している.
stdout
に出力される内容は >
に続けてファイル名を指定する
ことで, そのファイルに出力される. このような出力先を変更することをリダ
イレクト という. 例えば,
$ ls > filenamelistとすることで
filenamelist
というファイルに ls
コマンドの
実行結果が出力される. filenamelist
の中に何が出力されたかは
cat
[24.2.4]コマンド等で調べられる.
出力結果を追加していくには >
の代わりに >>
を用いる.
$ ls >> filenamelist何度もこれを実行すると, ファイルに追加されていっている様子が分かる.
stdout と stderr を比較すると, stdout は正常なデータの出力先であり,
stderr はメッセージの出力先である. この違いの次の例で見てみよう. (下図
参照.) ls コマンドには, -l オプションというのがあり, これを指定すると
ファイルの詳細な情報を合わせて出力する. 特に zzz
というファイル
の情報を知りたければ,
$ ls -l zzzと実行する. しかし, zzz という名前のファイルが存在しない場合には
ls: zzz: No such file or directoryとメッセージが表示される. この
ls -l zzz
の例では, zzz というファ
イルは存在しなときには, 正常なデータとしては何も出力すべきではな
い. `nothing' を出力すべきである. これが stdout に出力される内容である.
しかし, コマンドの使い方やファイル名の指定の仕方が間違っている可能性が
あるので何らかのメッセージを出しておいた方がいい. しかし, それは
ls
コマンドで期待されている データではない. そのようなメッセー
ジを stderr に出力する.
zzz というファイルが存在しない場合には,
$ ls -l zzz > filenamelistと stdout の出力先を filenamelist にリダイレクトしても, 相変わらず端末 には
ls: zzz: No such file or directoryと出力される. また filenamelist の中身は何もない.
$ ls +-----+ stdout "aaa.c bbb.c ccc.c" | +----------------------+ | ls | | | | | | +-------------------+ | +-----+ stderr | | | | V V TERMINAL $ ls > filenamelist +-----+ stdout "aaa.c bbb.c ccc.c" | +-----------------------------+ | ls | | | | | | +-------------------+ | +-----+ stderr | | | | V V TERMINAL finenamelist $ ls -l zzz +-----+ stdout "" | +----------------------+ | ls | | | |"ls: zzz: No ..." | | +-------------------+ | +-----+ stderr | | | | V V TERMINAL $ ls -l zzz > filenamelist +-----+ stdout "" | +-----------------------------+ | ls | | | |"ls: zzz: No such ..." | | +-------------------+ | +-----+ stderr | | | | V V TERMINAL finenamelist
stdin はプログラムに対する入力に用いられ, stdout と同様に入力元を端末
からファイルにリダイレクトすることができる.
先ほど ls > filenamelist
により作成したファイル名のリストを
wc
の入力にしてみる. (下図参照のこと.) wc
は, 標準入力か
ら入力されたファイルの文字数, 単語数, 行数を数えるプログラムである
[24.2.12]. -l
オプションを与えると行数のみを標準出力に出
力する.
$ wc -l < filenamelist矢印の向きが先程とは逆であることに注意する必要がある. この例では
filenamelist
に入っているファイル名のリストを wc
に与えて
おり, その行数 (即ちファイルの数) を表示している.
$ wc -l +-----+ stdout "5" "aaa bbb ccc | +----------------------+ ddd ... " | wc | | +--------------+ | | | stdin | +-------------------+ | | +-----+ stderr | | ^ | | | V V TERMINAL TERMINAL $ wc -l < filenamelist +-----+ stdout "15" "aaa.c bbb.c | +----------------------+ ccc.c ..." | wc | | +---------+ | | | stdin | +-------------------+ | | +-----+ stderr | | ^ | | | V V filenamelist TERMINAL
wc
の出力は先程と同様にファイルに出力するようにリダイレクトでき
る.
$ wc -l < filenamelist > filenumber +-----+ stdout "15" "aaa.c bbb.c | +-----------------------------+ ccc.c ..." | wc | | +---------+ | | | stdin | +-------------------+ | | +-----+ stderr | | ^ | | | V V filenamelist TERMINAL filenumber
このような場合に, ws
が エラーメッセージを stderr に出力すると
どうなるであろうか ? 先程と同様に 端末にメッセージが出力される. 例えば,
誤ったオプションを wc
に指定してみる.
$ wc -X < list > filenumber wc: illegal option -- X usage: wc [-clw] [file ...]stderr は端末に出力するままになっているので, 画面にメッセージが出力さ れ, ファイル
filenumber
には何も出力されていない.
stderr の出力先も stdout とは独立の別のファイルにリダイレクトすること ができるが, その指定の仕方はシェルに依存する [23.4.1].
$ wc -X < filenamelist > filenumber +-----+ stdout "" "aaa.c bbb.c | +-----------------------------+ ccc.c ..." | wc | | +---------+ | "wc: illegal option ..." | | stdin | +-------------------+ | | +-----+ stderr | | ^ | | | V V filenamelist TERMINAL filenumber
ここまでの例では, ls
の結果をまずファイル filenamelist
に保存し, それを wc -l
に渡した.
$ ls > filenamelist $ wc -l < filenamelist +-----+ +-----+ | | stdout stdin | | stdout "15" | ls +---------+ +------+ wc +------------+ | | | | | | | | +--+ | | | +---------+ | +-----+ | | | +-----+ stderr | | stderr | | ^ | | V V | V V TERMINAL filenamelist TERMINAL "aaa.c bbb.c ccc.c. ... "
このように一旦ファイルに保存しそれをそのまま別のプログラムの入力として
使う場合には, ファイルを介さず直接受け渡した方が効率がいい. それにはパ
イプ |
を使う. 下のように, 二つのプログラムを |
を挟んで
起動すると, 前のプログラムの標準出力が後ろのプログラムの照準入力に接続
される. ファイルを入力や出力に使うときは <
や >
を用い,
プログラムを入力や出力に用いるときには |
を使うところが, ファイ
ルのリダイレクトとは異なる.
$ ls | wc -l "aaa.c bbb.c ccc.c ..." +-----+ +-----+ | | stdout stdin | | stdout "15" | ls +->------------>+ wc +------------+ | | | | | | +--+ | +---------+ | +-----+ | +-----+ stderr | | stderr | | | V V V TERMINAL TERMINAL
この結果をファイルに出力するには, 同様に >
を用いて
リダイレクトする.
$ ls | wc -l > filenumber "aaa.c bbb.c ccc.c ..." +-----+ +-----+ | | stdout stdin | | stdout "15" | ls +->------------>+ wc +-------------------+ | | | | | | +--+ | +---------+ | +-----+ | +-----+ stderr | | stderr | | | V V V TERMINAL TERMINAL filenumber
また, wc -l
の標準出力の更に別のプログラムの標準入力に接続する
こともできる. ここで, awk
は出力を整形するプログラムの一つであ
る.
$ ls | wc -l | awk '{print $1, "files are found." }' "aaa.c bbb.c ccc.c ..." "15" "15 files ..." +-----+ +-----+ +-----+ | | stdout stdin | | stdout stdin | | stdout | ls +->------------>+ wc +->------------>+ awk +-----+ | | | | | | | | +--+ | +---+ | +--+ | +-----+ | +-----+ | +-----+ | | stderr | stderr | stderr | | V V V V TERMINAL TERMINAL TERMINAL
端末の設定によるが
^D |
入力の終了. |
^S |
出力の中断. |
^Q |
出力の再開. |
^C |
プロセスの中止. フォアグランドで実行中のプロセス(プログラム)を中止する. |
^Z |
プロセスの中断. |
シェルに依存するが,
& |
コマンドラインで指定してバックグラウンドで実行. |
fg | フォアグラウンドで実行させる. |
bg | バックグラウンドで実行させる. |
コマンド 又はシェルの内部コマンドとして
kill | プロセスにシグナルを送る. |
ps | プロセスの表示 |
例:
$ kill PID $ kill -15 PID $ kill -9 PID
ここで PID
は ps コマンド等で得られるプロセス ID.
実行中のプログラムを中止するには,
$ psによりプロセス(プログラム)のプロセスID (PID) を調べ, 例えばPIDが1234であったとしたら
$ kill -9 1234のようにシグナルを送るとこで中止できる. 中止されるたかどうかは, 再度 ps コマンドを実行することで確かめられる. 「-9」(KILL)では中止できない場合には, さらに「-15」(TERMinate)を試みる.
$ kill -15 1234
killの詳細は,
$ kill -lや
$ man killで調べられる.
以上で述べたプロセス制御の主要な部分はコマンドラインから gnome-system-monitor を実行することでGUIで操作できる.
$ gnome-system-monitorまた, gnome-system-monitorはメニューやアプレットに登録されていて, そこから起動できる場合もある.
シェルやプログラム内に留まらずそこから起動されるシェルやプログラムで参 照できる変数を環境変数といいます.
set コマンドを引数を与えずに実行すると, 全ての変数の値が設定が表示され ます.
$ set
新たに変数に代入を行なうには次のように = を用います.
$ LANG=ja_JP.ujis $ LESSCHARSET=japanese $ TERM=xterm $ PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
変数の内容を参照するには ``$
'' を用います.
$ echo ${PATH}
このことを用いれば, 既に設定されている値に追加することもできます.
$ PATH=${PATH}:/usr/local/samba/bin
シェル変数をそこから動されるシェルやプルグラムで参照できるようにするた
めには export
文により宣言を行ないます. export
に多くの
変数を渡す時はスペースで区切ります.
$ export LANG $ export LESSCHARSET $ export TERM PATH
[n]< file
ファイルからの入力. n はファイル記述子で ディフォ
ルトは 0.
[n]> file
ファイルからの出力. n はファイル記述子で ディフォ
ルトは 1.
$ ls a* b* 2> /dev/null # 1 (stdout) を file1 に, 2 (stderr) を file2 に. $ command > file1 2> file2
[n]>> file
ファイルへの追加書き込み.
[n1]>&[n2]
複数のファイル記述子を1つのファイルにリダイレ
クトするのに「[n1]>&[n2]
」を用いる. これにより, 既にオープン
されているファイルへの出力を行なえる. n1, n2 は ファイル記述子で,
n1 のディフォルトは1. n2 を「-」にすると, n1 がクローズされる. n1 を
「n2に結合されているファイル」 に結び付ける. n2 は既にオープンされ
ているファイル記述子である必要がある.
# stdout と stderr が file に出力される. $ command > file 2>&1 次のは 期待通りにはいかない. $ command 2>&1 > file # 次のはファイルが2度オープンされるてよくない. $ command > file 2> file # stderr にメッセージを出す. $ 1>&2 echo aaa # stdout と stderr をパイプする. $ command1 2>&1 | command2
[n1]<&n2
n1 のディフォルトは0.
See. dup(2), fcntl(2).
$?
最後に実行したコマンドの終了状態を保持する.
$ ./a.out $ echo $?
[20.7.2] 参照のこと.
xterm ではマウスの操作は次のようになっている.
左ボタン | 選択範囲の始点. 押したまま移動することで範囲の指定. |
真ん中ボタン (又は 左右の2つのボタンを同時に押す.) | 選択内容のペースト. |
右ボタン | 選択範囲の終点. |
詳細は マニュアルを参照のこと [man xterm].
emacs, xterm 等の X Window System 環境下で動作するプログラムは, 起
動時にウィンドウの大きさを指定することができます. それには
-geometry
オプションを使います. 例えば emacs を
の大きさで起動し, aaa.c というファイルを編集しようとするときには,
$ emacs -geometry 80x25 aaa.cとする.
同様にして, X Window System 環境下で動作するプログラムに共通して, 次の ような様々なオプションが利用できます.
オプション | 変更できる内容 | 例 |
-fn | フォント | xterm -fn 10x20 |
-fg | フォアグランド カラー | emacs -fg red aaa.c |
-bg | バックグラウンドカラー | emacs -bg gray aaa.c |
これらを組み合わせて
$ emacs -fg yellow -bg black aaa.cのようにも使える.
詳細は マニュアルを参照のこと [man X とりわけ OPTIONS のセクション].
cc は C 言語のコンパイラーである. ここでは, GNU の C コンパイラーである gcc を使うことを想定している. これが cc という名前になっている場合もある.
書式: cc [幾つかの [オプション] または [ファイル名] ]
ここでファイル名は, コンパイルする対象となるファイル名で,
特に指定しない場合は C のプログラムは最後が「.c
」で終わっている必要がある. プログラムを複数に分割している場合には複数のファイル名を与える場合もある. 次に挙げるようなオプションにより, コンパイルの仕方を調整することができる. オプションや与えるファイル名はその順序が重要であるので注意すること. 詳細は マニュアルや info を参照のこと.
$ cc aaa.c -lmとする.
a.out
というファイル名になっている.
このファイル名を指定するときに -o
オプションを用いる.
例えば, abc.c
というプログラムから xyz
という実行ファイルを作成するには
$ cc abc.c -o xyzとする.
$ gcc -Wall aaa.cのように指定して aaa.c をコンパイルすると, 非常に詳細にプログラムの誤りを報告してくれる[23.6.3]. プログラムに何らかの問題があることを報告しているので, このメッセージが全く出なくなるまでプログラムを修正するようにする.
実際のコンパイルが行なわれる前に, プリ プロセッサーに前処理を行なわせることができる. この処理は 「#include
」や「#define
」のように「#
」で始まる命令に対して行なわれる. ただし通常この処理は自動的に行なわれているので特に意識する必要はない. ここではプリプロセッサーに対する命令について概観する.
<
と>
で囲んで指定されたファイルは /usr/include
のようなシステムの標準的な場所に置かれていることを想定している.
#include <stdio.h>ユーザーが用意したヘッダーファイルは次のように
"
と "
で囲んで指定する.
#include "aaa.h"従ってユーザーの領域に作成されていることが想定されている.
""
で囲むことでヘッダーファイルの検索順序が制御できるのである.
#include <stdio.h> #define NUMBER 30 int main(void) { printf("%d\n", NUMBER); return 0; }
行の継続には \
を用いる.
様々な副作用があるので注意する必要がある.
#include <stdio.h> #define NUMBER 3+4 int main(void) { printf("%d\n", NUMBER * 5); return 0; }ここでは, 「3+4」 の結果に 「5」 を掛けた 「35」 を期待するが, 実際にはまず第一に置き換えが行なわれその後計算が行なわれるので「3+4 * 5」すなわち「23」が表示されることになる. このようなことを防ぐためには「(」と「)」で括っておく.
#include <stdio.h> #define NUMBER (3+4) int main(void) { printf("%d\n", NUMBER * 5); return 0; }
他にも副作用はあるがここでは省略する.
単純な数値の定義だけなら 次のように 型が確定していて デバッグが行ないやすい変数を用いる方がよい.
#include <stdio.h> int NUMBER = 3 + 4; int main(void) { printf("%d\n", NUMBER); return 0; }NUMBER の値が変更されないことが必要であるなら更に
const
を付けて
#include <stdio.h> const int NUMBER=30; int main(void) { printf("%d\n", NUMBER); return 0; }のようにすればよい.
#if 1 NEW CODE HERE. #else OLD CODE HERE. #endif
printf
関数により変数の値を出力させるようにしておくのも有用である.
$ cc -Wall aaa.c
例えば次のような内容のプログラムをコンパイルしてみる.
#include <stdio.h> int main(void) { int a, c; a = 3 b = 4; c = add(a, b); printf("a + b =%g\n", c); }この場合次のようなメッセージが出力される.
$ cc -Wall aaa.c aaa.c: In function `main': aaa.c:8: parse error before `b' aaa.c:9: warning: implicit declaration of function `add' aaa.c:9: `b' undeclared (first use this function) aaa.c:9: (Each undeclared identifier is reported only once aaa.c:9: for each function it appears in.) aaa.c:10: warning: double format, different type arg (arg 2) aaa.c:11: warning: control reaches end of non-void function
aaa.c:8:
のように出力されている部分の 8
がメッセージの対象
となっている行番号を表している.
「aaa.c:8: parse error before `b'
」 'b'
の前で構文解析ができなくなっている. この場合, その前の int a
の後ろに ;
が無い.
「aaa.c:9: warning: implicit declaration of function `add'
」
add
関数が陰に定義されている. 逆に陽に定義する必要がある.
呼び出しが行なわれるより前で関数の定義を記述するか, 型宣言だけを行なっておく.
「aaa.c:9: `b' undeclared (first use this function)
」
'b'
が定義されていない. 関数の前の方で int b;
のように型宣言をしておく.
「aaa.c:10: warning: double format, different type arg (arg 2)
」
出力 format
中では double
を出力するよう指定しているが,
整数である c
を渡している. 「arg 2
」の部分が printf に対する 2 番目の引数が誤っていることを示している.
「aaa.c:11: warning: control reaches end of non-void function
」
void 型でない関数の最後まで辿り着いてしまった. void 型でない関数は値を返すはずであるが, 値を返す return 文が関数の最後まで現れなかった.
この場合, return 0;
を最後に書いておく.
tee
gcc [23.6.1] はEUCで書かれた日本語文字は正しく評価できる. C言語のプログラム中で日本語のコメントを書きたいときや, 日本語の文字を出力したいときにはプログラムを EUC で書けばよい.
一旦作成した日本語を含むファイルの文字コードは, nkf [24.5.2] により変換することができる.
さらに, プログラムを実行し結果を表示する端末の文字コードを, プログラムに合わせてEUCにする必要がある. 各端末の設定方法は, 端末[23.2]を参照のこと.
プログラムの誤りを見つけるには, プログラムを 1 行ずつ実行したり, その時の変数の値を調べたり変更したりできるといい. このようなことを行なうのが「デバッガー」である.
gccによりコンパイルしたプログラムには gdb というデバッガーを用いることができる. この場合は, コンパイル時に, gdb でデバッグするためオプション -g をつけおく. 例えば, program.c というファイルに書かれたプログラムををデバッグする場合には, コンパイルする時に
$ cc -g program.cとする.
gdb を直接使ってもいいが, emacs, kdevelop, ddd, xxgdb 等のユーザーインターフェースを介して利用した方が使いやすい.
printf
関数や sin
関数のように
自分で関数の実体を書かずに予め用意されている関数を利用するときがある.
これらの関数は予めコンパイルを行ない, グループごとにライブラリーと呼ば
れる1つのファイルにまとめて保管されている. また, 自分でこのようなライ
ブラリーを作成することもできる. ライブラリーに納められた関数のうち,
printf
関数のように標準的に用いられる関数では特に必要ないが,
sin
関数のようにそうでない関数を用いるときにはそのことをコンパ
イラーに指示する必要がある.
例えば, 等の数学関数を計算するためのプログラムはlibmというライブラリーの中に集められている.
一般に, ライブラリーファイルは libxyz のように lib で始まる名前が付けられている.
この中の関数を使うには xyz でこのライブラリーを指定し -l オプションで - lxyz のように C コンパイラーに伝える.
$ cc aaa.c -lxyz
sin
関数が aaa.c というプログラムの中で使用されているときには, -lm を指定して次のようになる.
$ cc aaa.c -lm
順序が重要で,
$ cc -lm aaa.cはうまくいかない.
コンパイラーは 前から順に必要な関数をリストアップし,
実行ファイルに結合(リンク)していくからである.
最初に示したように cc aaa.c -lm
とすると,
まず aaa.c 内で呼び出されている関数のうち aaa.c の中に無い関数がリストアップされる.
リストアップされた関数が -lm
で指定された libm 内から検索され,
見つかったら出力する実行ファイルに結合される.
それでも未解決の関数は, 標準的なライブラリーの中から検索され, 結合される.
printf
のような標準関数が自動的に結合されることになる.
それでも解決できない関数がある場合は, リンクを失敗することになる.
逆に cc -lm aaa.c
とした場合は,
aaa.c で必要な関数はそれ以降で指定されたライブラリー内から検索されるが,
この場合は何もしていされていないため必要な関数が見つからないというエラーになる.
数学関数は, 標準関数であり, 標準的なライブラリーの中に含まれていても良さそうであるが, 同じsinでもその求め方が色々とありえるので, ユーザーが指定できるようになっている. 数学関数は, 計算機で値を求めるときには有理式を計算して近似的に求めることになるが, どの有理式を採用するかで精度や速度が異なる. また, ハードウェアで数値演算を行うプロセッサを用いて求める場合もあれば, ソフトウェアで有理式を計算して求める場合もある.
更に, 標準的でないライブラリーが標準的でないディレクトリーにある場合には
-L
オプションで指定する.
例えば, libX11 というファイル名のライブラリーが /usr/X11R6/lib 内にあるときには
$ cc aaa.c -L/usr/X11R6/lib -lX11 -lmのようになる.
実行ファイルは, 例えば次のような中間ファイル結合して作成される. これら
の作業は, 指定しない場合 cc
コマンドが自動的に行なっている.
aaa.c --(cc -c aaa.c)--> aaa.o ----------------------+ | bbb.c --(cc -c bbb.c)--> bbb.o ----------------------+ | xxx.c --(cc -c xxx.c)--> xxx.o --+ +--(ld)--> a.out | | yyy.c --(cc -c yyy.c)--> yyy.o --+--(ar)--> libx.a --+ | zzz.c --(cc -c zzz.c)--> zzz.o --+
cc aaa.c
は内部では, まず
aaa.c
を aaa.o
という計算機で直接実行できる形式に変換
する. aaa.o
のようなファイルが「オブジェクトファイル」.
-c
オプションをつけた cc
コマンドで行なう.
printf
関数の実体や sin
関数の実体
などがある. ar
コマンドで行なう.
ld
コマンド
で行なう.
$ cc aaa.c bbb.c ccc.c
aaa.o
, bbb.o
,
ccc.o
が生成される.
$ cc -c aaa.c $ cc -c bbb.c $ cc -c ccc.c
cc
コマンドに必要な全てのオブジェクトファイルを
指定すればいい.
$ cc aaa.o bbb.o ccc.o
aaa.c
だけを変更した場合には, オブジェクトファイルを生成
する過程のうち aaa.o
の作成だけを行なえばよいので, 大きなプロ
グラムの場合にはコンパイル時間が短縮される. すなわち,
$ cc -c aaa.c $ cc aaa.o bbb.o ccc.o
$ cc aaa.c bbb.o ccc.o
$ cc aaa.o bbb.o ccc.o -lm
$ cc aaa.o bbb.o ccc.o libx.a
コマンドの前の空白はスペースではなく, TAB を用いることに注意.
有用なマクロ:
マクロ | 意味 |
$@ |
ターゲット. |
$< |
依存リストの一番前のファイル. |
$^ |
依存リストの全てのファイル. |
例 1:
a.out : aaa.o bbb.o ccc.o ${CC} -g aaa.o bbb.o ccc.o -lm aaa.o : aaa.c aaa.h cc -c -g -Wall aaa.c bbb.o : bbb.c bbb.h cc -c -g -Wall bbb.c ccc.o : ccc.c ccc.h aaa.h cc -c -g -Wall ccc.c
例 2:
TARGET=aaa OBJS=aaa.o bbb.o ccc.o CC = gcc CFLAGS = -Wall DEBUGFLAGS = -g ${TARGET} : ${OBJS} ${CC} ${DEBUGFLAGS} $^ ${LDFLAGS} -o $@ clean : rm -f *.o *~ ${TARGET} core tags : etags `pwd`/* aaa.o : aaa.c aaa.h bbb.o : bbb.c bbb.h ccc.o : ccc.c ccc.h aaa.h .c.o: ${CC} -c ${DEBUGFLAGS} ${CFLAGS} $< -o $@
実行の例. 表記法 [1.2] のセクションを参照のこと.
$ emacs hello.c
hello.c の内容. emacs の画面で例えば次のようなプログラムを入力する.
#include <stdio.h> int main(void) { printf("Hello."); return 0; }ファイルに保存したのち, emacs を終了する.
$ cc hello.cこれにより, a.out という実行可能なファイルが作成される. このようにテキストで書かれたプログラムをコンピュータが実際に実行できる形式に変換することをコンパイルという.
$ ./a.outのように 「./」 を前につけて実行する. 「./」はカレントディレクトリを表す. 「Hello.」と表示されるはずである.
printf("Hello.");
の中の
"Hello."
を色々と変えてみると表示される結果が変わる.
慣れてきたら,
$ emacs hello.c &emacs がバックグラウンドジョブとして実行され, emacs 実行中も端末を使用することができる. これにより, emacs を終了することなく
$ cc hello.cなどの他のコマンドを実行することができる. ただし, コンパイルを行う前にファイルの保存を行う必要がある.
gcc
によるコンパイル時には -Wall
オプションをつける.
これにより, プログラムの誤りが詳細に出力される. 例えば,
$ cc -Wall hello.cより詳細な説明が[23.6.3]にあるので参照のこと.
プログラム中のコメントや, プログラムの入出力に日本語を用いためには, emacs [24.4.1]でプログラムファイルを保存する際, 文字コードとして euc-jp を選択する[24.4.2].
さらに, 入出力に日本語を用いるためには, 端末の入出力の文字コードをプログラムに合わせてEUCにする[23.2].
例えば, add.c
というファイルにプログラムを書いて, それをデバッ
グするには次のようにする. add.c
の中身は次のようになっていると
する.
#include <stdio.h> int add(int x, int y) { int z; z = x + y; return z; } int main(void) { int a, b, c; a = 3; b = 4; c = add(a, b); printf("a + b =%d\n", c); return 0; }
-g
オプションをつけて
$ cc -g add.cとする.
-o
オプションにより
指定した場合にはその名前を入力する.
(gdb)
と表示されて入力待ちになり, デバッグする準備
ができた.
(gdb)
と入力待ちになっているところで, run
と
入力し 「Enter」 を押すとプログラムが実行される.
(gdb) runこの場合途中で止まること無くプログラムが終了するまで実行される.
break
を用いる.
(gdb) break mainとすることで
main
関数に入った所で実行が停止する. 現在実行さ
れている行は, emacs上では, 「=>
」 により表示され
ている.
break
で停止した後に, プログラムの実行を再開す
るのに next
を用いることができる. next
では, 1 行だけ
プログラムを実行し, 再び実行を停止する.
(gdb) nextここで用いている例では,
main
関数の内部を1行ずつ実行すること
になる. add
関数の内部の行を1行ずつ実行することはない.
next
の代
りに step
を用いる. next
により main
関数内の
c=add(a,b);
の行まで実行する. その後, step を実行すると
add
関数の内部で実行が停止する.
(gdb) step
(gdb) continue
(gdb) kill
(gdb) quit
プログラムを停止している状態でその時の変数の値を調べることができる.
a
の値を表示させるには,
(gdb) print aこの時,
a
という変数が使える状態になっていなければならない.
(gdb) display aこの時,
a
という変数が使える状態になっていなければならない.
毎回 next
のようなコマンドを入力するのは面倒なので,
省略する方法が用意されている.
next
の場合 その省略形が n
なのでn
だけ入力して 「Enter」 を押せば十分である.
更に, 「Enter」だけを入力すると直前に行なったことここでは next
をもう一度実行することになる.
結局次々に1行ずつ実行していくには, 初めに next
を入力したらその後は「Enter」を何度も押せばいいことになる.
一行ずつ実行して目的の行に辿り着くのが面倒な時には break
が便利である.
break
は停止する場所を指定することができる.
停止する場所(ブレークポイント)を設定しておいて run
や continue
を実行すると, 設定場所に辿り着くまでは連続してプログラムが実行される.
次のような設定法がある.
break
には引数として関数名を与えることができる.
次の例ではadd
関数に入ったところでプログラムの実行が停止する.
(gdb) break add
(gdb) break 15
一旦設定したブレークポイントは, clear
により解除できる.
次の例では, 関数 add
に設定してあった ブレークポイントを解除する.
(gdb) clear add
frame
を実行する表示される.
上記のgdbコマンドは, emacs 中でgdbを起動している場合には, emacs のメニューバーの「gud」から実行することができる.
また, 次の内容をユーザーのホームディレクトリの .emacs
に書いておくと ファンクションキーだけで基本的な操作ができるようになる.
また, (set-buffer-process-coding-system 'euc-japan 'euc-japan)
により, gdb 実行中の日本語の入出力をEUCコードで行うように設定している.
(add-hook 'gdb-mode-hook '(lambda () (set-buffer-process-coding-system 'euc-japan 'euc-japan) (gud-def gud-break-main "break main" nil "Set breakpoint at main.") (gud-def gud-run "run" nil "run.") (gud-def gud-display "display %e" nil "display C expression at point.") ; kye bindings ... (global-set-key [f2] 'gud-break) (global-set-key [f3] 'gud-remove) (global-set-key [f4] 'gud-run) (global-set-key [f5] 'gud-cont) (global-set-key [f6] 'gud-finish) (global-set-key [f7] 'gud-display) (global-set-key [f8] 'gud-print) (global-set-key [f9] 'gud-next) (global-set-key [f10] 'gud-step) (setq mode-line-format "2:break 3:clear 4:run 5:continue 6:finish 7:display 8:print 9:next 10:step") ; initialization (gud-break-main nil) (gud-run nil)))
ただし, emacs のバージョンによっては
; (gud-break-main nil) ; (gud-run nil)))のようにして最後の2行をコメントにして実行しないようにしておく必要がある.
上の設定を行なった場合, デバッグの手順は次のようになる.
eclipse は様々なコンピュータ言語のプログラムを作成するための環境で, C言語を用いるために別途CDTが導入されている. また, コンパイルを行うために別 途コンパイラーが必要で, GNU のCコンパイラー gcc が導入されている.
$ eclipse
<なし>
」を選択する.
CはC++ とは異なるので注意すること.
プログラムファイルの編集.
#include <stdio.h> int main(void) { printf("Hello"); return 0; }
書き込んだのちは, 忘れずに保存すること.
プログラムを作成したら, コンピュータが解釈できる形式に変換(コンパイル)し, 実行させる. プログラム中に誤りが含まれている場合には, コンパイルできずソー スを修正する.
$ kdevelop
または, アプリケーションのメニューからKDevelop を起動する.
「 プロジェクト」「新規プロジェクト」により
「新規プロジェクト」 の「一般」タブを開き次のように設定し,
「次」のダイアログに移動する.
ここで, アプリケーション名が最終的な実行ファイルの名前になる. プログラムファイル(ソースファイル)や実行ファイルは, 場所で指定されるディレクトリ(フォルダ)内に置かれる. 場所は, ユーザが読み書き可能な権限を与えられているディレクトリを指定する必要がある.
KDevelopはコンパイラーとして gcc を用いるよう設定されている. この場合, プログラム自体やプログラムの入出力に EUC コードの漢字を用いることができる.
ただし, 以上の設定では, プログラム終了後の「何かのキーを押してください.」の表示が正しく行われない.
(c)1999-2013 Tetsuya Makimura