• 締切済み

エンディアンを知るには

リトルエンディアンのマシンで作ったバイナリデータをいろいろなマシンで読み込みたいのですが、一部がビッグエンディアンなので変換の必要があります。ただ、ソースを共通にしたいのでエンディアンを知ることができるマクロの値でエンディアンの違いの処理を行いたいのですが、どのようなマクロを使えばいいのでしょうか?

みんなの回答

  • isle
  • ベストアンサー率51% (77/150)
回答No.11

#9です アラインメントの話も出てきてたのでそちらにも返答をしておきます。 アラインメントを処理系が勝手に決めて、しかも変更できないとしたら、「一気にバイナリデータを、構造体配列に読み込む」ことができなくなってしまう可能性がありますね。 バイナリデータに合わせてアラインメントを調整するとどうなるでしょう。 ゲームだとアラインメントを無くしてデータサイズを切り詰めたりすることもあるでしょうか。 インテル系のCPUならアラインメントがどんなでもあらゆるデータ型にそのままアクセスできます(若干の処理速度の低下はあります)が、例えばARMなら奇数バイトからのshort(16ビット)型のアクセスはハードウェアレベルで不可能です。 要するに「一気にバイナリデータを、構造体配列に読み込む」ことに拘るのがいちばん無駄なことです。

  • isle
  • ベストアンサー率51% (77/150)
回答No.10

#9です マクロを使ってもビッグエンディアンのマシンで実行時にバイト位置を入れ替える処理が不要にはならないのでは? リトルエンディアンに限定しても「一気にバイナリデータを、構造体配列に読み込む」のが期待通り動作することが【特定の条件下以外では】保証されませんし、言語規格で保証された範囲で読み書きするルーチンを作っておくほうが後々まで役に立つと思います。 どうしてもプログラム側で「一気にバイナリデータを、構造体配列に読み込む」以上に無駄なことしたくなければ、「一部がビッグエンディアン」のマシンを捨てて「一気にバイナリデータを、構造体配列に読み込む」ことのできるマシンに入れ替えてもらいましょう。

moritan2
質問者

お礼

ご回答ありがとうございます。 マクロを使ってもビッグエンディアンのマシンで実行時にバイト位置を入れ替える処理が不要にはならないのでは? これはバイナリーデータを機種にあわせて、最初から正しいエンディアンにしておけばいいです。画面の解像度など機種よりちがいますから、画像データなどバイナリデータはも機種ごとに違うものになるので、構造体のデータも機種ごとに違うものになるのは私の場合は気になりません。しかし、ソースはPS2、WII、DS、開発機のcygwinで同じ物を使いたいので、できればENDHIANはマクロで処理したいのです。 報告が送れて申し訳なかったのですが、WII用のコンパイラで __BIG_ENDIAN__ が定義済みマクロとして使えることを確認できました。またこれ以外のマシンはすべてリトルエンディアンですので、これで問題は解決いたしました。 前にお答えいただいた方々にもお礼を申し上げます。ありがとうございました。

  • isle
  • ベストアンサー率51% (77/150)
回答No.9

そもそも1バイトずつ読み書きすれば、実行側がビッグエンディアンかリトルエンディアンか意識する必要はないのでは? fread(&l, sizeof(long), 1, fp); とか b[0] = fgetc(fp); b[1] = fgetc(fp); b[2] = fgetc(fp); b[3] = fgetc(fp); l = *(long*)&b[0]; みたいなことをやらなければ良いだけ。 バイナリがリトルエンディアンと分かっているなら l = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; で良くね?

moritan2
質問者

お礼

ご回答ありがとうございます。 これも、一気にバイナリデータを、構造体配列に読み込むのと比べて、無駄なコードが存在することになります。エンディアンやアラインメントはコンパイルの時点ではコンパイラは知っているはずなので、実行時になにかするのは無駄な処理のように思います。

  • DT200
  • ベストアンサー率38% (63/164)
回答No.8

質問ではマクロでとなっているので、回答として的確だと言えないかも知れませんが、 共通であるバイナリデータをネットワークバイトオーダー(ビッグエンディアン)に 統一してしまえば良いのでは?可搬性はぐんと高くなります。 そのために、バイナリデータを出力するときは htons、htonl でネットワークバイトオーダーに変換し、バイナリデータを読み込むときは ntohs、ntohl を使用してホストのバイトオーダーに変換する。 ソースは共通にできるはずです。

moritan2
質問者

お礼

ご回答ありがとうございます。 ご回答のやりかただと、バイトオーダーを変更するコードが無駄なコードになってしまいます。まあ、たいしたことはないのですが、単なるこだわりです。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.7

GCCであれば、__LITTLE_ENDIAN__のようなマクロがあらかじめ定義されたと思います。ただ、すべてのターゲットで一貫性のある定義が行われるかどうかは調査しないとわかりません。ただ、使用する処理系が限られていますので、上記のようなマクロが使えない場合でも、CPUを示すマクロなどを使って個別対応できるかと思います。(エンディアン判別用のヘッダを作るのがよいでしょうね) MIPSなどのバイエンディアンのCPUでは、バイトオーダーをコンパイルオプションで指定することになるので、むしろ確実に"あらかじめ定義されたマクロ"で判別できるはずです。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.6

> コンパイラはコンパイル時には次のような静的なデータをコンパイルする時には > エンディアンをわかっていなければならないはずと思うのですが。 > (以下略) バイエンディアン CPU の話ですか? 私も使ったことはないので推測ですが,デフォルトでは初期状態 (リセット直後) の エンディアンを使うのではないでしょうか.また,エンディアンを指定する #pragma もあるのかもしれません. 推測の上に余談ですが,データのエンディアンと,即値のエンディアンが異なる可能性 もありそうですね.(ああ,ややこしい….)

  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.5

一応、endian.h ていう、半標準のヘッダがあって、たとえば、 http://www.math.kobe-u.ac.jp/~kodama/tips-C-endian.html の「マクロによる判定」のようにすれば、いいことになってます。 あるいは、(中身は同じですが)boost/detail/endian.hppをincludeすれば、 BOOST_LITTLE_ENDIAN,BOOST_BIG_ENDIAN,BOOST_PDP_ENDIAN のどれかが定義されます。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.4

> コンパイラは gcc とお考えください。 手元には古い gcc しかありませんが,それでちょっと調べてみました. /usr/include/bits/endian.h で #define __BYTE_ORDER __LITTLE_ENDIAN というのがありました.これを使えばいいのでは? (確信なし) なお,#include するのは <bits/endian.h> ではなく <endian.h> です.

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.3

> #ifdef BIG_ENDIAN エンディアンを定義している標準的なマクロというものはおそらくないのではないかと思います. (知っていれば私もとっくに使っていたはず.) だから,上記のようなマクロは,プログラマが処理系ごとに定義せざるを得ないでしょう. エンディアン判定関数で動的に判定すれば,それが不要になります. (といっても,それで節約できる手間はわずかですけど.) それに,(私は使ったことありませんが) エンディアンを動的に切り替えられる バイエンディアンの CPU というのもあります.そういう CPU に対しては, そもそも上記のようなマクロを定義することはできません. というわけで,エンディアン判定関数は無駄ではないと思います. エンディアン (Wikipedia) http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3

moritan2
質問者

お礼

ご回答ありがとうございます。 マクロは無いのですね。 となりますと、Makefile に CFLAGS += -D BIG_ENDIAN とでも記述してやるしかないのでしょうか? この記述自体はたいした手間でもありませんが、後から仲間に、 「そんなの、XXXXXX を使えばいいじゃん、なんでわざわざこんな定義をする必要があるの?」などと言われるとくやしいので質問しました。 コンパイラはコンパイル時には次のような静的なデータをコンパイルする時にはエンディアンをわかっていなければならないはずと思うのですが。 int xxx=0x12345678; xxxが 12 34 56 78 なのか 78 56 34 12 なのかはコンパイル時に決定していないとコンパイラは困るのではありませんか?

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.2

ファイル内のどのデータがビッグエンディアンになるのかは 事前に (仕様として) わかっていて,CPU 側のエンディアンを 知りたいということでしょうか? C/C++ 関数・マクロ集 (処理系・OS 非依存) http://www5d.biglobe.ne.jp/~noocyte/Programming/CMacros.html ・エンディアンに関する関数・マクロ  ・実行時にエンディアンを判定する関数  ・エンディアンを変換 (big ⇔ little) する関数およびマクロ (CHAR_BIT 対応)  ・エンディアン名を取得する関数 (あらゆる4バイト・エンディアンおよび CHAR_BIT に対応) ・メモリ上のデータ操作関数・マクロ  ・データ (バイト列) をバイト逆順にする.

moritan2
質問者

お礼

ご回答ありがとうございます。 エンディアンを判定する関数というのは無駄ではないかと思います。 コンパイル時にはCPUが決まっているので、コンパイラはエンディアンを知っているはずであり、実行時に判定するコードは無駄なコードでないかと思います。 #ifdef BIG_ENDIAN // エンディアンの変換処理 #endif という感じで使えるマクロがありはず、と思うのですが。

moritan2
質問者

補足

コンパイラを特定しないといけませんでした。コンパイラは gcc とお考えください。CPU は Intel系(開発に使うPC)、ARM(DS)、MIPS(PS2、PSP)、PowerPC(WII) です。

関連するQ&A