Saya perlu menulis fungsi untuk mengubah big endian menjadi little endian di C. Saya tidak dapat menggunakan fungsi library apa pun.
c
swap
endianness
Alex Xander
sumber
sumber
Jawaban:
Dengan asumsi yang Anda butuhkan adalah pertukaran byte sederhana, cobalah sesuatu seperti
Konversi 16 bit unsigned:
swapped = (num>>8) | (num<<8);
Konversi 32-bit tanpa tanda tangan:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0 ((num<<8)&0xff0000) | // move byte 1 to byte 2 ((num>>8)&0xff00) | // move byte 2 to byte 1 ((num<<24)&0xff000000); // byte 0 to byte 3
Ini menukar urutan byte dari posisi 1234 menjadi 4321. Jika masukan Anda adalah
0xdeadbeef
, swap endian 32-bit mungkin memiliki keluaran0xefbeadde
.Kode di atas harus dibersihkan dengan makro atau setidaknya konstanta, bukan angka ajaib, tapi semoga membantu apa adanya
EDIT: seperti jawaban lain yang ditunjukkan, ada platform, OS, dan set instruksi alternatif tertentu yang bisa JAUH lebih cepat dari yang di atas. Di kernel Linux ada makro (misalnya cpu_to_be32) yang menangani endianness dengan cukup baik. Tetapi alternatif ini khusus untuk lingkungan mereka. Dalam praktiknya, ketekunan paling baik ditangani dengan menggunakan campuran pendekatan yang tersedia
sumber
((num & 0xff) >> 8) | (num << 8)
, gcc 4.8.3 menghasilkan saturol
instruksi. Dan jika konversi 32 bit ditulis sebagai((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, kompilator yang sama menghasilkan satubswap
instruksi.struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
mana ini adalah bitfield dengan masing-masing 8 bidang 1 bit. Tapi saya tidak yakin apakah itu secepat saran lainnya. Untuk int gunakanunion { int i; byte_t[sizeof(int)]; }
untuk membalikkan byte demi byte dalam integer.Dengan memasukkan:
#include <byteswap.h>
Anda bisa mendapatkan versi yang dioptimalkan dari fungsi pertukaran byte yang bergantung pada mesin. Kemudian, Anda dapat dengan mudah menggunakan fungsi berikut:
__bswap_32 (uint32_t input)
atau
__bswap_16 (uint16_t input)
sumber
#include <byteswap.h>
, lihat komentar di file .h itu sendiri. Posting ini berisi informasi yang berguna jadi saya memilih meskipun penulis mengabaikan persyaratan OP untuk tidak menggunakan fungsi lib.#include <stdint.h> //! Byte swap unsigned short uint16_t swap_uint16( uint16_t val ) { return (val << 8) | (val >> 8 ); } //! Byte swap short int16_t swap_int16( int16_t val ) { return (val << 8) | ((val >> 8) & 0xFF); } //! Byte swap unsigned int uint32_t swap_uint32( uint32_t val ) { val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); return (val << 16) | (val >> 16); } //! Byte swap int int32_t swap_int32( int32_t val ) { val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); return (val << 16) | ((val >> 16) & 0xFFFF); }
Pembaruan : Menambahkan pertukaran byte 64bit
int64_t swap_int64( int64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } uint64_t swap_uint64( uint64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | (val >> 32); }
sumber
int32_t
danint64_t
, apa alasan di balik penyamaran... & 0xFFFF
dan... & 0xFFFFFFFFULL
? Apakah ada sesuatu yang terjadi dengan ekstensi tanda di sini yang tidak saya lihat? Juga, mengapaswap_int64
kembaliuint64_t
? Bukankah begituint64_t
?swap_int64
dalam jawaban Anda. 1 untuk jawaban yang membantu, BTW!LL
tidak perlu di(u)swap_uint64()
banyak seperti yangL
tidak dibutuhkan di(u)swap_uint32()
. YangU
tidak dibutuhkan dalamuswap_uint64()
banyak hal sepertiU
tidak diperlukan diuswap_uint32()
Ini adalah versi yang cukup umum; Saya belum menyusunnya, jadi mungkin ada kesalahan ketik, tetapi Anda harus mengerti,
void SwapBytes(void *pv, size_t n) { assert(n > 0); char *p = pv; size_t lo, hi; for(lo=0, hi=n-1; hi>lo; lo++, hi--) { char tmp=p[lo]; p[lo] = p[hi]; p[hi] = tmp; } } #define SWAP(x) SwapBytes(&x, sizeof(x));
NB: Ini tidak dioptimalkan untuk kecepatan atau ruang. Ini dimaksudkan agar jelas (mudah di-debug) dan portabel.
Pembaruan 2018-04-04 Menambahkan assert () untuk menjebak kasus tidak valid n == 0, seperti yang terlihat oleh pemberi komentar @chux.
sumber
bswap
instruksi oleh kompiler X86 yang layak dengan pengoptimalan diaktifkan. Versi dengan parameter untuk ukuran ini tidak dapat melakukan itu.Jika Anda membutuhkan makro (mis. Sistem tertanam):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8)) #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
sumber
UINT
di nama mereka.Edit: Ini adalah fungsi perpustakaan. Mengikuti mereka adalah cara manual untuk melakukannya.
Saya sangat terkejut dengan jumlah orang yang tidak mengetahui __byteswap_ushort, __byteswap_ulong, dan __byteswap_uint64 . Tentu mereka spesifik untuk Visual C ++, tetapi mereka mengkompilasi menjadi kode yang bagus pada arsitektur x86 / IA-64. :)
Berikut adalah penggunaan
bswap
instruksi secara eksplisit , ditarik dari halaman ini . Perhatikan bahwa bentuk intrinsik di atas akan selalu lebih cepat dari ini , saya hanya menambahkannya untuk memberikan jawaban tanpa rutinitas perpustakaan.uint32 cq_ntohl(uint32 a) { __asm{ mov eax, a; bswap eax; } }
sumber
Sebagai lelucon:
#include <stdio.h> int main (int argc, char *argv[]) { size_t sizeofInt = sizeof (int); int i; union { int x; char c[sizeof (int)]; } original, swapped; original.x = 0x12345678; for (i = 0; i < sizeofInt; i++) swapped.c[sizeofInt - i - 1] = original.c[i]; fprintf (stderr, "%x\n", swapped.x); return 0; }
sumber
int i, size_t sizeofInt
dan bukan tipe yang sama untuk keduanya.berikut adalah cara menggunakan instruksi SSSE3 pshufb menggunakan intrinsik Intel, dengan asumsi Anda memiliki kelipatan 4
int
s:unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) { int i; __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); for (i = 0; i < length; i += 4) { _mm_storeu_si128((__m128i *)&destination[i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask)); } return destination; }
sumber
Akankah ini berhasil / lebih cepat?
uint32_t swapped, result; ((byte*)&swapped)[0] = ((byte*)&result)[3]; ((byte*)&swapped)[1] = ((byte*)&result)[2]; ((byte*)&swapped)[2] = ((byte*)&result)[1]; ((byte*)&swapped)[3] = ((byte*)&result)[0];
sumber
char
, bukanbyte
.Berikut adalah fungsi yang telah saya gunakan - diuji dan berfungsi pada semua tipe data dasar:
// SwapBytes.h // // Function to perform in-place endian conversion of basic types // // Usage: // // double d; // SwapBytes(&d, sizeof(d)); // inline void SwapBytes(void *source, int size) { typedef unsigned char TwoBytes[2]; typedef unsigned char FourBytes[4]; typedef unsigned char EightBytes[8]; unsigned char temp; if(size == 2) { TwoBytes *src = (TwoBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[1]; (*src)[1] = temp; return; } if(size == 4) { FourBytes *src = (FourBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[3]; (*src)[3] = temp; temp = (*src)[1]; (*src)[1] = (*src)[2]; (*src)[2] = temp; return; } if(size == 8) { EightBytes *src = (EightBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[7]; (*src)[7] = temp; temp = (*src)[1]; (*src)[1] = (*src)[6]; (*src)[6] = temp; temp = (*src)[2]; (*src)[2] = (*src)[5]; (*src)[5] = temp; temp = (*src)[3]; (*src)[3] = (*src)[4]; (*src)[4] = temp; return; } }
sumber
source
diselaraskan sesuai kebutuhan - namun jika asumsi tersebut tidak berlaku, kodenya adalah UB.EDIT: Fungsi ini hanya menukar endianness dari kata-kata 16 bit yang selaras. Fungsi yang sering diperlukan untuk encoding UTF-16 / UCS-2. EDIT AKHIR.
Jika Anda ingin mengubah endianess dari blok memori, Anda dapat menggunakan pendekatan saya yang sangat cepat. Larik memori Anda harus memiliki ukuran kelipatan 8.
#include <stddef.h> #include <limits.h> #include <stdint.h> void ChangeMemEndianness(uint64_t *mem, size_t size) { uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT; size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t); for(; size; size--, mem++) *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT); }
Fungsi semacam ini berguna untuk mengubah endianess file Unicode UCS-2 / UTF-16.
sumber
t know if it
secepat saran tetapi itu wokrsCHAR_BIT
bukannya8
penasaran karena0xFF00FF00FF00FF00ULL
bergantung padaCHAR_BIT == 8
. Perhatikan bahwaLL
tidak diperlukan dalam konstanta.CHAR_BIT
untuk menambah eksposur makro itu. Sedangkan untuk LL, ini lebih merupakan anotasi daripada apa pun. Ini juga kebiasaan yang saya tangkap sejak lama dengan kompiler buggy (pra standar) yang tidak akan melakukan hal yang benar.Cuplikan kode ini dapat mengubah 32bit nomor Endian kecil menjadi nomor Endian Besar.
#include <stdio.h> main(){ unsigned int i = 0xfafbfcfd; unsigned int j; j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24); printf("unsigned int j = %x\n ", j); }
sumber
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);
mungkin lebih cepat pada beberapa platform (mis. Mendaur ulang konstanta mask AND). Sebagian besar kompiler akan melakukannya, tetapi beberapa kompiler sederhana tidak dapat mengoptimalkannya untuk Anda.Jika Anda menjalankan prosesor x86 atau x86_64, big endian adalah native. begitu
untuk nilai 16 bit
unsigned short wBigE = value; unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
untuk nilai 32 bit
unsigned int iBigE = value; unsigned int iLittleE = ((iBigE & 0xFF) << 24) | ((iBigE & 0xFF00) << 8) | ((iBigE >> 8) & 0xFF00) | (iBigE >> 24);
Ini bukan solusi yang paling efisien kecuali jika kompilator mengenali bahwa ini adalah manipulasi tingkat byte dan menghasilkan kode pertukaran byte. Tetapi itu tidak bergantung pada trik tata letak memori dan dapat diubah menjadi makro dengan cukup mudah.
sumber