ページ 11

デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 20:00
by たかし
大学の課題でデバッグ(gdb)を使ってプログラムの間違いを見つけて修正する課題を出されたのですが、
家でやってもうまくデバッグすることができません。
まずMinGWというソフトをインストールしてデバッグしてみました。

$ gcc -g bad.c
GNU gdb (GDB) 7.4
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /path/example...done.
(gdb) run dic001 text001
Startign program: C :\MinGW\msys\1.0\home\*******\a.exe dic001 text001
[New Thread 7144.0x1d44]

Procram recieved signal SIGSEGV, Segmentation fault.
0x76e17732 in ntdll!RtlEqualSid () from C:Windows\system32\ntdll.dll

という感じで何行目にエラーがあるのかわかりません。
学校のパソコンでやった時はどの関数の何行目に間違いがあるのか出てきました。

また実行したプログラムは以下のものです。

コード:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define Max_Length  20 
#define Table_Size  127 
typedef enum { Empty, Dictionary, Text_File } kind;

typedef struct {
  char spell[Max_Length+1];
  kind flag;
} item;

item checktable[Table_Size];
int ptr;

main(int argc, char *argv[]){
  FILE *f;

  if (argc != 3){
    printf("引数の個数は二つでなければなりません\n");
    exit(1);
  }
  clear_checktable();
  f = fopen(argv[1], "r");
  if (f == NULL){
    printf("%s: 読むことができません\n", argv[1]);
    exit(1);
  }
  read_in(Dictionary, f);
  fclose(f);
  f = fopen(argv[2], "r");
  if (f == NULL){
    printf("%s: 読むことができません\n", argv[2]);
    exit(1);
  }
  read_in(Text_File, f);
  fclose(f);
  print_out();
  exit(0);
}

read_in(FILE *f, kind phase){
  int  c, k;
  char word[Max_Length+1];

  c = getc(f);
  while (c != EOF){
    if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'){
      k = 0;
      while (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'){
	if (k > Max_Length) {
	  printf("単語の長さが長過ぎます\n");
	  exit(1);
	}
	if (c <= 'Z')
	  c += 'a'-'A';
	word[k] = c;
	c = getc(f);
      } 
      word[k] = 0;
      insert_checktable(word, phase);
    }
    else
      c = getc(f);
  }
}

clear_checktable(){
  int  h;

  for (h = 0; h < Table_Size; h++)
    checktable[h].flag = Empty;
  ptr = 0;
}

insert_checktable(char *word, kind phase){
  int  h, start;

  h = 0;
  while (h < ptr){
    if (strcmp(word, checktable[h].spell) == 0){
      if (phase = Dictionary){
	printf("辞書中に同じ単語が複数個存在します\n");
	exit(1);
      }
      return;
    }
    h++;
  }
  if (ptr == Table_Size){
    printf("単語の種類が多過ぎます\n");
    exit(1);
  }
  strcpy(checktable[h].spell, word);
  checktable[h].flag = phase;
  ptr++;
}

print_out(){
  int  count, k;

  count = pack_checktable();
  sort_checktable(count);
  for (k = 0; k < count; k++);
  printf("%s\n", checktable[k].spell);
}

int pack_checktable(){
  int  k, h;

  for (h = 0; h < ptr; h++){
    if (checktable[h].flag == Text_File)
      continue;
    checktable[k] = checktable[h];
    k++;
  }
}

sort_checktable(int count){
  int  k, j, m;
  item tmp;

  for (k = 0; k < count; k++){
    m = k;
    for (j = k+1; j < count; j++)
      if (strcmp(checktable[j].spell, checktable[k].spell) < 0)
	m = j;
    tmp = checktable[k];
    checktable[k] = checktable[m];
    checktable[m] = tmp;
  }
}
ちなみに
text001とdic001には
a
という文字が入っています。

OSはwindows7です。

これでは課題が始められません。
どうすればいいのか教えてほしいです。
よろしくお願いします。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 20:06
by softya(ソフト屋)
肝心のgdb起動コマンドの部分が無いんですが、どうなっています?

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 20:27
by たかし
すみません、書き忘れていました。
gdb起動コマンドは次の通りに入力しました。

$ gdb a.exe

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 20:59
by softya(ソフト屋)
cygwinで試した所、その手順で問題なく表示されました。
MinGWは入れていないので確認できていないです。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 21:28
by box
playhuman さんが書きました: 学校のパソコンでやった時はどの関数の何行目に間違いがあるのか出てきました。
それは、コンパイル時のエラーメッセージのことではありませんか?
playhuman さんが書きました:

コード:

  read_in(Dictionary, f);
  read_in(Text_File, f);
read_in(FILE *f, kind phase){
第1引数と第2引数の順番は、本当にそれで正しいですか?

ところで、インデント(字下げ)をちゃんとしましょう。
個人的には、インデントが空白2個分では少ないように思います。
空白4個にすると、かなりメリハリのきいたコードに見えるようになると思います。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 21:34
by みけCAT
$ gcc -g3 bad.c
としてコンパイルしたらどうなりますか?

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 22:32
by taketoshi
質問に対する異なる指摘ではありますが
VisualStdio2008でコンパイルしてみました。

コード:

int pack_checktable(){
  int  k, h;
 
  for (h = 0; h < ptr; h++){
    if (checktable[h].flag == Text_File)
      continue;
    checktable[k] = checktable[h];
    k++;
  }
}
こちらの関数はint型を戻り値としていますが、return文がありません。
それ以外にも関数のボトムアップ宣言がされておらず、
main関数からそれ以下の関数が見えず呼び出すことができないです。
あと、pack_checktable関数内のローカル変数kは初期化されておりませんが大丈夫ですか?

何行目にエラーがあるとの表示が出たとの事ですが、
学校のパソコンにはVisualStdioが入っているのではないでしょうか。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 22:43
by たかし
みなさん、アドバイスありがとうございます。
softya(ソフト屋)様がおっしゃられたように、cygwinで試したらちゃんとできました。
ちなみにみけCAT様のおっしゃっていた -g3 でもcygwinだったらできました。
最初cigwinで試してもできなかったのですが、別のパソコンで試してみたらできました。
何故MinGWで出来なかったのかはよくわかりませんが、これで課題に取り掛かれます。
taketoshi様、box様、間違いの指摘ありがとうございます。とりあえず自分でも確認して修正しておきます。
あと学校のパソコンにvisualstdioが入っているかはわかりませんが、学校でcを書くときはemacsを使っています。
多分課題でわからないところがあったらまたみなさんに質問するかもしれませんが、その時はまたよろしくお願いします。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 22:45
by softya(ソフト屋)
あっ今回の場合、cygwinでも行番号はでませんよ。
ソースコードが狙い通りなら、標準ライブラリの関数内でエラーに成ると言うすごく意地悪なコードに成っています。
普通、課題にするとしても私なら標準ライブラリの関数でエラーにはしないと思うんですが、これが課題として出されたのならそういう課題って事でしょう。

【追記】
>学校でcを書くときはemacsを使っています。
と言うことはemacsからgdbを起動しているんでしょうか?

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月10日(金) 23:02
by たかし
softya(ソフト屋)様、私はあまり詳しくないのでemacsからgdbを起動しているかはよくは分からないのですが、
学校のパソコンにはUNIXが入っていて、端末エミュレータというのを起動してそこでgdbやgcc等のコマンドを入力しています。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 03:40
by h2so5
emacsからgdbを起動する場合は M-x gdb と入力して実行します。
そうではなくて $ gdb a.exe と入力している場合はgdbを単独で起動していることになります。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 10:48
by たかし
ということはgdb単独で起動してたということですね。
h2so5様、ありがとうございます。
それでまた、問題が出てしまいました。
さきほど指摘された間違いを修正してからデバッグしたら今度は文字化けのような現象が起きてしまいました。

$ gcc -g bad.c

$ gdb a.exe
GNU gdb (GDB) 7.6.50.20130508-cvs (cygwin-special)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-cygwin".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/******/a.exe...done.
(gdb) run dic001 text001
Starting program: /home/*******/a.exe dic001 text001
[New Thread 1604.0x11f4]
[New Thread 1604.0xa58]
▒▒▒▒▒▒▒ɓ▒▒▒▒P▒ꂪ▒▒▒▒▒▒▒݂▒▒܂▒
[Inferior 1 (process 1604) exited with code 01]

なのでcygwinの文字化け対策をググってみたら以下のコードを入力してプログラムの文字コードをJISで保存すると文字化けが治るとのことでした。

$ export LANG=ja_JP.SJIS

bad.cの文字コードをJISに指定して保存しました。
再びコンパイルしてgdbを起動してrunコマンドを入力したら以下のものが出力されました。

yoshiki@yoshiki-PC ~
$ gcc -g bad.c

yoshiki@yoshiki-PC ~
$ gdb a.exe
GNU gdb (GDB) 7.6.50.20130508-cvs (cygwin-special)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-cygwin".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/*******/a.exe...done.
(gdb) run dic001 text001
Starting program: /home/*******/a.exe dic001 text001
[New Thread 704.0x1798]
[New Thread 704.0x177c]
<-=qCf$KF1$8C18l$,J#?t8DB8:_$7$^$9
[Inferior 1 (process 704) exited with code 01]

先ほどよりはましになったものの、結局文字化けしてしまいました。
修正後のプログラムも載せておきます。
box様にご指摘いただいたインデントは、後で直しておきます。
今回はこのままでお願いします。すみません。

コード:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define Max_Length  20 
#define Table_Size  127 
typedef enum { Empty, Dictionary, Text_File } kind;
 
typedef struct {
  char spell[Max_Length+1];
  kind flag;
} item;
 
item checktable[Table_Size];
int ptr;
 
main(int argc, char *argv[]){
  FILE *f;
 
  if (argc != 3){
    printf("引数の個数は二つでなければなりません\n");
    exit(1);
  }
  clear_checktable();
  f = fopen(argv[1], "r");
  if (f == NULL){
    printf("%s: 読むことができません\n", argv[1]);
    exit(1);
  }
  read_in(f,Dictionary);
  fclose(f);
  f = fopen(argv[2], "r");
  if (f == NULL){
    printf("%s: 読むことができません\n", argv[2]);
    exit(1);
  }
  read_in(f,Text_File);
  fclose(f);
  print_out();
  exit(0);
}
 
read_in(FILE *f, kind phase){
  int  c, k;
  char word[Max_Length+1];
 
  c = getc(f);
  while (c != EOF){
    if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'){
      k = 0;
      while (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'){
    if (k > Max_Length) {
      printf("単語の長さが長過ぎます\n");
      exit(1);
    }
    if (c <= 'Z')
      c += 'a'-'A';
    word[k] = c;
    c = getc(f);
      } 
      word[k] = 0;
      insert_checktable(word, phase);
    }
    else
      c = getc(f);
  }
}
 
