Create  Edit  Diff  FrontPage  Index  Search  Changes  History  RSS  Source  Login

BasicEmacsAndGcc

はじめに

この文書は、LinuxなどのUnix系OSの初心者を対象に、Emacsを用いたC言語ソースの編集と、GCCによるコンパイルを行う手順を紹介するものです。

Cの文法やアルゴリズムについてはプログラミングの参考書・Webページに譲り、それらのサンプルを実際に入力してコンパイルするために必要な情報をまとめてみました。

ここに書いてある事に一通り慣れておけば、後は独学でCを勉強していく事が出来るんじゃないかと思います。

この文書にしたがって実際にEmacsの操作等を練習する場合には、次のように適当な空のディレクトリを作って、その中で作業をすることをお勧めします。([enter]はEnterキーを押すことを意味します)

~$ mkdir emacs_c[enter]
~$ cd emacs_c[enter]
~/emacs_c $

(以下では、プロンプトは省略して単に $ と書きます。また、プロンプトでは[enter]も省略します)

また、想定している環境は、

  • MS-Windowsで
  • Puttyを使って
  • Linuxにssh接続して使う

というものです。

Emacs

Emacsはテキストエディタの一種です。タブキーによる自動補完や、インデントの自動調整の機能などを持っています。

コンパイラやデバッガーをEmacsの上で動作させることで、統合開発環境のように使うこともできますが、この文書ではそのあたりは簡単に解説するにとどめています。

起動方法

コンソールで以下のように入力します。

$ emacs

画面最下行でなにかごちゃごちゃと表示されてから、次のようにEmacsの簡単な解説と著作権などが表示されます。

何かキーを押すと、*scratch*バッファが開かれた状態になります。画面の最初の方に書いていますが、これは、保存などを必要としない Emacsからのメッセージやユーザー(つまりあなた)のメモを入力する画面です。ここに書いた内容は明示的に指定しない限りファイルには保存されません。

終了

Emacsを終了するためには、次の2段階の入力を行います。

  1. Ctrlキーを押しながら x を押す
  2. Ctrlキーを押しながら c を押す

EmacsではこのようなCtrlキー(コントロールキー)を押しながら別のキーを押すという入力によってファイルの保存や新しいファイルのオープン、ウィンドウの切り替え等のコマンドを実行します。

以下では、「Ctrlキーを押しながら x を押す」を、C-x と書きます。他のたいていのEmacsの資料でも同様の記法を使っているので覚えておいてください。

また、この終了コマンドのような2段階(またはそれ以上)の入力を行う命令は、C-x C-cと書きます。

では、実際にEmacsを終了してみてください。

ファイル名を指定して起動する

編集したいファイルの名前が決まっている場合は、そのファイルが既に存在しているかどうか関わらず以下のように指定します。

$ emacs test.txt

これで、"test.txt"というファイルを指定した状態でEmacsが起動されます。

今回は(多分)まだtest.txtというファイルは存在していないので、空の画面が表示されていると思います。もしtest.txtが既に存在しているなら、その内容が表示されて、それを編集していくようになります。

さて、画面の下から2行目に、---と書かれた行があると思います。この行を見ながら、何か適当に何か文字を入力してみてください。画面下の---マークが**-に変わったと思います。

この部分は現在表示しているファイルが、読み込んだ後に編集されたかどうかを表します。

また、その左側は現在の文字コードを、右側(中央の辺り)には現在編集中のファイルの名前が表示されます。

では、この状態でEmacsを終了しようとしてみましょう。C-x C-cです。

画面の一番下に

Save file /home/euler/hiraku/emacs_c/test.txt? (y, n, !, ., q, C-r or C-h)

と表示されて入力待ちの状態になります。

Emacsでは何か問題が発生したときには、このように画面最下行でメッセージを表示します。

キャンセル

この状態で、C-gを入力してください。C-x C-cによる終了コマンドがキャンセルされて、元の状態に戻ります。

このように、警告が表示されたり、うっかりキーを押し間違えて変なコマンドを実行してして困ってしまったら、C-g を押すことでキャンセルすることが出来ます

保存して終了

