빅엔디안 - C++에서 빅 엔디안 값과 리틀 엔디안 값을 어떻게 변환합니까?




빅 라디안 (19)

C ++에서 빅 엔디안 값과 리틀 엔디안 값을 어떻게 변환합니까?

편집 : 명확성을 위해, 나는 한 CPU 아키텍처에서 다른 아키텍처로 이진 데이터 (배정 밀도 부동 소수점 값과 32 비트 및 64 비트 정수)를 변환해야합니다. 네트워킹과 관련이 없기 때문에 ntoh () 및 이와 유사한 함수는 여기서 작동하지 않습니다.

편집 # 2 : 내가 받아 들인 대답은 내가 목표로하고있는 컴파일러에 직접 적용된다 (그래서 내가 그것을 선택했다). 그러나 여기에 아주 좋은, 더 많은 휴대용 답변이 있습니다.


C에서와 같은 방식으로 :

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

또한 부호없는 문자의 벡터를 선언하고, 입력 값을 memcpy로 변환하고, 바이트를 다른 벡터로 반전하고, 바이트를 memcpy 할 수 있습니다.하지만 특히 64 비트 값의 경우 비트 트위 더보다 오랜 시간이 걸릴 것입니다.


HOST_ENDIAN_ORDER (그것이 무엇이든간에) LITTLE_ENDIAN_ORDER 또는 BIG_ENDIAN_ORDER로 변환 할 수있게 해주는이 코드가 있습니다. 템플릿을 사용하므로 HOST_ENDIAN_ORDER에서 LITTLE_ENDIAN_ORDER로 변환하려고 시도하고 컴파일 할 때 컴퓨터에서 동일하게 발생하면 코드가 생성되지 않습니다.

몇 가지 코멘트가있는 코드는 다음과 같습니다.

// 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);
}

htobeXX 및 betohXX 기능에 대해 언급 한 사람은 아무도 놀랍지 않습니다. 그것들은 endian.h에 정의되어 있으며 htonXX라는 네트워크 함수와 매우 유사합니다.


간단히 말하면 :

#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;
}

사용법 : swap_endian<uint32_t>(42) .


나는이 게시물에서 몇 가지 제안을 받아서 이것을 하나로 묶어서 만들었다.

#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);
}

네트워크 / 호스트 호환성을 위해이 작업을 수행하는 경우 다음을 사용해야합니다.

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)

다른 이유로이 작업을 수행하는 경우 여기에 제시된 byte_swap 솔루션 중 하나가 정상적으로 작동합니다.


다음은 제 자리에서 가치를 교환하기 위해 머리 꼭대기에서 나온 일반화 된 버전입니다. 성능이 문제가되면 다른 제안을하는 것이 좋습니다.

 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]);
    }

면책 조항 : 나는 이것을 컴파일하거나 아직 테스트하지 않았다.


대부분의 POSIX 시스템 (POSIX 표준에없는 시스템)에는 시스템이 사용하는 인코딩을 결정하는 데 사용할 수있는 endian.h가 있습니다. 거기에서 다음과 같이됩니다.

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];
}

이 명령은 빅 엔디안에서 리틀 엔디안으로 바꿉니다.

숫자 0xDEADBEEF (리틀 엔디안 시스템에서 0xEFBEADDE로 저장된 경우), ptr [0]은 0xEF, ptr [1]은 0xBE 등입니다.

그러나 네트워킹에 사용하려면 htons, htonl 및 htonll (및 그 반대의 ntoh, ntohl 및 ntohll)이 호스트 순서에서 네트워크 순서로 변환하는 데 유용 할 것입니다.


빅 엔디 언 32 비트 부호없는 정수가 2864434397과 같은 0xAABBCCDD처럼 보이는 경우 동일한 32 비트 부호없는 정수가 2864434397과 동일한 리틀 엔디안 프로세서에서 0xDDCCBBAA처럼 보입니다.

빅 엔디안 16 비트 부호없는 short가 43707과 동일한 0xAABB처럼 보이는 경우 동일한 16 비트 부호없는 short는 43707과 동일한 리틀 엔디안 프로세서에서 0xBBAA처럼 보입니다.

