2016-05-09 16 views
-2

コンパイラがリトルエンディアンかビッグエンディアンかをこのプログラムがどのように判断できるか教えてください。コンパイラがC言語のリトルエンディアンかビッグエンディアンかを判断

#include <stdio.h> 
#include <sys/types.h> 
int main(void) { 
    union { 
     long lungo; 
     char ch[sizeof(long)]; 
    } unione; 
    unione.lungo = 1; 
    if (unione.ch[sizeof(long)-1] == 0) 
     printf("little endian\n"); 
    else 
     printf("big endian\n"); 
    return (0); 
} 

特に私はプログラムのこの部分は何をすべきか理解していなかった。

union { 
     long lungo; 
     char ch[sizeof(long)]; 
    } unione; 

おかげ

+0

まず第一に、コンパイラは大きくてもリトルエンディアンでもなく、アーキテクチャは.. UB、あなたはlungoを書いていますが、chを読んでいます。 –

+1

@ SandBag_1996: 'char'とのユニオンによるエイリアスは合法です。 – Olaf

+4

Cの本を使って 'ユニオン 'が何であるかを学んでください。それがあなたの大きな問題です。 – Olaf

答えて

0

ユニオンのすべてのメンバーが同じメモリを占有しているため、互いにオーバーレイされています。 1人の組合員に書き込むと、すべて組合のメンバーが更新されます。

unioneに2人のメンバーを保存するように設定しました。 lungolongであり、chsizeof longバイト(システムに応じて4または8バイト)を保持するサイズのcharの配列です(この説明では4バイトと仮定します)。これらのメンバーは、互いに重なり合っています(同じ4バイトのメモリを占有します)。

ビッグエンディアンのシステム上でそれを覚えて、マルチバイトタイプの最も重要なバイトは、アドレスAに保存され、最下位バイトは、アドレスA + 3で保存されます。リトルエンディアンシステムでは、その順序が逆になります。最下位バイトがアドレスAに保存され、最上位バイトがアドレスA + 3で保存されます。

BE: A  A+1 A+2 A+3  where A is arbitrary address 
     ---- ---- ---- ---- 
lungo: 0x00 0x00 0x00 0x01 
     ---- ---- ---- ---- 
    LE: A+3 A+2 A+1 A 

アレイ、一方で、常に、a[0]は、アドレスAに格納されているように保存されています

したがって
BE: ch[0] ch[1] ch[2] ch[3] 
     ----- ----- ----- ----- 
lungo: 0x00 0x00 0x00 0x01 
     ----- ----- ----- ----- 
    LE: ch[3] ch[2] ch[1] ch[0] 

、リトルエンディアンのシステムで:我々は両方のビッグエンディアンとリトルエンディアンのシステム上lungoに関連してchを見れば、我々は以下を参照してくださいのでa[1]は、などA + 1、に格納されています、ch[0]は、lungoの最下位バイトに対応し、 hは値0x01を含んでいます。ビッグエンディアンシステムでは、lungoの最上位バイトに対応するch[0]は、値0x00を含みます。

これはエンディアンを決定する一般的なトリックですが、厳密には動作は定義されていません。あなたは組合の一人のメンバーに書き込みをし、別のメンバーから読むはずがありません。このテクニックは "マルチバイトタイプ"がcharまたはunsigned charの配列にきれいにマッピングされるために機能しますが、一般的に2つのマルチバイトタイプの間に完全に適用できるものではありません。

少なくとも私が知っている限り、システムのエンディアンを決めるのに100%のポータブルな、標準に準拠した方法はありません。私は

long l = 0x00010203; 
char *c = (char *) &l; 
if (c[0] == 0x03) 
    // little-endian 
else if (c[0] == 0x00) 
    // big-endian 
else 
    // something else 

のように、このまたはタイプpunningのいくつかの種類のようなトリックを伴う知っているすべてのものはやはり、これは良い習慣ではなく、おそらく定義されていませんが、それはほとんどの場合、「働きます」。

大小エンディアンのみが可能な順序ではなく、1つのシステムで複数の順序が可能です。 VAXenは、普通はリトルエンディアンで、32ビットの浮動小数点数を除いて、「中間エンディアン」で2301にレイアウトされています。

+0

私が思い出しているように、1つの 'union'メンバへの書き込みと' unsigned char [] 'のフィールド(同じサイズ)からの読み込みは、未定義の動作ではありません。 – chux

2
union { 
     long lungo; 
     char ch[sizeof(long)]; 
    } unione; 

これは組合です。そのメンバは同じメモリ領域のを占有します。 ...一つのメンバー(この場合は整数)に書き込むための一般的なトリックである

unione.lungo = 1; 

...そして他の(通常は文字列)を介してリードバック...

if (unione.ch[sizeof(long)-1] == 0) 

...前者のタイプのバイト単位の表現を得る。 (この場合、整数の "最初の"バイトは、整数に格納されている1がリトルエンディアンのプラットフォームになるかどうかを判断する)、そうでない場合にはビッグエンディアンになりますプラットフォーム)コメント@chuxに注意してください - 。エンディアンの他の形態では、彼らは非常にまれですがは、存在しません

付録J.1がとして「に保存された最後のもの以外の組合員の値を」指定します。

付録J.3.13は、 "任意のオブジェクトのバイト数、順序、およびエンコーディング(この国際標準で明示的に指定されていない場合)"を実装定義

セクション6.2.6(「タイプの表現」)は、「特定のオブジェクト表現はオブジェクトタイプの値を表す必要はない」オブジェクトの値 がこのような表現を持ち、lvalue式文字の種類がない場合、動作はで、定義されていないのはです。

したがって、未定義の動作ではありませんが、この構造体は...つまり、サイドラインの近くを歩いているとします。 ;-)

+0

これは法的ではありますが、実装に固有の変数が多すぎるために悪いことです。スタンダードに準拠しているので、標準タイプをシリアライズ/マーシャルするための移植可能な方法があります。 – Olaf

0

#define IS_BIG_ENDIAN (!*(unsigned char*)(void*)&(uint16_t){1})あなたのコンパイラにはuint16_tがある場合、それを行うには良い方法です。しかし、あなたに偽陽性を与えるかもしれない他の "エンディアン"スキームがあるかもしれません。よく分かりません。その時点で、私は反例を使ってダウンボートを歓迎するでしょう!

技術的にはあなたが(void*)を通じてC.

にそれを離れてキャストを取得しますが、「労働組合のトリックを」、C++で定義されていない使用上の挙動はCで余分ですが、C++で技術的に必要です。

+0

これは有効なルールに違反し、UBを呼び出します。組合はCでは完全に合法です。オブジェクトを整列させたいだけなら、いい考えはありません。私はgccが少なくともこのためのマクロを提供していると思います(確かではありませんが、ライブラリでもあります)。 – Olaf

+0

@Olaf:これは標準ライブラリのマクロではありません。私は恐れています。 (少なくともC99ではなく、私はC11に関してまだ100%自信がありません) – DevSolar

+2

@Olaf:可能ならば、これがUBだと思う理由を説明してもらえますか? n1548では、セクション6.5.7では、明示的に別の型のオブジェクトに文字型へのポインタを介してアクセスすることができます。 AFAIKこれは、エイリアシング規則を入力する際に​​常に例外となっています。 (この例外がなければ、例えば、移植性のある方法で 'memcpy'を実装することは不可能です)。 –

関連する問題