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


196

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

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

편집 # 2 : 내가 받아 들인 대답은 내가 타겟팅하는 컴파일러에 직접 적용됩니다 (그래서 내가 선택한 이유). 그러나 여기에는 매우 훌륭하고 이식성이 좋은 다른 답변이 있습니다.


21
네트워킹과 관련이 없더라도 hton은 제대로 작동합니다.
벤 콜린스

2
일반적으로 엔디안을 처리하는 가장 좋은 방법은 코드가 리틀 엔드 및 빅 엔디안 호스트 시스템에서 실행되도록하는 것입니다. 그게 효과가 있다면 아마 옳았을 것입니다. x86을 사용한다고 가정하면 위험합니다.
jakobengblom2

10
기계가 빅 엔디안 인 경우 hton ntoh는 작동하지 않습니다. 질문자 (asker)는 명시 적으로 변환을 수행하려고하기 때문입니다.
fabspro

6
@ jakobengblom2만이 이것을 언급하는 사람입니다. 이 페이지의 거의 모든 예는 기본 엔디안을 무시하지 않고 "스왑"바이트와 같은 개념을 사용합니다. 엔디안이 잘 정의 된 외부 파일 형식을 다루는 경우 가장 휴대하기 쉬운 방법은 외부 데이터를 바이트 스트림으로 취급하고 바이트 스트림을 기본 정수와 변환하는 것입니다. short swap(short x)엔디 언이 다른 플랫폼으로 이동하면 코드가 손상 될 수 있으므로 코드를 볼 때마다 울었습니다. Matthieu M은 아래에 정답입니다.
Mark Lakata

3
문제가 완전히 잘못되었다고 생각하고 있습니다. 이 작업은 "빅 엔디안 값과 리틀 엔디안 값을 어떻게 변환합니까?"가 아닙니다. 작업은 "특정 형식의 부동 소수점 및 정수 값을 내 플랫폼의 기본 형식으로 변환하는 방법"입니다. 올바르게 수행하면 모든 코드 관리에 기본 형식이 빅 엔디안, 리틀 엔디안, 혼합 엔디안 또는 삼원 일 수 있습니다.
David Schwartz

답변:


166

당신이 사용하는 경우 Visual C ++를 다음을 수행 : 당신은 다음과 같은 기능을 intrin.h 포함 전화 :

16 비트 숫자의 경우 :

unsigned short _byteswap_ushort(unsigned short value);

32 비트 숫자의 경우 :

unsigned long _byteswap_ulong(unsigned long value);

64 비트 숫자의 경우 :

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 비트 숫자 (문자)는 변환 할 필요가 없습니다.

또한 이들은 부호없는 정수에 대해서만 작동합니다.

float 및 double의 경우 호스트 시스템에 바이트 순서가 있거나 없을 수 있으므로 일반 정수를 사용하는 것이 더 어렵습니다. 빅 엔디안 머신에서 리틀 엔디안 수레를 얻을 수 있으며 그 반대도 가능합니다.

다른 컴파일러도 비슷한 본질을 가지고 있습니다.

예를 들어 GCC 에서는 여기에 설명 된대로 일부 내장을 직접 호출 할 수 있습니다 .

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(무엇을 포함 할 필요가 없습니다). Afaik bits.h는 gcc 중심이 아닌 방식으로 동일한 함수를 선언합니다.

16 비트 스왑은 비트 회전입니다.

롤링 대신 내장 함수를 호출하면 최상의 성능과 코드 밀도를 얻을 수 있습니다.


11
GCC으로, 내가 사용할 수 있습니다합니다 : #include <byteswap.h> int32_t bswap_32 (int32_t x)는 int64_t bswap_64 (int64_t X)
jmanning2k

5
__builtin_bswapX이후 GCC-4.3에서만 사용할 수 있습니다
매트 소목

20
그것은이 내장 함수는 / 언제나 / 스왑 바이트, 그렇지 않은 같은 것을 주목할 가치가있다 htonl, htons등, 당신은 바이트를 실제로 교체 할 때 상황의 맥락에서 알아야합니다.
Brian Vandenberg

