【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.20256

ファイルポインタの操作
投稿者---りん(2005/03/07 20:12:11)


はじめまして。
Microsoft Visual C++ 6.0を使用しています。

mainでファイルポインタを宣言し、OPENして関数に渡して
その先で出力しようとすると、fwriteの所でAccess Violationで落ちます。
コンパイルは通ってます。

ファイルポインタの渡し方がまずいのでしょうか?
どなたかご教授ください。
よろしくお願いいたします。

--------------------------------------------------------------


#define OUTFILE     "C:\\outdata.txt"   //出力


int main(){


    FILE*   outfp;      //出力データファイル

    int     ret = 0;    //戻り値判定



    //出力ファイルオープン

    outfp = fopen(OUTFILE, "w");
    if(outfp == NULL){
        printf("出力ファイルがオープンできませんでした。\n");
        return 0;
    }

    //出力処理

    ret = ComOutPut( outfp );
    
]

    
int ComOutPut( FILE  *fpOutFile )    
{
    char        test[4];

    memset(test, '\0', sizeof(test));
    strcpy(test, "2005");

    fwrite( test, sizeof( test ), 1, fpOutFile );

    if ( ferror( fpOutFile ) != 0 )
    {
        // エラー

        return RTN_NG;
    }

    return RTN_OK;
}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ファイルポインタの操作 20258 Sophist 2005/03/07 20:53:25
<子記事> Re:ファイルポインタの操作 20259 RiSK 2005/03/07 20:58:35
<子記事> Re:ファイルポインタの操作 20261 RAPT 2005/03/07 21:07:26
<子記事> もう一度確認してみます! 20263 りん 2005/03/07 22:53:15
<子記事> まだAccess Violationです・・・ 20266 りん 2005/03/08 10:06:07


No.20258

Re:ファイルポインタの操作
投稿者---Sophist(2005/03/07 20:53:25)


>コンパイルは通ってます。

と書かれていますが、ご提示のソースコードをそのままコピー&ペーストして
コンパイルすると、いくつかエラーが出ます。

例えば、
・適切なヘッダーファイルをインクルードしていないために出るエラー
・main()の終わりが正しくない
などです。

今一度ご確認いただけますか?



この投稿にコメントする

削除パスワード

No.20259

Re:ファイルポインタの操作
投稿者---RiSK(2005/03/07 20:58:35)


>コンパイルは通ってます。

WinXP Pro/VC6 ですが,通りません。
ちゃんとコピペしましたか?
確認および訂正して欲しいです。


>ファイルポインタの渡し方がまずいのでしょうか?
    char        test[4];
    // :
    strcpy(test, "2005");
これだとバッファオーバーランが起きます。
    
    memset(test, '\0', sizeof(test));
