PDA

View Full Version : Cでfscanfを使うとInternal server errorが出ることがある


nen-nen
2002/05/19, 10:00 PM
こんにちは、ねんネンです。
また初歩的な質問かもしれませんが、
次のようなプログラムを作ったときにInternal server errorなってしまうことがあります。

FILE* logfile;
char *log[50];
unsigned char i;

logfile = fopen("hoge","r+");
i=0;
while(scanf(logfile,"%s\n",log[i]) != EOF){
printf("%s<br>\n",log[i]); /*デバッグ用に出力*/
i++;
}
fprintf(logfile,"ほげ\n");
fclose(logfile);

なぜかデバッグ出力の文字列が0〜2個のときは大丈夫なのですがそれ以上になるとエラーが出てしまいます。しかも、空のlogファイルをアップするとエラーにならなくなりデバッグ出力が3つ以上になるとまたエラーになってしまいます。

これの対処方法はあるのでしょうか?

gen-ta
2002/05/20, 03:12 AM
●charポインタは、出来れば使わない事をおすすめします。
 どうしても使わざるを得ない場合は、宣言の直後に所定の
 長さの文字列のアドレスに初期化しておくと、不定アドレス
 への書き込みというエラーを避けられます。
 このエラーは、条件によっては一見正常に動作してしまう
 ためタチが悪いです。

●Cは「文字列型」を持たないため、文字列は文字の配列で
 表すことはご存知ですね?
 したがって、文字列の配列は文字の2次元配列になります。
 x文字の文字列をy個確保するには、char str[y][x]です。
 str[x][y]と間違え易いので注意してください。

●また、charに限らず、配列を使用する場合は宣言範囲外
 への書き込みには細心の注意を払うべきです。
 下の例でwhileをforに替えたのはこのためです。

●演算子やキャスト等、明示的カッコを活用しましょう。

ログ1件が256バイト以下で、50件まで扱う場合、私ならこう書きます。
(以下のリストはインデントに全角スペースを使ってるのでccによっては通りません。)

  #define MAXNO 50
  - - - - - -
  FILE* logfile;
  char log[MAXNO][256];
  unsigned char i;

  logfile = fopen("hoge","r+");
  for(i=0;i<MAXNO;i++){
   if(scanf(logfile,"%256s\n",&(log[i][0])) == EOF) break;
   printf("%256s<br>\n",&(log[i][0]));
  }
  fprintf(logfile,"ほげ\n");
  fclose(logfile);

これだと、件数や文字数が多くなるとメモリが不安ですね。
ログの加工や集計をする場合は、別途ファイルを開くと
割り切れば不安は解消します。

  FILE* logfile;
  char log[256];
  char *logptr; /*このままでは不定です。危険!*/
  unsigned char i;

  logptr=&log[0]; /*この一文が無いと落ちます*/

  logfile = fopen("hoge","r+");
  while(scanf(logfile,"%256s\n",logptr)) != EOF){
   printf("%256s<br>\n",logptr);
  }
  fprintf(logfile,"ほげ\n");
  fclose(logfile);

kyling
2002/05/20, 10:06 AM
char hoge[5] ;   // 箱のようなの

□<--- char* p_hoge = &hoge[1] ;




char* p_hoge[5] ; // 箱を指す矢印のようなの
<---
<---
<---
<---
<---
(初期化されてなかったら指してる場所は不定?)


で、
fscanf( logfile, "%s\n", log[i] ) ; は
log[i] の指してる箱を先頭として logfile から "%s\n" という形で読み込め〜ってことだから
どこかにきっちりと 「箱の列」 を用意して、log[i] がそれを指してないとダメだよね〜


簡単に書くとこんな感じ…かな?

gen-ta
2002/05/20, 12:06 PM
>きりんさんへ。
やっぱビジュアルな説明はわかりやすいなあ。

>nen-nenさんへ。
私の先のリストに誤りが有ります。
scanf(logfile,"%256s\n",logptr)では
*logptrに257文字入るから、
宣言は、char log[MAXNO][256+1]; でないとダメ
ですなあ。
偉そうな事書いといて、自分で間違ってました。
反省。

ついでに、補足。
●配列名は配列のポインタを示す事が出来るので、
 &(log[i][0]) を log[i] と書いてもOKですが、
 間違いの元ですので、おすすめしかねます。
 (事実、今回のnen-nenさんの誤解のタネはこの辺り
 に有ると推察します。)
 「明示的カッコ」と書いたのはこうした物の事です。
 最近のコンパイラは賢いですから、冗長な表記を
 しても最適化してくれるのでパフォーマンスは
 落ちませんよ。

nen-nen
2002/05/20, 04:29 PM
こんにちは、ねんネンです。

自分の書いたプログラムで読み込む文字数を制限したくなかったからcharポインタの配列にしたんです。
ここで提示したプログラムはログファイルがあって一番上に新しい1行を加え一番下の1行を別ファイルに吐き出すためのものです。

かなりメモリを使うような感じもしますが
char[MAX][];
のような感じで……あ、無理だ……

どうにかして文字数に制限なくscanfで読み込んでファイル操作できないでしょうか?

gen-ta
2002/05/20, 07:11 PM
VC等、独自機能で不定(可変)バイト変数を実装している
処理系もありますが、一般にはmallocでしょうか。

void *ptr;
ptr = malloc(filelength(int fileno(logfile)));
if (ptr == NULL) {
 fprintf(stderr, "More Memory!\n");
}
でファイル全体を読み込めるメモリの確保を
試みる事は可能です。

が、メモリを確実に開放する手段が無く、エラー
処理に制約が有るため、どちらかといえば、
処理・データ共に小規模の場合に向きます。

貧乏性の私としては、固定長ずつ読込み/書込みし、
\0(\n)を検出してサイズが足りなければ追加読込み
する方法がしっくり来ます。
ログのフォーマットを独自設定出来るなら、サイズ情報を
ファイル先頭や行にちりばめるのも手です。


− − − −【追伸】− − − −

脅かすわけじゃないですが、mallocを避けたい理由です。
OOM Killer
http://216.239.33.100/search?q=cache:QBaKTaveKBcC:www.linux.or.jp/JM/html/LDP_man-pages/man3/malloc.3.html+malloc+oom+killer&hl=ja&lr=lang_ja
Double Free  xreaもlinuxだそうです
http://216.239.33.100/search?q=cache:IiGHnQrJtRgC:www.jpcert.or.jp/wr/2002/wr021101.txt+malloc+double+free&hl=ja&lr=lang_ja

nen-nen
2002/05/20, 08:38 PM
こんにちは、ねんネンです。

指定バイト数ずつの読み込みを繰り替えするとは思いもよりませんでした。
fgets(log,256,logfile);を繰り返せばすべて読み込めますよね?
しかしながら、読み込みながら書き込むというのはファイルの書き込み位置と読み込み位置を行き来する上に読み込みバイト数が新たにファイルに書き込むバイト数より少ない場合はファイルの内容が壊れるのではないかと思います。
そこで思ったのですがファイルロックもかねて一時ファイルを作るつもりだったのでログファイルから必要行だけを一時ファイルにコピーしておいて、ロックファイルをあらたにWモードで開き新しく書き込むものと一緒に一時ファイルの内容も書き込むという方法は有用でしょうか?

gen-ta
2002/05/20, 08:46 PM
グッドアイデアです。

nen-nen
2002/05/20, 08:59 PM
それでは、この方法でやってみます。
しかしながらなんらかエラーによって途中でプロセスが終了されたときのことはちょっと気に成りますが……