Mendeteksi endianness secara terprogram dalam program C ++

211

Apakah ada cara terprogram untuk mendeteksi apakah Anda menggunakan arsitektur big-endian atau little-endian? Saya harus dapat menulis kode yang akan dijalankan pada sistem Intel atau PPC dan menggunakan kode yang persis sama (yaitu tidak ada kompilasi bersyarat).

Jay T
sumber
4
Demi kelengkapan, berikut adalah tautan ke pertanyaan orang lain tentang mencoba mengukur endianness (pada waktu kompilasi): stackoverflow.com/questions/280162/…
Faisal Vali
14
Mengapa tidak menentukan endianness pada waktu kompilasi? Itu tidak mungkin berubah saat runtime.
ephemient
3
AFAIK, tidak ada cara yang andal dan universal untuk melakukan itu. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Jawaban:

174

Saya tidak suka metode ini berdasarkan tipe punning - sering akan diperingatkan oleh kompiler. Persis seperti itulah serikat pekerja!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

Prinsipnya setara dengan jenis huruf seperti yang disarankan oleh orang lain, tetapi ini lebih jelas - dan menurut C99, dijamin benar. gcc lebih suka ini dibandingkan dengan penunjuk penunjuk langsung.

Ini juga jauh lebih baik daripada memperbaiki endianness pada waktu kompilasi - untuk OS yang mendukung multi-arsitektur (misalnya binary pada Mac os x), ini akan bekerja untuk ppc / i386, sedangkan sangat mudah untuk mengacaukan semuanya jika tidak .

David Cournapeau
sumber
51
Saya tidak merekomendasikan penamaan variabel "bint" :)
mkb
42
Apakah Anda yakin ini didefinisikan dengan baik? Dalam C ++ hanya satu anggota serikat dapat aktif pada satu waktu - yaitu Anda tidak dapat menetapkan menggunakan satu nama anggota dan membaca menggunakan yang lain (meskipun ada pengecualian untuk struct yang kompatibel dengan tata letak)
Faisal Vali
27
@ Matt: Saya melihat ke Google, dan binti tampaknya memiliki makna dalam bahasa Inggris yang saya tidak menyadari :)
David Cournapeau
17
Saya sudah menguji ini, dan pada gcc 4.0.1 dan gcc 4.4.1 hasil dari fungsi ini dapat ditentukan pada waktu kompilasi dan diperlakukan sebagai konstanta. Ini berarti bahwa kompiler akan jatuh jika cabang yang hanya bergantung pada hasil fungsi ini dan tidak akan pernah diambil pada platform yang dimaksud. Ini mungkin tidak benar dari banyak implementasi htonl.
Mahakuasa
6
Apakah solusi ini benar-benar portabel? Bagaimana jika CHAR_BIT != 8?
zorgit
80

Anda dapat melakukannya dengan menetapkan int dan menutup bit, tetapi mungkin cara termudah adalah dengan menggunakan ops konversi byte yang dibangun di dalam jaringan (karena pesanan byte jaringan selalu merupakan big endian).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

Mengotak-atik sedikit bisa lebih cepat, tetapi cara ini sederhana, mudah dan sangat tidak mungkin untuk dikacaukan.

Eric Petroelje
sumber
1
Operasi konversi jaringan juga dapat digunakan untuk mengubah segalanya menjadi big endian, sehingga memecahkan masalah lain yang mungkin dihadapi oleh Jay.
Brian
6
@sharptooth - slow adalah istilah yang relatif, tetapi ya, jika kecepatan benar-benar masalah, gunakan sekali pada awal program dan atur variabel global dengan endianness.
Eric Petroelje
5
htonl memiliki masalah lain: pada beberapa platform (windows?), itu tidak berada di perpustakaan runtime C yang tepat, tetapi di tambahan, perpustakaan terkait jaringan (socket, dll ...). Ini cukup menjadi penghalang bagi satu fungsi jika Anda tidak membutuhkan perpustakaan sebaliknya.
David Cournapeau
7
Perhatikan bahwa pada Linux (gcc), htonl tunduk pada pelipatan konstan pada waktu kompilasi, jadi ekspresi dari formulir ini tidak memiliki overhead runtime sama sekali (yaitu dilipat konstan menjadi 1 atau 0, dan kemudian penghilangan kode mati menghilangkan cabang lain dari if)
bdonlan
2
Juga, pada x86 htonl dapat (dan, di Linux / gcc) diimplementasikan dengan sangat efisien menggunakan inline assembler, terutama jika Anda menargetkan arsitektur mikro dengan dukungan untuk BSWAPoperasi.
bdonlan
61

