Subsections


23章 UNIX 環境

23.1 ログイン ログアウト

login, logout, shutdown


23.2 端末

端末は, プログラムを実行したり, コンパイルをしたりするなどして, コマンドを実行し結果を表示する機能を提供する. xterm kterm, gnome-terminal (GNOME端末), konsole などの X Window System 上で使用できる端末がある.

GNOME端末は, GNOMEデスクトップ上で右クリックして, 「端末を開く」ことで実行できる.

端末は入力や表示の機能を持ち, 入力されたコマンド(文字列)はシェル[23.4.1]と呼ばれる別のプログラムが解釈して, プログラムの実行を制御する.

23.2.1 日本語の入出力

プログラムで日本語の入出力を行う場合, 端末の文字コードをプログラムが入出力に用いる文字コード(EUC-JP)に合わせる必要がある.

GNOME端末では, メニューバー:「端末」$\Rightarrow$「文字コードの設定」から, 「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;
}

23.3 ファイルとディレクトリ


23.3.1 ファイルを操作するコマンド

コマンド 機能 本来の機能 詳細
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]を参照のこと.


23.3.2 ドットファイル

先頭に「.」(ドット)のつくファイル名を持つファイルは, 通常のファイルと は区別して, 各種設定用のファイルとして用いられる. 「.login」,「.emacs」 等がその例である. また, 「.」で始まる名前のディレクトリーも同じ目的で 利用される.

ls コマンドをそのまま用いた場合(ls),「.」で始まるファイル名やディ レクトリー名は,表示されない. -a オプションを与えると表示される ようになる(ls -l) [24.2.1].


23.3.3 ディレクトリ

ディレクトリとファイルの構造を視覚的に表すと 例えば次のようになってい る.

--+-bin
  |
  +-usr--+-include--+--stdio.h
  |      |          |
  |      |          +--math.h
  |      |          |
  |      |          +--stdlib.h
  |      |          |
  |      |
  |      +-lib
  |      |
  |      +-local
  |      |
  |
  |
  +-home-+-user1-+-file1
  |      |       |
         |       +-file2
         |       |
         |       +-dir---+-file3
         |       |       |
         |       |       +-file4
         |       |
         |       +-dir2--+-file5
         |               |
         |               +-file6
         |
         |
         +-user2
         | ...
         +-usern
         |

ディレクトリ
ファイルはまとめて1つのディレクトリと呼ばれる入れ 物の中に入れておくことができる. 更に, そのディレクトリの中にディレク トリを入れることもでき,ファイルを階層的に管理することができる.

一番上のディレクトリの中には, bin, usr, home等のディレクトリがあ る. 更に bin の中には include, lib, local 等のディレクトリがあり, そ の中に幾つかのファイルやディレクトリがある. 各ユーザーのためのディレ クトリ (ホームディレクトリ) は home 以下に置かれることが多い.

絶対パスと相対パス
ファイルやディレクトリを指定するには, それが どのディレクトリの中にあるかまでを指定する必要がある. この指定の仕方 には, 2種類の方法がある. 1つは一番上のディレクトリから見てどこにある かを指定する「絶対パス」で, もう一つは現在居るディレクトリからみてど こにあるかを指定する「相対パス」である. ディレクトリの中のファイル名 やディレクトリ名は / で区切って表現する.

絶対パス
絶対パスで指定する場合には, 一番上のディレクトリ(ルー トディレクトリ)を/ で表し, そこからの見てどこにあるかを指定す る. 例えば次のようになる.
ディレクトリ 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

絶対パスと相対パスの比較
相対パスは長いパス名を書かなくてよい分 簡便であるが, カレントディレクトリがどこであるかを意識しておく必要が ある分, 面倒であったり, 誤った操作をしてしまう可能性が高くなる.

ホームディレクトリ
各ユーザーには自 分で自由に使える専用のディレクトリ(ホームディレクトリ)が1つ用意され ている. これは, /home や/users のような名前のディレクトリの中に各ユー ザーのアカウント名と同じ名前のディレクトリがとなっていることが多い.


23.3.4 ディレクトリ の操作

主要なディレクトリ操作のためのコマンド.

