Saya perlu menulis fungsi untuk mengubah big endian menjadi little endian di C. Saya tidak dapat menggunakan fungsi library apa pun.
Saya perlu menulis fungsi untuk mengubah big endian menjadi little endian di C. Saya tidak dapat menggunakan fungsi library apa pun.
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 keluaran 0xefbeadde
.
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
((num & 0xff) >> 8) | (num << 8)
, gcc 4.8.3 menghasilkan satu rol
instruksi. Dan jika konversi 32 bit ditulis sebagai ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, kompilator yang sama menghasilkan satu bswap
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 gunakan union { 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)
#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);
}
int32_t
dan int64_t
, apa alasan di balik penyamaran ... & 0xFFFF
dan ... & 0xFFFFFFFFULL
? Apakah ada sesuatu yang terjadi dengan ekstensi tanda di sini yang tidak saya lihat? Juga, mengapa swap_int64
kembali uint64_t
? Bukankah begitu int64_t
?
swap_int64
dalam jawaban Anda. 1 untuk jawaban yang membantu, BTW!
LL
tidak perlu di (u)swap_uint64()
banyak seperti yang L
tidak dibutuhkan di (u)swap_uint32()
. Yang U
tidak dibutuhkan dalam uswap_uint64()
banyak hal seperti U
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.
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))
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;
}
}
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;
}
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;
}
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];
char
, bukan byte
.
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;
}
}
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.
t know if it
secepat saran tetapi itu wokrs
CHAR_BIT
bukannya 8
penasaran karena 0xFF00FF00FF00FF00ULL
bergantung pada CHAR_BIT == 8
. Perhatikan bahwa LL
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);
}
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.