it-swarm-tr.com

C++ 'da endian ve endian değerlerini nasıl dönüştürebilirim?

C++ 'da endian ve endian değerlerini nasıl dönüştürebilirim?

EDIT: Netlik sağlamak için, ikili verileri (çift duyarlıklı kayan nokta değerleri ve 32 bit ve 64 bit tam sayılar) bir CPU mimarisinden diğerine çevirmem gerekiyor. Bu ağ kurmayı içermez, bu nedenle ntoh () ve benzeri işlevler burada çalışmaz.

EDIT # 2: Kabul ettiğim cevap doğrudan hedeflediğim derleyicilere uygulanır (bu yüzden seçtim). Ancak burada çok daha iyi ve daha taşınabilir cevaplar var.

176
Uhall

Visual C++ kullanıyorsanız, şunları yapın: intrin.h dosyasını dahil edin ve aşağıdaki işlevleri çağırın:

16 bit sayılar için:

unsigned short _byteswap_ushort(unsigned short value);

32 bit sayılar için:

unsigned long _byteswap_ulong(unsigned long value);

64 bit sayılar için:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bitlik sayıların (karakterlerin) dönüştürülmesi gerekmez.

Ayrıca bunlar sadece işaretli tamsayılar için çalıştıkları işaretsiz değerler için tanımlanmıştır.

Şamandıralar ve çiftler için, Host makinelerinin bayt sıralarında olabileceği veya olamayacağı gibi düz tamsayılarda olduğu gibi daha zordur. Endian makinelerinde küçük endian yüzdeleri elde edebilirsiniz, bunun tersi de geçerlidir.

Diğer derleyiciler de benzer özelliklere sahiptir. 

GCCiçinde örneğin doğrudan arayabilirsiniz:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

(bir şey eklemenize gerek yok). Afaik bits.h, aynı işlevi gcc merkezli olmayan bir biçimde de bildirir.

16 bit takas sadece biraz döndürülür.

Kendi haddeleme yerine içsel olarak çağırmak size en iyi performansı ve btw kod yoğunluğunu verir. 

147
Nils Pipenbrinck

Basit ifadeyle:

#include <climits>

template <typename T>
T swap_endian(T u)
{
    static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");

    union
    {
        T u;
        unsigned char u8[sizeof(T)];
    } source, dest;

    source.u = u;

    for (size_t k = 0; k < sizeof(T); k++)
        dest.u8[k] = source.u8[sizeof(T) - k - 1];

    return dest.u;
}

kullanım: swap_endian<uint32_t>(42).

76
Alexandre C.

Kimden Bayt Sırası Hatalılığı Rob Pyke tarafından:

Veri akışınızın küçük bir endian kodlu 32 bit tam sayı olduğunu varsayalım. İşte onu nasıl çıkaracağınız (imzasız baytlar varsayılarak):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Büyük-endian ise, bu nasıl çıkarılacağı:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

TL; DR: platformunuzun yerel düzeninden endişe etmeyin, önemli olan tek şey okuduğunuz akışın bayt sırasıdır ve iyi tanımlanmış olmasını umarsınız.

Not: yorumda açıkça tür dönüşümünün olmadığı belirtildi, data'ın unsigned char veya uint8_t dizisi olması önemliydi. signed char veya char öğesinin kullanılması (imzalandıysa) data[x] öğesinin tam sayıya yükseltilmesine ve data[x] << 24 potansiyel olarak yükseltilmesine neden olacak 1 işaretini UB olan işaret bitine kaydırmak.

64
Matthieu M.

Bunu ağ/Ana bilgisayar uyumluluğu açısından yapıyorsanız kullanmanız gerekir:

ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)

ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)

Bunu başka bir sebepten dolayı yapıyorsanız, burada sunulan byte_swap çözümlerinden biri gayet iyi sonuç verir.

48
Frosty

Bu gönderiden birkaç öneri aldım ve bunları oluşturmak için bir araya getirdim:

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>

enum endianness
{
    little_endian,
    big_endian,
    network_endian = big_endian,

    #if defined(BOOST_LITTLE_ENDIAN)
        Host_endian = little_endian
    #Elif defined(BOOST_BIG_ENDIAN)
        Host_endian = big_endian
    #else
        #error "unable to determine system endianness"
    #endif
};