コマンド名 機能 詳細
cd 現在のディレクトリの変更 [24.3.1] cd dirname
pwd 現在のディレクトリの表示 [24.3.2] pwd
mkdir ディレクトリの作成 [24.3.3] mkdir dirname
rmdir ディレクトリの削除 [24.3.4] rmdir dirname

23.4 OS とのやりとり


23.4.1 コマンドライン

シェルは, 端末からコマンドの入力などを行ない, それに応じてプログラムの実行の制御を行う. シェルを実現するプログラムとしては, sh, bash, csh, tcsh, ... 等がある. csh や tcsh のような C シェルには良くない点があるので, Borne シェルである sh や bash を覚えた方がいい26.2.

更にシェルには, コマンド行編集や環境変数の設定の機能がある. コマンドラインの入力行の編集を行なうために便利な機能が用意されている場合がある. 実際のキー操作はシェルに完全に依存する. 例えば次のような機能がある.

tcsh が実行されている時に bash へ変更するには.

tcsh % exec bash
bash $
又は, (EDITOR変更の後)
$ chsh
を実行する.


23.4.2 端末からの入出力

特殊な端末からの入力には次のものがある. それぞれがどのキー入力に割り当 てられているかは設定に依存する. 現在の設定は

$ stty -a
により知ることができる. stty all の場合もある. 例えば, 端末か ら「入力の終了」を入力するには, stty コマンドにより 「eof =^D」 と得られたら 「Control + D」 を押す.

使用例:

名前 キー割り当ての例 説明
intr ^C 割り込み(interrupt)シグナルの送信.
quit ^\ 終了(quit)シグナルの送信.
erase ^H 最後に入力された文字の消去.
kill ^U 現在行の消去.
eof ^D 入力の終了 もしくは ファイルの終了 (End Of File) を報せる.
eol   行末(end of the line).
eol2 (np)   別の行末を表す文字.
swtch (np)   別のシェルレイヤー(shell layer)にスイッチ.
start ^Q 停止している出力の再開.
stop ^S 出力の停止.
susp ^S 停止(stop)シグナル送信.
dsusp (np)   入力をフラッシュしたのち 端末に stop を送信.
rprnt (np) ^R 現在行の再表示.
werase (np) ^W 最後に入力された単語の消去.
lnext (np) ^V 次に入力される文字を特殊文字であっ てもそのままの文字として受け取る

23.4.3 環境変数


23.4.4 標準入出力とパイプ

UNIX では, Standard Input (stdin), Standasrd Output (stdout), Standard Error (stderr) という仮想的なファイルが自動的にオープンされている. そ れぞれ 0, 1, 2という番号(ファイルディスクリプタ)で参照される. 特に指定 を行なっていないときには, stdin は端末からの入力, stdout と stderr は 端末への出力に対応している.


23.4.4.1 stdout

stdout に出力される内容は > に続けてファイル名を指定する ことで, そのファイルに出力される. このような出力先を変更することをリダ イレクト という. 例えば,

$ ls > filenamelist
とすることで filenamelist というファイルに ls コマンドの 実行結果が出力される. filenamelist の中に何が出力されたかは cat [24.2.4]コマンド等で調べられる.

出力結果を追加していくには > の代わりに >> を用いる.

$ ls >> filenamelist
何度もこれを実行すると, ファイルに追加されていっている様子が分かる.


23.4.4.2 stdout と stderr

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


23.4.4.3 stdin

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


23.4.4.4 パイプ

ここまでの例では, 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

23.4.5 プロセス制御

端末の設定によるが

^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はメニューやアプレットに登録されていて, そこから起動できる場合もある.


23.4.6 Bourne Shell (sh, bash)

23.4.6.1 ワイルドカード


23.4.6.2 環境変数

シェルやプログラム内に留まらずそこから起動されるシェルやプログラムで参 照できる変数を環境変数といいます.

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


23.4.6.3 リダイレクト, パイプ

See. dup(2), fcntl(2).


23.4.6.4 コマンドの戻り値

$? 最後に実行したコマンドの終了状態を保持する.

$ ./a.out
$ echo $?

[20.7.2] 参照のこと.

23.5 X Window System


23.5.1 マウスによるコピーとペースト

xterm ではマウスの操作は次のようになっている.

