Xcodeのfwrite文の出力結果においてビット列が反転する

こんにちは


old Mac(iMac DV G3)を活用するために、Xcode 2.5を利用してプログラムを試みております。

そこで、下記のような現象に遭遇しました。

Xcodeの設定変更などによる問題解決の方法等がございましたら、ご指導いただければ幸いです。

よろしくお願いいたします。


【課題】下記のテストプログラムを実行した結果、出力ファイル(out.bin)のビット列が反転した。

#include <stdio.h>

#include <stdlib.h>


int main (int argc, const char * argv[]) {

// insert code here...

long long_data;

short short_data;

FILE *fp;

const char filename[256]="out.bin";


long_data=512;

short_data=256;


if((fp = fopen(filename, "wb"))==NULL) exit(EXIT_FAILURE);

fwrite(&long_data, 4, 1, fp);

fwrite(&short_data, 2, 1, fp);

fclose(fp);

return 0;

}


結果(Hexeditで確認)

ユーザがアップロードしたファイル

※通常ならば、00 02 00 00 00 01になるように思います。


動作環境:

-iMac DV G3

-Mac OSX 10.4.11 Tiger

-Xcode 2.5, Xcode IDE: 799.0, Xcode Core: 798.0, ToolSupport: 794.0

iMac, Mac OS X (10.4.11)

投稿日 2012/09/06 07:54

返信
返信: 6

2012/09/06 15:49 kimukazu への返信

Little Endian / Big Endian によるものかと思います。

Intel CPU は LittleEndian ですが、PowerPC は BigEndian です。

例えば、256(10進数) = 0x0100(16進数) で、

BigEndian ではそのままの並びなので、スクリーンショットの通りで正常だと思います。

これは、ファイルの読み書きの時ではなく、メモリ上の変数の時点で影響があります。


ご存知でなければ、

"Little Endian/Big Endian" や、"バイトオーダー" でインターネットを検索するといろいろ出てくるかと思います。

また、

aIntValue_hostByteOrder = NSSwapLittleIntToHost( aIntValue_littleEndian )

aIntValue_littleEndian = NSSwapHostIntToLittle( aIntValue_hostByteOrder )

など、バイトオーダーを相互に変換するための関数があります。(他にもいろいろあります。)

また、POSIX(?) の関数でも、同様のものがあったと思います。

なお、Host Byte-Order とは、動作しているパソコン(というか、コンパイルしたバイナリのターゲットのCPU)のバイトオーダーです。


もし、IntelMac のバイトオーダー(LittleEndian)に合わせてファイルを読み書きするなら、

NSSwapHostIntToLittle() 等で LittleEndian にした値をファイルに書き込み、

ファイルから読み出す時には NSSwapLittleIntToHost() 等を使って変数に読み込みます。

なお、NSSwapLittleIntToHost() や NSSwapHostIntToLittle() は、

IntelCPU (LittleEndian) で稼働している場合(IntelMac 用のバイナリの場合)、何もせず、そのままの値を返します。

( なので、IntelCPU 用と PowerPC 用で、ソースコードを変更する必要はありません。)

または、ファイルにLittle Endian/Big Endianが分かるような値を書き込んでおいて、必要なら変換するとか。

2012/09/07 04:25 yui への返信

貴重なコメント、ありがとうございます。

まさにご指摘の事象です。大変参考になりました。

今後の課題は、Big Endianの環境にて、Little Endianのバイナリデータの入出力を実現するためのコードを具体的にどのように記述するかになります。

ご提供いただいた情報をもとに、当方でも具体的な解決策を検討したいと思います。

結果が得られましたら、また、報告させていただきます。

よろしくお願いいたします。

2012/09/07 05:19 kimukazu への返信

LittleEndian のバイナリデータを入出力するとのことであれば、

先の投稿に書いた通り、基本的には、

> もし、IntelMac のバイトオーダー(LittleEndian)に合わせてファイルを読み書きするなら、

> NSSwapHostIntToLittle() 等で LittleEndian にした値をファイルに書き込み、

> ファイルから読み出す時には NSSwapLittleIntToHost() 等を使って変数に読み込みます。

となるかと思います。


例えば、書き込みの方は、


#include <stdio.h>

#include <stdlib.h>


int main (int argc, const char * argv[]) {

// insert code here...

long long_data;

short short_data;

FILE *fp;

const char filename[256]="out.bin";


long_data=512;

short_data=256;


// 追加

long long_data_inLittleEndian = NSSwapHostLongToLittle( long_data );

short short_data_inLittleEndian = NSSwapHostShortToLittle( short_data );


if((fp = fopen(filename, "wb"))==NULL) exit(EXIT_FAILURE);

fwrite( &long_data_inLittleEndian, 4, 1, fp); // 変更

fwrite( &short_data_inLittleEndian, 2, 1, fp); // 変更

fclose(fp);

return 0;

}


で、読み込みも同様に、

{ long long_data;

short short_data;

long long_data_inLittleEndian;

short short_data_inLittleEndian;


// ファイルから、long_data_inLittleEndian、short_data_inLittleEndian に fread() などを使って読み込み。

// 例えば、fread( &long_data_inLittleEndian, 4, 1, fp );


long_data = NSSwapLittleLongToHost( long_data_inLittleEndian );

short_data = NSSwapLittleShortToHost( short_data_inLittleEndian );

// 以降、計算には long_data, short_data を使う。

}


なお、long_data_inLittleEndian、short_data_inLittleEndian は、

型が long や short など、一見、整数値のようですが、Byte の並びが逆で、整数値としては使ってはいけません。


なお、long の変数は、64bit 環境では 8byte ( sizeof(long) == 8 ) になります。( Mac OS X や Unix など。Windows-64bit では long は 4byte。)

なので、fwrite( &long_data_inLittleEndian, 4, 1, fp); などで、"4" と指定すると、64bit 環境では正常に動作しません。ご注意下さい。

long や int でなく、サイズが明確な int32_t, uint64_t などの型も使えますし、

バイトオーダー変更の関数として CFSwapInt64HostToLittle(), CFSwapInt32HostToLittle() などもあります。

64bit環境/32bit環境の両方でファイルを共通にするなら、ファイルの読み書きではそれらを使うことも検討すると良いと思います。

( 64bit 環境での変数のサイズ(4byte/8byteなど)は、「LP64」や「LLP64」などで検索するといろいろ出てくると思います。)

2012/09/07 08:57 yui への返信

的確なコメント、ありがとうございます。

お示しいただきました情報をもとに、下記コードを実行いたしました。

その結果、コンパイルは正常終了しますが、実行時には下記エラーが発生しました。

原因や問題解決の方法等がおわかりになりましたら、ご指導いただければ幸いです。

よろしくお願いいたします。

【エラー】

ユーザがアップロードしたファイル

【コード】

#include <stdio.h>

#include <stdlib.h>


int main (int argc, const char * argv[]) {

// insert code here...

uint32_t long_data, long_data_inLittleEndian;

uint16_t short_data, short_data_inLittleEndian;

FILE *fp;

const char filename[256]="out.bin";


long_data=512;

short_data=256;


long_data_inLittleEndian = NSSwapHostLongToLittle( long_data );

short_data_inLittleEndian = NSSwapHostShortToLittle( short_data );



if((fp = fopen(filename, "wb"))==NULL) exit(EXIT_FAILURE);

fwrite( &long_data_inLittleEndian, 4, 1, fp);

fwrite( &short_data_inLittleEndian, 2, 1, fp);

fclose(fp);

return 0;

}


※別途、CFSwapInt64HostToLittle(), CFSwapInt32HostToLittle()についても、調べて試してみます。

2012/09/07 16:30 kimukazu への返信

そうですね。エラーが起こりますね。

原因は、NSSwapHostLongToLittle() などの関数が定義されてない(見つからない)ことかと思います。

この場合は、ライブラリをリンクする必要があると思います。

また、ヘッダファイルも #import <xxxxx> などで読み込む必要もあります。


NSSwapHostLongToLittle() を使うには、Mac の GUI アプリケーションか、Foundation Tool ( type: Foundation の Command Line Tool ) の必要があると思います。

またプログラムの言語は、Objective-C のプログラム(拡張子は .c ではなく .m ) にする必要があるように思います。

もし良いなら、Xcodeで新規プロジェクトで type: Foundation の Command Line Tool ( または Foundation Tool という名前かも ) を作成して、

上に

#import <Foundation/Foundation>

というのがあるので、それを残したまま、上記のプログラムを書けば動くと思います。

Foundation Tool なら、CFSwapInt64HostToLittle() も動くと思います。

または、通常の Mac の GUI アプリケーションでも OK です。


CFSwapInt64HostToLittle() を使うなら、Command Line Tool の ( type: Foundation(上と同じ) または ) type: CoreFoundation で新規プロジェクトを作るとできると思います。CoreFoundation Tool なら、Mac でしか動きませんが、C言語です。


もし、Linux などでも動かすなら、同様の動作をする POSIX(?) の関数があるので、それを使う必要があると思います。

#include <xxxxx> でヘッダファイルを読み込んだり、何かのライブラリとリンクする必要もあるかもしれません。

多分、BigEndian/LittleEndian などでインターネットなどを検索すると、その関数が見つかると思うので、調べてみてください。

2012/09/07 17:42 yui への返信

ご丁寧なご回答、誠にありがとうございます。

ご指摘の通り、Foundation Toolにて新規プロジェクトを作成し、

ユーザがアップロードしたファイル

下記コードを実行した結果

【コード.m】

#import <Foundation/Foundation.h>

#include <stdio.h>

#include <stdlib.h>


int main (int argc, const char * argv[]) {

// insert code here...

uint32_t long_data, long_data_inLittleEndian;

uint16_t short_data, short_data_inLittleEndian;

FILE *fp;

const char filename[256]="out.bin";


long_data=512;

short_data=256;


long_data_inLittleEndian = NSSwapHostLongToLittle( long_data );

short_data_inLittleEndian = NSSwapHostShortToLittle( short_data );


if((fp = fopen(filename, "wb"))==NULL) exit(EXIT_FAILURE);

fwrite( &long_data_inLittleEndian, 4, 1, fp);

fwrite( &short_data_inLittleEndian, 2, 1, fp);

fclose(fp);

return 0;

}


期待通りの結果が得られました。

【結果】

ユーザがアップロードしたファイル

正解のご呈示、ありがとうございます。

このスレッドはシステム、またはAppleコミュニティチームによってロックされました。 問題解決の参考になる情報であれば、どの投稿にでも投票いただけます。またコミュニティで他の回答を検索することもできます。

Xcodeのfwrite文の出力結果においてビット列が反転する

Apple サポートコミュニティへようこそ
Apple ユーザ同士でお使いの製品について助け合うフォーラムです。Apple Account を使ってご参加ください。