clear_checktable(){
  int  h;
 
  for (h = 0; h < Table_Size; h++)
    checktable[h].flag = Empty;
  ptr = 0;
}
 
insert_checktable(char *word, kind phase){
  int  h, start;
 
  h = 0;
  while (h < ptr){
    if (strcmp(word, checktable[h].spell) == 0){
      if (phase = Dictionary){
    printf("辞書中に同じ単語が複数個存在します\n");
    exit(1);
      }
      return;
    }
    h++;
  }
  if (ptr == Table_Size){
    printf("単語の種類が多過ぎます\n");
    exit(1);
  }
  strcpy(checktable[h].spell, word);
  checktable[h].flag = phase;
  ptr++;
}
 
print_out(){
  int  count, k;
 
  count = pack_checktable();
  sort_checktable(count);
  for (k = 0; k < count; k++);
  printf("%s\n", checktable[k].spell);
}
 
int pack_checktable(){
  int  k, h;
  k=0;
 
  for (h = 0; h < ptr; h++){
    if (checktable[h].flag == Text_File)
      continue;
    checktable[k] = checktable[h];
    k++;
  }
}
 
sort_checktable(int count){
  int  k, j, m;
  item tmp;
 
  for (k = 0; k < count; k++){
    m = k;
    for (j = k+1; j < count; j++)
      if (strcmp(checktable[j].spell, checktable[k].spell) < 0)
    m = j;
    tmp = checktable[k];
    checktable[k] = checktable[m];
    checktable[m] = tmp;
  }
}
どうしたら治りますか?
よろしくお願いします。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 10:51
by みけCAT
bad.cの文字コードをJISではなくShift_JISで保存してください。
Windowsなら標準のはずです。

追記
その情報が載っていたサイトはどこですか?

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 10:57
by box
文字化けのような現象の件はさておき…
playhuman さんが書きました:

コード:

read_in(FILE *f, kind phase){
  int  c, k;
      k = 0;
    if (k > Max_Length) {
    word[k] = c;
      word[k] = 0;
read_in関数における変数kの役割がわかりません。
日本語で説明していただけますでしょうか。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 12:50
by かえで
>emacsからgdbを起動しているんでしょうか?

学校の課題でemacs上でgdbを使うわけがないのをわかってるくせに
わざわざそれを聞く意味がわからない。
初心者相手に優越感に浸りたいのでしょうか?

あと、gdbと言ってるのになぜかVisualStadioを使わせてるのも良くわからない。

>「日本語」で説明していただけますでしょうか

なんで君もわざわざ見下したような言い方しか出来ないのかな?
そもそも質問者はgdbを使用してデバッグという課題なわけで、
gdbの使い方を聞いているんですが?
boxさんもプログラムよりもまずは日本語を勉強した方がいいと思います。

おそらく出題者はステップ実行を使ってのデバッグを想定しているんだと思います。
「gdb ステップ実行」などで検索かけると良いのではないでしょうか?

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 14:28
by h2so5
かえで さんが書きました: 学校の課題でemacs上でgdbを使うわけがないのをわかってるくせに
わざわざそれを聞く意味がわからない。
初心者相手に優越感に浸りたいのでしょうか?
なぜ学校の課題でemacs上からgdbを使用しないことが自明なのでしょうか?
emacs上からgdbを使う機能はマニアックなものありませんから、使っていても不思議ではありません。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 14:46
by softya(ソフト屋)
>学校の課題でemacs上でgdbを使うわけがないのをわかってるくせに
>わざわざそれを聞く意味がわからない。
>初心者相手に優越感に浸りたいのでしょうか?

それは誤解だと思います。
emacsからgdbを使ったほうがソースコードが自動的に表示されるため非常に分かりやすく・便利になります。
そして、さほど難しい操作ではありません。
そうやって学習している学校も確かにありますので、別段難しい要求をしているとも思えません。

>あと、gdbと言ってるのになぜかVisualStadioを使わせてるのも良くわからない。

それは誤読だと思います。
VisualStadioのことを書いたのはtaketoshiさんであり、質問者のplayhumanさんではありません。
taketoshiさんはVisualStadioを使っているのでは?と疑問を呈したに過ぎません。

>なんで君もわざわざ見下したような言い方しか出来ないのかな?

かえでさん自身が見下したような書き方をされては説得力がありません。
また、この掲示板では言い争いを避けて頂けると助かります。
このトピックだと質問者であるplayhumanさんに迷惑がかかりますので、別トピックで【雑談】としてケンカ口調ではなく話し合って頂けると助かります。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 15:12
by かえで
私の判断でトピックを分離させて頂きました。
よろしくお願いします。

「【雑談】 書き込みについて。バッグ(gdb)を使ってプログラムのエラーを見つける • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13044

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 15:35
by box
問題発言ですので添削させて頂きます。
自重をお願いします。
このトピックを荒らさないでください。 by softya(ソフト屋)

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 16:09
by softya(ソフト屋)
色々とありましたのでplayhumanさんも書き込みにくいとは思いますが、文字コードの件はどうなりましたでしょうか?
cygwinで動かすプログラムでの日本語での表示については、UTF-8にすれば良いという話も有るみたいですがcygwinバージョンで変わるかもしれません。
お試し頂ければと思います。

SJISに関しては、ここかな?
「[cygwin]gccで作ったプログラムで,日本語が文字化けする」
http://nanoappli.com/blog/archives/3528

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 17:07
by たかし
Cpadには文字コードの指定にUTF-8がなかったのでプログラムをメモ帳にコピーしてUTF-8で保存したらちゃんと日本語が表示されました。
softya(ソフト屋)様、ありがとうございます。
みけCAT様、先ほどのコマンドが載っていたのは以下のサイトです。

http://www.cis.twcu.ac.jp/~osada/comp2a/cygwin.html

かえで様に教えていただいたステップ実行を検索したら、
デバッグを使ってミスを見つける手順を紹介しているサイトが出てきました。
使い方がまだわかっていなかったので助かりました。ありがとうございます。

またわからないことがあったら質問すると思いますが、よろしくお願いします。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 17:10
by softya(ソフト屋)
よかった表示されましたね。
実は私のcygwinではutf-8ではダメした。2009年バージョンってのが敗因かも。

解決したら、解決チェックをお願いしますね。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月11日(土) 18:09
by ISLe
LANG環境変数でシフトJISを指定して文字化けが直るのは、シフトJISで出力される文字列です。
いまのCygwinはデフォルトがUTF-8で、gccでコンパイルしたプログラムもデフォルトでUTF-8として文字列を出力します。
Cygwinのシェルに対してLANG環境変数でシフトJISを指定すると逆に既存プログラムの日本語メッセージが化けてしまいます。

gccに --input-charset=cp932 というオプションを付けるとシフトJISで書かれたソースファイルをUTF-8に変換してからコンパイルしてくれます。

Re: デバッグ(gdb)を使ってプログラムのエラーを見つける

Posted: 2013年5月12日(日) 00:45
by たかし
ISLe様、便利ですねその機能。
次回から使わせてもらいます、ありがとうございました。