namespace detail {

template<typename T, size_t sz>
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template<typename T>
struct swap_bytes<T, 1>
{
    inline T operator()(T val)
    {
        return val;
    }
};

template<typename T>
struct swap_bytes<T, 2>
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template<typename T>
struct swap_bytes<T, 4>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes<float, 4>
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template<typename T>
struct swap_bytes<T, 8>
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff00000000000000ull) >> 56) |
                (((val) & 0x00ff000000000000ull) >> 40) |
                (((val) & 0x0000ff0000000000ull) >> 24) |
                (((val) & 0x000000ff00000000ull) >> 8 ) |
                (((val) & 0x00000000ff000000ull) << 8 ) |
                (((val) & 0x0000000000ff0000ull) << 24) |
                (((val) & 0x000000000000ff00ull) << 40) |
                (((val) & 0x00000000000000ffull) << 56));
    }
};

template<>
struct swap_bytes<double, 8>
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template<endianness from, endianness to, class T>
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes<T, sizeof(T)>()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian,    big_endian,    T> { inline T operator()(T value) { return value; } };

} // namespace detail

template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
    // ensure the data is only 1, 2, 4 or 8 bytes
    BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
    // ensure we're only swapping arithmetic types
    BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

    return detail::do_byte_swap<from, to, T>()(value);
}
25
Steve Lorimer

Sizin için takas yapacak BSWAP adında bir derleme talimatı var, derece hızlı . Bunu okuyabilirsiniz burada .

Visual Studio veya daha doğrusu Visual C++ çalışma zamanı kütüphanesi, bunun için _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64() adı verilen platform iç mimarisine sahiptir. Diğer platformlar için de benzer şeyler olmalı, ancak ne deneceklerini bilmiyorum.

15
anon6439

Büyük endian'dan küçük endian'a geçiş prosedürü, küçük endian'dan büyük endian'a geçmekle aynıdır.

İşte bazı örnek kod:

void swapByteOrder(unsigned short& us)
{
    us = (us >> 8) |
         (us << 8);
}

void swapByteOrder(unsigned int& ui)
{
    ui = (ui >> 24) |
         ((ui<<8) & 0x00FF0000) |
         ((ui>>8) & 0x0000FF00) |
         (ui << 24);
}

void swapByteOrder(unsigned long long& ull)
{
    ull = (ull >> 56) |
          ((ull<<40) & 0x00FF000000000000) |
          ((ull<<24) & 0x0000FF0000000000) |
          ((ull<<8) & 0x000000FF00000000) |
          ((ull>>8) & 0x00000000FF000000) |
          ((ull>>24) & 0x0000000000FF0000) |
          ((ull>>40) & 0x000000000000FF00) |
          (ull << 56);
}
13
Kevin

Bunu şablonlarla yaptık. Böyle bir şey olabilir:

// Specialization for 2-byte types.
template<>
inline void endian_byte_swapper< 2 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    ushort* p_dest = reinterpret_cast< ushort* >(dest);
    ushort const* const p_src = reinterpret_cast< ushort const* >(src);
    *p_dest = (*p_src >> 8) | (*p_src << 8);
}

// Specialization for 4-byte types.
template<>
inline void endian_byte_swapper< 4 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    uint* p_dest = reinterpret_cast< uint* >(dest);
    uint const* const p_src = reinterpret_cast< uint const* >(src);
    *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24);
}
11
Mark

Bunu farklı platformlar arasında veri aktarmak için yapıyorsanız, ntoh ve hton fonksiyonlarına bakın.

8
Andrew

C ile aynı şekilde:

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

Ayrıca, imzasız karakterlerin bir vektörünü beyan edebilir, giriş değerini içine çekebilir, baytları başka bir vektöre tersine çevirebilir ve baytları dışarı çekebilirsiniz, ancak bu, özellikle 64-bit değerlerle, bit-twiddling'den daha uzun büyüklüklerde olur.

6
Ben Straub

Çoğu POSIX sisteminde (aracılığıyla POSIX standardında değildir), sisteminizin hangi kodlamayı kullandığını belirlemek için kullanılabilecek endian.h vardır. Oradan böyle bir şey var:

unsigned int change_endian(unsinged int x)
{
    unsigned char *ptr = (unsigned char *)&x;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

Bu, emri değiştirir (büyük-endian'dan küçük-endian'a):

0xDEADBEEF numaranız varsa (0xEFBEADDE olarak depolanmış küçük bir endian sisteminde), ptr [0] 0xEF, ptr [1] 0xBE vb. Olur.

Fakat eğer ağ için kullanmak istiyorsanız, htons, htonl ve htonll (ve bunların ters ntoh'ları, ntohl ve ntohll'leri), Host siparişinden network siparişine dönüştürme konusunda yardımcı olacaktır.

6
terminus

En azından Windows için htonl () 'nin gerçek meslektaşı _byteswap_ulong ()' den çok daha yavaş olduğunu unutmayın. Eski ws2_32.dll içine DLL kütüphane çağrısıdır, ikincisi bir BSWAP Assembly talimatıdır. Bu nedenle, platforma bağlı bir kod yazıyorsanız, hız için gerçekleri kullanmayı tercih edin:

#define htonl(x) _byteswap_ulong(x)

Bu, özellikle tüm hazırlayıcıların Big Endian'da kaydedildiği,.

5
user2699548

Cidden ... Neden tüm çözümlerin bu kadar olduğunu anlamıyorumkomplike! Herhangi bir işletim sisteminde, herhangi bir koşul altında herhangi bir boyutta herhangi bir türün yerini alan en basit, en genel şablon işlevi nedir?

template <typename T>
void SwapEnd(T& var)
{
    char* varArray = reinterpret_cast<char*>(&var);
    for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
}

C ve C++ 'ın birlikte büyü gücü! Basitçe orijinal değişken karakterini karakter karakteriyle değiştirin.

Unutma = "=" atama operatörünü kullanmadığımı hatırlayın çünkü endianness çevrildiğinde bazı nesneler karışacak ve kopya kurucu (veya atama operatörü) çalışmayacak. Bu nedenle, onları karaktere göre kopyalamak daha güvenilirdir.

Aramak için, sadece kullanın

double x = 5;
SwapEnd(x);

ve şimdi x endianness için farklıdır.

5

bunu sevdim, sadece stil için :-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
4
friedemann

Çoğu platformda verimli byteswap işlevleri sağlayan bir sistem başlık dosyası bulunur. Linux'ta <endian.h> içindedir. C++ ile güzelce sarın:

#include <iostream>

#include <endian.h>

template<size_t N> struct SizeT {};

#define BYTESWAPS(bits) \
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } \
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } \
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } \
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }

BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)

#undef BYTESWAPS

template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }

int main()
{
    std::cout << std::hex;
    std::cout << htobe(static_cast<unsigned short>(0xfeca)) << '\n';
    std::cout << htobe(0xafbeadde) << '\n';

    // Use ULL suffix to specify integer constant as unsigned long long 
    std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n';
}

Çıktı:

cafe
deadbeaf
feeddeafbeefcafe
4
Maxim Egorushkin

Büyük bir endian 32-bit işaretsiz tam sayı 2864434397'ye eşit 0xAABBCCDD'ye benziyorsa, aynı 32-bit işaretsiz tam sayı da 2864434397'ye eşit olan küçük bir endian işlemcide 0xDDCCBBAA'ya benzer.

Büyük bir 16 bit işaretsiz kısa, 43707'ye eşit 0xAABB'ye benziyorsa, aynı 16 bit işaretsiz kısa, aynı zamanda 43707 değerine sahip küçük bir işlemcideki 0xBBAA'ya benzer.

İşte byte'ları küçük-endian'dan büyük-endian'a veya tam tersi olarak değiştirmek için kullanışlı #define işlevleri.

// can be used for short, unsigned short, Word, unsigned Word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))

// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))

// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))
3
Adam Freeman

Host_ENDIAN_ORDER'den (ne olursa olsun) LITTLE_ENDIAN_ORDER veya BIG_ENDIAN_ORDER'e dönüştürmeme izin veren bu kod var. Bir şablon kullanıyorum, bu yüzden Host_ENDIAN_ORDER'den LITTLE_ENDIAN_ORDER'e dönüştürmeye çalışırsam ve derlediğim makine için aynı olurlarsa kod üretilmez.

