わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

2重ループで変数の値は何回変わるか,デバッガで確認

昨日付の記事の続きです.ソースを再掲します.

#include <stdio.h>

int main(void)
{
  int i = 0, j = 0;

  for (i = 1; i <= 9; i++) {
    for (j = 1; j <= 9; j++) {
      printf("%2d ", i * j);
    }
    printf("\n");
  }

  return 0;
}

上のプログラムを実行したとき,2重ループの中で変数jの値は何回,変わるかというのは,デバッガGDBで求めることができます.コマンドは以下のとおり.

$ gcc -g -O0 -o kuku kuku.c
$ gdb kuku
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
(略)
(gdb) b 7
Breakpoint 1 at 0x400593: file kuku.c, line 7.
(gdb) r
Starting program: /home/takehiko/tmp/kuku

Breakpoint 1, main () at kuku.c:7
7         for (i = 1; i <= 9; i++) {
(gdb) watch j
Hardware watchpoint 2: j
(gdb) c
Continuing.
Hardware watchpoint 2: j

Old value = 0
New value = 1
0x00000000004005a3 in main () at kuku.c:8
8           for (j = 1; j <= 9; j++) {
(gdb) c
Continuing.
Hardware watchpoint 2: j

Old value = 1
New value = 2
0x00000000004005c1 in main () at kuku.c:8
8           for (j = 1; j <= 9; j++) {
(略)
(gdb) c
Continuing.
 9 18 27 36 45 54 63 72 81

Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
__libc_start_main (main=0x40057d <main>, argc=1, argv=0x7fffffffe208,
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7fffffffe1f8) at libc-start.c:321
321     libc-start.c: そのようなファイルやディレクトリはありません.
(gdb) c
Continuing.
[Inferior 1 (process 22690) exited normally]
(gdb) q

GDBを使うために,コンパイル時に-gオプションをつけておきます.gdb kukuで,デバッガの使用を開始します.「(gdb) 」が,プロンプトです.
最初に「b 7」(とEnter.以下同じ)を与え,ブレークポイントを2重ループの先頭(実行する前)とします.「r」でrunし,ブレークポイントまで来て制御が止まります.「watch j」は,変数jの値が変わったところで止まるようウォッチせよという指示になります.「c」はcontinueを意味し,ここでは,jの値が変わるところまで,実行を進めます.jの値が変わると,「Old value = 0」「New value = 1」と,変わる前後の値を教えてくれて,それからまたプロンプトです.あとはcとEnterの繰り返しです.全部で92回,continueすると,「exited normally(通常終了)」となり,「q」でquitします.
ただ,このやり方で,jの値が変わる回数を数えるのは,効率的と言えません.
そこでRubyスクリプト(auto_gdb_kuku.rb)を作りました.いつものようにGistに置いておきます.
ruby auto_gdb_kuku.rbを実行すると,上と同じ動作を,途中のキー操作なしで行ってくれます.*1
これでも煩雑です.そこで,ruby auto_gdb_kuku.rb | grep 'New value'を実行します.すると,「New value」を含む行,言い換えるとjがどんな値に変わったかだけが,順に出力されます.
あともうひと息です.ruby auto_gdb_kuku.rb | grep 'New value' | wc -lを実行すれば,2重ループの中で変数jの値が何回変わったかを表す「90」だけが出力されます.

*1:九九の範囲はコマンドラインで指定できて,ruby auto_gdb_kuku.rb 3とすれば,かけられる数・かける数とも1から3までになります.かける数の上限も変えたければ,ruby auto_gdb_kuku.rb 3 2のようにします.