私のプログラムがコンパイルされているプラットフォームのエンディアンネスを(コンパイル時に)安全で移植可能な方法で判断できますか?私はCで書いています。
[編集]答えてくれてありがとう、ランタイムソリューションに固執することにしました。
これはコンパイル時のチェック用です
ブーストヘッダーファイル _endian.hpp
_ の情報を使用できます。これは、多くのプラットフォームに対応しています。
ランタイムチェックの編集
_bool isLittleEndian()
{
short int number = 0x1;
char *numPtr = (char*)&number;
return (numPtr[0] == 1);
}
_
整数を作成し、その最初のバイト(最下位バイト)を読み取ります。そのバイトが1の場合、システムはリトルエンディアンです。それ以外の場合はビッグエンディアンです。
それについて考える
はい、sizeof(char) == sizeof(short int)
である一部のプラットフォーム(考えられない)で潜在的な問題に遭遇する可能性があります。 _<stdint.h>
_で利用可能な固定幅のマルチバイト整数型を使用できます。プラットフォームにない場合は、ブーストヘッダーを使用に合わせて調整することもできます。 _stdint.hpp
_ =
compile-timeチェックの元の質問に答えるために、既存のすべてのコンパイラと将来のすべてのコンパイラで機能する標準化された方法はありません。既存のC、C++、およびPOSIX標準のうち、エンディアンを検出するマクロを定義しています。
ただし、既知のコンパイラのセットに制限する場合は、それらのコンパイラのドキュメントを参照して、エンディアンを定義するために使用する定義済みマクロ(存在する場合)を見つけることができます。 このページ は、検索できるいくつかのマクロをリストしているため、これらのマクロで機能するコードを次に示します。
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
defined(__BIG_ENDIAN__) || \
defined(__ARMEB__) || \
defined(__THUMBEB__) || \
defined(__AARCH64EB__) || \
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#Elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
defined(__LITTLE_ENDIAN__) || \
defined(__ARMEL__) || \
defined(__THUMBEL__) || \
defined(__AARCH64EL__) || \
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif
コンパイラがドキュメントから使用する定義済みマクロを見つけることができない場合は、定義済みマクロの完全なリストを吐き出してそこから何が機能するかを推測するように強制することもできます(ENDIAN、ORDER、またはプロセッサを探します)アーキテクチャ名)。 このページ さまざまなコンパイラでそれを行うためのいくつかのメソッドをリストします。
Compiler C macros C++ macros
Clang/LLVM clang -dM -E -x c /dev/null clang++ -dM -E -x c++ /dev/null
GNU GCC/G++ gcc -dM -E -x c /dev/null g++ -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++ cc -dM -E -x c /dev/null aCC -dM -E -x c++ /dev/null
IBM XL C/C++ xlc -qshowmacros -E /dev/null xlc++ -qshowmacros -E /dev/null
Intel ICC/ICPC icc -dM -E -x c /dev/null icpc -dM -E -x c++ /dev/null
Microsoft Visual Studio (none) (none)
Oracle Solaris Studio cc -xdumpmacros -E /dev/null CC -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP pgcc -dM -E (none)
最後に、Microsoft Visual C/C++コンパイラーは奇妙なものであり、上記のものはありません。幸いなことに、彼らは事前に定義されたマクロを文書化しました here 。ターゲットプロセッサアーキテクチャを使用してエンディアンを推測できます。 Windowsで現在サポートされているプロセッサはすべてリトルエンディアン(_M_IX86
、_M_X64
、_M_IA64
、および_M_ARM
はリトルエンディアン)、PowerPCなどの歴史的にサポートされているプロセッサー(_M_PPC
)ビッグエンディアンでした。しかし、より適切なことに、Xbox 360はビッグエンディアンのPowerPCマシンです。したがって、クロスプラットフォームライブラリヘッダーを記述している場合、_M_PPC
。
C99では、次のようにチェックを実行できます。
_#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
_
if (I_AM_LITTLE)
などの条件は、コンパイル時に評価され、コンパイラーがブロック全体を最適化できるようにします。
これが厳密に言えばC99の定数式(静的ストレージ期間データの初期化子で使用できるようにする)であるかどうかについての参考資料はありませんが、そうでない場合は、それは次善の策です。
C FAQ からの興味深い読み物:
おそらくできません。エンディアンを検出するための通常の手法には、ポインターまたはcharの配列、あるいはユニオンが含まれますが、プリプロセッサーの算術演算では長整数のみが使用され、アドレス指定の概念はありません。別の魅力的な可能性は次のようなものです
#if 'ABCD' == 0x41424344
しかし、これも信頼できません。
C++にconstexpr
関数を提供するための答えを拡張したい
_union Mix {
int sdat;
char cdat[4];
};
static constexpr Mix mix { 0x1 };
constexpr bool isLittleEndian() {
return mix.cdat[0] == 1;
}
_
mix
もconstexpr
なので、コンパイル時であり、constexpr bool isLittleEndian()
で使用できます。安全に使用できるはずです。
@Cheersandhthが以下に指摘したように、これらは問題があるようです。
理由は、C++ 11-Standard準拠ではないであり、 type punning は禁止されているためです。一度にactiveになることができるのは、常に1つのユニオンメンバーのみです。標準準拠のコンパイラを使用すると、エラーが発生します。
したがって、C++では使用しないでください。どうやら、Cでもできます。私は答えを教育目的のために残します:-)そして質問はCに関するものです...
これは、int
のサイズが4 char
sであると想定しています。これは、以下で正しく指摘されている@PetrVepřekとして常に与えられるとは限りません。コードを完全に移植可能にするには、ここでもっと賢くする必要があります。ただし、多くの場合、これで十分です。定義により、sizeof(char)
は常に_1
_であることに注意してください。上記のコードは、sizeof(int)==4
を想定しています。
コンパイル時ではなく、おそらく実行時です。以下は、エンディアンを判断するために書いたC関数です。
/* Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN */
#include <inttypes.h>
int endianness()
{
union { uint8_t c[4]; uint32_t i; } data;
data.i = 0x12345678;
return (data.c[0] == 0x78);
}
#include <stdint.h>
#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
適切なオプティマイザーは、コンパイル時にこれを解決します。 gccは-O1
。
もちろん stdint.h
はC99です。 ANSI/C89の移植性については、Doug Gwynの Instant C9x ライブラリを参照してください。
私はかつてこのような構造を使用しました:
_uint16_t HI_BYTE = 0,
LO_BYTE = 1;
uint16_t s = 1;
if(*(uint8_t *) &s == 1) {
HI_BYTE = 1;
LO_BYTE = 0;
}
pByte[HI_BYTE] = 0x10;
pByte[LO_BYTE] = 0x20;
_
-O2を指定したgccは、完全にコンパイル時間を短縮できました。つまり、_HI_BYTE
_変数と_LO_BYTE
_変数は完全に置き換えられ、アセンブラではpByteアクセスも*(unit16_t *pByte) = 0x1020;
と同等のものに置き換えられました。
コンパイル時間です。
私の知る限り、コンパイル時ではありません。
実行時に、既知のビット文字列にマルチバイト値を設定するなどの簡単なチェックを実行し、結果のバイトを検査できます。たとえば、ユニオンを使用して、
typedef union {
uint32_t Word;
uint8_t bytes[4];
} byte_check;
またはキャスティング、
uint32_t Word;
uint8_t * bytes = &Word;
完全に移植可能なエンディアン検査のために、ビッグエンディアン、リトルエンディアン、および混合エンディアンの両方のシステムを考慮する必要があることに注意してください。
CMake TestBigEndian を使用
INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
# big endian
ELSE (ENDIAN)
# little endian
ENDIF (ENDIAN)
EDIT2:この方法は機能しません。マルチバイト定数の表現はコンパイラ/プラットフォーム固有であり、確実に使用することはできません。 interjayが提供したリンク( http://www.ideone.com/LaKpj )は、失敗した例を示しています。 Solaris/SPARCでは、同じコンパイラgcc 4.3.3が正しい答えを出しますが、SUNStudio 12コンパイラは、そのリンクで使用されるx86上のgcc 4.3.4と同じ動作をします。
だから結論は出せるが、まだマルチバイト文字の適切な使用はできない
シンプルでコンパイル時間の長所があるこの新しい方法を見つけました。
switch('AB') { case 0x4142: printf("ASCII Big endian\n"); break; case 0x4241: printf("ASCII Little endian\n"); break; case 0xC1C2: printf("EBCDIC Big endian\n"); break; case 0xC2C1: printf("EBCDIC Little endian\n"); break; }
編集:
プリプロセッサでそれを作る方法さえ見つけました:
#if 'AB' == 0x4142
#error "ASCII Big endian\n"
#Elif 'AB' == 0x4241
#error "ASCII Little endian\n"
#Elif 'AB' == 0xC1C2
#error "EBCDIC Big endian\n"
#Elif 'AB' == 0xC2C1
#error "EBCDIC Little endian\n"
#else
#error "unknown coding and endianness\n"
#endif
そして、誰かが尋ねる前に、マルチバイト文字定数はANSI-C(C90でも)ですが、実装定義です。ここに私が見つけた最初の便利なアプリケーションがあります。