İşte bazı yorumlar ile kod:

// We define some constant for little, big and Host endianess. Here I use 
// BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the Host indianess. If you
// don't want to use boost you will have to modify this part a bit.
enum EEndian
{
  LITTLE_ENDIAN_ORDER,
  BIG_ENDIAN_ORDER,
#if defined(BOOST_LITTLE_ENDIAN)
  Host_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER
#Elif defined(BOOST_BIG_ENDIAN)
  Host_ENDIAN_ORDER = BIG_ENDIAN_ORDER
#else
#error "Impossible de determiner l'indianness du systeme cible."
#endif
};

// this function swap the bytes of values given it's size as a template
// parameter (could sizeof be used?).
template <class T, unsigned int size>
inline T SwapBytes(T value)
{
  union
  {
     T value;
     char bytes[size];
  } in, out;

  in.value = value;

  for (unsigned int i = 0; i < size / 2; ++i)
  {
     out.bytes[i] = in.bytes[size - 1 - i];
     out.bytes[size - 1 - i] = in.bytes[i];
  }

  return out.value;
}

// Here is the function you will use. Again there is two compile-time assertion
// that use the boost librarie. You could probably comment them out, but if you
// do be cautious not to use this function for anything else than integers
// types. This function need to be calles like this :
//
//     int x = someValue;
//     int i = EndianSwapBytes<Host_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
inline T EndianSwapBytes(T value)
{
  // A : La donnée à swapper à une taille de 2, 4 ou 8 octets
  BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);

  // A : La donnée à swapper est d'un type arithmetic
  BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);

  // Si from et to sont du même type on ne swap pas.
  if (from == to)
     return value;

  return SwapBytes<T, sizeof(T)>(value);
}
3
Mathieu Pagé

Sadece hiçbir çözümü görmediğim için kendi çözümümü buraya eklediğimi düşündüm. Küçük ve taşınabilir C++ şablonlu bir işlevdir ve yalnızca bit işlemlerini kullanan taşınabilir.

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}
2
Joao

İşte, bir değeri yerine taktığım için kafamın üstünden çıkan genelleştirilmiş bir versiyon. Performans bir sorunsa, diğer öneriler daha iyi olur.

 template<typename T>
    void ByteSwap(T * p)
    {
        for (int i = 0;  i < sizeof(T)/2;  ++i)
            std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
    }

Sorumluluk reddi: Bunu derlemeye ya da henüz test etmeye çalışmadım.

2
Mark Ransom

Bir Word'deki bitlerin sırasını tersine çevirmek için ortak modeli kullanırsanız ve her bir baytın içindeki bitleri ters çeviren kısmı çıkarırsanız, o zaman yalnızca bir Word'deki baytları ters çeviren bir şey kalır. 64 bit için:

x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff00ff00ff);

Derleyici gerekir gereksiz bit maskeleme işlemlerini temizler (deseni vurgulamak için bıraktım), ancak değilse ilk satırı şu şekilde yeniden yazabilirsiniz:

x = ( x                       << 32) ^  (x >> 32);

Bu normalde çoğu mimaride tek bir döndürme komutunu basitleştirmelidir (tüm işlemin muhtemelen bir komut olduğunu göz ardı ederek).

Bir RISC işlemcide büyük ve karmaşık sabitler derleyicinin zorluğuna neden olabilir. Yine de sabitlerin her birini önemsiz bir şekilde önceki değerden hesaplayabilirsiniz. Gibi:

uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */
x = ((x & k) << 32) ^ ((x >> 32) & k);
k ^= k << 16;
x = ((x & k) << 16) ^ ((x >> 16) & k);
k ^= k << 8;
x = ((x & k) <<  8) ^ ((x >>  8) & k);

İsterseniz, bunu bir döngü olarak yazabilirsiniz. Verimli olmayacak, ama sadece eğlence için:

int i = sizeof(x) * CHAR_BIT / 2;
uintmax_t k = (1 << i) - 1;
while (i >= 8)
{
    x = ((x & k) << i) ^ ((x >> i) & k);
    i >>= 1;
    k ^= k << i;
}

Ve tam olması için, işte ilk formun basitleştirilmiş 32 bit versiyonu:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);
2
sh1

