Bagaimana cara mengkonversi antara nilai big-endian dan little-endian di C ++?
EDIT: Untuk lebih jelasnya, saya harus menerjemahkan data biner (nilai floating point presisi ganda dan integer 32-bit dan 64-bit) dari satu arsitektur CPU ke arsitektur CPU lainnya. Ini tidak melibatkan jaringan, jadi ntoh () dan fungsi serupa tidak akan berfungsi di sini.
EDIT # 2: Jawaban yang saya terima berlaku langsung ke kompiler yang saya targetkan (itulah sebabnya saya memilihnya). Namun, ada jawaban lain yang sangat bagus dan lebih portabel di sini.
c++
endianness
Uhall
sumber
sumber
short swap(short x)
kode, karena itu akan rusak jika Anda pindah ke platform dengan endianness yang berbeda. Matthieu M memiliki satu-satunya jawaban yang benar di bawah ini.Jawaban:
Jika Anda menggunakan Visual C ++ lakukan hal berikut: Anda memasukkan intrin.h dan memanggil fungsi-fungsi berikut:
Untuk angka 16 bit:
Untuk nomor 32 bit:
Untuk nomor 64 bit:
Nomor 8 bit (karakter) tidak perlu dikonversi.
Juga ini hanya ditentukan untuk nilai yang tidak ditandatangani yang berfungsi untuk bilangan bulat yang ditandatangani juga.
Untuk mengapung dan menggandakan itu lebih sulit karena dengan bilangan bulat biasa karena ini mungkin atau tidak mungkin ada di byte-order mesin host. Anda bisa mendapatkan pelampung little-endian pada mesin big-endian dan sebaliknya.
Kompiler lain juga memiliki intrinsik yang serupa.
Dalam GCC misalnya, Anda dapat langsung memanggil beberapa builtin seperti yang didokumentasikan di sini :
(tidak perlu memasukkan sesuatu). Afaik bits.h mendeklarasikan fungsi yang sama dengan cara non-gcc-centric juga.
16 bit swap itu hanya sedikit-putar.
Memanggil intrinsik alih-alih menggulirkan sendiri memberi Anda kinerja terbaik dan densitas kode antara ..
sumber
__builtin_bswapX
hanya tersedia dari GCC-4.3 dan seterusnyahtonl
,htons
, dll Anda harus tahu dari konteks situasi Anda saat untuk benar-benar menukar byte.htonl
danntohl
tanpa khawatir tentang konteks akan bekerja ketika menulis kode portabel karena platform mendefinisikan fungsi-fungsi ini akan menukar itu jika itu sedikit / mid-endian dan pada big-endian itu akan menjadi no-op. Namun, ketika decoding tipe file standar yang didefinisikan sebagai little-endian (katakanlah BMP), kita masih harus tahu konteksnya dan tidak bisa hanya mengandalkanhtonl
danntohl
.Sederhananya:
penggunaan:
swap_endian<uint32_t>(42)
.sumber
Dari The Byte Order Fallacy oleh Rob Pike:
TL; DR: jangan khawatir tentang tatanan asli platform Anda, yang terpenting adalah urutan byte dari aliran yang Anda baca, dan Anda lebih baik berharap itu didefinisikan dengan baik.
Catatan: ada komentar di komentar bahwa tidak ada konversi tipe eksplisit, penting untuk
data
menjadi arrayunsigned char
atauuint8_t
. Menggunakansigned char
atauchar
(jika ditandatangani) akandata[x]
dipromosikan menjadi bilangan bulat dandata[x] << 24
berpotensi memindahkan 1 ke bit tanda yang merupakan UB.sumber
Jika Anda melakukan ini untuk keperluan kompatibilitas jaringan / host Anda harus menggunakan:
Jika Anda melakukan ini karena alasan lain, salah satu solusi byte_swap yang disajikan di sini akan berfungsi dengan baik.
sumber
htonl
danntohl
tidak bisa pergi ke endian kecil di platform big-endian.Saya mengambil beberapa saran dari pos ini dan menyatukannya untuk membentuk ini:
sumber
Prosedur untuk beralih dari big-endian ke little-endian sama dengan beralih dari little-endian ke big-endian.
Berikut beberapa contoh kode:
sumber
Ada instruksi perakitan yang disebut BSWAP yang akan melakukan swap untuk Anda, sangat cepat . Anda dapat membacanya di sini .
Visual Studio, atau lebih tepatnya perpustakaan runtime Visual C ++, memiliki platform intrinsik untuk ini, yang disebut
_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. Serupa harus ada untuk platform lain, tapi saya tidak tahu apa yang akan mereka sebut.sumber
Kami telah melakukan ini dengan templat. Anda dapat melakukan sesuatu seperti ini:
sumber
Jika Anda melakukan ini untuk mentransfer data antar platform yang berbeda, lihat fungsi ntoh dan hton.
sumber
Cara yang sama Anda lakukan di C:
Anda juga bisa mendeklarasikan vektor karakter yang tidak ditandatangani, memcpy nilai input ke dalamnya, membalikkan byte ke vektor lain dan memcpy byte keluar, tetapi itu akan membuat urutan besarnya lebih lama daripada memutar-mutar bit, terutama dengan nilai 64-bit.
sumber
Pada sebagian besar sistem POSIX (melalui itu tidak dalam standar POSIX) ada endian.h, yang dapat digunakan untuk menentukan pengkodean apa yang digunakan sistem Anda. Dari sana ada sesuatu seperti ini:
Ini menukar urutan (dari big endian ke little endian):
Jika Anda memiliki angka 0xDEADBEEF (pada sistem endian kecil yang disimpan sebagai 0xEFBEADDE), ptr [0] akan menjadi 0xEF, ptr [1] adalah 0xBE, dll.
Tetapi jika Anda ingin menggunakannya untuk jaringan, maka htons, htonl dan htonll (dan ntohs terbalik, ntohl dan ntohll) akan membantu untuk mengubah dari pesanan host ke pesanan jaringan.
sumber
htonl
dan berteman terlepas dari apakah kasus penggunaan itu ada hubungannya dengan jaringan. Urutan byte jaringan adalah big-endian, jadi anggap saja fungsi-fungsi tersebut sebagai host_to_be dan be_to_host. (Namun, tidak membantu jika Anda memerlukan host_to_le.)Perhatikan bahwa, setidaknya untuk Windows, htonl () jauh lebih lambat daripada rekan intrinsiknya _byteswap_ulong (). Yang pertama adalah panggilan pustaka DLL ke ws2_32.dll, yang terakhir adalah satu instruksi perakitan BSWAP. Oleh karena itu, jika Anda menulis beberapa kode yang bergantung pada platform, lebih baik menggunakan intrinsik untuk kecepatan:
Ini mungkin sangat penting untuk pemrosesan gambar .PNG di mana semua bilangan bulat disimpan di Big Endian dengan penjelasan "Orang dapat menggunakan htonl () ..." {untuk memperlambat program Windows, jika Anda tidak siap}.
sumber
Sebagian besar platform memiliki file header sistem yang menyediakan fungsi byteswap yang efisien. Di Linux ada di
<endian.h>
. Anda dapat membungkusnya dengan baik di C ++:Keluaran:
sumber
saya suka yang ini, hanya untuk gaya :-)
sumber
char[]
mengatakan 'Kesalahan: jenis tidak lengkap tidak diizinkan'Serius ... Saya tidak mengerti mengapa semua solusi begitu rumit ! Bagaimana dengan fungsi template paling sederhana dan paling umum yang menukar semua jenis ukuran dalam kondisi apa pun di sistem operasi apa pun ????
Ini adalah kekuatan ajaib dari C dan C ++ secara bersamaan! Cukup tukar karakter variabel asli dengan karakter.
Butir 1 : Tidak ada operator: Ingatlah bahwa saya tidak menggunakan operator penetapan sederhana "=" karena beberapa objek akan kacau ketika endianness dibalik dan pembuat salinan (atau operator penugasan) tidak akan berfungsi. Oleh karena itu, lebih baik menyalinnya dengan char.
Butir 2 : Waspadai masalah penyelarasan: Perhatikan bahwa kami menyalin ke dan dari array, yang merupakan hal yang benar untuk dilakukan karena kompiler C ++ tidak menjamin bahwa kami dapat mengakses memori yang tidak selaras (jawaban ini diperbarui dari aslinya formulir untuk ini). Misalnya, jika Anda mengalokasikan
uint64_t
, kompiler Anda tidak dapat menjamin bahwa Anda dapat mengakses byte ke-3 itu sebagai auint8_t
. Oleh karena itu, hal yang benar untuk dilakukan adalah menyalin ini ke array char, menukarnya, lalu menyalinnya kembali (jadi tidakreinterpret_cast
). Perhatikan bahwa kompiler sebagian besar cukup pintar untuk mengubah apa yang Anda lakukan kembali kereinterpret_cast
jika mereka mampu mengakses masing-masing byte terlepas dari keselarasan.Untuk menggunakan fungsi ini :
dan sekarang
x
berbeda dalam endianness.sumber
new
/delete
untuk mengalokasikan buffer untuk ini?!?sizeof(var)
adalah konstanta waktu kompilasi, jadi Anda bisa melakukannyachar varSwapped[sizeof(var)]
. Atau Anda bisa melakukanchar *p = reinterpret_cast<char*>(&var)
dan bertukar di tempat.for(size_t i = 0 ; i < sizeof(var) ; i++)
bukanstatic_cast<long>
. (Atau sebenarnya, swap di tempat akan menggunakan naik dan turunchar*
sehingga hilang juga).Saya memiliki kode ini yang memungkinkan saya mengkonversi dari HOST_ENDIAN_ORDER (apa pun itu) ke LITTLE_ENDIAN_ORDER atau BIG_ENDIAN_ORDER. Saya menggunakan templat, jadi jika saya mencoba mengonversi dari HOST_ENDIAN_ORDER ke LITTLE_ENDIAN_ORDER dan keduanya sama untuk mesin yang saya kompilasi, tidak ada kode yang akan dihasilkan.
Berikut adalah kode dengan beberapa komentar:
sumber
Jika integer unsigned 32-bit big-end terlihat seperti 0xAABBCCDD yang sama dengan 2864434397, maka integer 32-bit unsigned yang sama terlihat seperti 0xDDCCBBAA pada prosesor little-endian yang juga sama dengan 2864434397.
Jika sebuah big-endian 16-bit unsigned short pendek terlihat seperti 0xAABB yang sama dengan 43707, maka short notigned 16-bit yang sama itu terlihat seperti 0xBBAA pada prosesor little-endian yang juga sama dengan 43707.
Berikut adalah beberapa fungsi #define yang berguna untuk bertukar byte dari little-endian ke big-endian dan sebaliknya ->
sumber
Berikut ini adalah versi umum yang saya dapatkan dari atas kepala saya, untuk menukar nilai di tempat. Saran lain akan lebih baik jika kinerja adalah masalah.
Penafian: Saya belum mencoba untuk mengkompilasi atau mengujinya.
sumber
Jika Anda mengambil pola umum untuk membalik urutan bit dalam sebuah kata, dan memilah bagian yang membalikkan bit dalam setiap byte, maka Anda ditinggalkan dengan sesuatu yang hanya membalikkan byte dalam sebuah kata. Untuk 64-bit:
Compiler harus membersihkan operasi bit-masking yang berlebihan (saya membiarkannya untuk menyoroti polanya), tetapi jika tidak, Anda dapat menulis ulang baris pertama dengan cara ini:
Itu biasanya harus disederhanakan menjadi instruksi rotasi tunggal pada sebagian besar arsitektur (mengabaikan bahwa seluruh operasi mungkin satu instruksi).
Pada prosesor RISC konstanta yang besar dan rumit dapat menyebabkan kesulitan pada kompiler. Anda dapat menghitung setiap konstanta dari yang sebelumnya dengan mudah. Seperti itu:
Jika suka, Anda dapat menuliskannya sebagai satu lingkaran. Itu tidak akan efisien, tetapi hanya untuk bersenang-senang:
Dan untuk kelengkapan, inilah versi 32-bit dari bentuk pertama yang disederhanakan:
sumber
Hanya berpikir saya menambahkan solusi saya sendiri di sini karena saya belum melihatnya di mana pun. Ini adalah fungsi temporer C ++ kecil dan portabel dan portabel yang hanya menggunakan operasi bit.
sumber
Saya sangat terkejut tidak ada yang menyebutkan fungsi htobeXX dan betohXX. Mereka didefinisikan dalam endian.h dan sangat mirip dengan fungsi jaringan htonXX.
sumber
Dengan menggunakan kode di bawah ini, Anda dapat bertukar antara BigEndian dan LittleEndian dengan mudah
sumber
Saya baru-baru ini menulis makro untuk melakukan ini dalam C, tetapi sama-sama valid di C ++:
Ia menerima segala jenis dan membalik byte dalam argumen yang diteruskan. Contoh penggunaan:
Yang mencetak:
Di atas sempurna copy / paste-mampu, tetapi ada banyak hal yang terjadi di sini, jadi saya akan memecah cara kerjanya sepotong demi sepotong:
Hal penting pertama adalah bahwa seluruh makro terbungkus dalam sebuah
do while(0)
blok. Ini adalah ungkapan yang umum untuk memungkinkan penggunaan titik koma normal setelah makro.Selanjutnya adalah penggunaan variabel bernama
REVERSE_BYTES
sebagaifor
penghitung loop. Nama makro itu sendiri digunakan sebagai nama variabel untuk memastikan bahwa itu tidak berbenturan dengan simbol lain yang mungkin ada di ruang lingkup di mana makro digunakan. Karena nama sedang digunakan dalam ekspansi makro, itu tidak akan diperluas lagi ketika digunakan sebagai nama variabel di sini.Dalam
for
loop, ada dua byte yang direferensikan dan XOR ditukar (jadi nama variabel sementara tidak diperlukan):__VA_ARGS__
mewakili apa pun yang diberikan kepada makro, dan digunakan untuk meningkatkan fleksibilitas dari apa yang mungkin diteruskan (meskipun tidak banyak). Alamat argumen ini kemudian diambil dan dilemparkan keunsigned char
pointer untuk memungkinkan pertukaran byte-nya melalui[]
subscript array .Poin aneh terakhir adalah kurangnya
{}
kawat gigi. Mereka tidak perlu karena semua langkah dalam setiap swap bergabung dengan operator koma , menjadikannya satu pernyataan.Akhirnya, perlu dicatat bahwa ini bukan pendekatan yang ideal jika kecepatan adalah prioritas utama. Jika ini merupakan faktor penting, beberapa makro spesifik-jenis atau arahan khusus platform yang dirujuk dalam jawaban lain kemungkinan merupakan pilihan yang lebih baik. Pendekatan ini, bagaimanapun, adalah portabel untuk semua jenis, semua platform utama, dan kedua bahasa C dan C ++.
sumber
__VA_ARGS__
?Wow, saya tidak bisa mempercayai beberapa jawaban yang saya baca di sini. Sebenarnya ada instruksi dalam pertemuan yang melakukan ini lebih cepat dari yang lainnya. bswap. Anda cukup menulis fungsi seperti ini ...
Hal ini JAUH lebih cepat daripada intrinsik yang telah disarankan. Saya sudah membongkar dan melihat mereka. Fungsi di atas tidak memiliki prolog / epilog sehingga hampir tidak memiliki overhead sama sekali.
Melakukan 16 bit sama mudahnya, dengan pengecualian bahwa Anda akan menggunakan xchg al, ah. bswap hanya berfungsi pada register 32-bit.
64-bit sedikit lebih rumit, tetapi tidak terlalu rumit. Jauh lebih baik daripada semua contoh di atas dengan loop dan template dll.
Ada beberapa peringatan di sini ... Pertama, bswap hanya tersedia pada CPU 80x486 ke atas. Adakah yang berencana menjalankannya pada 386?!? Jika demikian, Anda masih dapat mengganti bswap dengan ...
Perakitan inline juga hanya tersedia dalam kode x86 di Visual Studio. Fungsi telanjang tidak bisa di-line dan juga tidak tersedia di build x64. Saya misalnya, Anda harus menggunakan intrinsik kompiler.
sumber
_byteswap_ulong
dan_uint64
(misalnya dalam jawaban yang diterima) keduanya mengkompilasi untuk menggunakanbswap
instruksi. Saya akan terkejut tetapi tertarik untuk mengetahui apakah ASM ini jauh lebih cepat karena hanya menghilangkan prolog / epilog - apakah Anda membandingkannya?Teknik portabel untuk menerapkan aksesor endian non-inplace yang ramah bagi pengoptimal-ramah. Mereka bekerja pada setiap kompiler, setiap penyelarasan batas dan setiap pemesanan byte. Rutin yang tidak selaras ini ditambah, atau diperdebatkan, tergantung pada endian asli dan perataan. Sebagian daftar tetapi Anda mendapatkan ide. BO * adalah nilai konstan berdasarkan pemesanan byte asli.
Typedef ini memiliki manfaat meningkatkan kesalahan kompiler jika tidak digunakan dengan accessors, sehingga mengurangi bug accessor yang terlupakan.
sumber
Berikut cara membaca ganda yang disimpan dalam format IEEE 754 64 bit, bahkan jika komputer host Anda menggunakan sistem yang berbeda.
Untuk sisa rangkaian fungsi, termasuk rutinitas menulis dan integer, lihat proyek github saya
https://github.com/MalcolmMcLean/ieee754
sumber
Bertukar byte dengan Anda 3-langkah-xor trik sekitar pivot dalam fungsi templat memberikan solusi O yang fleksibel dan cepat (ln2) yang tidak memerlukan pustaka, gaya di sini juga menolak jenis 1 byte:
sumber
Sepertinya cara aman adalah menggunakan htons pada setiap kata. Jadi, jika Anda memiliki ...
Di atas akan menjadi no-op jika Anda menggunakan sistem big-endian, jadi saya akan mencari apa pun yang digunakan platform Anda sebagai kondisi waktu kompilasi untuk memutuskan apakah htons adalah no-op. Lagipula itu O (n). Pada Mac, itu akan menjadi seperti ...
sumber
Jika Anda memiliki C ++ 17 maka tambahkan header ini
Gunakan fungsi template ini untuk menukar byte:
sebut saja seperti:
sumber
Cari sedikit perubahan, karena pada dasarnya ini yang perlu Anda lakukan untuk bertukar dari sedikit -> big endian. Kemudian tergantung pada ukuran bit, Anda mengubah cara Anda menggeser bit.
sumber