web-dev-qa-db-ja.com

C ++プログラムでプログラムによりエンディアンを検出する

あなたがビッグエンディアンまたはリトルエンディアンのアーキテクチャ上にいるかどうかをプログラムで検出する方法はありますか? IntelまたはPPCシステムで実行するコードを記述し、まったく同じコードを使用できるようにする必要があります(つまり、条件付きコンパイルなし)。

197
Jay T

型パニングに基づくメソッドは好きではありません。コンパイラによって警告されることがよくあります。それがまさに組合の目的です!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

原則は、他の人が提案したタイプケースと同等ですが、これはより明確であり、C99によると、正しいことが保証されています。 gccは、直接ポインターキャストよりもこれを好みます。

これは、コンパイル時にエンディアンを修正するよりもはるかに優れています-マルチアーキテクチャをサポートするOS(たとえば、Mac OS Xのファットバイナリ)では、ppc/i386の両方で機能しますが、それ以外の場合は非常に簡単に混乱します。

170

Intを設定してビットをマスクすることでそれを行うことができますが、おそらく最も簡単な方法は、組み込みのネットワークバイト変換opsを使用することです(ネットワークバイト順は常にビッグエンディアンなので)。

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

ビットをいじるのはより速くなる可能性がありますが、この方法は単純で簡単で、混乱することはほとんどありません。

80
Eric Petroelje

この記事 をご覧ください。

マシンのタイプを判別するコードを次に示します

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
60
Andrew Hare

GCC 8+やClang 7+などのC++ 20コンパイラにアクセスできる場合は、 std::endian を使用できます。

注:std::endian<type_traits>で始まりましたが、 移動されました 2019ケルン会議で<bit>になりました。 GCC 8、Clang 7、8、および9では<type_traits>に、GCC 9+およびClang 10+では<bit>に含まれています。

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}
39
Lyberta

これは通常、コンパイル時に(特にパフォーマンス上の理由で)コンパイラから利用可能なヘッダーファイルを使用するか、独自に作成することで行われます。 Linuxでは、ヘッダーファイル「/usr/include/endian.h」があります。

33
bill

Ehm ...コンパイラがテストを単純に最適化し、固定値を戻り値として出力することに誰も気付いていないことに驚いています。これにより、上記のすべてのコード例が無駄になります。返される唯一のものは、コンパイル時のエンディアンです!はい、私は上記のすべての例をテストしました。 MSVC 9.0(Visual Studio 2008)の例を以下に示します。

純粋なCコード

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

逆アセンブリ

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

おそらく、この機能だけでコンパイル時の最適化をオフにすることは可能かもしれませんが、私にはわかりません。それ以外の場合は、アセンブリではハードコード化できますが、移植性はありません。その場合でも、それでも最適化される可能性があります。本当にくだらないアセンブラが必要だと思うようになり、既存のすべてのCPU /命令セットに同じコードを実装します。

また、ここで誰かが、エンディアンは実行中に変化しないと言いました。違う。そこにはバイエンディアンのマシンがあります。エンディアンは、実行中に変化する可能性があります。また、リトルエンディアンとビッグエンディアンだけでなく、他のエンディアン(なんという言葉)もあります。

コーディングが嫌いであると同時に好きです...

15
Coriiander

プリプロセッサがデフォルトで定義するマクロについて誰も言及していないことに驚いた。これらはプラットフォームによって異なりますが、独自のエンディアンチェックを記述するよりもはるかにクリーンです。

例えば; GCCが定義する組み込みマクロ(X86-64マシン上)を見ると:

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

PPCマシンでは次のようになります:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

:| gcc -dM -E -x c -マジックはすべての組み込みマクロを出力します)。

14
DaveR

Int変数を宣言します。

int variable = 0xFF;

次に、char *ポインターを使用してさまざまな部分に移動し、それらの部分に何があるかを確認します。

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

どちらが0xFFバイトを指しているかに応じて、エンディアンを検出できます。これにはsizeof(int)> sizeof(char)が必要ですが、説明されているプラ​​ットフォームには間違いなく当てはまります。

14
sharptooth

詳細については、このコードプロジェクトの記事をご覧ください エンディアンネスの基本概念

実行時にエンディアン型を動的にテストする方法は?

コンピュータアニメーションFAQで説明されているように、次の関数を使用して、コードがリトルエンディアンシステムまたはビッグエンディアンシステムで実行されているかどうかを確認できます。