左ボタン 選択範囲の始点. 押したまま移動することで範囲の指定.
真ん中ボタン (又は 左右の2つのボタンを同時に押す.) 選択内容のペースト.
右ボタン 選択範囲の終点.

詳細は マニュアルを参照のこと [man xterm].


23.5.2 起動時のオプション

emacs, xterm 等の X Window System 環境下で動作するプログラムは, 起 動時にウィンドウの大きさを指定することができます. それには -geometry オプションを使います. 例えば emacs を $80 \times 25$ の大きさで起動し, 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 のセクション].

23.6 プログラム開発


23.6.1 cc: C コンパイラー

cc は C 言語のコンパイラーである. ここでは, GNU の C コンパイラーである gcc を使うことを想定している. これが cc という名前になっている場合もある.

書式: cc [幾つかの [オプション] または [ファイル名] ]

ここでファイル名は, コンパイルする対象となるファイル名で, 特に指定しない場合は C のプログラムは最後が「.c」で終わっている必要がある. プログラムを複数に分割している場合には複数のファイル名を与える場合もある. 次に挙げるようなオプションにより, コンパイルの仕方を調整することができる. オプションや与えるファイル名はその順序が重要であるので注意すること. 詳細は マニュアルや info を参照のこと.

-lm 数学関数を使う
プログラム中で $sin$$\log$ 等の数学関数を使用しているときは, これらの関数の実体をプログラムに結合する必要がある. (23.6.7参照.) 例えば aaa.c とプログラムから実行プログラムを作成するには
$ cc aaa.c -lm
とする.

-l

-L

-o 出力ファイル名を指定する
特に指定をしない場合には, 実行形式のファイルは a.out というファイル名になっている. このファイル名を指定するときに -o オプションを用いる. 例えば, abc.c というプログラムから xyz という実行ファイルを作成するには
$ cc abc.c -o xyz
とする.

-c

これ以降では, gcc に固有な事柄である.

-Wall オプションでプログラムを検査する
-Wall オプションを
$ gcc -Wall aaa.c
のように指定して aaa.c をコンパイルすると, 非常に詳細にプログラムの誤りを報告してくれる[23.6.3]. プログラムに何らかの問題があることを報告しているので, このメッセージが全く出なくなるまでプログラムを修正するようにする.

-g オプション
デバッグ用の情報を含んだプログラムを出力する.

日本語
漢字を使うときには 漢字コードを EUC にする  [23.6.5].

注意
cc は aaa.c のように最後が .c で終わっていないファイルはコンパイルできない. cc はプログラムがどの言語で記述されているかを ファイル名の最後の部分 (拡張子) で判別しているからである.


23.6.2 cpp: C プリプロセッサー

実際のコンパイルが行なわれる前に, プリ プロセッサーに前処理を行なわせることができる. この処理は 「#include」や「#define」のように「#」で始まる命令に対して行なわれる. ただし通常この処理は自動的に行なわれているので特に意識する必要はない. ここではプリプロセッサーに対する命令について概観する.

#include
ファイルの取り込みを行なう. 次のように <> で囲んで指定されたファイルは /usr/include のようなシステムの標準的な場所に置かれていることを想定している.
#include <stdio.h>
ユーザーが用意したヘッダーファイルは次のように ""で囲んで指定する.
#include "aaa.h"
従ってユーザーの領域に作成されていることが想定されている. ""で囲むことでヘッダーファイルの検索順序が制御できるのである.

#define
マクロを定義する. これにより置き換えが行なわれる. 次の例では 「NUMBER」 の部分が 「30」に置き換えられる.
#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
#if 1

NEW CODE HERE.

#else

OLD CODE HERE.

#endif


23.6.3 プログラムの誤りを見つける


23.6.4 実行結果の記録


23.6.5 C での日本語の入出力

gcc  [23.6.1] はEUCで書かれた日本語文字は正しく評価できる. C言語のプログラム中で日本語のコメントを書きたいときや, 日本語の文字を出力したいときにはプログラムを EUC で書けばよい.

一旦作成した日本語を含むファイルの文字コードは, nkf [24.5.2] により変換することができる.

