fread関数で2回目から意図しない処理に

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Ohagi
記事: 31
登録日時: 3年前

fread関数で2回目から意図しない処理に

#1

投稿記事 by Ohagi » 3年前

04 00 00 00
04 01 00 00
 ・
 ・
 ・
というfwrite関数で書きだしたbinファイルを読みたいのですが

printfのデバッグを見ると1度目は上手く読み込まれたのですが、二回目から意図しない処理になってしまいました。

今回以外にもfread関数を使っていて意図しない処理になってしまうことがあるので困っています。

1回目は正常に読み込まれるのに二回目からおかしくなるのはどうして?

回答していただけると助かります。

/* デバッグ結果 */
main:tmp[0] = 4
main:tmp[1] = 0
main:tmp[2] = 0
main:tmp[3] = 0
main:05
main:tmp[0] = 104
main:tmp[1] = 0
main:tmp[2] = 0
main:tmp[3] = 0
main:05
main:tmp[0] = 204
main:tmp[1] = 0
main:tmp[2] = 0
main:tmp[3] = 0

/* デバッグ結果 */

コード:

int main(int argc, char *argv[])
{
  FILE *fp = 0;
  int tmp[4] = {0};


  /* ファイルオープン */
  open_bin(&fp, argc, argv);


  /* データが読み込める間ループ */
  while (fread(tmp, sizeof(char), 4, fp) > 0) {
    /*debug*/printf("main:tmp[0] = %2x\n", tmp[0]);
    /*debug*/printf("main:tmp[1] = %2x\n", tmp[1]);
    /*debug*/printf("main:tmp[2] = %2x\n", tmp[2]);
    /*debug*/printf("main:tmp[3] = %2x\n", tmp[3]);
    
 
  }
  return 0;
}


アバター
みけCAT
記事: 6182
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: fread関数で2回目から意図しない処理に

#2

投稿記事 by みけCAT » 3年前

具体的な入力と意図した出力、open_binの定義を教えていただけますか?

tmpはint型を要素とする配列なのに、環境によってはintより小さい可能性があるchar型4個分しか読み込んでいないのはなぜですか?
オフトピック
妙だな…提示されたコードのループでは出力されるはずのないmain:05が出力されているぞ。
open_bin()がfpに適切な値を設定すると仮定すれば未定義動作は無いと思うが…?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

box
記事: 1737
登録日時: 8年前

Re: fread関数で2回目から意図しない処理に

#3

投稿記事 by box » 3年前

kazuki2655 さんが書きました:
ファイルオープン関数を含めて、すべてのコードを見せていただけるとありがたいです。
kazuki2655 さんが書きました:

コード:

  int tmp[4] = {0};
  while (fread(tmp, sizeof(char), 4, fp) > 0) {
tmp[]はint型のつもりですか?それともchar型のつもりですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

Ohagi
記事: 31
登録日時: 3年前

Re: fread関数で2回目から意図しない処理に

#4

投稿記事 by Ohagi » 3年前

御二方、回答ありがとうございます。

関数の仕様も理解しているつもりで、最小コードで意図しない結果となり、自分ではお手上げだったので回答頂けると助かります。

コード:

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

void open_bin(FILE **fp, int ac, char *av[])
{

  if (ac < 2) {

    puts("使い方:pg_name bin_name");
    exit(EXIT_FAILURE);

  } else {

    if ((*fp = fopen(av[1], "r")) == NULL) {
      puts("ファイルが開けません。");
    } else {
      puts("ファイルを開きました。");
      putchar('\n');
    }
  }
}

int main(int argc, char *argv[])
{
  FILE *fp = 0;
  int tmp[4] = {0};
  
  /* ファイルオープン */
  open_bin(&fp, argc, argv);


  /* データが読み込める間ループ */
  while (fread(tmp, sizeof(char), 4, fp) > 0) {
    /*debug*/printf("main:tmp[0] = %2x\n", tmp[0]);
    /*debug*/printf("main:tmp[1] = %2x\n", tmp[1]);
    /*debug*/printf("main:tmp[2] = %2x\n", tmp[2]);
    /*debug*/printf("main:tmp[3] = %2x\n", tmp[3]);
  }
  return 0;
}
pg.bin
fwrite関数で数値をバイナリで書き込んだファイルです。
16進数表記で見やすいように改行をすると、次のようになります。
04 00 00 00
04 01 00 00
04 02 00 00
00 02 27 10
04 03 00 02
00 00 00 03
00 02 00 01
00 01 00 01
00 01 00 01
08 01 00 64
07 00 00 5C
06 00 00 04
00 00 00 00

これをfread関数で読み込みたいので、意図した出力結果としては次のようになります。

/* デバッグ結果 */
main:tmp[0] = 4
main:tmp[1] = 0
main:tmp[2] = 0
main:tmp[3] = 0

main:tmp[0] = 4
main:tmp[1] = 1
main:tmp[2] = 0
main:tmp[3] = 0

main:tmp[0] = 4
main:tmp[1] = 2
main:tmp[2] = 0
main:tmp[3] = 0

(以下省略)


fread関数のサイズをsizeof(char)にしたのは、binファイルの8ビット分をint型配列tmpの一要素に格納したかったからです。

デバック結果にあるmain:05はメイン関数内のログ用に出力していたもので、ここに書く前に余分かと思い削除してしまいました。混乱させてすみません。

一回目は正常に読み取れているようなのですが(そう見えてるだけで一回目から正常に読み取れてない?)、二回目から一風変わってしまうのが不思議です。

アバター
みけCAT
記事: 6182
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: fread関数で2回目から意図しない処理に

#5

投稿記事 by みけCAT » 3年前

kazuki2655 さんが書きました:一回目は正常に読み取れているようなのですが(そう見えてるだけで一回目から正常に読み取れてない?)
そのとおり、最初の4バイトはたまたま1バイトに収まる値なので、たまたま結果が変わらないだけですね。
kazuki2655 さんが書きました:fread関数のサイズをsizeof(char)にしたのは、binファイルの8ビット分をint型配列tmpの一要素に格納したかったからです。
一旦char型を要素とする配列に読み込んでから変換しましょう。
また、環境依存性を減らすためにuint8_tまたはunsigned charを使う方がいいでしょう。
また、ファイルを開けたかのチェックとファイルのクローズもした方がいいでしょう。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h> /* uint8_tを使えるようにする */

/* 省略 */

int main(int argc, char *argv[])
{
  FILE *fp = 0;
  int tmp[4] = {0};
  uint8_t buffer[4] = {0};
  int i;
  
  /* ファイルオープン */
  open_bin(&fp, argc, argv);
  if (fp == NULL) return 1;

  /* データが読み込める間ループ */
  while (fread(buffer, sizeof(uint8_t), 4, fp) > 0) {
    for (i = 0; i < 4; i++) {
      tmp[i] = buffer[i];
      /*debug*/printf("main:tmp[%d] = %2x\n", i, tmp[i]);
    }
    putchar('\n');
  }
  fclose(fp);
  return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

can110
記事: 26
登録日時: 4年前

Re: fread関数で2回目から意図しない処理に

#6

投稿記事 by can110 » 3年前

テキストモードで開いているようですが、読込時に、改行コードの変換など意図しない処理がされる可能性があります。
バイナリモードで開きましょう。

コード:

    if ((*fp = fopen(av[1], "rb")) == NULL) { // バイナリモードで開く

Ohagi
記事: 31
登録日時: 3年前

Re: fread関数で2回目から意図しない処理に

#7

投稿記事 by Ohagi » 3年前

>>can110さん

ご指摘ありがとうございます。

バイナリモードでの読み込みになってませんでした(汗)



>>みけcatさん
サンプルプログラムの提供ありがとうございます。

提供してもらったプログラムをコンパイル・実行してみたろ問題なく動作しとても参考になりました。

それを参考に自分のコードを修正して色々確認してみたところ

fread関数の結果を記憶する変数をint型からchar型に変更したところ同じように意図した結果となりました。

>>一旦char型を要素とする配列に読み込んでから変換しましょう。

int型もchar型も同じ算術型の整数型でサイズ以外は大きく違いが無いようなきがするのですが

具体的に何がどう問題だったのか教えてもらえませんか。

fread関数の仕様上、記憶させる変数と、記憶サイズ(sizeof(***))の型は一致させる必要があるのでしょうか?

アバター
みけCAT
記事: 6182
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: fread関数で2回目から意図しない処理に

#8

投稿記事 by みけCAT » 3年前

kazuki2655 さんが書きました:fread関数の仕様上、記憶させる変数と、記憶サイズ(sizeof(***))の型は一致させる必要があるのでしょうか?
いいえ。例えば

コード:

FILE* fp;
char hoge[512];

/* fpを適切に初期化 */

fread(hoge, sizeof(char), 64, fp);
のように、配列の一部にだけ要素を読み込んでも問題ありません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6182
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: fread関数で2回目から意図しない処理に

#9

投稿記事 by みけCAT » 3年前

kazuki2655 さんが書きました:具体的に何がどう問題だったのか教えてもらえませんか。
ここでは、char型のサイズは8ビット、sizeof(int)は4、リトルエンディアンとします。 (sizeof(char)が1であることは規格で決まっています)

このとき、

コード:

fread(tmp, sizeof(char), 4, fp);
はファイルからデータを読み取り、うまく読み取れるとtmp用に確保されたメモリ領域の先頭から4オクテットにそのデータを書き込むので、
04 01 00 00 を読み取ったとすると

コード:

 tmp[0]      tmp[1]      tmp[2]      tmp[3]
+-----------+-----------+-----------+-----------+
|04 01 00 00|00 00 00 00|00 00 00 00|00 00 00 00|
+-----------+-----------+-----------+-----------+
となり、tmp[1]~tmp[3]は更新されません。

一方、unsigned char型を要素とする配列に読み込むと、

コード:

 buffer
 [0][1][2][3]
+--+--+--+--+
|04|01|00|00|
+--+--+--+--+
となるので、各要素に正しい数値が入ります。
この数値をint型の配列の各要素に代入することで、正しい値を取得することができます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Ohagi
記事: 31
登録日時: 3年前

Re: fread関数で2回目から意図しない処理に

#10

投稿記事 by Ohagi » 3年前

みけCATさん、とてもわかり易い回答ありがとうございます!!

読み取る要素を4にしておけば自動的に配列のポインタを操作してくれるのだろうと、勝手に思い込んでいましたがそういうわけではないのですね。

コードを記述する際にもっと型のサイズなどを意識して書いてみようと思います。

回答してくださった皆さん、ありがとうございます。

閉鎖

“C言語何でも質問掲示板” へ戻る