ビッグエンディアンまたはリトルエンディアンマシンを決定するCマクロ定義?


107

マシンのエンディアンを決定する1行のマクロ定義はありますか?次のコードを使用していますが、マクロに変換するには時間がかかりすぎます。

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

2
同じコードをマクロに含めてみませんか?
シャープトゥース2010年

4
Cプリプロセッサだけでは、エンディアンの移植性を判断することはできません。また、希望0の代わりに、NULL最終的なテストで、との変更1 test_endian他の何かにオブジェクト:-)。
Alok Singhal、2010年

2
また、なぜマクロが必要なのですか?インライン関数も同じことを行い、はるかに安全です。
シャープトゥース2010年

13
@Sharptooth、マクロはその値がコンパイル時にわかっている可能性があるため魅力的です。つまり、プラットフォームのエンディアンを使用して、たとえばテンプレートのインスタンス化を制御したり、#ifディレクティブでコードの異なるブロックを選択することさえできます。
ロブ・ケネディ

3
それは事実ですが、非効率的です。リトルエンディアンのCPUがあり、リトルエンディアンのデータをネットワークまたはファイルに書き込んでいる場合は、目的を果たさないようにデータをアンパックしたり再パックしたりすることは避けたいと思います。私は以前、生計を立てるためにビデオドライバを作成していました。ピクセルをビデオカードに書き込む場合、可能な限りすべての場所を最適化することが非常に重要です。
Edward Falk

回答:


102

任意のバイトオーダーをサポートするコード。次のファイルに入れる準備ができていますorder32.h

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
    O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

リトルエンディアンシステムをチェックするには、

O32_HOST_ORDER == O32_LITTLE_ENDIAN

11
ただし、実行時までエンディアンを決定することはできません。次の理由でコンパイルに失敗します。/ ** isLittleEndian :: result-> 0または1 * / struct isLittleEndian {enum isLittleEndianResult {result =(O32_HOST_ORDER == O32_LITTLE_ENDIAN)}; };
user48956 2010

3
実行時まで結果を取得することは不可能ですか?
k06a 2010

8
なんでcharuint8_tこのタイプが利用できない場合は使用して失敗します(これはで確認できます#if UINT8_MAX)。CHAR_BITはから独立していることに注意してくださいuint8_t
Andreas Spindler、

2
これは、C ++でUBです:stackoverflow.com/questions/11373203/...
Lyberta

3
私は完全を期すために、ミックスに多くのものを投げてみましょう:O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */
エドワード・フォーク

49

C99複合リテラルをサポートするコンパイラーがある場合:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

または:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

ただし、一般的には、ホストプラットフォームのエンディアンに依存しないコードを作成する必要があります。


のホストエンディアンに依存しない実装の例ntohl()

uint32_t ntohl(uint32_t n)
{
    unsigned char *np = (unsigned char *)&n;

    return ((uint32_t)np[0] << 24) |
        ((uint32_t)np[1] << 16) |
        ((uint32_t)np[2] << 8) |
        (uint32_t)np[3];
}

3
「ホストプラットフォームのエンディアンに依存しないコードを記述してみてください」。残念ながら、「私たちはPOSIX互換性レイヤーを作成していることはわかっていますが、ホストプラットフォームのエンディアンに依存しているため、ntohを実装したくありません」と常に耳を貸さなかった;-)。グラフィック形式の処理と変換コードは、私が見たもう1つの主要な候補です。常にntohlを呼び出すことにすべてを基づいて考えたいとは思わないでしょう。
スティーブジェソップ2010年

5
ntohlホストプラットフォームのエンディアンに依存しない方法で実装できます。
ca

1
@cafホストエンディアンに依存しない方法でntohlをどのように記述しますか?
HayriUğurKoltuk 2012年

3
@AliVeli:実装例を回答に追加しました。
ca

6
「(*(uint16_t *) "\ 0 \ xff" <0x100)」は、どれだけ最適化しても、少なくともgcc 4.5.2では定数にコンパイルされないというレコードも追加する必要があります。常に実行可能なコードを作成します。
エドワードフォーク

43

標準はありませんが、を含む多くのシステムで<endian.h>は、探すべきいくつかの定義を提供します。