리틀 엔디안에서 빅 엔디안으로 바이트를 스왑하는 편리한 몇 가지 함수가 있습니다. ->

// 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))

빅 엔디안에서 리틀 엔디안으로 이동하는 절차는 리틀 엔디 언에서 빅 엔디안으로가는 것과 같습니다.

다음은 몇 가지 예제 코드입니다.

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);
}

와우, 내가 여기서 읽은 대답 중 일부를 믿을 수 없었다. 실제로이 작업을 다른 작업보다 빠르게 수행하는 어셈블리에 대한 지침이 있습니다. bswap. 다음과 같은 함수를 작성할 수 있습니다.

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

그것은 제안 된 intrinsic보다 훨씬 빠릅니다. 나는 그들을 분해하고 보았다. 위의 함수에는 프롤로그 / 에필로그가 없으므로 오버 헤드가 거의 없습니다.

unsigned long _byteswap_ulong(unsigned long value);

xchg al을 사용하는 것을 제외하고는 16 비트를 사용하는 것이 쉽습니다. bswap은 32 비트 레지스터에서만 작동합니다.

64 비트는 좀 더 까다 롭지 만 지나치게 그렇게하지는 않습니다. 위의 루프 및 템플릿 등 모든 예제보다 훨씬 뛰어납니다.

여기에 몇 가지주의 사항이 있습니다 ... 우선 bswap은 80x486 이상의 CPU에서만 사용할 수 있습니다. 386에서 실행 계획을 세우고있는 사람이 있습니까? 그렇다면 bswap을 대체 할 수 있습니다 ...

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

또한 인라인 어셈블리는 Visual Studio의 x86 코드에서만 사용할 수 있습니다. naked 함수는 줄 수 없으며 x64 빌드에서는 사용할 수 없습니다. 그 경우, 컴파일러 내장 함수를 사용해야 할 것입니다.


이 스타일이 마음에 듭니다. :-)

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

최소한 Windows에서는 htonl ()이 내장 함수 인 _byteswap_ulong ()보다 훨씬 느립니다. 전자는 ws2_32.dll에 대한 DLL 라이브러리 호출이고 후자는 BSWAP 어셈블리 명령어입니다. 따라서 플랫폼에 종속적 인 코드를 작성하는 경우 내장 함수를 사용하여 속도를 향상 시키십시오.

#define htonl(x) _byteswap_ulong(x)

이것은 Big Endian에서 "하나의 htonl ()을 사용할 수 있습니다 ..."{준비되지 않은 경우 일반적인 Windows 프로그램의 속도를 늦추려면}과 함께 모든 정수가 저장되는 .PNG 이미지 처리에 특히 중요합니다.


최적화 된 친숙한 정렬되지 않은 비대 전형 엔디안 접근자를 구현하기위한 휴대용 기술. 이들은 모든 컴파일러, 모든 경계 정렬 및 모든 바이트 정렬 작업을합니다. 이러한 정렬되지 않은 루틴은 네이티브 엔디안 및 정렬에 따라 보완되거나 잘못 표시됩니다. 부분적인 목록이지만 아이디어를 얻을 수 있습니다. BO *는 기본 바이트 순서에 따라 상수 값입니다.

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];
}

이러한 typedef는 접근 자와 함께 사용하지 않으면 컴파일러 오류를 발생시키는 이점이 있으므로 잊어 버린 접근 자 버그가 완화됩니다.

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 */

한 단어의 비트 순서를 바꾸기위한 공통 패턴을 취하고 각 바이트 내에서 비트를 뒤집는 부분을 추려내면 한 단어 내에서 바이트 만 뒤집는 것을 남겨 둡니다. 64 비트의 경우 :

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

컴파일러 불필요한 비트 마스킹 작업을 제거 해야 하지만 (패턴을 강조하기 위해 왼쪽으로 두었습니다), 그렇지 않으면 첫 번째 줄을 다음과 같이 다시 작성할 수 있습니다.

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

