C Definisi makro untuk menentukan mesin big endian atau little endian?


107

Apakah ada definisi makro satu baris untuk menentukan endianness mesin. Saya menggunakan kode berikut tetapi mengubahnya menjadi makro akan terlalu lama.

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

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

2
Mengapa tidak menyertakan kode yang sama ke dalam makro?
gigi tajam

4
Anda tidak dapat menentukan endianness hanya dengan C preprocessor. Anda juga ingin 0bukannya NULLdalam tes akhir Anda, dan mengubah salah satu test_endianobjek menjadi yang lain :-).
Alok Singhal

2
Juga mengapa makro diperlukan? Fungsi sebaris akan melakukan hal yang sama dan jauh lebih aman.
gigi tajam

13
@Sharptooth, makro menarik karena nilainya mungkin diketahui pada waktu kompilasi, yang berarti Anda dapat menggunakan ketangguhan platform Anda untuk mengontrol instansiasi template, misalnya, atau bahkan mungkin memilih blok kode yang berbeda dengan sebuah #ifarahan.
Rob Kennedy

3
Itu benar, tapi tidak efisien. Jika saya memiliki cpu little-endian, dan saya menulis data little-endian ke kabel atau ke file, saya lebih suka menghindari pembongkaran dan pengemasan ulang data tanpa tujuan. Saya biasa menulis driver video untuk mencari nafkah. Hal ini sangat penting ketika menulis piksel untuk kartu video untuk mengoptimalkan setiap tempat yang Anda bisa.
Edward Falk

Jawaban:


102

Kode yang mendukung urutan byte arbitrer, siap untuk dimasukkan ke dalam file bernama 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

Anda akan memeriksa sistem little endian melalui

O32_HOST_ORDER == O32_LITTLE_ENDIAN

11
Ini tidak memungkinkan Anda memutuskan endian-an sampai runtime. Berikut ini gagal untuk mengkompilasi karena. / ** isLittleEndian :: result -> 0 atau 1 * / struct isLittleEndian {enum isLittleEndianResult {result = (O32_HOST_ORDER == O32_LITTLE_ENDIAN)}; };
pengguna48956

3
Apakah tidak mungkin mendapatkan hasil hingga runtime?
k06a

8
Kenapa char? Lebih baik gunakan uint8_tdan gagal jika jenis ini tidak tersedia (yang dapat diperiksa oleh #if UINT8_MAX). Perhatikan bahwa CHAR_BITtidak bergantung uint8_t.
Andreas Spindler


3
Izinkan saya memasukkan satu lagi ke dalam campuran, untuk kelengkapan:O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */
Edward Falk

49

Jika Anda memiliki kompiler yang mendukung literal majemuk C99:

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

atau:

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

Secara umum, Anda harus mencoba menulis kode yang tidak bergantung pada endianness platform host.


Contoh implementasi host-endianness-independent dari 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
"Anda harus mencoba menulis kode yang tidak bergantung pada endianness platform host". Sayangnya permohonan saya, "Saya tahu kami sedang menulis lapisan kompatibilitas POSIX, tetapi saya tidak ingin mengimplementasikan ntoh, karena itu tergantung pada endianness platform host" selalu tidak didengar ;-). Penanganan format grafis dan kode konversi adalah kandidat utama lain yang pernah saya lihat - Anda tidak ingin mendasarkan semuanya dengan memanggil ntohl sepanjang waktu.
Steve Jessop

5
Anda dapat mengimplementasikan ntohldengan cara yang tidak bergantung pada endianness platform host.
kafe

1
@caf bagaimana Anda akan menulis ntohl dengan cara host-endianness-independent?
Hayri Uğur Koltuk

3
@AliVeli: Saya telah menambahkan contoh implementasi ke jawabannya.
kafe

6
Saya juga harus menambahkan sebagai catatan, bahwa "(* (uint16_t *)" \ 0 \ xff "<0x100)" tidak akan dapat dikompilasi menjadi sebuah konstanta, tidak peduli seberapa banyak saya mengoptimalkan, setidaknya dengan gcc 4.5.2. Itu selalu membuat kode yang dapat dieksekusi.
Edward Falk

43

Tidak ada standar, tetapi pada banyak sistem termasuk <endian.h>akan memberi Anda beberapa definisi untuk dicari.