Silakan lihat artikel ini :

Berikut ini beberapa kode untuk menentukan jenis mesin Anda

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
Andrew Hare
sumber
25
Ingatlah bahwa itu tergantung pada panjang dan panjang int yang berbeda, yang hampir selalu terjadi tetapi tidak dijamin.
David Thornley
10
Saya telah bekerja pada sistem embedded di mana int pendek dan char adalah ukuran yang sama ... Saya tidak ingat apakah int reguler juga seukuran itu (2 byte) atau tidak.
rmeador
2
mengapa jawaban ini cukup banyak JAWABAN HANYA yang TIDAK membuat saya berpikir "kawan, apa yang kamu lakukan?", yang merupakan kasus dari sebagian besar jawaban di sini: o
hanshenrik
2
@Shillard int setidaknya harus sebesar itu, tetapi tidak ada persyaratan standar untuk char yang dibatasi hingga kurang! Jika Anda melihat keluarga TI F280x, Anda akan menemukan bahwa CHAR_BIT adalah 16 dan sizeof (int) == sizeof (char) sementara batas yang Anda sebutkan dijaga dengan sangat baik ...
Aconcagua
5
Mengapa tidak menggunakan uint8_t dan uint16_t?
Rodrigo
58

Anda dapat menggunakan std::endianjika Anda memiliki akses ke kompiler C ++ 20 seperti GCC 8+ atau Clang 7+.

Catatan: std::endianmulai <type_traits>tetapi dipindahkan ke <bit>pada 2019 pertemuan Cologne. GCC 8, Dentang 7, 8 dan 9 memilikinya <type_traits>sedangkan GCC 9+ dan Dentang 10+ memilikinya <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
}
Lyberta
sumber
5
Karena setiap orang saya memiliki akses ke C ++ 17 dan 20 draft / proposal, tetapi, seperti yang sekarang, apakah ada kompiler C ++ 20 yang pernah ada?
Xeverous
@Xeverous Ini hanya membutuhkan pencacahan scoped jadi saya curiga sebagian besar vendor akan menambahkannya ke implementasi stdlib mereka sebagai salah satu perubahan sebelumnya.
Pharap
@Xeverous GCC 8 dirilis dan mendukungnya.
Lyberta
Dari 30+ jawaban pertanyaan, ini tampaknya satu-satunya, yang sepenuhnya akurat (dengan jawaban lain yang setidaknya sebagian benar).
IInspectable
40

Ini biasanya dilakukan pada waktu kompilasi (khusus untuk alasan kinerja) dengan menggunakan file header yang tersedia dari kompiler atau buat sendiri. Di linux Anda memiliki file header "/usr/include/endian.h"

tagihan
sumber
8
Saya tidak percaya ini belum terpilih. Ini tidak seperti endianness akan berubah di bawah program yang dikompilasi, jadi tidak pernah ada kebutuhan untuk uji runtime.
Dolda2000
@ Dolda2000 Ini berpotensi, melihat mode endian ARM.
Tyzoid
10
@Tyzoid: Tidak, program yang dikompilasi akan selalu berjalan di bawah mode endian yang dikompilasikan, bahkan jika prosesor mampu melakukannya.
Dolda2000
16

Saya terkejut tidak ada yang menyebutkan macro yang didefinisikan oleh pre-processor secara default. Meskipun ini akan bervariasi tergantung pada platform Anda; mereka jauh lebih bersih daripada harus menulis cek-endian Anda sendiri.

Sebagai contoh; jika kita melihat makro bawaan yang didefinisikan GCC (pada mesin X86-64):

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

Pada mesin PPC saya mendapatkan:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

( :| gcc -dM -E -x c -Keajaiban mencetak semua makro built-in).

DaveR
sumber
7
Makro ini tidak muncul sama sekali. Misalnya, dalam gcc 4.4.5 dari repo Redhat 6, menjalankan echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'mengembalikan apa-apa, sedangkan gcc 3.4.3 (dari /usr/sfw/bintoh) di Solaris memiliki definisi di sepanjang baris ini. Saya telah melihat masalah serupa di VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Brian Vandenberg
15