이는 일반적으로 대부분의 아키텍처에서 단일 회전 명령어로 단순화해야합니다 (전체 연산이 하나의 명령어 일 가능성은 무시 됨).

RISC 프로세서에서 크고 복잡한 상수는 컴파일러 문제를 일으킬 수 있습니다. 당신은 trivially 이전의 각각에서 상수를 계산할 수 있습니다. 이렇게 :

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);

원한다면 루프로 작성할 수 있습니다. 그것은 효율적이지는 않지만 재미만을위한 것입니다 :

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;
}

그리고 완성을 위해 다음은 첫 번째 형식의 단순화 된 32 비트 버전입니다.

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

매우 빠른 속도로 스왑을 수행 할 BSWAP이라는 어셈블리 명령어가 있습니다. 당신은 here 그것에 대해 읽을 수 있습니다.

Visual Studio 또는보다 정확하게 Visual C ++ 런타임 라이브러리에는 _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64() 라고하는 플랫폼 내장 함수가 있습니다. 비슷하게 다른 플랫폼에도 존재해야하지만, 나는 그들이 무엇을 부를지 알지 못한다.


최근에 C에서이 작업을 수행하는 매크로를 작성했지만 C ++에서도 똑같이 유효합니다.

#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)

그것은 모든 유형을 허용하고 전달 된 인수의 바이트를 반대로합니다. 예제 사용법 :

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]);
}

어떤 지문 :

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

위의 내용은 완벽하게 복사 / 붙여 넣기가 가능하지만 여기서는 많은 부분이 있으므로 조각별로 작동하는 방법을 설명 할 것입니다.

첫 번째 중요한 점은 전체 매크로가 do while(0)블록으로 둘러싸여 있다는 것입니다 . 이것은 매크로 뒤에 일반적인 세미콜론 사용을 허용 하는 일반적인 관용구 입니다.

다음 REVERSE_BYTESfor루프 카운터 로 명명 된 변수를 사용하는 것입니다 . 매크로 자체의 이름은 변수 이름으로 사용되어 매크로가 사용되는 모든 범위의 다른 기호와 충돌하지 않도록합니다. 이름이 매크로의 확장 내에서 사용되고 있기 때문에 여기서 변수 이름으로 사용될 때 이름은 다시 확장되지 않습니다.

for루프 내에서 참조되는 두 바이트와 XOR이 스왑 되므로 (임시 변수 이름은 필요하지 않음) :

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

__VA_ARGS__어떤 매크로가 주어 졌는지를 나타내며 (많은 부분이 아니더라도) 전달 될 수있는 것의 유연성을 높이기 위해 사용됩니다. 그런 다음이 인수의 주소를 가져 와서 unsigned char포인터로 캐스팅하여 배열 []첨자 를 통해 바이트 스와핑을 허용합니다 .

마지막 특이점은 {}중괄호 가 없다는 것입니다 . 각 스왑의 모든 단계가 쉼표 연산자 와 결합되어 하나의 명령문이 되므로 필요하지 않습니다 .

마지막으로, 속도가 최우선이면이 방법이 이상적인 방법이 아니라는 점에 유의할 가치가 있습니다. 이것이 중요한 요소 인 경우, 다른 응답에서 참조되는 특정 유형의 매크로 또는 특정 플랫폼 지정 문 중 일부가 더 나은 옵션 일 수 있습니다. 그러나이 접근 방식은 모든 유형, 모든 주요 플랫폼 및 C 및 C ++ 언어로 이식 가능합니다.


호스트 컴퓨터가 다른 시스템을 사용하는 경우에도 IEEE 754 64 비트 형식으로 저장된 double을 읽는 방법은 다음과 같습니다.

/*
* 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;
    }
}

쓰기 및 정수 루틴을 포함한 나머지 함수 모음은 내 github 프로젝트를 참조하십시오.

https://github.com/MalcolmMcLean/ieee754


이것이 기본적으로 당신이 작은 -> 빅 엔디안에서 바꾸기 위해해야 ​​할 모든 것이기 때문에 비트 쉬프트를보세요. 그런 다음 비트 크기에 따라 비트 이동 방법을 변경합니다.





endianness