では改めて、C-x C-cで終了コマンドを実行してください。

画面最下行のメッセージ

Save file /home/euler/hiraku/emacs_c/test.txt? (y, n, !, ., q, C-r or C-h)

は、変更を行ったtext.txtファイルを保存するか?と聞いてきており、あなたはこれに対して

  • y : Yes 保存する
  • n : No 保存しない(続いて、保存していないが本当に終了するか?と聞いてきます。その質問にはyesまたはnoで答えます)
  • ! : 全部保存する(Emacsでは、複数のファイルを同時に編集することができます)
  • . : このファイルだけ保存し、(もしあっても)他のファイルは保存しない
  • C-r : Review 編集中の内容を確認するために、元の画面に戻る(ただし、さらに変更することが出来ないようになります。qを押すと、再びEmacsの終了処理に戻ってファイルをどうするか質問されます)
  • C-h : Help 何を入力したらどうなるかを解説するヘルプ画面が表示される

からいずれかを選ぶことが出来ます。

せっかくなので、ヘルプを見てみましょう。C-hです。

ヘルプを表示した状態でも yを押せば、そのままファイルを保存して終了します。

また、一度C-gでキャンセルすると、ヘルプも自動的に消えます。その状態でもう一度C-x C-cで終了コマンドを実行し、そのままyを押しても、ファイルを保存して終了します。

終了しないで保存

もう一度Emacsをtest.txtを指定して起動しましょう。

$ emacs test.txt

そしてまた適当に文字を入力してください。

先ほどの、画面下から2行目の---が**-に変わる話を覚えていますか?画面の表示が--*になっていることを確認して、今度はファイルの保存コマンドを実行してみましょう。C-x C-s(Save file)です。**-が---に戻ったと思います。これで、編集した内容がファイルに保存されました。

別のファイルを開く

test.txtを開いたままで、別のファイルを開いてみましょう。それには、まずC-x C-f(Find file)とします。

最下行に

Find file: ~/emacs_c/

と表示されて、カーソルがここに移動します。これは、これから開くファイル名を聞かれている状態で、ファイル名を入力することでそのファイルが新しく開かれます。つまり、そのファイルが既に存在するならその内容が読み込まれ、まだ存在していないなら新しく空のファイルが作られることになります。

ではここに、test.cと入力してEnterを押してください。

Find file: ~/emacs_c/test.c[enter]

これで、test.cを編集する状態になりました。早速、Cのプログラムを入力してみましょう。以下の内容を入力してください。

#include <stdio.h>

int main()
{
  puts("Hello, world!!");
  return 0;
}

ソースを見やすくするためにputsの左側に空白を入れる事を「インデントする」と言いますが、Emacsはこのインデントを自動的に行う機能がついています。

まず、空白を入れずに 'puts'まで入力してください。その状態で次の'('を入力すると、自動的にその行の左に空白は挿入される(インデントされる)と思います。

また、次の行の'return 0;'も、空白を入れずに入力してみてください。今度は、最後の';'を入力したところでインデントされたと思います。

全部入力したら、またC-x C-sで保存してください。

おまけのTIPS

Windowsからputtyを使ってコンソールにつないでいる人は、手入力しないで済ます方法もあります。

  1. このページのさっきのサンプルソースを選択状態にしてコピーする
  2. Puttyの上で右クリックする

これで、Windowsのコピペの内容がPuttyから自動的にEmacsに入力されます。

注意!

これはPuttyの機能なので、Emacsを起動していなくても使えます。というか、使えてしまえます

例えば、(この例を実際に真似してはいけません!!)

rm *
ls -al

といった文字列を選択・コピーし、コンソールの待ち状態を表示しているputtyの上で右クリックしてしまうと、そのディレクトリのファイルを全部削除する命令が実行されてしまいます。気をつけましょう。

バッファの切り替え

さて、ここまでで次のような作業をしてきました。

  1. test.txtを開いて編集
  2. test.txtを保存
  3. test.cを作成
  4. test.cを保存

そして現在、test.cの内容が表示されているわけですが、このとき、test.txtはどうなったのでしょうか?