8
@Jason은 8 비트 숫자가 크고 작은 엔디안에서 동일하기 때문입니다. :-)
Nils Pipenbrinck

2
@BrianVandenberg 오른쪽; 사용 htonl하고 ntohl는 작은 / 중간 엔디안에 있는지를 교환 할 이러한 기능을 정의하는 플랫폼부터 휴대용 코드를 작성할 때 작동합니다 상황에 대한 걱정없이 빅 엔디안 그것은 어떤 조합 없을 것입니다. 리틀 엔디안 (BMP 말)로 정의 된 표준 파일 형식을 디코딩 그러나, 하나는 여전히 상황을 알고 있고 단지에 의존 할 수 htonlntohl.
legends2k

86

간단히 말해서:

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


3
공감하십시오. 방금 uchars를 사용하고 4 대 1, 3 대 2, 2 대 3 및 1 대 4를 할당했지만 크기가 다른 경우 더 유연합니다. 1 세대 펜티엄 IIRC에서 6 개의 시계. BSWAP는 1 클럭이지만 플랫폼에 따라 다릅니다.

2
@RocketRoy : 그렇습니다. 속도가 문제로 판명되면 플랫폼 및 유형별 intrisics로 과부하를 작성하는 것이 매우 간단합니다.
Alexandre C.

3
@MihaiTodor : 문자 배열을 통한 타입 캐스팅을위한 이러한 공용체 사용은 표준에 의해 명시 적으로 허용됩니다. 예를 들어 참조하십시오. 이 질문 .
Alexandre C.

4
@AlexandreC. C ++ 표준에는없고 C에만 있습니다.이 코드가있는 C ++에서는이 코드가 정의되지 않은 동작입니다.
Rapptz

4
@Rapptz : 3.10 : "프로그램이 다음 유형 중 하나 이외의 glvalue를 통해 객체의 저장된 값에 액세스하려고하면 동작이 정의되지 않습니다 : [...] char 또는 unsigned char type. ". 어쩌면 여기에 뭔가 빠졌지 만 char 포인터를 통해 모든 유형에 액세스하는 것이 명시 적으로 허용되었다는 것이 분명했습니다.
Alexandre C.

75

에서 바이트 주문 착오 롭 파이크에 의해 :

데이터 스트림에 리틀 엔디안 인코딩 된 32 비트 정수가 있다고 가정 해 봅시다. 부호없는 바이트를 가정하여 추출하는 방법은 다음과 같습니다.

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

빅 엔디안이라면 추출 방법은 다음과 같습니다.

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

TL; DR : 플랫폼 기본 순서에 대해 걱정하지 마십시오 . 카운트는 모두 읽고있는 스트림의 바이트 순서이며 잘 정의되어 있기를 바랍니다.

참고 : 주석에서 명시 적 유형 변환이없는 것으로 언급되었으므로 또는 data의 배열이어야합니다 . 사용 또는 (서명 경우)가 발생합니다 정수로 승진하고 잠재적 UB이다 부호 비트에 1을 이동.unsigned charuint8_tsigned charchardata[x]data[x] << 24


5
이것은 멋지지만 정수와 변형에만 적용되는 것 같습니다. 수레 / 복식으로 무엇을해야합니까?
Brett

1
@ v.oddou : 예, 아니오, 메모리 매핑 된 파일은 네트워크 프레임과 정확히 동일합니다. 당신이 동의하는 경우 없는 문제가 모든 것을 직접 읽고 자신의 엔디 언을 : 그것은 빅 엔디안인지 리틀 엔디안은, 첫 번째 공식을 사용하는 경우, 두 번째를 사용합니다. 엔디안이 일치하는 경우 그 가치가있는 컴파일러는 불필요한 변환을 최적화합니다.
Matthieu M.

2
@meowsqueak : 예, 바이트 순서 만 바뀌고 각 바이트 내의 비트 순서는 바뀌지 않기 때문에 작동합니다.
Matthieu M.

3
느슨하게 관련된 메모에서 링크 된 게시물은 불쾌한 읽기입니다 ... 남자는 간결함을 소중히 여기는 것처럼 보이지만 실제로 엔디안과 관련하여 밝지 않은 나쁜 프로그래머 모두에 대해 긴 분노를 쓰는 것을 선호했습니다. 상황을 설명하고 그의 솔루션이 항상 작동하는 이유.
Ad N