Gerçekten hiç kimsenin htobeXX ve betohXX fonksiyonlarından bahsetmediğine şaşırdım. Endian.h dosyasında tanımlanmıştır ve htonXX ağ işlevlerine çok benzer.

1
Nick

Aşağıdaki kodları kullanarak kolayca BigEndian ve LittleEndian arasında geçiş yapabilirsiniz 

#define uint32_t unsigned 
#define uint16_t unsigned short

#define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \
(((uint16_t)(x) & 0xff00)>>8))

#define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \
(((uint32_t)(x) & 0x0000ff00)<<8)| \
(((uint32_t)(x) & 0x00ff0000)>>8)| \
(((uint32_t)(x) & 0xff000000)>>24))
1
pz64_

Her Word'de htons kullanmak güvenli bir yol gibi görünüyor. Yani, eğer varsa ...

std::vector<uint16_t> storage(n);  // where n is the number to be converted

// the following would do the trick
std::transform(Word_storage.cbegin(), Word_storage.cend()
  , Word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });

Eğer büyük bir endian sistemindeyseniz yukarıdakiler bir no-op olur, bu yüzden htonların bir no-op olmadığına karar vermek için platformunuzun bir derleme zamanı koşulu olarak kullandığı her şeyi arardım. Sonuçta O(n). Mac'te, bunun gibi bir şey olurdu ...

#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(Word_storage.cbegin(), Word_storage.cend()
  , Word_storage.begin(), [](const uint16_t input)->uint16_t {
  return htons(input); });
#endif
0
cycollins

İyileştirici dostu hizaya girmemiş, yerinde olmayan endian erişim sağlayıcılarını uygulamak için taşınabilir teknik. Her derleyici, her sınır düzenlemesi ve her bayt siparişi üzerinde çalışırlar. Bu hizalanmamış rutinler, yerli endian ve hizalamaya bağlı olarak tamamlanır veya karıştırılır. Kısmi listeleme ancak fikir sahibi oluyorsunuz. BO *, yerel bayt sıralamasına göre sabit değerlerdir.

uint32_t sw_get_uint32_1234(pu32)
uint32_1234 *pu32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32_1234[0] = (*pu32)[BO32_0];
  bou32.u32_1234[1] = (*pu32)[BO32_1];
  bou32.u32_1234[2] = (*pu32)[BO32_2];
  bou32.u32_1234[3] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_1234(pu32, u32)
uint32_1234 *pu32;
uint32_t u32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_1234[0];
  (*pu32)[BO32_1] = bou32.u32_1234[1];
  (*pu32)[BO32_2] = bou32.u32_1234[2];
  (*pu32)[BO32_3] = bou32.u32_1234[3];
}

#if HAS_SW_INT64
int64 sw_get_int64_12345678(pi64)
int64_12345678 *pi64;
{
  union {
    int64_12345678 i64_12345678;
    int64 i64;
  } boi64;
  boi64.i64_12345678[0] = (*pi64)[BO64_0];
  boi64.i64_12345678[1] = (*pi64)[BO64_1];
  boi64.i64_12345678[2] = (*pi64)[BO64_2];
  boi64.i64_12345678[3] = (*pi64)[BO64_3];
  boi64.i64_12345678[4] = (*pi64)[BO64_4];
  boi64.i64_12345678[5] = (*pi64)[BO64_5];
  boi64.i64_12345678[6] = (*pi64)[BO64_6];
  boi64.i64_12345678[7] = (*pi64)[BO64_7];
  return(boi64.i64);
}
#endif

int32_t sw_get_int32_3412(pi32)
int32_3412 *pi32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32_3412[2] = (*pi32)[BO32_0];
  boi32.i32_3412[3] = (*pi32)[BO32_1];
  boi32.i32_3412[0] = (*pi32)[BO32_2];
  boi32.i32_3412[1] = (*pi32)[BO32_3];
  return(boi32.i32);
}

void sw_set_int32_3412(pi32, i32)
int32_3412 *pi32;
int32_t i32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32 = i32;
  (*pi32)[BO32_0] = boi32.i32_3412[2];
  (*pi32)[BO32_1] = boi32.i32_3412[3];
  (*pi32)[BO32_2] = boi32.i32_3412[0];
  (*pi32)[BO32_3] = boi32.i32_3412[1];
}

uint32_t sw_get_uint32_3412(pu32)
uint32_3412 *pu32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32_3412[2] = (*pu32)[BO32_0];
  bou32.u32_3412[3] = (*pu32)[BO32_1];
  bou32.u32_3412[0] = (*pu32)[BO32_2];
  bou32.u32_3412[1] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_3412(pu32, u32)
uint32_3412 *pu32;
uint32_t u32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_3412[2];
  (*pu32)[BO32_1] = bou32.u32_3412[3];
  (*pu32)[BO32_2] = bou32.u32_3412[0];
  (*pu32)[BO32_3] = bou32.u32_3412[1];
}

float sw_get_float_1234(pf)
float_1234 *pf;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f_1234[0] = (*pf)[BO32_0];
  bof.f_1234[1] = (*pf)[BO32_1];
  bof.f_1234[2] = (*pf)[BO32_2];
  bof.f_1234[3] = (*pf)[BO32_3];
  return(bof.f);
}

void sw_set_float_1234(pf, f)
float_1234 *pf;
float f;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f = (float)f;
  (*pf)[BO32_0] = bof.f_1234[0];
  (*pf)[BO32_1] = bof.f_1234[1];
  (*pf)[BO32_2] = bof.f_1234[2];
  (*pf)[BO32_3] = bof.f_1234[3];
}

double sw_get_double_12345678(pd)
double_12345678 *pd;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d_12345678[0] = (*pd)[BO64_0];
  bod.d_12345678[1] = (*pd)[BO64_1];
  bod.d_12345678[2] = (*pd)[BO64_2];
  bod.d_12345678[3] = (*pd)[BO64_3];
  bod.d_12345678[4] = (*pd)[BO64_4];
  bod.d_12345678[5] = (*pd)[BO64_5];
  bod.d_12345678[6] = (*pd)[BO64_6];
  bod.d_12345678[7] = (*pd)[BO64_7];
  return(bod.d);
}

void sw_set_double_12345678(pd, d)
double_12345678 *pd;
double d;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d = d;
  (*pd)[BO64_0] = bod.d_12345678[0];
  (*pd)[BO64_1] = bod.d_12345678[1];
  (*pd)[BO64_2] = bod.d_12345678[2];
  (*pd)[BO64_3] = bod.d_12345678[3];
  (*pd)[BO64_4] = bod.d_12345678[4];
  (*pd)[BO64_5] = bod.d_12345678[5];
  (*pd)[BO64_6] = bod.d_12345678[6];
  (*pd)[BO64_7] = bod.d_12345678[7];
}

Bu tip tanımlamalar, erişimcilerle kullanılmazsa derleyici hatalarının arttırılması ve böylece unutulan erişimci hatalarının azaltılması avantajına sahiptir.

typedef char int8_1[1], uint8_1[1];

typedef char int16_12[2], uint16_12[2]; /* little endian */
typedef char int16_21[2], uint16_21[2]; /* big endian */

typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */

typedef char int32_1234[4], uint32_1234[4]; /* little endian */
typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */
typedef char int32_4321[4], uint32_4321[4]; /* big endian */

typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */
typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */
typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */

typedef char float_1234[4]; /* little endian */
typedef char float_3412[4]; /* Alpha Micro, PDP-11 */
typedef char float_4321[4]; /* big endian */

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */
0
BSalita

Geçenlerde bunu C'de yapmak için bir makro yazdım, ancak C++ dilinde aynı derecede geçerli:

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

Herhangi bir türü kabul eder ve iletilen argümandaki bayt sayısını tersine çevirir.

int main(){
    unsigned long long x = 0xABCDEF0123456789;
    printf("Before: %llX\n",x);
    REVERSE_BYTES(x);
    printf("After : %llX\n",x);

    char c[7]="nametag";
    printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
    REVERSE_BYTES(c);
    printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}

Hangi yazdırır:

Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman

Yukarıdakiler mükemmel kopyalanabilir/yapıştırılabilir, ancak burada çok şey var, bu yüzden nasıl çalıştığını parça parça ayıracağım:

Dikkate değer ilk şey, tüm makronun bir do while(0) bloğunda yer almasıdır. Bu bir ortak deyimdir makrodan sonra normal noktalı virgül kullanımına izin vermek için.

