fread関数と補数表現の対処法

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

fread関数と補数表現の対処法

#1

投稿記事 by Ohagi » 3年前

コード:

int b[][] = { 
{199, ・・・ }, ・・・
};
for () {
fwrite(&b[i][j], sizeof(char), 1, fp)}
}
でバイナリとして書きだしたファイルを

コード:

char buffer[4] = {0};
for () {
  fread(buffer, sizeof(char), 4, fp)
  m[i++] = buffer[0];
  m[i++] = buffer[1];
  m[i++] = buffer[2];
  m[i++] = buffer[3];
}
と読み込みたいのですが

fwrite関数でバイナリとして書き込んだb[j] = 199 = 1100 0111を
上のfread関数で読み込むとbuffer[3] = 0xffff ffc7と読み込まれます。

知識不足でよくわからないのですが
他の小さな数は読み取れて、一番大きな199が読み取れない、8ビットの先頭ビットがセットされている
ことから符号付き8ビットの-128~127の範囲オーバーでバグっているような気がします。

となると、fread関数で読み取る際のサイズをcharからintにすればビット数が16ビットに増えて
1100 0111の先頭ビットが符号ビットとしてみられなくなると思うのですが
8ビット単位で配列に読み取って配列に格納したいのに
fread関数のsizeof(char)の部分をsizeof(int)に変更すると16ビットずつ読み取り配列に格納することになり
変換作業などがとても大変になると思います。

やりたいことや現在の状況を上手く説明できていないと思いますが、
こういう場合はどういうふうに対処すれば良いのでしょうか?
なにか良い手はないでしょうか?
回答の方お願いします。

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

Re: fread関数と補数表現の対処法

#2

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

kazuki2655 さんが書きました:こういう場合はどういうふうに対処すれば良いのでしょうか?
負の数を扱わないのであれば、unsigned char型かuint8_t型を使うといいでしょう。
kazuki2655 さんが書きました:となると、fread関数で読み取る際のサイズをcharからintにすればビット数が16ビットに増えて
1100 0111の先頭ビットが符号ビットとしてみられなくなると思うのですが
8ビット単位で配列に読み取って配列に格納したいのに
fread関数のsizeof(char)の部分をsizeof(int)に変更すると16ビットずつ読み取り配列に格納することになり
変換作業などがとても大変になると思います。
最近の「普通」のパソコン向けの環境ではintは32ビットであることが多いと思いますが、
ArduinoなどのマイコンもしくはDOSなどの古い環境向けのコードを書いているのですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: fread関数と補数表現の対処法

#3

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

kazuki2655 さんが書きました:

コード:

int b[][] = { 
{199, ・・・ }, ・・・
};
for () {
fwrite(&b[i][j], sizeof(char), 1, fp)}
}
でバイナリとして書きだしたファイルを
そもそも、int型のデータの最初の1バイトだけ書き出すと言うのは、
マシンのエンディアンに依存し、行儀が良くないでしょう。
従って、b[j]のデータを一旦uint8_t型かunsigned char型の変数に格納し、それをファイルに書き出す方がいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

YuO
記事: 936
登録日時: 8年前
住所: 東京都世田谷区

Re: fread関数と補数表現の対処法

#4

投稿記事 by YuO » 3年前

オフトピック
コード中のmって何?
kazuki2655 さんが書きました:fwrite関数でバイナリとして書き込んだb[j] = 199 = 1100 0111を
上のfread関数で読み込むとbuffer[3] = 0xffff ffc7と読み込まれます。

これは,どうやって確認しましたか。
例えば,printf("0x%02x", buffer[3])のように調べたのであれば,charが符号付きかつ負数を2の補数で表す処理系において,
char型の数値-57 (ビット表現:1100-0111) がint型に変換されて,それを16進数で出力していることになります。
現代の個人が扱える環境においてint型は32bitであることが多いでしょうから,出力のビット表現は1111-1111/1111-1111/1111-1111/1100-0111になります。
# -は4bitの境界,/はオクテット (大抵の環境ではバイトに等しい) の境界。

kazuki2655 さんが書きました:やりたいことや現在の状況を上手く説明できていないと思いますが、
こういう場合はどういうふうに対処すれば良いのでしょうか?
なにか良い手はないでしょうか?

バイナリデータを取り扱うのであれば,unsigned char型を使うことです。
charが符号付きであるか符号無しであるかは,<limits.h>のCHAR_MINが0であるかを調べればよいとはいえ,
文字以外のためにchar型を使わず,最初からunsigned char型を使うことで「文字では無くバイナリ」であることを示せます。

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

Re: fread関数と補数表現の対処法

#5

投稿記事 by Ohagi » 3年前

みけCATさん
返信ありがとうございます。
符号なしcharも考えたのですが負の数も扱う予定なので困っていました。
型はunsigned char型にして、こちらでフラグを設けて負の数を表現すればいけそうが気がしてきました。
型が違っても上手く動いていたので、あまり気にしていませんでしたが言われてみると変ですね。
ご指摘ありがとうございます。

YuOさん
返信ありがとうございます。
ご指摘の通りprintf("0x%02x", buffer[3])で確認していました。
バグによる想定外の動作だと勘違いしていましたが、暗黙の型変換の影響だったんですね。
型にunsigned charにして、別途符号ビットを設けるようにしてみようと思いますが
負の数を扱う場合のメジャーな方法とかはあるのでしょうか?

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

Re: fread関数と補数表現の対処法

#6

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

kazuki2655 さんが書きました:符号なしcharも考えたのですが負の数も扱う予定なので困っていました。
型はunsigned char型にして、こちらでフラグを設けて負の数を表現すればいけそうが気がしてきました。
扱えない範囲のデータは扱えません。 (トートロジー)
ファイルフォーマットを変えてよく、かつファイルサイズを小さくすることがあまり重要でないなら、素直にint16_tなどの扱うデータ全範囲が入る型を使えばいいのではないでしょうか?
ファイルサイズを小さくしたいなら、例えば
  • 符号をビットマップかなんかで別に詰めて保存する
  • 数値を可変長で保存する
  • gzipかなんかで圧縮する
などの方法が考えられるでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: fread関数と補数表現の対処法

#7

投稿記事 by Ohagi » 3年前

回答してくださった御二方、ありがとうございました。

閉鎖

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