1
이 방법을 사용하는 경우 데이터를 (서명되지 않은 char *)로 전송해야합니다.
joseph

51

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

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 솔루션 중 하나가 제대로 작동합니다.


2
네트워크 바이트 순서는 내가 믿는 큰 엔디안입니다. 네트워크 코드를 사용하지 않더라도 이러한 기능을 염두에두고 사용할 수 있습니다. 그러나 ntohf 또는 htonf 플로트 버전은 없습니다
Matt

2
Matt H. 그것은 대부분 정확합니다. 모든 컴퓨터 시스템에 리틀 엔디안 바이트 순서가있는 것은 아닙니다. 모토로라 68k, PowerPC 또는 다른 빅 엔디안 아키텍처로 작업하고 있다면 이러한 기능은 이미 '네트워크 바이트 순서에 있기 때문에 바이트를 전혀 교환하지 않습니다.
Frosty

2
불행하게도, htonl그리고 ntohl빅 엔디안 플랫폼에서 리틀 엔디안에 갈 수 없습니다.
Brian Vandenberg

2
@celtschk, 이해; 그러나 OP는 빅 엔디안 환경에서도 엔디안을 전환하는 방법을 원합니다.
브라이언 반덴버그

4
불가피한 질문을 피하기 위해 : BE 플랫폼에 LE가 필요한 이유는 여러 가지가있다. 여러 파일 형식 (bmp, fli, pcx, qtm, rtf, tga)은 거의 엔디안 값을 사용하지 않습니다.
브라이언 반덴버그

26

나는이 게시물에서 몇 가지 제안을하고 이것을 구성하기 위해 함께 모았습니다.

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

당신은 또한 포함해야 <cstdint> 또는 <stdint.h> 예를 들어, uint32_t에 대한
ADY

17

빅 엔디안에서 리틀 엔디안으로가는 절차는 리틀 엔디안에서 빅 엔디안으로가는 절차와 동일합니다.

예제 코드는 다음과 같습니다.

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

2
여기에 게시 된 마지막 함수는 올바르지 않으므로 다음과 같이 편집해야합니다. void swapByteOrder (unsigned long long & ull) {ull = (ull >> 56) | ... (ull << 56); }
Eric Burnett

14
비트 및 (&)가 아닌 논리 및 (&&)를 사용하는 것이 옳지 않다고 생각합니다. C ++ 사양에 따르면 두 피연산자는 모두 암시 적으로 bool로 변환되며 이는 원하는 것이 아닙니다.
트레버 로빈슨

16

BSWAP라는 조립 명령이 있으며, 스왑을 매우 빠르게 수행 합니다. 여기에서 읽을 수 있습니다 .

Visual Studio 또는보다 정확하게는 Visual C ++ 런타임 라이브러리에는이를위한 플랫폼 내장 기능이 _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()있습니다. 다른 플랫폼에서도 비슷해야하지만 플랫폼이 무엇인지 알지 못합니다.


좋은 연결입니다. x86 어셈블러에 관심을 가지게되었습니다.
PP.

1
BSWAP의 타이밍 결과가 여기에 표시됩니다. gmplib.org/~tege/x86-timing.pdf ... 그리고 여기 ... agner.org/optimize/instruction_tables.pdf

12

템플릿으로이 작업을 수행했습니다. 다음과 같이 할 수 있습니다.

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

8

다른 플랫폼간에 데이터를 전송하기 위해이 작업을 수행하는 경우 ntoh 및 hton 기능을 살펴보십시오.


7

C에서와 같은 방식으로

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

부호없는 문자로 구성된 벡터를 선언하고 입력 값을 memcpy로 입력하고 다른 벡터로 바이트를 되돌리고 바이트를 memcpy로 변환 할 수 있지만 특히 64 비트 값의 경우 비트 트위들 링보다 수십 배 더 오래 걸립니다.


7