30
#if __BYTE_ORDER == __LITTLE_ENDIANおよびでエンディアンをテストし#elif __BYTE_ORDER == __BIG_ENDIANます。そして、#errorそうでなければ生成します。
To1ne、2011年

6
<endian.h>はWindowsでは利用できません
rustyx

2
または定義されていない限り、AndroidおよびChromiumプロジェクトが使用します。endian.h__APPLE___WIN32
patryk.beza 2016

1
OpenBSD 6.3では、<endian.h>は名前の前にアンダースコアを付けずに#if BYTE_ORDER == LITTLE_ENDIAN(またはBIG_ENDIAN)提供します。_BYTE_ORDERシステムヘッダー専用です。__BYTE_ORDER存在しません。
ジョージケーラー

@ To1ne Windowsは(少なくとも現在のところ)x86とARMマシンでしか動作しないため、エンディアンがWindowsに関連しているとは思えません。x86は常にLEであり、ARMはどちらかのアーキテクチャを使用するように構成可能です。
SimonC 2018

27

実行時にエンディアンを検出するには、メモリを参照できる必要があります。標準Cを使用する場合、メモリ内の変数を宣言するにはステートメントが必要ですが、値を返すには式が必要です。単一のマクロでこれを行う方法がわかりません—これがgccに拡張機能がある理由です:-)

.hファイルを用意する場合は、次のように定義できます。

static uint32_t endianness = 0xdeadbeef; 
enum endianness { BIG, LITTLE };

#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                   : *(const char *)&endianness == 0xde ? BIG \
                   : assert(0))

その後、ENDIANNESSマクロを自由に使用できます。


6
リトルエンディアンとビッグエンディアン以外のエンディアンの存在を認めるので、私はこれが好きです。
Alok Singhal、2010年

6
そういえば、1つのタイプのストレージ表現しかテストしないので、マクロINT_ENDIANNESSまたはUINT32_T_ENDIANNESSを呼び出す価値があるかもしれません。ARM ABIには、整数型がリトルエンディアンですが、倍精度浮動小数点数はミドルエンディアンです(各単語はリトルエンディアンですが、符号ビットが含まれる単語が他の単語の前に来ます)。そのため、コンパイラチームは1日ほど興奮していました。
スティーブジェソップ

19

プリプロセッサのみに依存したい場合は、事前定義されたシンボルのリストを把握する必要があります。プリプロセッサ演算には、アドレス指定の概念はありません。

Macの GCC は__LITTLE_ENDIAN____BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

次に、プラットフォームの検出#ifdef _WIN32などに基づいて、プリプロセッサの条件ディレクティブをさらに追加できます。


6
Linux上のGCC 4.1.2はこれらのマクロを定義していないようですが、GCC 4.0.1および4.2.1はMacintosh上で定義しています。したがって、どのコンパイラを使用するかを決定することが許可されている場合でも、クロスプラットフォーム開発の信頼できる方法ではありません。
ロブ・ケネディ

1
そうそう、それはMac上のGCCによってのみ定義されているからです。
Gregory Pakosz、2011

注:私のGCC(Mac)は#define __BIG_ENDIAN__ 1およびを定義してい#define _BIG_ENDIAN 1ます。

OpenBSD / amd64用のclang 5.0.1にはがあり#define __LITTLE_ENDIAN__ 1ます。このマクロは、gcc機能ではなく、clang機能のようです。gcc一部のMac のコマンドはgccではなく、clangです。
ジョージケーラー

MacのGCC 4.2.1は当時GCC
でした

15

私はこれが求められていたと信じています。私はこれをmsvcのリトルエンディアンマシンでのみテストしました。誰かが大きなエンディアンマシンで確認してください。

    #define LITTLE_ENDIAN 0x41424344UL 
    #define BIG_ENDIAN    0x44434241UL
    #define PDP_ENDIAN    0x42414443UL
    #define ENDIAN_ORDER  ('ABCD') 

    #if ENDIAN_ORDER==LITTLE_ENDIAN
        #error "machine is little endian"
    #elif ENDIAN_ORDER==BIG_ENDIAN
        #error "machine is big endian"
    #elif ENDIAN_ORDER==PDP_ENDIAN
        #error "jeez, machine is PDP!"
    #else
        #error "What kind of hardware is this?!"
    #endif

補足として(コンパイラ固有)、アグレッシブなコンパイラを使用すると、「デッドコードの除去」最適化を使用して、次の#ifようなコンパイル時間と同じ効果を得ることができます。

    unsigned yourOwnEndianSpecific_htonl(unsigned n)
    {
        static unsigned long signature= 0x01020304UL; 
        if (1 == (unsigned char&)signature) // big endian
            return n;
        if (2 == (unsigned char&)signature) // the PDP style
        {
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        if (4 == (unsigned char&)signature) // little endian
        {
            n = (n << 16) | (n >> 16);
            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
            return n;
        }
        // only weird machines get here
        return n; // ?
    }

上記は、コンパイラが完全に内のコードを削除し、コンパイル時に定数の値を認識しているという事実に依存しているif (false) { ... }とのようなコードを置き換えるif (true) { foo(); }foo();、コンパイラは最適化を行いません、あなたはまだ正しいコードを取得しますが少し遅く:最悪のシナリオ。


私はこの方法が好きですが、間違っている場合は修正してください。これは、ビルド対象のマシンでコンパイルしているときにのみ機能します。
leetNightshade

3
また、gccは複数文字の文字定数が原因でエラーをスローします。したがって、ポータブルではありません。
エドワードフォーク

2
どのコンパイラがあなたに書かせています'ABCD'か?
Ryan Haining

2
多くのコンパイラは、緩和されたコンプライアンスモードでマルチバイト文字定数を許可しますが、上部を実行するclang -Wpedantic -Werror -Wall -ansi foo.cとエラーになります。(Clangと具体的に:-Wfour-char-constants -Werror

@Edward Falk コードに複数文字の定数があることはエラーではありません。これは、実装定義の動作C11 6.4.4.4です。10. gccなどは、設定によっては警告またはエラーになる場合がありますが、Cエラーではありません。複数文字の文字定数を使用することは確かに一般的ではありません。
chux-モニカを2016年


8

あなたがすることができ、実際のアクセス中の化合物リテラル(C99)を使用して、一時的なオブジェクトのメモリ:

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

コンパイル時に評価するGCC。


私はそれが好きです。C99でコンパイルしていることを知るための移植可能なコンパイル時の方法はありますか?
Edward Falk

1
ああ、それがGCCでない場合はどうなりますか?
Edward Falk

1
@EdwardFalkはい。#if __STDC_VERSION__ >= 199901L
イェンス

7

「Cネットワークライブラリ」は、エンディアンを処理する関数を提供します。つまり、htons()、htonl()、ntohs()およびntohl()... nは「ネットワーク」(つまりビッグエンディアン)であり、hは「ホスト」(つまり、コード)。

これらの明らかな「関数」は(通常)マクロとして定義されます[<netinet / in.h>を参照]。したがって、それらを使用するためのランタイムオーバーヘッドはありません。

次のマクロは、これらの「関数」を使用してエンディアンを評価します。

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

加えて:

システムのエンディアンを知る必要があるのは、エンディアンが未知の別のシステムによって読み込まれる可能性のある変数を[ファイル/その他]に書き出すときだけです(プラットフォーム間の互換性のため)。 )...このような場合は、エンディアン関数を直接使用することをお勧めします。

#include <arpa/inet.h>

#define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')

// Result will be in 'host' byte-order
unsigned long  jpeg_magic = JPEG_MAGIC;

// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long  jpeg_magic = htonl(JPEG_MAGIC);

これは、エンディアンを判断するための迅速な方法を探していた質問には実際には答えません。
オーレン2013年

@オーレン:あなたの正当な批判に関して、私は元の質問をより直接的に扱う詳細を前に付け加えました。
BlueChip

6

マクロではなくインライン関数を使用します。さらに、マクロのあまり良くない副作用である何かをメモリに保存する必要があります。

次のように、静的変数またはグローバル変数を使用して短いマクロに変換できます。

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)

一番簡単なのでこれが一番だと思います。ただし、混合エンディアンに対するテストは行われません
HayriUğurKoltuk、2012年