さらに, プログラムを実行し結果を表示する端末の文字コードを, プログラムに合わせてEUCにする必要がある. 各端末の設定方法は, 端末[23.2]を参照のこと.


23.6.6 gdb: デバッガー

プログラムの誤りを見つけるには, プログラムを 1 行ずつ実行したり, その時の変数の値を調べたり変更したりできるといい. このようなことを行なうのが「デバッガー」である.

gccによりコンパイルしたプログラムには gdb というデバッガーを用いることができる. この場合は, コンパイル時に, gdb でデバッグするためオプション -g をつけおく. 例えば, program.c というファイルに書かれたプログラムををデバッグする場合には, コンパイルする時に

$ cc -g program.c
とする.

gdb を直接使ってもいいが, emacs, kdevelop, ddd, xxgdb 等のユーザーインターフェースを介して利用した方が使いやすい.


23.6.7 ライブラリのリンク

printf 関数や sin 関数のように 自分で関数の実体を書かずに予め用意されている関数を利用するときがある. これらの関数は予めコンパイルを行ない, グループごとにライブラリーと呼ば れる1つのファイルにまとめて保管されている. また, 自分でこのようなライ ブラリーを作成することもできる. ライブラリーに納められた関数のうち, printf 関数のように標準的に用いられる関数では特に必要ないが, sin 関数のようにそうでない関数を用いるときにはそのことをコンパ イラーに指示する必要がある.

例えば, $\sin x$ 等の数学関数を計算するためのプログラムは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
のようになる.

23.6.8 ライブラリーの中の関数

ライブラリー関数をリンクするには実体がどのライブラリーの中にあるのかを 知る必要がある. 標準的なライブラリーは /usr/lib の下にある. nm を使う.


23.6.9 分割コンパイル

実行ファイルは, 例えば次のような中間ファイル結合して作成される. これら の作業は, 指定しない場合 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 --+

オブジェクトファイル (.o)
cc aaa.c は内部では, まず aaa.caaa.o という計算機で直接実行できる形式に変換 する. aaa.o のようなファイルが「オブジェクトファイル」. -c オプションをつけた cc コマンドで行なう.
ライブラリー (.a)
複数のオブジェクトファイルを1つのファイルにし たもの. この中には, printf 関数の実体や sin 関数の実体 などがある. ar コマンドで行なう.
実行可能ファイル (a.out)
オブジェクトファイルとライブラリー中の コンパイル済の必要な関数を結合することで得られる. ld コマンド で行なう.

23.6.10 make: 作業を指示する.

make により依存関係を元にコンパイルを自動化できる.

コマンドの前の空白はスペースではなく, 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 $@

23.6.11 開発用コマンド

ar, diff, nm, patch

23.7 開発環境


23.7.1 Emacs

23.7.1.1 プログラムの作成から実行まで

  1. 端末上で以下の作業を行う. 端末としては, 例えばデスクトップで右クリックして「端末を開く」ことにより, GNOME端末を利用できる.

  2. emacsで, 例えば hello.cという名前のファイルにプログラムを入力する.

    実行の例. 表記法 [1.2] のセクションを参照のこと.

    $ emacs hello.c
    

    hello.c の内容. emacs の画面で例えば次のようなプログラムを入力する.

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello.");
        return 0;
    }
    
    ファイルに保存したのち, emacs を終了する.

  3. Cコンパイラー(cc23.6.1)で実行可能な形式に変換する.
    $ cc hello.c
    
    これにより, a.out という実行可能なファイルが作成される. このようにテキストで書かれたプログラムをコンピュータが実際に実行できる形式に変換することをコンパイルという.

  4. 作成したプログラムを実行する.
    $ ./a.out
    
    のように 「./」 を前につけて実行する. 「./」はカレントディレクトリを表す. 「Hello.」と表示されるはずである.

  5. printf("Hello."); の中の "Hello." を色々と変えてみると表示される結果が変わる.