Bir sonraki adım, for loop'un sayacı olarak REVERSE_BYTES adlı bir değişkenin kullanılmasıdır. Makronun kendisi, makronun kullanıldığı yerde kapsamda olabilecek diğer sembollerle çakışmamasını sağlamak için değişken bir ad olarak kullanılır. Ad, makro genişlemesinde kullanıldığından, burada değişken bir isim olarak kullanıldığında tekrar genişletilmeyecektir.

for döngüsü içinde, başvurulan iki bayt vardır ve XOR takas edilir (yani geçici bir değişken adı gerekli değildir):

((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]

__VA_ARGS__ /, makroya verilenleri temsil eder ve geçirilebilecek olanın esnekliğini artırmak için kullanılır (çok fazla olmasa da). Ardından, bu argümanın adresi alınır ve bycode'ların dizi unsigned char aboneliği ile değiştirilmesine izin vermek için bir [] göstergesine atılır.

Son tuhaf nokta, {} ayraçlarının olmamasıdır. Gerekli değildir, çünkü her bir takas adımındaki adımların tümü virgül operatörü ile birleştirilir, bunlar birer ifadedir.

Son olarak, hız öncelikli ise, bunun ideal bir yaklaşım olmadığını belirtmekte fayda var. Bu önemli bir faktörse, diğer cevaplarda referans verilen bazı türe özgü makrolar veya platforma özgü yönergeler daha iyi bir seçenek olabilir. Bununla birlikte, bu yaklaşım her türlü, tüm büyük platformlar ve hem C hem de C++ dilleri için taşınabilirdir.

0
Ryan Hilbert

Ana bilgisayarınız farklı bir sistem kullanıyor olsa da, IEEE 754 64 bit biçiminde saklanan bir çiftin nasıl okunacağını öğrenin.

/*
* read a double from a stream in ieee754 format regardless of Host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}

Yazma ve tamsayılı yordamlar da dahil olmak üzere diğer işlevler paketi için github projeme bakın

https://github.com/MalcolmMcLean/ieee754

0
Malcolm McLean

Bir şablon işlevinde bir pivot etrafında 3 adım xor veya hile ile takas bayt, bir kütüphane gerektirmeyen esnek, hızlı O(ln2) çözümü verir, burada da stil 1 bayt türlerini reddeder:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}
0
Quinn Carver

Boost::endian dosyasını deneyin ve KENDİNİZİ UYGULAMAYIN!

İşte bir link var

0
morteza

Vay, burada okuduğum bazı cevaplara inanamadım. Aslında Meclis'te bunu her şeyden daha hızlı yapan bir talimat var. bswap. Bunun gibi bir fonksiyon yazabilirsiniz ...

__declspec(naked) uint32_t EndianSwap(uint32 value)
{
    __asm
    {
        mov eax, dword ptr[esp + 4]
        bswap eax
        ret
    }
}

Önerilen gerçeklerden&CCEDIL;OKdaha hızlıdır. Onları söküp baktım. Yukarıdaki fonksiyonda prolog/epilog yoktur, bu yüzden neredeyse hiç ek yükü yoktur.

unsigned long _byteswap_ulong(unsigned long value);

16 bit yapmak, xchg al, ah. bswap sadece 32 bit kayıtlarda çalışır.

64-bit biraz daha zor, ama aşırı öyle değil. Döngü ve şablonlarla ilgili yukarıdaki örneklerin hepsinden çok daha iyi.

Burada bazı uyarılar var ... Öncelikle bswap sadece 80x486 CPU'larda ve üstü sürümlerde mevcut. 386'da çalıştırmayı planlayan var mı? Eğer öyleyse, hala bswap ile ... değiştirebilirsiniz.

mov ebx, eax
shr ebx, 16
xchg bl, bh
xchg al, ah
shl eax, 16
or eax, ebx

Ayrıca satır içi derleme yalnızca Visual Studio'da x 86 kodunda kullanılabilir. Çıplak bir işlev astarlanamaz ve ayrıca x64 yapılarında kullanılamaz. Bence bu, derleyici içgüdüsünü kullanmak zorunda kalacaksınız.

0
The Welder