Ehm ... Ini mengejutkan saya bahwa tidak ada yang menyadari bahwa kompiler hanya akan mengoptimalkan tes, dan akan memberikan hasil yang tetap sebagai nilai pengembalian. Ini membuat semua contoh kode di atas, tidak berguna secara efektif. Satu-satunya hal yang akan dikembalikan adalah endianness pada waktu kompilasi! Dan ya, saya menguji semua contoh di atas. Berikut ini adalah contoh dengan MSVC 9.0 (Visual Studio 2008).

Kode C murni

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

Membongkar

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

Mungkin dimungkinkan untuk mematikan optimasi waktu kompilasi APAPUN hanya untuk fungsi ini, tapi saya tidak tahu. Kalau tidak, itu mungkin untuk hardcode dalam perakitan, meskipun itu tidak portabel. Dan bahkan itu pun mungkin akan dioptimalkan. Itu membuat saya berpikir saya perlu assembler yang benar-benar jelek, mengimplementasikan kode yang sama untuk semua set CPU / instruksi yang ada, dan juga .... sudahlah.

Juga, seseorang di sini mengatakan bahwa endianness tidak berubah selama run-time. SALAH. Ada mesin bi-endian di luar sana. Endianness mereka dapat bervariasi eksekusi durng. JUGA, tidak hanya Little Endian dan Big Endian, tetapi juga endiannesses lain (kata apa).

Saya benci dan suka coding pada saat yang sama ...

Ketumbar
sumber
11
Bukankah Anda harus mengkompilasi ulang untuk berjalan pada platform yang berbeda?
bobobobo
2
Meskipun bekerja dengan baik untuk MSVC, itu tidak untuk semua versi GCC dalam semua keadaan. Oleh karena itu, "pemeriksaan run-time" di dalam loop kritis mungkin benar tidak bercabang pada waktu kompilasi, atau tidak. Tidak ada jaminan 100%.
Cyan
21
Tidak ada yang namanya prosesor x86 big-endian. Bahkan jika Anda menjalankan Ubuntu pada prosesor biendian (seperti ARM atau MIPS), executable ELF selalu besar (MSB) atau endian kecil (LSB). Tidak ada executable biendian yang dapat dibuat sehingga tidak diperlukan pemeriksaan runtime.
Fabel
4
Untuk mematikan optimisasi dalam metode ini gunakan 'volatile union ...' Ini memberitahu kompiler bahwa 'u' dapat diubah di tempat lain dan data harus dimuat
mishmashru
1
Agar fungsi ini mengembalikan nilai yang berbeda pada saat runtime daripada pengoptimal menghitung bahwa itu akan menyiratkan bahwa pengoptimal disadap. Apakah Anda mengatakan bahwa ada beberapa contoh kode biner yang dioptimalkan yang dikompilasi yang dapat berjalan pada dua arsitektur berbeda dari endianness yang berbeda, meskipun ada asumsi yang dibuat oleh pengoptimal (seluruh program) selama kompilasi yang tampaknya tidak sesuai dengan setidaknya satu dari mereka ilmu bangunan?
Scott
13

Deklarasikan variabel int:

int variable = 0xFF;

Sekarang gunakan pointer * char ke berbagai bagian dan periksa apa yang ada di bagian-bagian itu.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

Tergantung pada yang menunjuk ke 0xFF byte sekarang Anda dapat mendeteksi endianness. Ini membutuhkan sizeof (int)> sizeof (char), tapi itu pasti benar untuk platform yang dibahas.

sharptooth
sumber
8

Untuk perincian lebih lanjut, Anda mungkin ingin memeriksa artikel proyek ini Konsep dasar tentang Endianness :

Bagaimana cara menguji tipe Endian secara dinamis saat run time?

Seperti yang dijelaskan dalam FAQ Animasi Komputer, Anda dapat menggunakan fungsi berikut untuk melihat apakah kode Anda berjalan pada sistem Little-atau Big-Endian: Collapse

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

Kode ini memberikan nilai 0001h ke integer 16-bit. Pointer char kemudian ditugaskan untuk menunjuk pada byte pertama (paling tidak signifikan) dari nilai integer. Jika byte pertama dari integer adalah 0x01h, maka sistemnya adalah Little-Endian (0x01h berada di alamat terendah, atau paling tidak signifikan). Jika 0x00j maka sistemnya adalah Big-Endian.