慣れてきたら,

  1. 端末でコマンドを全て入力するのは煩雑なので, 矢印キーやタブキーの使い方 [23.4.1] に慣れておくとよい. これにより, 入力する手間が大幅に省ける.

  2. ファイル編集のときに最後に&を付けて emacs を起動する.
    $ emacs hello.c &
    
    emacs がバックグラウンドジョブとして実行され, emacs 実行中も端末を使用することができる. これにより, emacs を終了することなく
    $ cc hello.c
    
    などの他のコマンドを実行することができる. ただし, コンパイルを行う前にファイルの保存を行う必要がある.

  3. emacsの基本的な使い方を [24.4.1] にまとめてあるので参照のこと.

  4. gcc によるコンパイル時には -Wallオプションをつける. これにより, プログラムの誤りが詳細に出力される. 例えば,
    $ cc -Wall hello.c
    
    より詳細な説明が[23.6.3]にあるので参照のこと.

23.7.1.2 日本語の入出力とコメント

プログラム中のコメントや, プログラムの入出力に日本語を用いためには, emacs [24.4.1]でプログラムファイルを保存する際, 文字コードとして euc-jp を選択する[24.4.2].

さらに, 入出力に日本語を用いるためには, 端末の入出力の文字コードをプログラムに合わせてEUCにする[23.2].

23.7.1.3 gdbを用いたデバッグ

例えば, 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;
}

run
(gdb) と入力待ちになっているところで, run と 入力し 「Enter」 を押すとプログラムが実行される.
(gdb) run
この場合途中で止まること無くプログラムが終了するまで実行される.

break
プログラム中のある場所で一旦実行を停止させるためには, break を用いる.
(gdb) break main
とすることで main 関数に入った所で実行が停止する. 現在実行さ れている行は, emacs上では, 「=>」 により表示され ている.

next
一旦 break で停止した後に, プログラムの実行を再開す るのに next を用いることができる. next では, 1 行だけ プログラムを実行し, 再び実行を停止する.
(gdb) next
ここで用いている例では, main 関数の内部を1行ずつ実行すること になる. add 関数の内部の行を1行ずつ実行することはない.

step
関数の内部の各行を 1 行ずつ実行するには, next の代 りに step を用いる. next により main 関数内の c=add(a,b); の行まで実行する. その後, step を実行すると add 関数の内部で実行が停止する.
(gdb) step

finish
現在の実行中の関数が終了するまで実行する.

continue
一度停止したプログラムを最後まで実行させる.
(gdb) continue

kill
途中でプログラムを終了させる.
(gdb) kill

quit
デバッグを終了する.
(gdb) quit

プログラムを停止している状態でその時の変数の値を調べることができる.

print
変数の値を表示する. 引数として表示したい変数1つを指定す る. 例えば 変数 a の値を表示させるには,
(gdb) print a
この時, a という変数が使える状態になっていなければならない.

display
常にある変数の値を表示させ続ける. プログラムの実行が停 止するたびごとに指定した変数を自動的に表示させられる. 例えば,
(gdb) display a
この時, a という変数が使える状態になっていなければならない.

23.7.1.4 デバッグの実際

毎回 nextのようなコマンドを入力するのは面倒なので, 省略する方法が用意されている. next の場合 その省略形が n なのでn だけ入力して 「Enter」 を押せば十分である. 更に, 「Enter」だけを入力すると直前に行なったことここでは next をもう一度実行することになる. 結局次々に1行ずつ実行していくには, 初めに next を入力したらその後は「Enter」を何度も押せばいいことになる.

一行ずつ実行して目的の行に辿り着くのが面倒な時には break が便利である. break は停止する場所を指定することができる. 停止する場所(ブレークポイント)を設定しておいて runcontinue を実行すると, 設定場所に辿り着くまでは連続してプログラムが実行される. 次のような設定法がある.

一旦設定したブレークポイントは, clear により解除できる. 次の例では, 関数 add に設定してあった ブレークポイントを解除する.

(gdb) clear add

frame
emacs 上でソースファイルが表示されていない場合には, frame を実行する表示される.

23.7.1.5 デバッグ時のキー入力の簡略化

上記の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行をコメントにして実行しないようにしておく必要がある.

上の設定を行なった場合, デバッグの手順は次のようになる.