대부분의 POSIX 시스템 (POSIX 표준이 아님)에는 endian.h가 있으며 시스템에서 어떤 인코딩을 사용하는지 결정하는 데 사용할 수 있습니다. 거기에서 다음과 같은 것이 있습니다.

unsigned int change_endian(unsigned 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 (및 그 반대의 ntohs, ntohl 및 ntohll)이 호스트 순서에서 네트워크 순서로 변환하는 데 도움이됩니다.


6
재밌습니다. opengroup.org/onlinepubs/9699919799/toc.htm 의 POSIX 표준 에는 헤더 '<endian.h>`가 없습니다.
Jonathan Leffler

1
htonl유스 케이스가 네트워킹과 관련이 있는지 여부에 관계없이 사용 하고 친구를 사 can 수 있습니다 . 네트워크 바이트 순서는 빅 엔디안이므로 해당 함수를 host_to_be 및 be_to_host로 취급하십시오. (하지만 host_to_le이 필요한 경우에는 도움이되지 않습니다.)
Peter Cordes

5

적어도 Windows의 경우 htonl ()은 고유 한 _byteswap_ulong ()보다 훨씬 느립니다. 전자는 ws2_32.dll에 대한 DLL 라이브러리 호출이고 후자는 BSWAP 어셈블리 명령 중 하나입니다. 따라서 플랫폼 종속 코드를 작성하는 경우 속도에 내장 함수를 사용하는 것이 좋습니다.

#define htonl(x) _byteswap_ulong(x)

이것은 모든 정수가 Big Endian에 저장된 .PNG 이미지 처리에 특히 중요 할 수 있습니다.


4

대부분의 플랫폼에는 효율적인 바이트 스왑 기능을 제공하는 시스템 헤더 파일이 있습니다. Linux에서는에 <endian.h>있습니다. C ++로 멋지게 포장 할 수 있습니다.

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

산출:

cafe
deadbeaf
feeddeafbeefcafe

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

감사합니다. betoh () 및 letoh ()를 테스트하는 것을 잊었습니다.
Maxim Egorushkin

4

나는 스타일을 위해 이것을 좋아한다 :-)

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

나는에 오류가 char[]'오류 : 불완전한 유형은 허용되지 않습니다'라고
포틀랜드 러너를

4

진심으로 ... 나는 모든 솔루션이 왜 그렇게 복잡한 지 이해하지 못한다 ! 어떤 운영 체제의 환경에서도 모든 유형의 크기를 바꾸는 가장 단순하고 일반적인 템플릿 기능은 어떻습니까?

template <typename T>
void SwapEnd(T& var)
{
    static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
    std::array<char, sizeof(T)> varArray;
    std::memcpy(varArray.data(), &var, sizeof(T));
    for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
        std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    std::memcpy(&var, varArray.data(), sizeof(T));
}

C와 C ++의 마법의 힘입니다! 원래 변수를 문자별로 바꾸면됩니다.

포인트 1 : 연산자 없음 : 엔디안이 반전되고 복사 생성자 (또는 할당 연산자)가 작동하지 않을 때 일부 개체가 엉망이되기 때문에 간단한 할당 연산자 "="를 사용하지 않았습니다. 따라서 문자별로 문자를 복사하는 것이 더 안정적입니다.

포인트 2 : 정렬 문제에 유의하십시오 : C ++ 컴파일러가 정렬되지 않은 메모리에 액세스 할 수 있음을 보장하지 않기 때문에 배열로 복사하거나 배열에서 복사하는 것이 좋습니다.이 답변은 원래 메모리에서 업데이트되었습니다. 이것에 대한 양식). 예를 들어, 당신이 할당한다면 uint64_t, 컴파일러는 그것의 3 번째 바이트에 접근 할 수 있다고 보장 할 수 없습니다 uint8_t. 따라서 올바른 작업은 이것을 char 배열에 복사하고 스왑 한 다음 다시 복사하는 것입니다 (no no reinterpret_cast). 컴파일러는 reinterpret_cast정렬에 관계없이 개별 바이트에 액세스 할 수 있는 경우에 수행 한 작업을 다시 변환 할 수있을 정도로 똑똑 합니다.

이 기능을 사용하려면 :