実は、test.txtもEmacsに読み込まれたままの状態になっています。この、Emacsがtest.txtとtest.cの2つのファイルを読み込んでいる状態を、test.txtとtest.cの2つのバッファがあると言います。そして現在はtest.cのバッファが表示されている わけです。

では、表示するバッファをtest.txtに切り替えてみましょう。C-x C-b [enter](switch Buffer)です。

何回かC-x C-b [enter]して、切り替わる様子を試してみてください。

3つ目のファイル

今度は、sample.cというファイルを作ってみましょう。C-x C-fして'sample.c'と入力して[enter]、でしたね。

sample.cを開いたら、以下のように入力し、保存して下さい。

#include <stdio.h>
#include <math.h>
#include <unistd.h>

int main()
{
  imt i;
  for(i=0; i<300; i++)
    {
      printf("%d, %f\n", i, sin(3.14*i/300));
      sleep(1);
    }

  return 0;
}

OK?では、ここでC-x C-b [enter]して別のバッファに移ってください。test.txtとtest.cのどちらかに移ったと思います。ここで再びsample.cのバッファに移りたくなったとします。C-x C-b [enter]と入力しても、多分残りの (test.txtかtest.cの片方)が表示されてしまうので、もう一回C-x C-b [enter]しないといけません。

直接sample.cのバッファに移りたいときは、もっと早い方法があります。C-x C-bを入力し、すぐに[enter]を押さずに、sample.c と入力します。そして[enter]。これで、直接sample.cのバッファに移動できます。

え?いちいち 's' 'a' 'm' 'p' 'l' 'e' '.' 'c'と入力するよりC-x C-b[enter]2回のほうが早い?

タブキー補完

ここで、タブキー補完を紹介します。

まず、sample.c以外のバッファに移ってください。多分、今はsample.cのバッファなので、C-x C-b [enter]です。

ここからsample.cのバッファに一発で戻りましょう。

まず、C-x C-bして、次に's'と入力します。'sample.c'全部ではなく、最初の's'だけです。

入力したら、そこでタブキーを押してください。自動的に残りの'ample.c'が入力されたはずです。あとはそのまま[enter]。こっちのほうが早いでしょう?

次。今度はsample.cのバッファからtest.txtのバッファに移動しましょう。

  1. C-x C-bする
  2. 't'を入力
  3. タブキーを押す

おっと、'test.'で止まってしまいました。ここでさらにもう一回タブキーを押してみましょう。画面下半分にメッセージが表示されます。

In this buffer, type RET to select the completion near point.

Possible completions are:
test.c                             test.txt

「'test.'で始まるバッファは'test.c'と'test.txt'があるけどどっちにするの?」と聞いてきているわけです。

'test.txt'に移りたいので、残りの'txt'を入力するわけですが、もちろんここでもタブキー補完が使えます。最初の't'だけ入力してタブキーを押せば、残りの'xt'が補完されるので、あとは[enter]を押すだけです。

マルチウィンドウ

これまでも時々、Emacsが何らかのメッセージを表示するために画面を分割するのを見てきましたが、もちろん我々が自分で画面を分割して使うことも出来ます。

C-x 2と入力してください。これで、画面が上下に分割されます。

分割された上下の領域をウィンドウと呼びますが、そのどちらも自由に移動することが出来ます。将来、長いソースコードを入力したときに、ソースの最初のほうを一方に表示して参考にしながら、もう一方でソースの末尾に入力をつ追加していく、なんて使い方をすることが出来ます。

今、入力カーソルは上下どちらのウィンドウにあるでしょうか?どちらにしても、別のウィンドウにカーソルを移動させるときにはC-x C-o(Other window)を使います。

さらに、それぞれのウィンドウで別々のバッファを表示することも出来ます。バッファの切り替えには、いつもどおりにC-x C-bを使います。あるいは、ウィンドウを2つに分けたところでC-x C-fを使って新しいファイルを開いても良いです。

また、上下ではなく左右にウィンドウを分割することも出来ます。C-x 3です。

multi_window.png

これは、ウィンドウ分割をやりすぎた例。どうしましょう?