tidak ada
sumber
6

Cara C ++ adalah menggunakan boost , di mana pengecekan dan cetakan preprosesor terkotak di dalam perpustakaan yang sangat teruji.

Perpustakaan Predef (boost / predef.h) mengenali empat jenis endianness .

The Endian Perpustakaan direncanakan untuk diserahkan ke standar C ++, dan mendukung berbagai operasi pada data endian-sensitif.

Seperti yang dinyatakan dalam jawaban di atas, Endianness akan menjadi bagian dari c ++ 20.

fuzzyTew
sumber
1
FYI, tautan "empat jenis endianness" terputus,
Remy Lebeau
diperbaiki dan dibuat wiki
fuzzyTew
5

Kecuali jika Anda menggunakan kerangka kerja yang telah porting ke PPC dan prosesor Intel, Anda harus melakukan kompilasi bersyarat, karena PPC dan platform Intel memiliki arsitektur perangkat keras yang berbeda, jalur pipa, bus, dll. Ini membuat kode perakitan benar-benar berbeda antara keduanya

Sedangkan untuk menemukan endianness, lakukan hal berikut:

short temp = 0x1234;
char* tempChar = (char*)&temp;

Anda akan mendapatkan tempChar menjadi 0x12 atau 0x34, dari mana Anda akan mengetahui endianness.

samoz
sumber
3
Ini bergantung pada kependekan tepatnya 2 byte yang tidak dijamin.
sharptooth
3
Namun itu akan menjadi taruhan yang cukup aman berdasarkan pada dua arsitektur yang diberikan dalam pertanyaan.
Daemin
8
Sertakan stdint.hdan gunakan int16_tuntuk bukti di masa depan terhadap kekurangan yang berbeda pada platform lain.
Denise Skidmore
4

Saya akan melakukan sesuatu seperti ini:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

Seiring garis ini, Anda akan mendapatkan fungsi efisien waktu yang hanya melakukan perhitungan sekali.

Jeremy Mayhew
sumber
bisakah kamu sebaris itu? tidak yakin apakah inline menyebabkan beberapa blok memori dari variabel statis
aah134
4

Seperti yang dinyatakan di atas, gunakan trik penyatuan.

Ada beberapa masalah dengan yang disarankan di atas, terutama bahwa akses memori yang tidak selaras sangat lambat untuk sebagian besar arsitektur, dan beberapa kompiler bahkan tidak akan mengenali predikat konstan seperti itu sama sekali, kecuali jika kata disejajarkan.

Karena hanya tes endian yang membosankan, inilah fungsi (templat) yang akan membalik input / output integer sewenang-wenang menurut spesifikasi Anda, terlepas dari arsitektur host.

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

Pemakaian:

Untuk mengkonversi dari endian yang diberikan ke host, gunakan:

host = endian(source, endian_of_source)

Untuk mengonversi dari host endian ke endian yang diberikan, gunakan:

output = endian(hostsource, endian_you_want_to_output)

Kode yang dihasilkan adalah secepat menulis perakitan tangan di dentang, pada gcc itu sedikit lebih lambat (belum dibuka &, <<, >>, | untuk setiap byte) tetapi masih layak.

kat
sumber
4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}
Paolo Brandoli
sumber
1
Apakah ini setara? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel
4

Jangan gunakan a union !

C ++ tidak mengizinkan jenis hukuman via unions!
Membaca dari bidang persatuan yang bukan bidang terakhir yang ditulis untuk adalah perilaku yang tidak terdefinisi !
Banyak kompiler mendukungnya sebagai ekstensi, tetapi bahasa tidak memberikan jaminan.

Lihat jawaban ini untuk lebih jelasnya:

https://stackoverflow.com/a/11996970


Hanya ada dua jawaban valid yang dijamin portabel.

Jawaban pertama, jika Anda memiliki akses ke sistem yang mendukung C ++ 20,
adalah menggunakan std::endiandari <type_traits>header.

(Pada saat penulisan, C ++ 20 belum dirilis, tetapi kecuali ada sesuatu yang mempengaruhi std::endian inklusi, ini akan menjadi cara yang disukai untuk menguji endianness pada waktu kompilasi dari C ++ 20 dan seterusnya.)

C ++ 20 Selanjutnya

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

