C ++에서 빅 엔디안과 리틀 엔디안 값을 어떻게 변환합니까?
편집 : 명확성을 위해 이진 데이터 (배정 밀도 부동 소수점 값 및 32 비트 및 64 비트 정수)를 한 CPU 아키텍처에서 다른 CPU 아키텍처로 변환해야합니다. 이것은 네트워킹과 관련이 없으므로 ntoh () 및 이와 유사한 함수는 여기서 작동하지 않습니다.
편집 # 2 : 내가 받아 들인 대답은 내가 타겟팅하는 컴파일러에 직접 적용됩니다 (그래서 내가 선택한 이유). 그러나 여기에는 매우 훌륭하고 이식성이 좋은 다른 답변이 있습니다.
답변
당신이 사용하는 경우 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 비트 스왑은 비트 회전입니다.
롤링 대신 내장 함수를 호출하면 최상의 성능과 코드 밀도를 얻을 수 있습니다.
답변
간단히 말해서:
#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)
.
답변
에서 바이트 주문 착오 롭 파이크에 의해 :
데이터 스트림에 리틀 엔디안 인코딩 된 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 char
uint8_t
signed char
char
data[x]
data[x] << 24
답변
네트워크 / 호스트 호환성을 위해이 작업을 수행하는 경우 다음을 사용해야합니다.
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 솔루션 중 하나가 제대로 작동합니다.
답변
나는이 게시물에서 몇 가지 제안을하고 이것을 구성하기 위해 함께 모았습니다.
#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);
}
답변
빅 엔디안에서 리틀 엔디안으로가는 절차는 리틀 엔디안에서 빅 엔디안으로가는 절차와 동일합니다.
예제 코드는 다음과 같습니다.
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라는 조립 명령이 있으며, 스왑을 매우 빠르게 수행 합니다. 여기에서 읽을 수 있습니다 .
Visual Studio 또는보다 정확하게는 Visual C ++ 런타임 라이브러리에는이를위한 플랫폼 내장 기능이 _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
있습니다. 다른 플랫폼에서도 비슷해야하지만 플랫폼이 무엇인지 알지 못합니다.