ウィンドウを分割した状態でC-x 1とすると、ウィンドウの分割を解除して、現在カーソルがあるウィンドウだけが表示される状態にできます。

single_window.png

ああ、すっきり

Emacsのその他の機能

詳しいところは専門の解説ページに任せるとして、とりあえず覚えておくと便利なコマンドをいくつか紹介します

名前をつけて保存

ファイルを編集した後、別の名前で保存したいときには、C-x C-w(Write file)と入力します。

新しいファイル名を聞いてくるので、入力してやるとその名前でファイルを保存します。元のファイルはその前に保存した状態(していないならファイルを開いたときのまま)で残ります。

バッファの中の移動

そのまえに、ちょっと長めの文章をtest.txtに入力しておいてください。面倒なら、このページを全部選択してputtyにコピペすれば良いです。

上下左右

カーソルキーでも移動できますが、

C-f(Forward)
C-b(Backward)
C-p(Previous line)
C-n(Next line)

でも移動できます。

次のページ、前のページ

ページ単位で移動するときは、C-vです。1ページ分だけ下に移動します。

1ページ分だけ上に移動するには、M-vです。M-Metaキーを押しながらという意味です。

…ないですね、Metaキー。Metaキーは一部の特殊なキーボードに付いているキーらしいのですが、少なくとも僕の手元のJISキーボードには付いていません。

こういうときは、代わりにAltキーが使えます。M-vは、Altキーを押しながらvです。

ただし!たまに、このMetaの代わりにAltを使うという方法が使えない環境があります。そのような場合に備えて、次の方法も練習しておいてください。

  1. ESCキーを押す
  2. ESCキーを離す(この段階で少し待つと、最下行にESC-と表示される。待つ必要はない)
  3. vを押す

押しながらではないのがポイントです。

行の先頭、末尾

カーソルのある行の、先頭(一番左)と末尾(一番左)に移動するには、C-a(先頭)、C-e(末尾)を使います。

ファイル先頭、末尾

ファイルの一番最初の行や一番最後の行に移動する方法もあります。

最初の行に移動するにはM-<、最後の行はM->です。

指定行ジャンプ

M-x goto-line[enter]とすると、何行目にジャンプしたいか聞いてくるので、行番号を入力します。

コピペ

文字列をEmacsの中でコピーしたり貼り付けたりするには、次のようにします。

  1. コピーしたい文字列の始点にカーソルを移動する
  2. C-[space](Ctrlキーを押しながらスペースバー) (最下行にMark setと出ます)
  3. コピーしたい文字列の終点に移動
  4. M-wでコピー、C-wで切り取りになります
  5. 貼り付けたい場所に移動
  6. C-yで貼り付け
やり直し(アンドゥ)

文字列を削除しすぎた場合などに、直前のコマンドを取り消す方法です。

C-x u(Undo))を使います。何回でも使えます。

まとめてインデント

sample.cの、各行のインデント空白を全部削除して、平べったくしてみてください。こんな感じ。

#include <stdio.h>
#include <math.h>
#include <unistd.h>

int main()
{
int i;
for(i=0; i<300; i++)
{
printf("%d, %f\n", i, sin(3.14*i/300));
sleep(1);
}

return 0;
}

次に、ファイル先頭に移動(C-<)して、Mark set(C-[space])してください。

そしたら今度は、ファイルの最後に移動(C->)します。OK?

最後に、M-C-\します。CtrlキーとAltキーを押しながら'\'を押すか、

  1. Escキーを押す
  2. Escキーを離す
  3. C-\する

で入力できます。

何が起こるかはやってみてのお楽しみ(て、題名に書いちゃってますが)

括弧のつけ方が気に入らないあなたへ(ややマニアック)

「いやぁ、やっぱfor文の括弧はこうでしょ」

    for(i=0; i<300; i++)
    {
         printf("%d, %f\n", i, sin(3.14*i/300));
         sleep(1);
    }

という人は、ホームディレクトリの.emacsファイルに次の内容を追加してください。 システムによっては、.emacs.my.elなどに書けと指示される場合もあります。