Sebelum C ++ 20, satu-satunya jawaban yang valid adalah untuk menyimpan integer dan kemudian memeriksa byte pertamanya melalui jenis hukuman.
Berbeda dengan penggunaanunion s, ini diizinkan oleh sistem tipe C ++.

Penting juga untuk diingat bahwa untuk portabilitas optimal static_castharus digunakan,
karena reinterpret_castimplementasi didefinisikan.

Jika suatu program mencoba untuk mengakses nilai yang tersimpan dari suatu objek melalui nilai lain selain salah satu dari jenis berikut perilaku tidak terdefinisi: ... a charatau unsigned charketik.

C ++ 11 Selanjutnya

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

C ++ 11 Selanjutnya (tanpa enum)

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 ++ 98 / C ++ 03

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);
}
Pharap
sumber
3
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");

Ini solusi lain. Mirip dengan solusi Andrew Hare.

Neeraj
sumber
3

belum diuji, tetapi dalam pikiran saya, ini harus bekerja? karena itu akan menjadi 0x01 pada little endian, dan 0x00 pada big endian?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
hanshenrik
sumber
3

Menyatakan:

Posting awal saya salah dinyatakan sebagai "waktu kompilasi". Bukan, itu bahkan tidak mungkin dalam standar C ++ saat ini. Constexpr TIDAK berarti fungsi selalu melakukan perhitungan waktu kompilasi. Terima kasih Richard Hodges untuk koreksi.

waktu kompilasi, solusi constexpr non-makro, C ++ 11:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}
zhaorufei
sumber
2
Apakah ada alasan tertentu Anda menggunakan char unsigned di atas uint8_t?
Kevin
0 overhead runtime ... saya suka itu!
hanshenrik
Saya kira, ini mendeteksi endiannes dari mesin pembuat, bukan targetnya?
hutorny
2
Bukankah ini UB di C ++?
rr-
6
ini tidak legal dalam konteks constexpr. Anda tidak dapat mengakses anggota serikat yang belum diinisialisasi secara langsung. Tidak ada cara untuk mendeteksi endianness secara legal pada waktu kompilasi tanpa sihir preprosesor.
Richard Hodges
2

Anda juga dapat melakukan ini melalui preprocessor menggunakan sesuatu seperti boost file header yang dapat ditemukan boost endian


sumber
1

Kecuali jika header endian hanya GCC, ia menyediakan makro yang dapat Anda gunakan.

#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!");
...
Mark A. Libby
sumber
Bukankah ini __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__dan __ORDER_BIG_ENDIAN__?
Xeverous
1

Jika Anda tidak ingin kompilasi bersyarat, Anda bisa menulis kode independen endian. Berikut ini sebuah contoh (diambil dari Rob Pike ):

Membaca integer yang disimpan dalam little-endian pada disk, dengan cara endian independen:

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Kode yang sama, mencoba memperhitungkan endianness mesin:

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
fjardon
sumber
Ide yang bagus sekali! Dan sekarang mari kita transfer bilangan bulat Anda melalui soket jaringan ke perangkat yang tidak dikenal.
Maksym Ganenko
@ MaksymGanenko Saya tidak mendapatkan komentar Anda. Apakah ini ironi? Saya tidak menyarankan untuk tidak menentukan endianness dari data serial. Saya menyarankan untuk tidak menulis kode tergantung pada endianness dari mesin yang menerima data.
fjardon
@MaksymGanenko Jika Anda downvote, Anda bisa menjelaskan mengapa jawabannya salah. Paling tidak untuk membantu pembaca potensial memahami mengapa mereka tidak harus mengikuti jawaban saya.
fjardon
0
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Jon Bright
sumber
0

Bagaimana dengan ini?

#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;
}
Abhay
sumber
0

Ini versi C lainnya. Ini mendefinisikan makro yang disebut wicked_cast()untuk inline type punning via C99 union literals dan __typeof__operator non-standar .

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

Jika bilangan bulat adalah nilai byte tunggal, endianness tidak masuk akal dan kesalahan waktu kompilasi akan dihasilkan.

Christoph
sumber
0

Cara C compiler (setidaknya semua orang yang saya tahu) bekerja endianness yang telah diputuskan pada waktu kompilasi. Bahkan untuk prosesor biendian (seperti ARM dan MIPS) Anda harus memilih endianness pada waktu kompilasi. Lebih jauh lagi endianness didefinisikan dalam semua format file umum untuk executable (seperti ELF). Meskipun dimungkinkan untuk membuat gumpalan kode biandian biner (untuk beberapa eksploitasi server ARM mungkin?) Mungkin harus dilakukan dalam perakitan.

