あなたがビッグエンディアンまたはリトルエンディアンのアーキテクチャ上にいるかどうかをプログラムで検出する方法はありますか? IntelまたはPPCシステムで実行するコードを記述し、まったく同じコードを使用できるようにする必要があります(つまり、条件付きコンパイルなし)。
型パニングに基づくメソッドは好きではありません。コンパイラによって警告されることがよくあります。それがまさに組合の目的です!
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の両方で機能しますが、それ以外の場合は非常に簡単に混乱します。
Intを設定してビットをマスクすることでそれを行うことができますが、おそらく最も簡単な方法は、組み込みのネットワークバイト変換opsを使用することです(ネットワークバイト順は常にビッグエンディアンなので)。
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
ビットをいじるのはより速くなる可能性がありますが、この方法は単純で簡単で、混乱することはほとんどありません。
この記事 をご覧ください。
マシンのタイプを判別するコードを次に示します
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
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
}
これは通常、コンパイル時に(特にパフォーマンス上の理由で)コンパイラから利用可能なヘッダーファイルを使用するか、独自に作成することで行われます。 Linuxでは、ヘッダーファイル「/usr/include/endian.h」があります。
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 /命令セットに同じコードを実装します。
また、ここで誰かが、エンディアンは実行中に変化しないと言いました。違う。そこにはバイエンディアンのマシンがあります。エンディアンは、実行中に変化する可能性があります。また、リトルエンディアンとビッグエンディアンだけでなく、他のエンディアン(なんという言葉)もあります。
コーディングが嫌いであると同時に好きです...
プリプロセッサがデフォルトで定義するマクロについて誰も言及していないことに驚いた。これらはプラットフォームによって異なりますが、独自のエンディアンチェックを記述するよりもはるかにクリーンです。
例えば; 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 -
マジックはすべての組み込みマクロを出力します)。
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)が必要ですが、説明されているプラットフォームには間違いなく当てはまります。
詳細については、このコードプロジェクトの記事をご覧ください エンディアンネスの基本概念 :
実行時にエンディアン型を動的にテストする方法は?
コンピュータアニメーション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の場合、システムはビッグエンディアンです。
C++の方法は boost を使用することで、プリプロセッサのチェックとキャストは非常に徹底的にテストされたライブラリ内で区分されます。
Predef Library(boost/predef.h)は 4種類のエンディアン を認識します。
Endian Library は、C++標準に提出する予定であり、エンディアンに依存するデータのさまざまな操作をサポートします。
上記の回答で述べたように、エンディアンネスはc ++ 20の一部になります。
PPCおよびIntelプロセッサに移植されたフレームワークを使用している場合を除き、PPCおよびIntelプラットフォームは完全に異なるハードウェアアーキテクチャ、パイプライン、バスなど。これにより、アセンブリコードは2つの間で完全に異なります。
エンディアンを見つけるには、次の手順を実行します。
short temp = 0x1234;
char* tempChar = (char*)&temp;
TempCharは0x12または0x34のいずれかになり、エンディアンネスがわかります。
上記のように、ユニオントリックを使用します。
ただし、上記のアドバイスにはほとんど問題がありません。最も顕著なのは、ほとんどのアーキテクチャで非境界整列メモリアクセスが遅いことで有名です。また、一部のコンパイラは、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では少し遅くなります(すべてのバイトで&、<<、>>、|を展開)、それでもまともです。
bool isBigEndian()
{
static const uint16_t m_endianCheck(0x00ff);
return ( *((uint8_t*)&m_endianCheck) == 0x0);
}
私はこのようなことをします:
bool isBigEndian() {
static unsigned long x(1);
static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
return result;
}
これらの線に沿って、計算を1回だけ行う時間効率の良い関数が得られます。
テストされていませんが、私の心では、これはうまくいくはずですか?原因は、リトルエンディアンでは0x01になり、ビッグエンディアンでは0x00になりますか?
bool runtimeIsLittleEndian(void)
{
volatile uint16_t i=1;
return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
宣言:
コンパイル時、非マクロ、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;
}
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のソリューションに似ています。
条件付きコンパイルが必要ない場合は、エンディアンに依存しないコードを記述するだけです。次に例を示します( 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
また、 boost endian にあるブーストヘッダーファイルなどを使用して、プリプロセッサを介してこれを行うこともできます。
エンディアンヘッダーが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!");
...
int i=1;
char *c=(char*)&i;
bool littleendian=c;
これはどう?
#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;
}
Cコンパイラ(少なくとも私が知っているすべての人)のエンディアンネスの動作方法hasは、コンパイル時に決定されます。 ARM och MIPSのようなビエンディアンプロセッサの場合でも、コンパイル時にエンディアンを選択する必要があります。さらに、エンディアンは、実行可能ファイル(ELFなど)のすべての一般的なファイル形式で定義されます。ビアンディアンコードのバイナリBLOBを作成することは可能ですが(おそらくARMサーバーエクスプロイトの場合は?)、おそらくAssemblyで実行する必要があります。
エンディアンネス -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;
}
union
!を使用しないでください
C++は、union
sを介した型のパニングを許可しません!
最後に書き込まれたフィールドではない共用体フィールドからの読み取りは、undefined behaviour!
多くのコンパイラーが拡張機能としてサポートしていますが、言語は保証しません。
詳細については、この回答を参照してください。
https://stackoverflow.com/a/1199697
移植性が保証されている有効な答えは2つだけです。
最初の答えは、C++ 20をサポートするシステムにアクセスできる場合、
は、std::endian
ヘッダーの<type_traits>
を使用することです。
(執筆時点では、C++ 20はまだリリースされていませんが、何かがstd::endian
のインクルージョンに影響しない限り、これはC++ 20以降のコンパイル時にエンディアンネスをテストするための好ましい方法です。 )
constexpr bool is_little_endian = (std::endian::native == std::endian::little);
C++ 20より前の場合、唯一の有効な答えは、整数を格納してから、型パニングを使用して最初のバイトを検査することです。union
sの使用とは異なり、これはC++の型システムで明示的に許可されています。
また、最適な移植性のためにstatic_cast
を使用する必要があることを覚えておくことが重要です。reinterpret_cast
は実装が定義されているためです。
プログラムが、次のタイプ以外のglvalueを介してオブジェクトの保存された値にアクセスしようとする場合、動作は未定義です:...
char
またはunsigned char
タイプ。
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;
}
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);
}
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バージョンがあります。これは、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);
}
整数がシングルバイト値の場合、エンディアンは意味をなさないため、コンパイル時エラーが生成されます。
それを決定する迅速で標準的な方法はありませんが、これはそれを出力します:
#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;
}