起動
「M-x gdb」を実行し, 実行ファイル名を入力する.
F2 break
現在行にブレークポイントを設定する. プログラムファイル 上にカーソルがないと設定できない.
F3 clear
現在行のブレークポイントを解除する.
F4 run
プログラムが終了した後に再度プログラムを実行する.
F5 continue
あれば次のブレークポイント, なければもしくはプログ ラムが終了するまで実行.
F6 finish
関数を終了.
F7 display
カーソルがある位置の変数の値を常に表示するように設定 する.
F8 print
カーソルがある位置の変数の値を表示する.
F9 next
次の行まで実行.
F10 step
関数の内部まで実行.


23.7.2 Eclipseで編集, 実行

eclipse は様々なコンピュータ言語のプログラムを作成するための環境で, C言語を用いるために別途CDTが導入されている. また, コンパイルを行うために別 途コンパイラーが必要で, GNU のCコンパイラー gcc が導入されている.

23.7.2.1 eclipseの起動

  1. eclipseの起動

  2. 自動的に「ワークスペース・ランチャー」が起動しワーク スペースを指定できる. このフォルダー(ディレクトリ)は, ユーザーが作成した プログラムなどを保存するためのものである. ユーザーのホームディレクトリ にworkspaceと言うディレクトリを作成するよう表示されるので, それ をワークスペースとして用いることにする.

  3. 初回のみ
    1. 次に, 自動的に Experimantal feature activationを使用 可能にするか, 使用不可にするかを尋ねるダイアログボックスが開く. 新しい機 能であるが不安定である場合があるので, 「使用不可」にしておく.

    2. 「ようこそ」の画面が開く. 早速プログラムを作成する 場合は, 「ワークベンチにジャンプ」する.

23.7.2.2 新規プロジェクトを作成する

  1. 「Cプロジェクト」の画面を開く.

  2. 「Cプロジェクト」の画面で

パースペクティブ:
eclipse に拡張をを行う. 「C/C++パースペクティブ」 は, C言語やC++言語のプログラムの作成に適した拡張を行う.

Debug
プログラムを1行ずつ実行したり, 変数の値を調べたりすることで, プログラムの誤りを発見し修正する. これを実現するための情報を別途用意し, これを参照しながら実行が制御される. Debugでは, 実行ファイルの作成と同時に, (多くの場合)このような情報を実行ファイルに埋め込む. したがってファイルサイ ズは大きくなる.

Release
プログラムが完成したら, Debug のために用いる情報は必要なく なる. Release では, Debug用の情報を削除(strip)した完成品を作成する.

23.7.2.3 プログラムファイルの作成

  1. 「プロジェクト・エクスプローラー」の中に, 先ほど設定したプロジェクト 名のフォルダーが作成されている.
  2. 新規にプログラム(ソース)を記述するファイルを用意するため, 「新規ソース・ファイル」ダイヤログを開く. そのためには, 「ファイル」$\Rightarrow$「新規」 $\Rightarrow$ 「ソースファイル」を選択す る. また,「プロジェクト・エクスプローラー」の中で右クリックすることでも, 選択できる.

  3. 「新規ソース・ファイル」の画面で, ソースファイルを指定する.

プログラムファイルの編集.

  1. 準備されたソースファイルにプログラムを書き込む. 例えば,
    #include <stdio.h>
    
    int main(void)
    {
    	printf("Hello");
       return 0;
    }
    

    書き込んだのちは, 忘れずに保存すること.

23.7.2.4 コンパイルと実行

プログラムを作成したら, コンピュータが解釈できる形式に変換(コンパイル)し, 実行させる. プログラム中に誤りが含まれている場合には, コンパイルできずソー スを修正する.

  1. 「プロジェクト・エクスプローラー」中で, 実行させたいプロジェクト名を 選択し, メニューバーの「プロジェクト」から「プロジェクトのビルド」をする. 実行ファイルの作成(ビルド)の結果は, 「コンソール」および「問題」に表示される. ここで問題点が指摘されなくなるまでソースを修正する必要がある. また, ビル ドする前に毎回ソースを保存する必要があるので注意すること.

  2. メニューバーの「実行」$\Rightarrow$「実行」により, プログラムを実行す ることができる. この場合も, 「プロジェクト・エクスプローラー」中で, 実行 させたいプロジェクト名が予め選択されている必要があるので注意すること. 実行の結果は, 「コンソール」に表示される.


23.7.3 KDevelop