Fabel
sumber
-1

Seperti yang ditunjukkan oleh Coriiander, sebagian besar (jika tidak semua) dari kode-kode di sini akan dioptimalkan pada saat kompilasi, sehingga binari yang dihasilkan tidak akan memeriksa "endianness" pada saat run time.

Telah diamati bahwa executable yang diberikan tidak boleh berjalan dalam dua byte perintah yang berbeda, tetapi saya tidak tahu apakah itu selalu terjadi, dan sepertinya hack bagi saya memeriksa pada waktu kompilasi. Jadi saya memberi kode fungsi ini:

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

MinGW tidak dapat mengoptimalkan kode ini, meskipun ia mengoptimalkan kode lain di sini. Saya percaya itu karena saya meninggalkan nilai "acak" yang dialokasikan pada memori byte yang lebih kecil seperti sebelumnya (setidaknya 7 bitnya), sehingga kompiler tidak dapat mengetahui apa nilai acak itu dan tidak mengoptimalkan fungsi pergi.

Saya juga memberi kode fungsi sehingga pemeriksaan hanya dilakukan sekali, dan nilai pengembalian disimpan untuk pengujian berikutnya.

Pembunuh Tex
sumber
Mengapa mengalokasikan 4 byte untuk bekerja pada nilai 2-byte? Mengapa menyamarkan nilai yang tidak ditentukan 0x7FE? Kenapa pakai malloc()sama sekali? itu sia-sia. Dan _BEapakah (walaupun kecil) memori bocor dan kondisi balapan menunggu untuk terjadi, manfaat dari caching hasil secara dinamis tidak sebanding dengan masalahnya. Saya akan melakukan sesuatu yang lebih seperti ini: static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }Sederhana dan efektif, dan jauh lebih sedikit pekerjaan yang harus dilakukan saat runtime.
Remy Lebeau
@RemyLebeau, inti dari jawaban saya adalah menghasilkan kode yang tidak dioptimalkan oleh kompiler. Tentu, kode Anda jauh lebih sederhana, tetapi dengan optimisasi yang diaktifkan itu hanya akan menjadi boolean konstan setelah dikompilasi. Seperti yang saya nyatakan pada jawaban saya, saya tidak benar-benar tahu apakah ada beberapa cara untuk mengkompilasi kode C dengan cara yang dapat dieksekusi sama dijalankan pada kedua byte perintah, dan saya juga penasaran untuk melihat apakah saya bisa melakukan pemeriksaan saat runtime meskipun ada optimasi.
Tex Killer
@TexKiller lalu mengapa tidak menonaktifkan optimisasi kode saja? Menggunakan volatile, atau #pragma, dll.
Remy Lebeau
@RemyLebeau, saya tidak tahu kata kunci tersebut pada waktu itu, dan saya menganggapnya sebagai tantangan kecil untuk mencegah pengoptimalan kompiler dengan apa yang saya ketahui.
Tex Killer
-1

sementara tidak ada cara cepat dan standar untuk menentukannya, ini akan menampilkannya:

#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; 
} 
yekanchi
sumber
-1

Lihat ilustrasi Endianness - C-Level Code.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS 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;
} 
Gimel
sumber
-2

Saya membaca buku pelajaran: Sistem Komputer: perspektif seorang programmer , dan ada masalah untuk menentukan endian mana yang menggunakan program C.

Saya menggunakan fitur pointer untuk melakukan itu sebagai berikut:

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

Karena int membutuhkan 4 byte, dan char hanya membutuhkan 1 byte. Kita bisa menggunakan pointer char untuk menunjuk ke int dengan nilai 1. Jadi jika komputer sedikit endian, char yang menunjuk pointer char adalah dengan nilai 1, jika tidak, nilainya harus 0.

Archimedes520
sumber
ini akan ditingkatkan dengan menggunakan int32t.
antar
1
^ jika Anda ingin melakukan nitpick, yang terbaik di sini adalah int16_fast_t. dan kode saat ini @ Archimedes520 tidak akan bekerja pada lengkungan di mana int asli int8;) (yang mungkin bertentangan dengan standar c di tempat pertama, meskipun)
hanshenrik