30
Uji ketangguhan dengan #if __BYTE_ORDER == __LITTLE_ENDIANdan #elif __BYTE_ORDER == __BIG_ENDIAN. Dan menghasilkan elemen lain #error.
To1ne

6
<endian.h>tidak tersedia di Windows
rustyx

2
Proyek Android dan Chromium menggunakan endian.hkecuali __APPLE__atau _WIN32ditentukan.
patryk.beza

1
Di OpenBSD 6.3, <endian.h> menyediakan #if BYTE_ORDER == LITTLE_ENDIAN(atau BIG_ENDIAN) tanpa garis bawah sebelum namanya. _BYTE_ORDERhanya untuk header sistem. __BYTE_ORDERtidak ada.
George Koehler

@ To1ne Saya ragu bahwa Endianness relevan untuk Windows, karena Windows (setidaknya saat ini) hanya berjalan pada mesin x86 dan ARM. x86 selalu LE dan ARM dapat dikonfigurasi untuk menggunakan salah satu arsitektur.
SimonC

27

Untuk mendeteksi endianness pada waktu proses, Anda harus dapat merujuk ke memori. Jika Anda tetap menggunakan C standar, mendeklarasikan variabel dalam memori memerlukan pernyataan, tetapi mengembalikan nilai memerlukan ekspresi. Saya tidak tahu cara melakukan ini dalam satu makro — inilah alasan gcc memiliki ekstensi :-)

Jika Anda ingin memiliki file .h, Anda dapat menentukan

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

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

lalu Anda bisa menggunakan ENDIANNESSmakro sesuka Anda.


6
Saya suka ini karena ia mengakui keberadaan ketekunan selain yang kecil dan besar.
Alok Singhal

6
Ngomong-ngomong, mungkin ada baiknya memanggil makro INT_ENDIANNESS, atau bahkan UINT32_T_ENDIANNESS, karena ini hanya menguji representasi penyimpanan dari satu jenis. Ada ARM ABI di mana tipe integralnya adalah little-endian, tetapi ganda adalah middle-endian (setiap kata adalah little-endian, tetapi kata dengan sedikit tanda di dalamnya muncul sebelum kata lain). Itu menyebabkan kegembiraan di antara tim penyusun selama sekitar satu hari, saya dapat memberi tahu Anda.
Steve Jessop

19

Jika Anda hanya ingin mengandalkan preprocessor, Anda harus mencari tahu daftar simbol yang telah ditentukan sebelumnya. Aritmatika preprocessor tidak memiliki konsep pengalamatan.

GCC di Mac mendefinisikan __LITTLE_ENDIAN__atau__BIG_ENDIAN__

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

Kemudian, Anda dapat menambahkan lebih banyak arahan bersyarat praprosesor berdasarkan deteksi platform seperti #ifdef _WIN32dll.


6
GCC 4.1.2 di Linux tampaknya tidak mendefinisikan makro tersebut, meskipun GCC 4.0.1 dan 4.2.1 mendefinisikannya di Macintosh. Jadi ini bukan metode yang dapat diandalkan untuk pengembangan lintas platform, bahkan saat Anda diizinkan untuk menentukan kompiler mana yang akan digunakan.
Rob Kennedy

1
oh ya itu karena ini hanya ditentukan oleh GCC di Mac.
Gregory Pakosz

Catatan: GCC saya (di Mac) mendefinisikan #define __BIG_ENDIAN__ 1dan #define _BIG_ENDIAN 1.

clang 5.0.1 untuk OpenBSD / amd64 memiliki #define __LITTLE_ENDIAN__ 1. Makro ini tampaknya merupakan fitur clang, bukan fitur gcc. The gccperintah dalam beberapa Mac tidak gcc, itu dentang.
George Koehler

GCC 4.2.1 di Mac adalah GCC saat itu
Gregory Pakosz

15

Saya yakin inilah yang diminta. Saya hanya menguji ini pada mesin endian kecil di bawah pnidui. Seseorang mohon konfirmasi pada mesin big endian.

    #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

Sebagai catatan tambahan (khusus kompilator), dengan kompilator agresif Anda dapat menggunakan pengoptimalan "penghapusan kode mati" untuk mencapai efek yang sama seperti waktu kompilasi #ifseperti ini:

    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; // ?
    }

Hal di atas bergantung pada fakta bahwa kompilator mengenali nilai konstan pada waktu kompilasi, sepenuhnya menghapus kode di dalamnya if (false) { ... }dan mengganti kode seperti if (true) { foo(); }dengan foo();skenario kasus terburuk: kompilator tidak melakukan pengoptimalan, Anda masih mendapatkan kode yang benar tetapi sedikit lebih lambat.


Saya suka metode ini, tetapi perbaiki saya jika saya salah: ini hanya berfungsi ketika Anda mengompilasi pada mesin yang Anda buat, benar?
leetNightshade

3
gcc juga memunculkan kesalahan karena konstanta karakter multi-karakter. Jadi, tidak portabel.
Edward Falk

2
kompiler apa yang membiarkan Anda menulis 'ABCD'?
Ryan Haining

2
Banyak kompiler akan mengizinkan konstanta karakter multibyte dalam mode kepatuhan santai, tetapi menjalankan bagian teratas dengan clang -Wpedantic -Werror -Wall -ansi foo.cdan itu akan error. (Dentang dan ini khusus: -Wfour-char-constants -Werror)

@ Edward Falk Ini bukan kesalahan untuk memiliki konstanta multi-karakter dalam kode. Ini adalah perilaku yang ditentukan oleh implementasi C11 6.4.4.4. 10. gcc dan lainnya mungkin / mungkin tidak memperingatkan / error tergantung pada pengaturan, tetapi ini bukan kesalahan C. Tentu tidak populer menggunakan konstanta karakter multi-karakter.
chux - Kembalikan Monica

10

Jika Anda mencari tes waktu kompilasi dan Anda menggunakan gcc, Anda dapat melakukan:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

Lihat dokumentasi gcc untuk informasi lebih lanjut.


3
Ini jelas merupakan jawaban terbaik untuk siapa pun yang menggunakan gcc
rtpax

2
__BYTE_ORDER__tersedia sejak GCC 4.6
Benoit Blanchon

8

Anda dapat di akses fakta memori dari objek sementara dengan menggunakan literal senyawa (C99):

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

GCC mana yang akan dievaluasi pada waktu kompilasi.


Saya suka itu. Apakah ada cara portabel dan waktu kompilasi untuk mengetahui bahwa Anda sedang mengompilasi dengan C99?
Edward Falk

1
Oh, dan bagaimana jika bukan GCC?
Edward Falk

1
@Tokopedia #if __STDC_VERSION__ >= 199901L.
Jens

7

'Perpustakaan jaringan C' menawarkan fungsi untuk menangani ketangguhan. Yaitu htons (), htonl (), ntohs () dan ntohl () ... di mana n adalah "jaringan" (mis. Big-endian) dan h adalah "host" (yaitu kesempurnaan mesin yang menjalankan kode).

'Fungsi' yang jelas ini (umumnya) didefinisikan sebagai makro [lihat <netinet / in.h>], jadi tidak ada overhead waktu proses untuk menggunakannya.

Makro berikut menggunakan 'fungsi' ini untuk mengevaluasi ketangguhan.

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

Sebagai tambahan:

Satu-satunya saat saya perlu mengetahui endian'ness dari suatu sistem adalah ketika saya menulis variabel [ke file / lainnya] yang dapat dibaca oleh sistem lain yang tidak diketahui endian'ness (untuk kompatibilitas lintas platform ) ... Dalam kasus seperti ini, Anda mungkin lebih suka menggunakan fungsi endian secara langsung:

#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);

Ini tidak benar-benar menjawab pertanyaan yang sedang mencari cara cepat untuk menentukan ketekunan.
Oren

@Oren: Sehubungan dengan kritik Anda yang valid, saya telah menambahkan detail yang membahas pertanyaan asli secara lebih langsung.
BlueChip

6

Gunakan fungsi sebaris daripada makro. Selain itu, Anda perlu menyimpan sesuatu dalam memori yang merupakan efek samping makro yang tidak terlalu bagus.

Anda bisa mengubahnya menjadi makro pendek menggunakan variabel statis atau global, seperti ini:

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

Saya pikir ini adalah yang terbaik karena paling sederhana. namun tidak diuji melawan campuran endian
Hayri Uğur Koltuk

1
Mengapa tidak s_endianessdisetel ke 1 untuk memulai?
SquareRootOfTwentyThree

5

Meskipun tidak ada #define portabel atau sesuatu yang dapat diandalkan, platform menyediakan fungsi standar untuk mengonversi ke dan dari endian 'host' Anda.

Umumnya, Anda melakukan penyimpanan - ke disk, atau jaringan - menggunakan 'network endian', yang merupakan BIG endian, dan komputasi lokal menggunakan host endian (yang pada x86 disebut LITTLE endian). Anda menggunakan htons()dan ntohs()dan teman untuk mengonversi di antara keduanya.


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

6
Ini juga menghasilkan kode yang dapat dieksekusi, bukan konstanta. Anda tidak dapat melakukan "#if IS_BIG_ENDIAN"
Edward Falk

Saya suka solusi ini karena tidak bergantung pada perilaku standar C / C ++ yang tidak ditentukan, sejauh yang saya mengerti. Ini bukan waktu kompilasi tetapi satu-satunya solusi standar untuk itu adalah menunggu c ++ 20 std :: endian
ceztko

4

Jangan lupa bahwa kesungguhan bukanlah keseluruhan cerita - ukuran charmungkin bukan 8 bit (mis. DSP), negasi pelengkap dua tidak dijamin (mis. Cray), penyelarasan ketat mungkin diperlukan (mis. SPARC, juga ARM muncul di tengah -endian saat tidak sejajar), dll, dll.

Mungkin ide yang lebih baik untuk menargetkan arsitektur CPU tertentu sebagai gantinya.

Sebagai contoh:

#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
}

Perhatikan bahwa solusi ini juga sayangnya tidak ultra-portabel, karena bergantung pada definisi khusus kompiler (tidak ada standar, tetapi berikut adalah kompilasi bagus dari definisi tersebut).


3

Coba ini:

#include<stdio.h>        
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{

   TEST;
}

2

Harap perhatikan bahwa sebagian besar jawaban di sini tidak portabel, karena penyusun hari ini akan mengevaluasi jawaban tersebut dalam waktu kompilasi (bergantung pada pengoptimalan) dan mengembalikan nilai tertentu berdasarkan endian tertentu, sedangkan endian mesin yang sebenarnya dapat berbeda. Nilai di mana endianness diuji, tidak akan pernah mencapai memori sistem sehingga kode yang dijalankan sebenarnya akan mengembalikan hasil yang sama terlepas dari endian yang sebenarnya.

untuk contoh , di ARM Cortex-M3 endianness diimplementasikan akan mencerminkan dalam AIRCR.ENDIANNESS Status bit dan compiler tidak dapat mengetahui nilai ini dalam waktu kompilasi.

Hasil kompilasi untuk beberapa jawaban yang disarankan di sini:

https://godbolt.org/z/GJGNE2 untuk jawaban ini ,

https://godbolt.org/z/Yv-pyJ untuk ini jawaban , dan seterusnya.

Untuk mengatasinya, Anda harus menggunakan volatilekualifikasi. Yogeesh H TJawabannya 's adalah yang paling dekat untuk penggunaan kehidupan nyata saat ini, tapi karena Christophmenyarankan solusi yang lebih komprehensif, memperbaiki sedikit untuk itu jawabannya akan membuat jawabannya lengkap, hanya menambahkan volatileuntuk deklarasi union: static const volatile union.

Ini akan memastikan penyimpanan dan pembacaan dari memori, yang diperlukan untuk menentukan ketangguhan.


2

Jika Anda membuang preprocessor #defines

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

Anda biasanya dapat menemukan hal-hal yang akan membantu Anda. Dengan logika waktu kompilasi.

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

Namun, berbagai kompiler mungkin memiliki definisi yang berbeda.


0

Jawaban saya tidak seperti yang ditanyakan tetapi sangat mudah untuk mengetahui apakah sistem Anda little endian atau big endian?

Kode:

#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

Kode C untuk memeriksa apakah suatu sistem adalah little-endian atau big-indian.

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

Makro untuk menemukan endiannes

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

atau

#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
Makro pertama salah dan akan selalu mengembalikan "Big-Endian". Pergeseran bit tidak dipengaruhi oleh endianness - endianness hanya memengaruhi pembacaan dan penyimpanan ke memori.
GaspardP
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.