strcpyするならこれは不要ですね。

    int main(){
int main(void) にして,return 0;



この投稿にコメントする

削除パスワード

No.20261

Re:ファイルポインタの操作
投稿者---RAPT(2005/03/07 21:07:26)


> strcpy(test, "2005");
ここでアクセス違反が発生しています。
"2005" は、実際には、'2','0','0','5','\0' の5バイトなので、
char test[5];
と宣言する必要があります。

ついでに言えば、fwrite()のプロトタイプは以下のとおりなので、
fwrite( test, 1, sizeof( test ), fpOutFile );
が正しいかと思われます。
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

> OPENして
細かい事ですが、ここではきちんと、fopenと書いた方が良いです。


メモリ破壊を起こしているので、今回のスレ主の環境では、fwrite()で
アクセス違反とのコトですが、私の環境では、ファイルクローズ時に
アクセス違反になりました。

このように、メモリ破壊が行なわれると原因不明の事象が再現性なく発生
するので気をつけましょう。

あと、fopen()したらfclose()する癖をつけておいたほうが良いと思います。


それから、ソースを掲載する時は、コンパイル可能なコードを掲載して下さい。
以下は私が勝手にコンパイル可能なコードに修正したものです。
(Windows2000sp4/VC++6.0sp6/Console-Appで動作確認)

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

#define OUTFILE  "c:\\outdata.txt"  //出力

enum{ RTN_OK, RTN_NG };

int ComOutPut( FILE  *fpOutFile )
{
    char test[5];
    strcpy(test, "2005");
    fwrite( test, 1, sizeof( test ), fpOutFile );
    if ( ferror( fpOutFile ) != 0 )
    {
        // エラー
        return RTN_NG;
    }
    return RTN_OK;
}

int main(void)
{
    FILE* outfp = NULL; //出力データファイル
    int   ret = 0;      //戻り値判定

    //出力ファイルオープン
    outfp = fopen(OUTFILE, "w");
    if(outfp == NULL){
        printf("出力ファイルがオープンできませんでした。\n");
        return 0;
    }

    //出力処理
    ret = ComOutPut( outfp );
    fclose(outfp);
    return ret;
}




この投稿にコメントする

削除パスワード

No.20262

Re:ファイルポインタの操作
投稿者---Blue(2005/03/07 21:19:43)


>    fwrite( test, 1, sizeof( test ), fpOutFile );
これだとNULL文字まで出力されますがOKなんでしょうか?

fwrite( test, 1, strlen( test ), fpOutFile );
のほうがよろしいのでは?



この投稿にコメントする

削除パスワード

No.20264

Re:ファイルポインタの操作
投稿者---RAPT(2005/03/08 00:00:59)


>> fwrite( test, 1, sizeof( test ), fpOutFile );
> これだとNULL文字まで出力されますがOKなんでしょうか?
> fwrite( test, 1, strlen( test ), fpOutFile );
> のほうがよろしいのでは?
そうですね。コピペしてしまいました。



この投稿にコメントする

削除パスワード

No.20263

もう一度確認してみます!
投稿者---りん(2005/03/07 22:53:15)


みなさん、ご親切にどうもありがとうございます。

実際のコーディングを丸ままではなく、適宜抽出して
書いてしまったのでいろいろと抜けが出てしまいました。
混乱させてもうしわけありません。

文字列のコピーのとこでもしくじっていたようで・・・。

現在、ソースの前におりませんので明日もう一度確認して
またこちらに書き込みしたいと思います。

それと気になる点。

1.実際はComOutPutはDLLになっています。
  mainと同じところにComOutPutを記述すると
  エラーなく処理が正常に終了します。
  (偶然でしょうか?)

とりあえずメモリ破壊を起こしているところを修正し
再度実行してみます!


#Cって難しい・・・でも奥が深いですね。
#みなさん尊敬します(>_<)



この投稿にコメントする

削除パスワード

No.20266

まだAccess Violationです・・・
投稿者---りん(2005/03/08 10:06:07)


おはようございます。

みなさんに指摘された所を修正し、再度実行しましたが
fwrite( test, strlen( test ), 1, fpOutFile ); のところで

「ハンドルされていない例外はTESTCOMEXE.exe(NTDLL.DLL)にあります:0xC0000005:Access Violation。」

となります。

まだメモリ破壊になっているのでしょうか?
それともDLLにしたせいでなのでしょうか?
DLL作成の仕方がおかしいでしょうか?

ご教授お願い致します。
(DLL作成時の他のcppファイルやヘッダーファイルが必要であればまた開示いたします)


---------------------------------------------------------------
以下、COMM.cpp
---------------------------------------------------------------

#include "COMM.h"


int ComOutPut( FILE  *fpOutFile )
{
    char        test[5];
    strcpy(test, "2005");

    fwrite( test, strlen( test ), 1, fpOutFile );

    if ( ferror( fpOutFile ) != 0 )
    {
        // エラー
        return RTN_NG;
    }

    return RTN_OK;
}



---------------------------------------------------------------
以下、COMM.h
---------------------------------------------------------------

#ifndef COMM_H
#define COMM_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <math.h>
#include <float.h >
#include <io.h>
#include <windows.h>
#include <time.h>
#include <share.h>
#include <errno.h>

/*-----------------------------*/
/*システム共通DEFINE           */
/*-----------------------------*/
#define     RTN_OK                  0               //正常
#define     RTN_NG                  1               //異常

/*-----------------------------*/
/*プロトタイプ                 */
/*-----------------------------*/
//出力処理
__declspec( dllexport )
int ComOutPut( FILE* );

#endif


---------------------------------------------------------------
以下、testcomexe.cpp
---------------------------------------------------------------

#include "COMM.h"

//テスト用データファイル
#define OUTFILE     "C:\\outdata.txt"   //出力

int main(){


    FILE*   outfp;      //出力データファイル
    int     ret = 0;    //戻り値判定


    //出力ファイルオープン
    outfp = fopen(OUTFILE, "w");
    if(outfp == NULL){
        printf("出力ファイルがオープンできませんでした。\n");
        return 0;
    }

    //出力処理
    ret = ComOutPut( outfp );

    fclose(outfp);
    return 0;
}




この投稿にコメントする

削除パスワード

No.20267

Re:まだAccess Violationです・・・
投稿者---Blue(2005/03/08 10:21:03)


>fwrite( test, strlen( test ), 1, fpOutFile );
以下MSDNより引用

fwrite ストリームにデータを書き込みます。 size_t fwrite( const void*buffer, size_t size, size_t count, FILE *stream ); 引数 buffer 書き込むデータへのポインタ size バイト単位の項目サイズ count 書き込まれる最大項目数 stream FILE 構造体へのポインタ
ですよ。指定の仕方が間違っています。 # 文字列なら fputs でいいような



この投稿にコメントする

削除パスワード

No.20268

Re:まだAccess Violationです・・・
投稿者---りん(2005/03/08 11:25:01)


>size
> バイト単位の項目サイズ

というのは、書き込むサイズ(文字列長)のことで

>count
> 書き込まれる最大項目数

というのは、書き込む回数なのだと思っていたのですが
違うのでしょうか?


fwrite( test, 1, strlen( test ), fpOutFile )
は、1バイトづつ strlen( test )回書き込むことで

fwrite( test, strlen( test ), 1, fpOutFile )
は、strlen( test )長を1回で書き込むという意味だから
どっちでもいいのかなーと。

MSDNのヘルプに
「fwrite 関数はそれぞれ size 長の項目を最大 count 数だけ buffer から、出力 stream に書き込みます。」
とあったのでそう思っていました。


fwrite( test, 1, strlen( test ), fpOutFile );

に修正して実行してみましたが、やはり同じエラーが出ます。


># 文字列なら fputs でいいような

fputsを使うべき時とfwriteを使うべき時がいまいちわかりません。
確かにfputsのほうが簡単ですね。



この投稿にコメントする

削除パスワード

No.20269

Re:まだAccess Violationです・・・
投稿者---Blue(2005/03/08 11:34:56)


>	というのは、書き込む回数なのだと思っていたのですが
>	違うのでしょうか?
すいません。よく考えたら同じような。。。

ComOutPut内でfpOutFileは有効なんですよね。
逆に読込モードで開いて、読込はできるんでしょうかね?



この投稿にコメントする

削除パスワード

No.20271

Re:まだAccess Violationです・・・
投稿者---りん(2005/03/08 11:51:18)


>ComOutPut内でfpOutFileは有効なんですよね。
>逆に読込モードで開いて、読込はできるんでしょうかね?

有効かどうなのかがわからないのですが・・・。

mainで

outfp = fopen(OUTFILE, "r");
if(outfp == NULL){
    printf("出力ファイルがオープンできませんでした。\n");
    return 0;
}


とし、ComOutPutで

char list[256];
int  numread;

numread = fread( list, sizeof( char ), 1, fpOutFile );


としてみましたが、やはりfreadのところで同じエラーで落ちます。



この投稿にコメントする

削除パスワード

No.20272

Re:まだAccess Violationです・・・
投稿者---かずま(2005/03/08 13:05:19)


> としてみましたが、やはりfreadのところで同じエラーで落ちます。

コンパイルオプション /MD を exe と dll の両方のコンパイルに追加してみてください。

宿題です。うまくいったら、なぜうまくいくのかを説明してください。

それから、main 側は __declspec(dllimport) ... です。


この投稿にコメントする

削除パスワード

No.20274

うまくいきました!!
投稿者---りん(2005/03/08 15:38:16)



コンパイルオプションを /MTd から /MD に変更し
main 側の定義を __declspec( dllexport ) から __declspec(dllimport) に変更して実行したところ
エラーが出ずに処理を終えることができました!!


>宿題です。うまくいったら、なぜうまくいくのかを説明してください。

間違ってるかもしれませんが・・・

/MTd の使用する LIBCMTD.LIB はマルチスレッド、スタティックリンク用で
/MD の使用する MSVCRT.LIB はマルチスレッド、ダイナミックリンク用だから

でしょうか。

/MTd はDLL作成用のコンパイルオプションではなかった
としか言い表せません。


Cを始めて間もない、コンパイルオプションというのも今回始めて知ったほどのひよっこですので、
なにとぞちゃんとした正解をご教授ください。

よろしくお願いします。



この投稿にコメントする

削除パスワード

No.20281

Re:うまくいきました!!
投稿者---かずま(2005/03/09 02:38:01)


> コンパイルオプションを /MTd から /MD に変更し

/MTd を指定しているのなら、/MDd にしたほうがよかったのでは。

さて、fopen や fwrite などの関数は 6種類のライブラリファイルに含まれて
おり、どれを使うかは /ML, /MT, /MD, /MLd, /MTd /MDd で指定します。
小文字の d のつくのはデバッグ用なので実質は 3種類です。

何のオプションも指定せず普通にコンパイルすると .exe ファイルができ、
/ML のスタティックライブラリがリンクされます。

/LD オプションを指定してコンパイルすると、.dll ファイルとができ、
/MT のマルチスレッドスタティックライブラリがリンクされます。

スタティックライブラリがリンクされたということは、.exe と .dll がそれぞれ
独自の fopen と fwrite を持っていることになります。これらが引数で
渡される FILE 構造体だけを参照し変更するするのであれば、何も問題なく
期待通りの動作をするでしょう。実際、.dll のほうを /ML にすると、動くはずです。

しかし、マルチスレッドライブラリでは、ひとつの FILE 構造体が 2つのスレ
ッドから同時に参照され、変更されて、その内容がぐちゃぐちゃになるのを防
ぐために、排他制御を行っています。そのための情報が別々の場所にあったら
問題になるのだと思われます。

/MD を指定してダイナミックライブラリにすると、.exe と .dll にリンクさ
れるインポートライブラリ(.lib ファイル) はそれぞれ別のものになりますが、
そこには関数の入り口しかなく、関数の実体は msvcrXX.dll というダイナミッ
クライブラリの中に一つあることになって整合性が取れます。

こんな説明で理解できますか?


この投稿にコメントする

削除パスワード

No.20286

ありがとうございました>皆様へ
投稿者---りん(2005/03/09 09:43:23)



かずまさん、ご回答ありがとうございます。

かずまさんの説明で分かったか?と聞かれればたぶん40%くらいしか
理解できてないと思います。
分からない語句もたくさんありますし、Cの仕組みの理解も不十分です。

分からないことを一つずつ自分で確かめながら、かずまさんの文章が100%
理解できるようにこれからがんばりたいと思います。

ここで親切に教えてくださった皆様、この場を借りてお礼申し上げます。
本当にありがとうございましたm(__)m



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