1
s_endianess最初から1に設定されていないのはなぜですか?
SquareRootOfTwentyThree

5

移植可能な#defineや依存するものはありませんが、プラットフォームは「ホスト」エンディアンとの間で変換するための標準関数を提供します。

一般に、ディスクまたはネットワークへのストレージは、BIGエンディアンである「ネットワークエンディアン」と、ホストエンディアン(x86ではLITTLEエンディアン)を使用したローカル計算を使用して行います。htons()and ntohs()とfriendsを使用して、2つの間の変換を行います。


4
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)

6
これにより、定数ではなく実行可能コードも生成されます。「#if IS_BIG_ENDIAN」を実行できませんでした
エドワードフォーク

私が理解している限り、C / C ++標準の未定義の動作に依存しないため、このソリューションが好きです。それはコンパイル時間ではありませんが、そのための唯一の標準的な解決策はc ++ 20 std :: endian
ceztko

4

そのエンディアンが全体の話ではありません忘れてはいけない-の大きさcharかもしれないが8ビット(例えばDSPの)ではない、2の補数否定(例えばクレイ)保証されていない、厳格な位置合わせが必要になる場合があります(例えばSPARC、またにスプリングをARM 真ん中-アライメントされていない場合はエンディアン)など

代わりに特定のCPUアーキテクチャをターゲットにすることをお勧めします。

例えば:

#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
  #define USE_LITTLE_ENDIAN_IMPL
#endif

void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
  // Intel x86-optimized, LE implementation
#else
  // slow but safe implementation
#endif
}

それはコンパイラ固有の定義に依存するこのソリューションは、残念ながらも、ウルトラポータブルはないことに注意してください(そこには標準がありませんが、ここにあるような定義の素敵なコンパイル)。



2

実際のマシンのエンディアンは異なる場合がありますが、今日のコンパイラーはコンパイル時間(最適化によって異なります)でそれらの回答を評価し、特定のエンディアンに基づいて特定の値を返すため、ここでの回答のほとんどは移植可能ではないことに注意してください。エンディアンがテストされる値はシステムメモリに到達しないため、実際に実行されるコードは、実際のエンディアンに関係なく同じ結果を返します。

、ARMのCortex-M3で実装エンディアンは、ステータスビットAIRCR.ENDIANNESSに反映されますと、コンパイラは、コンパイル時にこの値を知ることができません。

ここで提案されているいくつかの回答のコンパイル出力:

https://godbolt.org/z/GJGNE2ため、この答えは、

https://godbolt.org/z/Yv-pyJため、この答えは。

それを解決するには、volatile修飾子を使用する必要があります。Yogeesh H Tの答えは、今日の実際の使用法に最も近いものですが、Christophより包括的なソリューションを提案しているので、彼の答えを少し修正すると、答えが完全になりvolatileますstatic const volatile union。ユニオン宣言に追加するだけです。

これは、エンディアンを決定するために必要なメモリへの保存とメモリからの読み取りを保証します。


2

プリプロセッサをダンプする場合#defines

gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null

あなたは通常あなたを助けるものを見つけることができます。コンパイル時ロジック付き。

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

ただし、さまざまなコンパイラでは定義が異なる場合があります。


0

私の答えは質問どおりではありませんが、システムがリトルエンディアンかビッグエンディアンかを見つけるのは本当に簡単ですか?

コード:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}

0

システムがリトルエンディアンかビッグインディアンかをチェックするためのCコード。

int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
    puts("This system is little-endian");
else
    puts("This system is big-endian");

-3

エンディアンを見つけるマクロ

#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))

または

#include <stdio.h>

#define ENDIAN() { \
volatile unsigned long ul = 1;\
volatile unsigned char *p;\
p = (volatile unsigned char *)&ul;\
if (*p == 1)\
puts("Little endian.");\
else if (*(p+(sizeof(unsigned long)-1)) == 1)\
puts("Big endian.");\
else puts("Unknown endian.");\
}

int main(void) 
{
       ENDIAN();
       return 0;
}

3
最初のマクロは正しくなく、常に「ビッグエンディアン」を返します。ビットシフトはエンディアンの影響を受けません-エンディアンはメモリへの読み取りと保存にのみ影響します。
GaspardP 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.