double x = 5;
SwapEnd(x);

이제 x엔디안이 다릅니다.


2
이것은 어디에서나 작동하지만, 생산 된 어셈블리 ocde는 차선책 일 것입니다 : 내 질문을보십시오 stackoverflow.com/questions/36657895/…
j_kubik

new/ delete를 사용 하여 버퍼를 할당합니까?!? sizeof(var)컴파일 타임 상수이므로 할 수 있습니다 char varSwapped[sizeof(var)]. 또는 char *p = reinterpret_cast<char*>(&var)제자리에서 교환하고 교체 할 수 있습니다.
Peter Cordes

@ 피터이 대답은 빠르고 더러운 것이 포인트를 증명하기 위해 만들어졌습니다. 제안을 이행하겠습니다. 그러나, 당신은 거기에 주어진 50- 라인 솔루션에 비해 메가 SO AH가 될 필요는없고 5- 라인 솔루션을 다운-투표 할 필요는 없습니다. 더 이상 말하지 않겠다
양자 물리학 자

이 답변은 잘못된 엔디안 데이터에서 생성자와 오버로드 된 연산자에주의를 기울이는 데 유용한 포인트를 제공하므로 코드가 끔찍하지 않으면 다운 코드를 제거하고 기뻐합니다. 좋은 컴파일러는 bswap으로 컴파일 할 수 있습니다. 교수. 또한을 for(size_t i = 0 ; i < sizeof(var) ; i++)대신 사용 하는 것이 좋습니다 static_cast<long>. 또는 실제로 내부 스왑은 오름차순과 내림차순을 사용 char*하므로 어쨌든 사라집니다.
Peter Cordes

예를 들어 std :: swap을 사용하여 Mark Ransom의 답변 을 바꾸어 현재 위치로 되돌립니다.
Peter Cordes

3

이 코드를 사용하여 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);
}

3

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

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

다음은 바이트를 리틀 엔디안에서 빅 엔디안으로 또는 그 반대로 바꾸는 편리한 #define 함수입니다.

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

2

여기에 가치를 교환하기 위해 머리 꼭대기에서 나온 일반화 된 버전이 있습니다. 성능에 문제가 있으면 다른 제안이 더 좋습니다.

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

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


2

단어의 비트 순서를 반대로 바꾸는 공통 패턴을 취하고 각 바이트 내에서 비트를 반전시키는 부분을 제거하면 단어 내에서 바이트를 반전시키는 무언가가 남습니다. 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 프로세서에서 크고 복잡한 상수는 컴파일러 문제를 일으킬 수 있습니다. 그래도 이전 상수에서 각 상수를 간단하게 계산할 수 있습니다. 이렇게 :

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

2

어디에서 보지 못했기 때문에 여기에 내 솔루션을 추가했다고 생각했습니다. 비트 연산 만 사용하는 작고 이식 가능한 C ++ 템플릿 함수 및 이식 가능합니다.

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

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


2

아래 코드를 사용하면 BigEndian과 LittleEndian을 쉽게 전환 할 수 있습니다

#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

최근에 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 ++ 언어 모두에 이식 가능합니다.


일부 코드에서 이것을 발견했습니다. 도대체 나를 혼란스럽게했다. 설명 주셔서 감사합니다. 그러나 왜 사용 __VA_ARGS__합니까?
asr9

0

와우, 내가 여기서 읽은 답변 중 일부를 믿을 수 없었습니다. 실제로 어셈블리에는 다른 것보다 더 빠른 명령이 있습니다. bswap. 당신은 단순히 이와 같은 함수를 작성할 수 있습니다 ...

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

그것은이다 훨씬 빨리 제안 된 내장 함수보다. 나는 그들을 분해하고 보았다. 위 함수에는 프롤로그 / 에필로그가 없으므로 사실상 오버 헤드가 없습니다.

unsigned long _byteswap_ulong(unsigned long value);

xchg al, ah를 사용하는 것을 제외하고는 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 코드에서만 사용할 수 있습니다. 네이 키드 함수는 줄 수 없으며 x64 빌드에서는 사용할 수 없습니다. 그런 경우에는 컴파일러 내장 함수를 사용해야합니다.