23.7.3.1 新規プログラムを作成するための準備

  1. GNOME端末などの端末からKDevelopを起動する.
    $ kdevelop
    

    または, アプリケーションのメニューからKDevelop を起動する.

    「 プロジェクト」$\Rightarrow$「新規プロジェクト」により 「新規プロジェクト」「一般」タブを開き次のように設定し, 「次」のダイアログに移動する.

    ここで, アプリケーション名が最終的な実行ファイルの名前になる. プログラムファイル(ソースファイル)や実行ファイルは, 場所で指定されるディレクトリ(フォルダ)内に置かれる. 場所は, ユーザが読み書き可能な権限を与えられているディレクトリを指定する必要がある.

  2. 「プロジェクトオプション」タブで以下の変更をし,「次」に移動する. ここで, CFLAGSはCコンパイラー(ここではgcc)に渡すオプションで, gcc に 「gcc -Wall program.c」のようオプションを与えてコンパイルの仕方を変更して, コンパイルをを行う. 「-Wall」を指定すると, 全て(all)の警告(Warning)を出力することになる. プログラムの誤りを発見する助けとなる.

  3. 以降のタブでは, 特に指定することは無い. 新規プロジェクト作成を「完了」すると, プログラム作成画面が開く。

23.7.3.2 プログラム作成と実行

  1. プログラム作成画面でプログラムを入力する. 「簡単な Hello world プログラム」を選択した場合には, 予め 「Hello world!」を出力するプログラムが準備されている.

  2. メニューの「ビルド」「プロジェクトをビルド」により, コンパイルを行う.

  3. 初回のみ「automake & 関連処理を実行してからconfigureしますか?」と聞かれる. 「Run Them」を選択し, プログラムを自動生成できるよう設定する.

  4. メッセージウィンドウに「成功」が表示されたらプログラム作成に成功したことになる。

  5. エラーメッセージが表示されたらその部分を修正し, 再度ビルドする. エラーメッセージをクリックすると問題がある部分にカーソルがジャンプする. その行が誤っている場合もあるが, それより前の行に問題がある場合もあるので注意すること.

  6. メニューの「ビルド」$\Rightarrow$「プログラムの実行」により完成したプログラムを実行できる.

23.7.3.3 デバッグ

--以降 ドラフト--

  1. 予め実行を停止したい行にカーソルを移動しておき、 メニュー:「デバッグ」$\Rightarrow$「ブレークポイントを切替え」によりブレークポイントを設定しておく. 複数設定することもできるし, プログラム実行中に設定したり, 解除したりすることもできる. これによりプログラム実行後, ブレークポイントが設定されている行を実行する直前で実行が停止する.

  2. メニュー:「デバッグ」$\Rightarrow$「実行」により, ブレークポイントが設定されている箇所までプログラムを実行する.

  3. さらにメニュー:デバッグ$\Rightarrow$続けるにより次のブレークポイントまで実行を続ける. ブレークポイントが設定されていない場合は, プログラムの最後まで実行を続け終了する.

  4. また, メニュー:「デバッグ」にある「ステップオーバー」, 「ステップイントゥ」, 「ステップアウト」により, ブレークポイント以降の行を1行ずつ実行することができる. ここで「ステップイントゥ」は関数呼出がある行で関数内部までジャンプし関数内部も1行ずつ実行する. ステップオーバーは関数内部へはジャンプせず次の行を実行する. 「ステップアウト」は実行中の関数からリターンするまで実行を続ける.

  5. プログラムの出力は, 「アプリケーション」ウィンドウに表示される.

  6. 左側の「変数」のウィンドウに各時点での変数の値が表示される. これにより, 意図した通りにプログラムが動作しているかどうかを確かめることができる. さらにこのウィンドウ中でプログラム実行中に変数の値を変更できる.

23.7.3.4 日本語入出力

KDevelopはコンパイラーとして gcc を用いるよう設定されている. この場合, プログラム自体やプログラムの入出力に EUC コードの漢字を用いることができる.

ただし, 以上の設定では, プログラム終了後の「何かのキーを押してください.」の表示が正しく行われない.

23.8 ネットワーク

ssh, ftp.

(c)1999-2013 Tetsuya Makimura