C'de big endian'ı little endian'a dönüştürmek için bir fonksiyon yazmam gerekiyor. Herhangi bir kütüphane fonksiyonu kullanamıyorum.
C'de big endian'ı little endian'a dönüştürmek için bir fonksiyon yazmam gerekiyor. Herhangi bir kütüphane fonksiyonu kullanamıyorum.
Yanıtlar:
İhtiyacınız olan şeyin basit bir bayt değişimi olduğunu varsayarsak,
İmzasız 16 bit dönüştürme:
swapped = (num>>8) | (num<<8);
İmzasız 32 bit dönüştürme:
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
Bu, bayt emirlerini 1234 konumlarından 4321 konumuna değiştirir. Girişiniz öyleyse 0xdeadbeef
, 32-bit endian takasın çıktısı olabilir 0xefbeadde
.
Yukarıdaki kod, sihirli sayılar yerine makrolarla veya en azından sabitlerle temizlenmelidir, ancak umarım olduğu gibi yardımcı olur
DÜZENLEME: Başka bir cevabın işaret ettiği gibi, yukarıdakilerden ÇOK daha hızlı olabilen platform, işletim sistemi ve talimat setine özgü alternatifler vardır. Linux çekirdeğinde, endianness'ı oldukça güzel bir şekilde ele alan makrolar (örneğin cpu_to_be32) vardır. Ancak bu alternatifler çevrelerine özeldir. Pratikte bitkinlik en iyi şekilde mevcut yaklaşımların bir karışımı kullanılarak ele alınır
((num & 0xff) >> 8) | (num << 8)
, gcc 4.8.3 tek bir rol
komut oluşturur . Ve 32 bit dönüşüm olarak yazılırsa ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, aynı derleyici tek bir bswap
komut üretir .
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;}
alanlarıyla değiştirdim: burası her biri 1 bit olan 8 alanlı bir bit alanıdır. Ancak bunun diğer öneriler kadar hızlı olup olmadığından emin değilim. İnts union { int i; byte_t[sizeof(int)]; }
için tamsayıdaki bayt baytını ters çevirmek için kullanın .
Dahil ederek:
#include <byteswap.h>
makineye bağlı bayt değiştirme işlevlerinin optimize edilmiş bir sürümünü edinebilirsiniz. Ardından aşağıdaki işlevleri kolayca kullanabilirsiniz:
__bswap_32 (uint32_t input)
veya
__bswap_16 (uint16_t input)
#include <byteswap.h>
, .h dosyanın kendisinde yorumu görmek. Bu gönderi faydalı bilgiler içeriyor, bu yüzden yazara bir kitaplık işlevi kullanmama için OP gereksinimini göz ardı etmesine rağmen oy verdim.
#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);
}
Güncelleme : 64bit bayt takas eklendi
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
ve int64_t
varyant, maskeleme arkasındaki mantık nedir ... & 0xFFFF
ve ... & 0xFFFFFFFFULL
? Burada işaret uzantısıyla ilgili görmediğim bir şeyler mi var? Ayrıca, neden swap_int64
geri dönüyor uint64_t
? Olması gerekmiyor int64_t
mu?
swap_int64
Cevabınızda dönüş değerinin türünü değiştirmek isteyebilirsiniz . Faydalı cevap için +1, BTW!
LL
Gereksiz olan (u)swap_uint64()
bir çok gibi L
gerekli değildir (u)swap_uint32()
. U
İçinde gerekli değildir uswap_uint64()
gibi pek U
de gerekli değildiruswap_uint32()
İşte oldukça genel bir versiyon; Derlemedim, bu yüzden büyük olasılıkla yazım hataları var, ama şunu anlamalısın,
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: Bu değil hız veya alanı için optimize. Açık (hata ayıklaması kolay) ve taşınabilir olması amaçlanmıştır.
Güncelleme 2018-04-04 yorumcu @chux tarafından tespit edildiği gibi geçersiz n == 0 durumunu yakalamak için assert () eklendi.
bswap
, optimizasyon etkinleştirilmiş iyi bir X86 derleyicisi tarafından tek bir talimatla derlenir . Boyut parametresi olan bu sürüm bunu yapamadı.
Makrolara ihtiyacınız varsa (örn. Gömülü sistem):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
UINT
onların adında var.
Düzenle: Bunlar kütüphane işlevleridir. Bunları takip etmek, bunu yapmanın manuel yoludur.
Farkında olmayan insanların sayısı beni kesinlikle şaşırttı __Byteswap_ushort, __byteswap_ulong ve __byteswap_uint64'ten şaşırttı . Elbette bunlar Visual C ++ 'ya özgüdür, ancak x86 / IA-64 mimarilerinde bazı lezzetli kodlara derleme yaparlar. :)
İşte bu sayfadan alınanbswap
talimatın açık bir kullanımı . Yukarıdaki içsel formun her zaman bundan daha hızlı olacağına dikkat edin , sadece bir kütüphane rutini olmadan bir cevap vermek için ekledim.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
Şaka olarak:
#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
Her ikisi için aynı tip değil, kullanımı meraklı .
4 saniyenin katlarına sahip olduğunuzu varsayarak, Intel özünü kullanarak SSSE3 komutu pshufb'u kullanmanın bir yolu int
:
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;
}
Bu işe yarayacak mı / daha hızlı olacak mı?
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
değil byte
.
İşte kullandığım bir işlev - test edilmiş ve herhangi bir temel veri türü üzerinde çalışıyor:
// 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
gerektiği gibi hizalanır - ancak bu varsayım geçerli değilse, kod UB'dir.
DÜZENLEME: Bu işlev yalnızca hizalanmış 16 bitlik kelimelerin sonluluğunu değiştirir. UTF-16 / UCS-2 kodlamaları için genellikle gerekli bir işlev. SONU DÜZENLE.
Bir bellek bloğunun dayanıklılığını değiştirmek istiyorsanız, benim son derece hızlı yaklaşımımı kullanabilirsiniz. Bellek dizinizin boyutu 8'in katı olmalıdır.
#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);
}
Bu tür bir işlev, Unicode UCS-2 / UTF-16 dosyalarının dayanıklılığını değiştirmek için kullanışlıdır.
t know if it
, öneriler kadar hızlı değil ama uyandı: github.com/heatblazer/helpers/blob/master/utils.h
CHAR_BIT
bağımlı 8
olduğu gibi merak yerine . Sabit olarak gerekli olmadığını unutmayın . 0xFF00FF00FF00FF00ULL
CHAR_BIT == 8
LL
CHAR_BIT
bu makronun teşhirini artırmak için yazdı . LL'ye gelince, bu her şeyden çok bir açıklama. Aynı zamanda uzun zaman önce buggy derleyicilerle (standart öncesi) yakaladığım ve doğru şeyi yapmayan bir alışkanlık.
Bu kod parçacığı, 32 bitlik küçük Endian numarasını Büyük Endian numarasına dönüştürebilir.
#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);
}
Bir x86 veya x86_64 işlemci üzerinde çalışıyorsanız, big endian yereldir. yani
16 bit değerler için
unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
32 bit değerler için
unsigned int iBigE = value;
unsigned int iLittleE = ((iBigE & 0xFF) << 24)
| ((iBigE & 0xFF00) << 8)
| ((iBigE >> 8) & 0xFF00)
| (iBigE >> 24);
Derleyici, bunun bayt seviyesi manipülasyonu olduğunu fark etmediği ve bayt takas kodu oluşturmadığı sürece bu en verimli çözüm değildir. Ancak herhangi bir bellek düzeni hilesine bağlı değildir ve oldukça kolay bir şekilde bir makroya dönüştürülebilir.