1
_byteswap_ulong그리고 _uint64(예를 들어 허용 된 답변에서) bswap명령어 를 사용하도록 컴파일합니다 . 놀랍지 만 프롤로그 / 에필로그 만 생략했을 때이 asm이 훨씬 더 빠른지 알고 싶습니다. 벤치마킹 했습니까?
ZachB

@stdcall이 질문은 휴대용 솔루션을 요구하지 않았거나 플랫폼에 대해 언급하지 않았습니다. 내 대답이 말했듯이 위의 내용은 엔디안 스왑을하는 가장 빠른 방법입니다. 물론, X86이 아닌 플랫폼에서 이것을 작성하면 작동하지 않지만, 앞서 언급했듯이 컴파일러가 지원하는 경우 컴파일러 내장 함수로 제한됩니다.
용접기

@ZachB이 특별한 경우에, 프롤로그와 에필로그를 생략하면 본질적으로 단 하나의 명령 만 실행하기 때문에 상당한 절약 효과를 얻을 것이라고 생각합니다. 프롤로그는 스택으로 밀고 빼기를 수행하고베이스 포인터를 설정 한 다음 끝에서 유사해야합니다. 나는 그것을 벤치마킹하지는 않았지만, 위의 0은 의존성 체인을 가지고 있기 때문에 알몸이되지 않고서는 얻을 수 없습니다. 어쩌면 좋은 컴파일러가 그것을 인라인 할 수도 있지만 다른 볼 파크에 있습니다.
용접기

2
혹시. 그러나 숫자 배열을 교체하는 일반적인 경우 다른 답변에서 논의 된 컴파일러 내장 함수는 SSE / AVX 확장을 사용하고 BSHUAP를 능가하는 PSHUFB를 생성합니다. 참조 wm.ite.pl/articles/reverse-array-of-bytes.html
ZachB

OP가 x86 용 솔루션 만 필요로 지정하지 않았을 때 플랫폼 별 솔루션을 게시하는 것은 IMHO 형식이 좋지 않습니다. 그리고 iOS 및 Android (ARM 또는 MIPS CPU를 사용하는)와 같이 매우 널리 사용되는 많은 OS에서 사용할 수없는 경우 다른 솔루션을 비난하기 위해
Jens Alfke

0

최적화 프로그램에 적합하지 않은 비인 플레이스 엔디 언 접근자를 구현하기위한 휴대용 기술. 모든 컴파일러, 모든 경계 정렬 및 모든 바이트 순서에서 작동합니다. 이러한 정렬되지 않은 루틴은 기본 엔디안 및 정렬에 따라 보완되거나 완화됩니다. 부분 리스팅이지만 아이디어를 얻습니다. 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 */

2
이 질문에서 C ++ 태그는 차이를 만듭니다. C ++과 공용체로 인해 정의되지 않은 동작이 많이 있습니다.
jww

0

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

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


0

템플릿 함수의 피벗 주위에 오래된 3 단계 xor 트릭을 사용하여 바이트 스와핑하면 라이브러리가 필요없는 유연하고 빠른 O (ln2) 솔루션이 제공되며 여기 스타일은 1 바이트 유형을 거부합니다.

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

안전한 방법은 각 단어에 hton을 사용하는 것 같습니다. 그래서, 당신이 가지고 있다면 ...

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

빅 엔디안 시스템을 사용하는 경우 위의 내용은 no-op 일 것이므로 htons가 no-op인지 여부를 결정하기 위해 플랫폼이 컴파일 타임 조건으로 사용하는 것을 찾으십시오. 결국은 O (n)입니다. Mac에서는 다음과 같습니다.

#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

C ++ 17 인 경우이 헤더를 추가하십시오.

#include <algorithm>

이 템플릿 함수를 사용하여 바이트를 교체하십시오.

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

다음과 같이 호출하십시오.

swapEndian (stlContainer);

-4

비트 시프트를 찾으십시오. 이것은 기본적으로 little-> big endian에서 교체하기 위해해야 ​​할 모든 것입니다. 그런 다음 비트 크기에 따라 비트 이동 방식을 변경합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.