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).
c++
algorithm
endianness
Jay T
sumber
sumber
Jawaban:
Saya tidak suka metode ini berdasarkan tipe punning - sering akan diperingatkan oleh kompiler. Persis seperti itulah serikat pekerja!
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 .
sumber
CHAR_BIT != 8
?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).
Mengotak-atik sedikit bisa lebih cepat, tetapi cara ini sederhana, mudah dan sangat tidak mungkin untuk dikacaukan.
sumber
BSWAP
operasi.Silakan lihat artikel ini :
sumber
Anda dapat menggunakan
std::endian
jika Anda memiliki akses ke kompiler C ++ 20 seperti GCC 8+ atau Clang 7+.Catatan:
std::endian
mulai<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>
.sumber
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"
sumber
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):
Pada mesin PPC saya mendapatkan:
(
:| gcc -dM -E -x c -
Keajaiban mencetak semua makro built-in).sumber
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
mengembalikan apa-apa, sedangkan gcc 3.4.3 (dari/usr/sfw/bin
toh) 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).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
Membongkar
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 ...
sumber
Deklarasikan variabel int:
Sekarang gunakan pointer * char ke berbagai bagian dan periksa apa yang ada di bagian-bagian itu.
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.
sumber
Untuk perincian lebih lanjut, Anda mungkin ingin memeriksa artikel proyek ini Konsep dasar tentang Endianness :
sumber
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.
sumber
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:
Anda akan mendapatkan tempChar menjadi 0x12 atau 0x34, dari mana Anda akan mengetahui endianness.
sumber
stdint.h
dan gunakanint16_t
untuk bukti di masa depan terhadap kekurangan yang berbeda pada platform lain.Saya akan melakukan sesuatu seperti ini:
Seiring garis ini, Anda akan mendapatkan fungsi efisien waktu yang hanya melakukan perhitungan sekali.
sumber
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.
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.
sumber
sumber
#define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Jangan gunakan a
union
!C ++ tidak mengizinkan jenis hukuman via
union
s!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::endian
dari<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
Sebelum C ++ 20, satu-satunya jawaban yang valid adalah untuk menyimpan integer dan kemudian memeriksa byte pertamanya melalui jenis hukuman.
Berbeda dengan penggunaan
union
s, ini diizinkan oleh sistem tipe C ++.Penting juga untuk diingat bahwa untuk portabilitas optimal
static_cast
harus digunakan,karena
reinterpret_cast
implementasi didefinisikan.C ++ 11 Selanjutnya
C ++ 11 Selanjutnya (tanpa enum)
C ++ 98 / C ++ 03
sumber
Ini solusi lain. Mirip dengan solusi Andrew Hare.
sumber
belum diuji, tetapi dalam pikiran saya, ini harus bekerja? karena itu akan menjadi 0x01 pada little endian, dan 0x00 pada big endian?
sumber
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:
sumber
Anda juga dapat melakukan ini melalui preprocessor menggunakan sesuatu seperti boost file header yang dapat ditemukan boost endian
sumber
Kecuali jika header endian hanya GCC, ia menyediakan makro yang dapat Anda gunakan.
sumber
__BYTE_ORDER__
,__ORDER_LITTLE_ENDIAN__
dan__ORDER_BIG_ENDIAN__
?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:
Kode yang sama, mencoba memperhitungkan endianness mesin:
sumber
sumber
Bagaimana dengan ini?
sumber
Ini versi C lainnya. Ini mendefinisikan makro yang disebut
wicked_cast()
untuk inline type punning via C99 union literals dan__typeof__
operator non-standar .Jika bilangan bulat adalah nilai byte tunggal, endianness tidak masuk akal dan kesalahan waktu kompilasi akan dihasilkan.
sumber
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.
sumber
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:
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.
sumber
0x7FE
? Kenapa pakaimalloc()
sama sekali? itu sia-sia. Dan_BE
apakah (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.volatile
, atau#pragma
, dll.sementara tidak ada cara cepat dan standar untuk menentukannya, ini akan menampilkannya:
sumber
Lihat ilustrasi Endianness - C-Level Code.
sumber
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:
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.
sumber