こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

ファイルの読み込みが変

Slackware(64bit)というLinuxでプログラミングしていました。
(gcc 4.3.3 glibc 2.9)
そこで出た問題点をこのソースに書きます。
#include<unistd.h>
#include<stdio.h>
char buf[800];
FILE *fd;
void op(void)
{
fd=fopen("/sys/class/power_supply/AC/online","r");
}
int main(void)
{
int i,x;
op();
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}
(使い回しなので余計なものがついてます)
fopenで開くファイルはパソコンのACアダプタが入ると'1'、入らないと'0'になります。
このプログラムを実行しました。

1(ACアダプタつけてenter)
0(はずしてenter)
0(つけてenter)
0(つけてenter)
0(つけてenter)

では、fdをmainで宣言して、opの引数にすればとやってみたら、fscanfに渡るfdが、NULLでSIGSEGVを起こしました。
どうして、こうなるのでしょうか? 複数の関数でファイルをどう扱えばいいのですか?

投稿日時 - 2009-06-28 17:18:05

QNo.5081692

困ってます

質問者が選んだベストアンサー

質問者さんがやりたかったのは、こんな感じだと思う。
#include<unistd.h>
#include<stdio.h>
char buf[800];
void op(FILE **fd)/*受け取るのはFILE *fdのアドレス*/
{
*fd= fopen("/sys/class/power_supply/AC/online","r");
}
int main(void)
{
int i,x;
FILE *fd;
op(&fd);/*渡すのは、FILE *fdのあるアドレス。つまりFILE **を渡す*/
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}
こういうことをしたかったと思うけど、これじゃ、まったく意味無し。

op()は戻り値が無いので「FILE *を返す」って仕様にすれば、FILE *のアドレスを引数に渡す必要は無くなる。

で、そういうふうに書き換えると
#include<unistd.h>
#include<stdio.h>
char buf[800];
FILE * op(void)
{
return fopen("/sys/class/power_supply/AC/online","r");
}
int main(void)
{
int i,x;
FILE *fd;
fd = op();
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}
んで、良く見ると、これも意味ない。単に
fopen("/sys/class/power_supply/AC/online","r");

op();
に置き換わっただけ。余計な関数コールが間に挟まるだけで、何の利点もない。

「fopenに長ったらしく書くのがめんどう臭い」って言うなら
#include<unistd.h>
#include<stdio.h>
#define op() fopen("/sys/class/power_supply/AC/online","r")
char buf[800];
int main(void)
{
int i,x;
FILE *fd;
fd = op();
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}
って書けば済む。

それと
fscanf(fd,"%d",&x);

rewind(fd);
の繰り返しで、デバイスの最新の状態が返ってくる保障は無い。

なので、オープンしたFILEポインタをあっちこっちで使い回すって考えは通用しない。

もし、FILEストリームが「バッファリングあり」になっていれば、デバイスにアクセスするのは最初の1回だけで、2回目以降はバッファに残ってる「前回読んだ値」が返されるだけ。

で、それが嫌でストリームを「バッファリングなし」にしたとしても、デバイスが「rewindが不可能なデバイス」なのであれば、rewindがエラーになり「2回目以降は何が起こるか判らない」と言う事になる。

今回みたいな「ハードウェアデバイスにアクセスする時」は、面倒でも「読み込みのたびに、毎回、ファイルのオープンとクローズを行う」のが鉄則。

何故なら「オープン直後であれば、デバイスの最新の状態が得られるのが保障される筈」だから。

結局は「1回だけオープンして、得られたFILEポインタを使い回そう」としたのが間違い。

「FILEポインタの使い回し」が通用するのは、オープンしたのがファイルとか標準入力とか標準出力とか「rewind等での読み直しが保障されているか、バッファリングを行われても動作に影響がないデバイスを読み書きする時」だけ。

今回のような動作デバイスに対するアクセスでは、ほぼ通用しない。

>複数の関数でファイルをどう扱えばいいのですか?
などと馬鹿な事は考えないで「必要な時に毎回オープンして読み込んだらすぐにクローズをする」って方法に変える事。

じゃないと、正しく動作する保障は得られない。

なので、私なら
int GetStat(void)
{
  FILE *devfp;
  int Result;
  devfp = fopen("/sys/class/power_supply/AC/online","r");
  if (!devfp) return -1;
  Result = fgetc(devfp);
  fclose(devfp);
  if (Result == EOF) return -1;
  return Rresult == '0' ? 0 : 1;
}
って関数を書いて
int main(void)
{
  int i;
  for (i=0;i<5;i++)
  {
    printf("%d",GetStat());
    getchar();
  }
  return 0;
}
って書く。これが、最も安全で最も安定した書き方だと思う。

ハードウェア、ソフトウェア、FILEストリームに対する理解が不充分と思われるので、充分に理解するまで「FILEポインタの使い回し」などのような「高等な技」は使わない方が良いでしょう。10年早いです。

投稿日時 - 2009-06-29 11:30:13

お礼

ありがとうございます。
まいりました。

なにぶん工業高校でしか正式な勉強してないもので すいません。

投稿日時 - 2009-06-29 17:47:48

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.3

>op(fd);

これだと、op関数の中でセットしたfdの値が、main関数に戻ってきません。
引数で指定したfdの変更結果をmain関数に戻すには、fdのアドレスを渡す必要があります。

投稿日時 - 2009-06-28 19:21:55

お礼



ああ、なるほど! 普通の変数の箱の中がアドレスととらえれば! 

ありがとうございます。

投稿日時 - 2009-06-29 17:39:51

ANo.2

ポインタと、ポインタのポインタを理解してない事から、起きるバグかと思いますよ。

投稿日時 - 2009-06-28 17:33:48

ANo.1

>では、fdをmainで宣言して、opの引数にすればとやってみたら

そのソースを見せてください。

投稿日時 - 2009-06-28 17:29:22

補足

これです。
#include<unistd.h>
#include<stdio.h>
char buf[800];
FILE *op(void)
{
return fopen("/sys/class/power_supply/AC/online","r");
}
int main(void)
{
int i,x;
FILE *fd;
fd=op();
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}

投稿日時 - 2009-06-28 17:44:26

お礼

まちがえました!
#include<unistd.h>
#include<stdio.h>
char buf[800];
void op(FILE *fd)
{
fd= fopen("/sys/class/power_supply/AC/online","r");
}
int main(void)
{
int i,x;
FILE *fd;
op(fd);
for (i=0;i<5;i++)
{
fscanf(fd,"%d",&x);
printf("%d",x);
getchar();
rewind(fd);
}
fclose(fd);
return 0;
}

投稿日時 - 2009-06-28 17:49:48

あなたにオススメの質問