マシンのエンディアンを決定するための 1 行のマクロ定義はありますか。次のコードを使用していますが、マクロに変換するには時間がかかりすぎます。
unsigned char test_endian( void )
{
int test_var = 1;
unsigned char *test_endian = (unsigned char*)&test_var;
return (test_endian[0] == 0);
}
マシンのエンディアンを決定するための 1 行のマクロ定義はありますか。次のコードを使用していますが、マクロに変換するには時間がかかりすぎます。
unsigned char test_endian( void )
{
int test_var = 1;
unsigned char *test_endian = (unsigned char*)&test_var;
return (test_endian[0] == 0);
}
任意のバイト オーダーをサポートするコードは、次のファイルに配置できます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
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];
}
標準はありませんが、含む多くのシステムで<endian.h>
は、探すべきいくつかの定義が得られます。
実行時にエンディアンを検出するには、メモリを参照できる必要があります。標準 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
マクロを自由に使用できます。
プリプロセッサのみに依存したい場合は、事前定義されたシンボルのリストを把握する必要があります。プリプロセッサの算術演算には、アドレス指定の概念がありません。
Mac 上のGCCは__LITTLE_ENDIAN__
、または__BIG_ENDIAN__
$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1
#ifdef _WIN32
次に、プラットフォームの検出などに基づいて、プリプロセッサの条件付きディレクティブを追加できます。
これが求められていたものだと思います。これは、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();
実際、複合リテラル (C99) を使用して、一時オブジェクトのメモリにアクセスできます。
#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
コンパイル時に評価する GCC。
「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);
マクロではなくインライン関数を使用してください。その上、何かをメモリに保存する必要がありますが、これはマクロのあまり良くない副作用です。
次のように、静的変数またはグローバル変数を使用して短いマクロに変換できます。
static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
移植可能な #define や依存するものはありませんが、プラットフォームは「ホスト」エンディアンとの間で変換するための標準関数を提供します。
一般に、ディスクまたはネットワークへのストレージは、 BIGエンディアンである「ネットワーク エンディアン」を使用し、ホスト エンディアン (x86 ではLITTLEエンディアン) を使用してローカル計算を行います。htons()
andと friends を使用しntohs()
て、2 つの間で変換します。
エンディアンがすべてではないことを忘れないでください - のサイズはchar
8 ビットではない可能性があり (例: DSP)、2 の補数の否定は保証されていません (例: Cray)、厳密な位置合わせが必要な場合があります (例: 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
}
残念ながら、このソリューションはコンパイラ固有の定義に依存するため、ウルトラポータブルではないことに注意してください (標準はありませんが、そのような定義の優れたコンパイルがあります) 。
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
これを試して:
#include<stdio.h>
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{
TEST;
}
プリプロセッサ #defines をダンプする場合
gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null
通常、あなたに役立つものを見つけることができます。コンパイル時ロジック付き。
#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
ただし、コンパイラによって定義が異なる場合があります。
この質問はcppでも実際にあるので、ここで質問しました。
それだけ#if __cplusplus > 201703L
#include <bit>
#include <iostream>
using namespace std;
int main()
{
if constexpr (endian::native == endian::big)
cout << "big-endian";
else if constexpr (endian::native == endian::little)
cout << "little-endian";
else
cout << "mixed-endian";
}
システムがリトル エンディアンかビッグ インディアンかをチェックするための 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");
私の答えは尋ねられたとおりではありませんが、システムがリトルエンディアンかビッグエンディアンかを見つけるのは本当に簡単ですか?
コード:
#include<stdio.h>
int main()
{
int a = 1;
char *b;
b = (char *)&a;
if (*b)
printf("Little Endian\n");
else
printf("Big Endian\n");
}