ページ 11

ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 17:30
by ゆーずぃ
2日悩んだのですが、結局分からなかったので質問させて頂きます。
fopenの失敗処理までをセットにしたFileOpen関数を作ったのですが、fpに実引数として渡したポインタに代入が行われません。というか実引数として渡されていないような感もあります。私の考えだと、FILE構造体のポインタを引数として渡しているので、fopenの結果をfilenameのアドレスに直接代入して関数が終わる、といったイメージです。
必要なところだけ抜粋したのでおかしなところもあるかもしれません。よろしくお願いします。

環境はWindows xp
Visual C++ 2008 です。



以下ソースです。
#include <stdio.h>
#include <stdlib.h>


void FileOpen(FILE *fp , const char *filename,const char *mode);

void main(void){

    FILE *inputfile;
    int c;

    FileOpen(inputfile , "hoge.txt" , "r");

    while((c = fgetc(inputfile)) != EOF){
    printf("%c",c);
    }

}

//ファイルが開ければファイルポインタを返す(代入で)
void FileOpen(FILE *fp , const char *filename,const char *mode){    

    if((fp = fopen(filename,mode)) == NULL){                 //ファイルが開けるか
        printf("FILEが開けません。終了します。");
        exit(EXIT_FAILURE);        
    }
}

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 17:39
by スキマ妖怪
引数をポインタのポインタにするか、

void FileOpen(FILE **fp , const char *filename,const char *mode){

if((*fp = fopen(filename,mode)) == NULL){ //ファイルが開けるか
printf("FILEが開けません。終了します。");
exit(EXIT_FAILURE);
}
}


戻り値で返してあげるとうまくいくと思いますよ。

FILE *FileOpen(FILE *fp , const char *filename,const char *mode){

if((fp = fopen(filename,mode)) == NULL){ //ファイルが開けるか
return NULL;
}
return fp;
}

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 18:17
by ゆーずぃ
どちらでも解決しました!ありがとうございます!!
出来ればもう一つ教えて頂きたいのですが、今回初めてポインタのポインタを使用しました。理屈はなんとなく分かるのですが、今まで使いどころがいまいち分からなかったのです。
ですので、今回ポインタのポインタで解決した理由や、ポインタのポインタの他の使いどころなどを教えては頂けませんか?宜しくお願いします(><)

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 19:51
by スキマ妖怪
呼び出し元のポインタ変数の中身(アドレス)を変更するためには、
ポインタ自体のアドレスが必要になるからとしか…

難しく考えなくても、単に「ポインタ」のポインタというだけです。


説明下手でスイマセンm(_ _)m

使い所についても、ムリに使う必要はないと思います。
必要ならいやでも使うことになるでしょうし
(具体例思いつきませんでしたorz)

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 22:14
by あまち
例えば
void funcA(int A)
{
A = 2;
}

void funcB(int *B)
{
*B = 2;
}
という2つの関数をmain関数から実行する場合を考えてください。
int C = 0;  // ・・・①
funcA( C);  // ・・・②
funcB(&C);  // ・・・②
①でint型の値を格納する領域がメモリに確保され
確保された領域にint型の0を示す値が格納されます。
funcAは引数を値渡しで渡しますので
②を実行してもCの値は変化しません。
funcBは引数を参照渡しで渡しますので
③を実行するとCの値が変化します。

ここで着目してほしいのは
ある関数に引数として渡した変数の値を変更したい場合、
その関数には変更したい変数のポインタを渡している
ということです。


今回変更したいのはFILE *fpですよね。
なのでFILE *fpのポインタを渡してあげればよいのです。

ついでですが、
void funcC(int *C)
{
printf("C = 0x%X\n", C); // ・・・②
C = NULL;
printf("C = 0x%X\n", C); // ・・・③
}
という関数をmain関数から実行する場合を考えてください。
int C = 0;
printf("C = 0x%X\n", &C); // ・・・①
funcC(&C);  // ・・・④
printf("C = 0x%X\n", &C); // ・・・④
結果は下記のようになります。
C = 0x12345678
C = 0x12345678
C = 0x0
C = 0x12345678

何が言いたいかというと
参照渡しは変数の値が格納されているアドレスを渡し
そのアドレスの先(要は変数の値)を変更するだけであって
アドレスそのものを変更することはできないのです。


これで説明になったかはわかりませんが・・・
何かの助けになれば幸いです。

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月14日(水) 22:23
by フリオ
 
 今回のように、呼び出した関数内で変数の値を変更し、
それを呼び出しもとの関数に反映させたい場合に、意味をよく理解せずに
"引数としてポインタを渡す"
と憶えると、今回のように、
ポインタを渡しているのに値の変更が反映されない、
ということが起こります。
これは、
"値を変更したい変数へのポインタ を渡す"
です。

 Cでは関数に渡せるのは値だけで、C++の"参照"の様に変数自体を関数に渡す手段がありません。
なので、その変数へのポインタ の値を渡し、その変数のある場所(その変数が保持する値のある場所)を
教えることで、その値を書き換えます。

 ですから、int型の変数の値を変更したい場合は、int型 へのポインタである int *型、
FILE *型の変数の値を変更したい場合は、FILE *型 へのポインタである FILE **型
を渡します。
 
画像

Re:ファイルポインタの正しい扱い方を教えて下さい。

Posted: 2010年7月15日(木) 02:00
by ゆーずぃ
皆さん、ご回答ありがとうございます!

>スキマ妖怪さん

とかく難しく考えがちだったのですが、スキマ妖怪さんのお陰で身構えずに学ぶことができました。ありがとうございます。

>あまちさん

検証ソース、ありがとうございます!抽象的な説明よりも理解でき、とても分かり易かったです。

>フリオさん

>ですから、int型の変数の値を変更したい場合は、int型 へのポインタである int *型、
 FILE *型の変数の値を変更したい場合は、FILE *型 へのポインタである FILE **型
 を渡します。

この一文で目の前が拓けましたwありがとうございます!


皆さんのおかげで、完全にとは言えないでしょうが、理解できました!お三方の説明の順番がいい具合に順を追っていてとても分かり易かったですw

本当にありがとうございました。心から感謝致します。