#define BIG_ENDIAN      0
#define LITTLE_ENDIAN   1
int TestByteOrder()
{
   short int Word = 0x0001;
   char *byte = (char *) &Word;
   return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

このコードは、値0001hを16ビット整数に割り当てます。次に、整数値の最初の(最下位)バイトを指すようにcharポインターが割り当てられます。整数の最初のバイトが0x01hの場合、システムはリトルエンディアンです(0x01hは最下位または最下位アドレスにあります)。 0x00hの場合、システムはビッグエンディアンです。

8
none

C++の方法は boost を使用することで、プリプロセッサのチェックとキャストは非常に徹底的にテストされたライブラリ内で区分されます。

Predef Library(boost/predef.h)は 4種類のエンディアン を認識します。

Endian Library は、C++標準に提出する予定であり、エンディアンに依存するデータのさまざまな操作をサポートします。

上記の回答で述べたように、エンディアンネスはc ++ 20の一部になります。

6
fuzzyTew

PPCおよびIntelプロセッサに移植されたフレームワークを使用している場合を除き、PPCおよびIntelプラットフォームは完全に異なるハードウェアアーキテクチャ、パイプライン、バスなど。これにより、アセンブリコードは2つの間で完全に異なります。

エンディアンを見つけるには、次の手順を実行します。

short temp = 0x1234;
char* tempChar = (char*)&temp;

TempCharは0x12または0x34のいずれかになり、エンディアンネスがわかります。

5
samoz

上記のように、ユニオントリックを使用します。

ただし、上記のアドバイスにはほとんど問題がありません。最も顕著なのは、ほとんどのアーキテクチャで非境界整列メモリアクセスが遅いことで有名です。また、一部のコンパイラは、Word境界整列がない限り、そのような定数述語さえまったく認識しません。

単なるエンディアンテストは退屈なので、ここでは(テンプレート)関数を使用します。これは、ホストアーキテクチャに関係なく、仕様に従って任意の整数の入出力を反転します。

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == Host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

使用法:

指定されたエンディアンからホストに変換するには、次を使用します。

Host = endian(source, endian_of_source)

ホストエンディアンから特定のエンディアンに変換するには、次を使用します。

output = endian(hostsource, endian_you_want_to_output)

結果のコードは、clangでハンドアセンブリを記述するのと同じくらい高速ですが、gccでは少し遅くなります(すべてのバイトで&、<<、>>、|を展開)、それでもまともです。

5
kat
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
4
Paolo Brandoli

私はこのようなことをします:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

これらの線に沿って、計算を1回だけ行う時間効率の良い関数が得られます。

4
Jeremy Mayhew

テストされていませんが、私の心では、これはうまくいくはずですか?原因は、リトルエンディアンでは0x01になり、ビッグエンディアンでは0x00になりますか?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
3
hanshenrik

宣言:

最初の投稿が「コンパイル時間」として誤って宣言されています。そうではなく、現在のC++標準では不可能です。 constexprは、関数が常にコンパイル時の計算を行うことを意味しません。リチャード・ホッジスの訂正に感謝します。

コンパイル時、非マクロ、C++ 11 constexprソリューション:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
3
zhaorufei
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

これは別の解決策です。 Andrew Hareのソリューションに似ています。

3
Neeraj

条件付きコンパイルが必要ない場合は、エンディアンに依存しないコードを記述するだけです。次に例を示します( Rob Pike から取得):

ディスク上のリトルエンディアンに保存された整数を、エンディアンに依存しない方法で読み取ります。

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

マシンのエンディアンを考慮しようとする同じコード:

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
2
fjardon

また、 boost endian にあるブーストヘッダーファイルなどを使用して、プリプロセッサを介してこれを行うこともできます。

2
nmushell

エンディアンヘッダーがGCC専用でない限り、使用可能なマクロが提供されます。

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...
1
Mark A. Libby
int i=1;
char *c=(char*)&i;
bool littleendian=c;
1
Jon Bright

これはどう?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}
1
Abhay

Cコンパイラ(少なくとも私が知っているすべての人)のエンディアンネスの動作方法hasは、コンパイル時に決定されます。 ARM och MIPSのようなビエンディアンプロセッサの場合でも、コンパイル時にエンディアンを選択する必要があります。さらに、エンディアンは、実行可能ファイル(ELFなど)のすべての一般的なファイル形式で定義されます。ビアンディアンコードのバイナリBLOBを作成することは可能ですが(おそらくARMサーバーエクスプロイトの場合は?)、おそらくAssemblyで実行する必要があります。

0
Fabel

エンディアンネス -Cレベルコードの図を参照してください。

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 
0
gimel

union!を使用しないでください

C++は、unionsを介した型のパニングを許可しません!
最後に書き込まれたフィールドではない共用体フィールドからの読み取りは、undefined behaviour
多くのコンパイラーが拡張機能としてサポートしていますが、言語は保証しません。

詳細については、この回答を参照してください。

https://stackoverflow.com/a/1199697


移植性が保証されている有効な答えは2つだけです。

最初の答えは、C++ 20をサポートするシステムにアクセスできる場合、
は、std::endianヘッダーの<type_traits>を使用することです。

(執筆時点では、C++ 20はまだリリースされていませんが、何かがstd::endianのインクルージョンに影響しない限り、これはC++ 20以降のコンパイル時にエンディアンネスをテストするための好ましい方法です。 )

C++ 20以降

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

C++ 20より前の場合、唯一の有効な答えは、整数を格納してから、型パニングを使用して最初のバイトを検査することです。
unionsの使用とは異なり、これはC++の型システムで明示的に許可されています。

また、最適な移植性のためにstatic_castを使用する必要があることを覚えておくことが重要です。
reinterpret_castは実装が定義されているためです。

プログラムが、次のタイプ以外のglvalueを介してオブジェクトの保存された値にアクセスしようとする場合、動作は未定義です:... charまたはunsigned charタイプ。

C++ 11以降

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C++ 11以降(enumなし)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C++ 98/C++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}
0
Pharap

別のCバージョンがあります。これは、C99ユニオンリテラルと非標準の__typeof__演算子を介したインライン型パンニング用のwicked_cast()というマクロを定義します。

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

整数がシングルバイト値の場合、エンディアンは意味をなさないため、コンパイル時エラーが生成されます。

0
Christoph

それを決定する迅速で標準的な方法はありませんが、これはそれを出力します:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 
0
yekanchi