(add-hook 'c-mode-common-hook
 '(lambda ()
 (c-set-style "k&r")
 ))

これで、括弧のインデントの仕方が変わります。

「いやぁ、やっぱLispのインデントは…」 知りません!

おまけ 簡単行ジャンプ

.emacsに

(global-set-key "\C-cj" 'goto-line)

と書いておくと、M-x goto-line[enter]の代わりにC-c jで行ジャンプが使えるようになります。


他にも色々な機能があるので、専門の解説書かWebで検索してみてください。

GCC

今度は、C言語のソースファイルをコンパイルして、実行できるようにしてみましょう。

Emacsを終了して下さい。もし、ファイルを保存していなければどうするか聞かれるので、適当に答えておいて下さい。

まず、一番簡単なtest.cから。コンパイルは基本的にこんな感じでやります。

$ gcc test.c

これだけ。a.outというプログラムが作られます。'a.out'は、特に指定しなかった場合のデフォルトの名前です。

これを実行するには

$ ./a.out

とします。a.outの前に'./'と入力しないといけません。これは、「現在のディレクトリ(カレントディレクトリ)の」という意味です。

システムにインストールされている emacs とか gcc等のプログラムはディレクトリ指定なしで実行できますが、それ以外の、特に自分でコンパイルして作ったプログラムはディレクトリを指定しなければ実行できません。これにはもちろん理由があるのですが、今回は省略。とりあえずヒントを出しておくと、例えば自分で'emacs'という名前のプログラムを作った場合を考えてみてください。または、別の誰かが'emacs'という名前で、何か悪さをするプログラムを作っていた場合はどうでしょうか?

次に、a.out以外の名前のプログラムが出来るようにしてみましょう。test.cをコンパイルして作るのですから、出来たプログラムはtestという名前で実行したいですよね?

$ gcc test.c -o test

コンパイルして作るプログラムの名前は、'-o'オプションで指定します。出来たプログラムの実行は

$ ./test

です。

数学関数などを含むソース

次は、sample.cをコンパイルしてみましょう。これは、数学関数であるsin関数を使っています。

$ gcc sample.c -o sample
sample.c: 関数 `main' 内:
sample.c:7: error: `imt' undeclared (first use in this function)
sample.c:7: error: (Each undeclared identifier is reported only once
sample.c:7: error: for each function it appears in.)
sample.c:7: error: 文法エラー before "i"
sample.c:8: error: `i' undeclared (first use in this function)

おっと。

どうも、ソースに入力ミスがあって怒られたようです。こういう場合、どこが良くなかったのかヒントになるメッセージが表示されるので、よく読んでみましょう。

sample.c: 関数 `main' 内:
sample.c:7: error: `imt' undeclared (first use in this function)

これは、sample.cの、main関数の中の、7行目に問題があるということを意味しています。「imtという単語は宣言されていませんよ」と書いていますね。

C言語では、言語自体があらかじめ備えている単語(予約語)や、ソースの中で宣言された単語しか使うことが出来ません。ここでは、予約語のint(整数)のつもりで間違ってimtと書いてしまったので怒られたわけです。

ちなみに、sample.cで使ってるprintf等の関数は、予約語ではありません。sample.cでも宣言していないのにどうして使えるのかというと、これはstdio.hというファイルの中で宣言されていて、sample.cの最初の方の

#include <stdio.h>

でそのファイルを使うと指示しているからです。

では、Emacsを起動してsample.cを修正しましょう。直したら保存してまたEmacsを終了。

$ gcc sample.c -o sample
/tmp/ccqXpi5g.o(.text+0x40): In function `main':
: undefined reference to `sin'
collect2: ld はステータス 1 で終了しました

また怒られました…

今度のポイントは

: undefined reference to `sin'
collect2: ld はステータス 1 で終了しました

この2行です。まず、下の「ld はステータス 1 で終了しました」ですが、これはソースファイル自体には(少なくとも文法的な)間違いがなかったことを意味しています。

そして、「undefined reference to `sin'」はsin関数なんて知りませんよといった意味です。

実はGCCは、そのままではprintfなどの限られた関数しか使うことが出来ません。では、sin関数はどうやったら使えるのでしょうか?こういうときにはマニュアルを調べると良いです。

マニュアル

LinuxなどのUnix系OSでは、manコマンドでマニュアルを読むことが出来ます。例えばsin関数のマニュアルは

$ man sin

とすることで読むことが出来ます。一部抜粋しましょう。

名前
       sin, sinf, sinl - 正弦 (サイン) 関数

書式
       #include <math.h>

       double sin(double x);
       float sinf(float x);
       long double sinl(long double x);

       -lm でリンクする。

この「-lmでリンクする。」が重要です。これは、コンパイルするときに-lmと指定することで、sin関数が使えるようになることを意味しています。

え?こんなに丁寧なマニュアルじゃなかった?

OSによっては、ここまで書いていないことがあります。そんなときには、JMのオンラインマニュアルを使わせてもらいましょう。

では、改めて

$ gcc sample.c -o sample -lm

今度はうまくコンパイルされました。早速実行してみましょう。

$ ./sample

このプログラムは、0から3.14までの値を500個の区間に分割して、その始点の値をsin関数に掛けたものを、1秒ごとに出力しています。だから、終わるまで5分ほどかかります。

…待っていられません!!!

プログラムを強制終了するときには、C-cを押します。今はEmacsを使っていませんが、同じ要領でCTRLキーを押しながらcを押せば、強制終了します。

screenの紹介

このsample.cだと、'sleep(1);'で1秒待つという処理を行っているだけなので、これを削除してしまえば良い訳ですが、将来、実際に時間のかかる数値計算のプログラムとかを実行したときにはどうしましょう?

puttyを通してプログラムを実行している場合、ネットの接続が切れるとそこで動かしていたプログラムは強制終了してしまいます。

ここで、screenを紹介します。

$ screen

として起動すると、コンソールが初期化されたと思います。この状態でsampleを起動しましょう。

$ ./sample

だらだらと動き始めました。ここで、いきなりputtyを終了します!そしてまたputtyを起動してシステムに接続してみましょう。なにもプログラムが起動したりはしていませんね?

では、screenを再び起動しましょう。ただし、-rオプションをつけます

$ screen -r

なんと!さっき起動したsampleが、だらだらと計算を続けているではありませんか!!

これが、screenです。システム側に仮想的なコンソールを作って、その中でプログラムを動作させます。

システムとの接続が切れても、仮想的なコンソールは生き残るのでその中で実行したプログラムも継続して動作します。

接続を戻した後、-rオプションをつけてscreenを起動しなおすと、動いている(はずの)仮想的なコンソールが再び表示されます。

そしてマニアックな世界へ

まぁ、そんなにマニアックでもないんですが、触りだけ紹介します。

まず、次の2つのファイルをEmacsで作ってください。

buggy.c

#include <stdio.h>

int main(int argc, char**argv)
{
  imt i;
  for(i=0; i<10; i++)
    puts("Hello");

  for(;;argv++)
    puts(*argv);

  return 0;
}

Makefile

buggy : buggy.c
clean :
	rm buggy

(最後の行の'rm buggy'の手前の空白は、スペースキーではなく、タブキーを1回押して入力してください。コピペした場合はスペースキーを使った扱いになっているので、空白を一度消してタブキーを押してください)

準備は出来ましたか?

では、Emacsが起動した状態で、M-x compile[enter]と入力してください。

最下行に

Compile command: make -k

と表示されるので、[enter]を押します。(保存していないファイルがあったら、保存するかと聞いてきます)

これで、Emacsがコンパイル処理を勝手に行ってくれます。

エラーが出ましたね?またintをimtと間違えてます。ここで、C-x `を押します。エラーが発生した箇所に、自動的に移動してくれるはずです。

imtをintと直したら、もう一回コンパイルしてもらいましょう。M-x compile[enter][enter]です。

今度はエラーは出ないはずです。実行してみましょう。Emacsを終了…しなくて良いです。

M-x shell[enter]としてください。Emacsの中で、コンソール(正確にはシェル)が起動して、プログラムを実行できるようになります。

buggyプログラムは、適当なオプションを与えて起動すると、まずHelloを10回表示してからオプションに指定した文字を表示するプログラムです。例えばこんな感じです。

$ ./buggy abc 123
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
./buggy
abc
123
セグメンテーション違反です

セグメンテーション違反です!!!

これは、プログラムにバグがあって、文法的には間違っていないけどポインタの使い方を誤っていて、不正なメモリ領域を読み書きしようとしたときに出るエラーです。

では、どの辺でポインタの使い方を間違えているのか調べていきましょう。まずは、プログラムのコンパイルをやり直すために、make cleanします。

$ make clean

そして再びコンパイルするわけですが、今度はmakeにオプションを与えます。まず、M-x compile[enter]してください。

Compile command: make -k

こう表示されたら、続けて、LDFLAGS=-g[enter]と入力してください。

Compile command: make -k LDFLAGS=-g

あるいは、M-x compileを使わず、Emacsの中のシェルで直接makeを実行しても良いです。例えばこんな感じ

$ make clean
$ make LDFLAGS=-g

これで、デバッグ用のプログラムが出来ました。

デバッガを起動します。M-x gdb[enter]として、最下行が

Run gdb (like this): gdb

となったら、実行プログラムである buggy を指定して

Run gdb (like this): gdb buggy[enter]

とします。

Current directory is /home/euler/hiraku/emacs_c/
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-vine-linux"...
(gdb)

こうなりました?では、runコマンドを実行します。これで、デバッガの中でbuggyプログラムが実行されます。プログラムにオプションを与えて実行したければ、runの後に続けてオプションを入力してから[enter]です。

(gdb) run abc 123[enter]
Starting program: /home/euler/hiraku/emacs_c/buggy
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
/home/euler/hiraku/emacs_c/buggy
abc
123

Program received signal SIGSEGV, Segmentation fault.
0x400a4223 in strlen () from /lib/i686/libc.so.6
(gdb)

"Segmentation faultによるSIGSEGVシグナルを受け取った"というメッセージとともに、プログラムが停止します。

0x400a4223 in strlen () from /lib/i686/libc.so.6

は、停止したのが/lib/i686/libc.so.6というファイルのにあるstrlen関数の中だと言っています。

strlen関数?実はこれは、buggy.cで使っているputs関数の中で使われている関数です。なんでそんなことが分かるのか?

では、デバッグを始めましょう。プログラムの実行が停止した状態で、upコマンドを実行してください。

(gdb) up[enter]
#1  0x4008e4e6 in puts () from /lib/i686/libc.so.6
(gdb)

今度は、/lib/i686/libc.so.6にあるputs関数の中に来たと言っています。

upコマンドは、関数呼び出しを順番に巻き戻して、呼び出し元を表示していくコマンドです。さらにupしてみましょう。

gdb_up.png

来ました!buggy.cの10行目のputsが呼び出し元だと表示されるとともに、実際にbuggy.cの10行目が表示されています。

実はこのプログラムのバグは、putsを呼び出しているもう1行上のfor文で終了条件を指定していない事なのですが、少なくともどの辺りがバグっているのかがデバッガを使うことで分かると思います。

それでは、修正してみましょう。9行目を

for(;*argv!=NULL;argv++)

としてください。

10行目の左側に表示されている矢印(=>)はデバッガからの情報を元にEmacsが追加してくれたものですが、これは削除する必要はありません。

9行目を修正したら、保存して、もう一回コンパイルしましょう。

そしてgdbのバッファに戻って、もう一度runします。

(gdb) run abc 123[enter]
The program being debugged has been started already.
Start it from the beginning? (y or n)

「デバッグのために既に実行中のプログラムを、最初から実行しなおしますか?」と聞いているのでy[enter]と答えます。

`/home/euler/hiraku/emacs_c/buggy' has changed; re-reading symbols.
Starting program: /home/euler/hiraku/emacs_c/buggy abc 123
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
/home/euler/hiraku/emacs_c/buggy
abc
123

Program exited normally.
(gdb)

今度は正常に実行が終了しました。

Last modified:2008/07/11 23:53:42
Keyword(s):
References: