街中で見かけて「グッときた人」の思い出

すみません。
教えてください。

リトルエンディアンからビッグエンディアンに変換しないと
いけません。
エンディアンについては勉強したつもりですが、
どうしてもわからないことがあります。
ご存知の方、教えていただせんか?
CPUはリトルです。

まず、エンディアンの違いについては
以下のように認識しています。

x = 0xAABBCCDD

メモリの配置方法が、

トリルだと DD CC BB AA
ビックだと AA BB CC DD

だと思っています。32ビットの場合です。
で、これを変換するには、htonlで変換可能だと思っっています。
(試したところ可能でした)


で次に、32ビットを超えるデータ、たとえば100バイトとかを
mallocにして変数に代入しました。

この時はエンディアン変換(ファイルに出力する際)は必要ないのでしょうか?
試しに出力すると、

x = 0x AA BB CC DD EE FF GG ・・・・・・ZZ (100バイトと仮定)

バイナリでの出力結果は

AA BB CC DD EE FF GG ・・・・・となっていました。

私の認識だと、本CPUはリトルエンディアンのため、

ZZ ・・・・・・・・ DD CC BB AA (四バイトずつ反転しているデータ)

が出力されるものと思っていました。(反転してメモリに格納されるため)


リトル/ビックを意識しないといけないのは、
2バイトや4バイトの時のみで、それを超える大きなデータ(100バイト)などは
意識せず、そのままバイナリ出力しても、ビックエンディアンで出力されると
いうことでよろしいでしょうか?

そうなると、エンディアンってなんだんだ???と混乱しています。


わかりにくい説明で大変申し訳ござませんが、
よろしくお願いいたします。

A 回答 (7件)

C言語で説明しますが、特にC言語を知らなくとも大丈夫だと思います。



まず、C言語では
char 1byte整数
short 2byte整数
long 4byte整数
と決まっています。

この場合
char c = 0x12;
これに関しては、ビッグエンディアンもリトルエンディアンも関係ありません。
ですから、
char cs[100];
と言うのも、同様にビッグエンディアンもリトルエンディアンも関係ありません。
たぶん、質問者さんが言われている100バイトのデータがこれに該当すると思います。

そして問題なのは、
short s = 0x1234;
long l = 0x12345678;
など、2バイト以上の型の並び順です。

リトルエンディアンで言えば
s を1バイト単位で見た場合、c[0]=0x34,c[1]=0x12となります。
l を1バイト単位で見た場合、c[0]=0x87,c[1]=0x65,c[2]=0x43,c[3]=0x21となります。

逆にビッグエンディアンの場合は
s を1バイト単位で見た場合、c[0]=0x12,c[1]=0x34となります。
l を1バイト単位で見た場合、c[0]=0x12,c[1]=0x34,c[2]=0x56,c[3]=0x78となります。

10進数で、たとえてるなら「一、十、百、千」と4桁の10進数があった場合
リトルエンディアンだと並びが、「一、十、百、千」
ビッグエンディアンだと並びが、「千、百、十、一」
になると言うもので、つまり並びの重みの向きが違うと言うものです。

アドレス(番地)は、1バイトごとに付けられます。
つまり、
char a[4];とあり、もしa[0]が0番地とした場合、
a[4]は3番地となります。
そして、4バイト整数は連続した4つの番地を使う訳ですが、この場合の0番地が大きい位なのか、小さい位なのかでビッグエンディアン/リトルエンディアンと呼ばれるようになります。

また、最近では8バイト(64bit)も普通に扱えるようになりましたが、これも考えは同じです。

そして、雑学的なことを書けば、リトルエンディアンのメリットは、たとえばlongで定義したエリアの中身が実際には0か1しか値が入らないと言うケースはしばしば発生します。
それをもし、何か間違ってcharで参照したとしても、それは正確に0か1が参照できるのです。(つまり1バイトに収まる値ならば正しく参照可能)
昔は、このようなことをメモリが少なかったため裏技的にやる人もいましたが、もちろん今ではトラブル以外の何ものでもないため、絶対にやりません。
    • good
    • 0

もはや余談の範疇ですが, 世の中には「PDP エンディアン」のような奴もいるので一筋縄ではいかなかったりします. なんか, もっと変態ちっくなやつもあるみたいだけど.



で実行時に「どうしてもエンディアンを知りたい」と思ったら
#include <limits.h>

union FindLongEndian {
unsigned long lval;
unsigned char byteorder[sizeof (unsigned long)];
} x;

x.lval = 0;
for (unsigned int i = 1; i <= sizeof (unsigned long); ++i) {
x.lval = (x.lval << CHAR_BIT) + i;
}
とすれば x.byteorder[] に入ってくれる... かな?

ところで「char が 1バイト, short が 2バイト, long が 4バイト」に決まってる言語が想像つかないんだけど, 何かあったっけ?
    • good
    • 0

私が普段使っている、MacOSX(64bitカーネル)のgccでは、sizeof(long)==8 なのですが...


<「longは4バイトと決まっている」


さて。
同じmallocでも、
char *buff = malloc(100) ;
short * s = (short *) buff ;
として
s[0]=0x1234 ;
とすると、
buff[0]==0x34 ;
buff[1]==0x12 ;
となります(sizeof(short)==2,リトルエンディアンの場合)
これがビッグエンディアンだと
buff[0]==0x12 ;
buff[1]==0x34 ;
となります。

・buff[0],buff[1]のchar2つが、s[0]のshort一つになる:これがcharが100個か、2個の塊(short)が50個か、ということ。
・そのどちらに上位が入るか、がエンディアン

マス目に書いてみたり、Excelみたいな表に書いてみたりすると、イメージしやすいかもしれません
    • good
    • 0

あー、そういえば質問の例示だと100byte全体で反転してますねぇ>#3


後ろの文章しか読んでませんでした。
    • good
    • 0

「4Byteデータ25個のカタマリ」だとすると (4バイトごとに格納されるので)


DD CC BB AA HH GG FF EE ...
のような気がしますが>#2.
    • good
    • 0

ケースバイケース、としか言いようがありません。


質問のケースでは、「100Byteのデータ」というのが所詮「1Byteデータ100個のカタマリ」でしかないためです。

これが「4Byteデータ25個のカタマリ」であればあなたの想定通りの結果になります。

この回答への補足

ご回答ありがとうございます。

Tacosanさんに追加で質問を書いて登校してから
あっ思いました。結論は以下ということですよね。
間違っていたら、ご指摘ください。

-----------------
質問のケースでは、「100Byteのデータ」というのが所詮「1Byteデータ100個のカタマリ」でしかないためです。

これが「4Byteデータ25個のカタマリ」であればあなたの想定通りの結果になります。
-----------------

なので、

long int の場合は4バイト確保されているので、
メモリ上には逆に入る。

char *buff に

いくらmalloc(1000)をしたところで、
それは1バイトのデータが1000ということなので、
反転されない。(1バイトはそのままメモリに格納されるから)

ということですよね?

まさに、
---------------------------------
「100Byteのデータ」というのが所詮「1Byteデータ100個のカタマリ」でしかないためです。
---------------------------------
ということですね。

少しエンディアンについて理解できた気がします。

補足日時:2011/08/01 18:43
    • good
    • 0

「htonl でビッグエンディアンに変換できる」というのは正しいといえば正しいんだけど, より正確には


ホストのバイトオーダーからネットワークバイトオーダーに変換する
です. もちろん「ネットワークバイトオーダー」は「ビッグエンディアン」なので, リトルエンディアンのホストで実行すれば結果的に「リトルエンディアンからビッグエンディアンに変換できる」ということにはなります.

後の例は
・「32ビットを超えるデータ、たとえば100バイトとかをmallocにして変数に代入しました。」というのがどのように入れているのかが分からん
・「バイナリでの出力」はどのようにしているのか不明
など, ここには書かれていないことがいくつかあるのでなんとも言えません.

具体的に「こんなコードでこうやったんだけど....」というのが出てくれば言いようもある.

この回答への補足

みなさん、ご回答ありがとうございます。
説明不足で申し訳ないです。


>・「32ビットを超えるデータ、たとえば100バイトとかをmallocにして変数に代入しました。」
>というのがどのように入れているのかが分からん

ですが、

a[] = {"AA","BB","CC"・・・・・ZZ}(100バイト)

char *buff;
buff = malooc(100);
memcoy(buff, a, 100)


として代入しています。



>・「バイナリでの出力」はどのようにしているのか不明

は、

"wb" で開いたfpに、

fwrite(buff, 1, 100, fp)

としています。


この時、ファイルには、

AABBCCDD・・・・・ZZと出力されています。

ZZ・・・・・BBAA とは出力されていません。


リトルエンディアンなのに、そのまま出力されている現象が
理解できていません。

ちなみに

int32 a = 0xAABBCCDD;

とすると、DDCCBBAAと出力されています。

以上、よろしくお願いいたします。

補足日時:2011/08/01 18:37
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!