如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
如果您这样做是为了在不同平台之间传输数据,请查看ntoh和hton函数。
其他回答
来这里寻找一个Boost解决方案,失望地离开,但最终在其他地方找到了它。你可以使用boost::endian::endian_reverse。它被模板化/重载了所有的基元类型:
#include <iostream>
#include <iomanip>
#include "boost/endian/conversion.hpp"
int main()
{
uint32_t word = 0x01;
std::cout << std::hex << std::setfill('0') << std::setw(8) << word << std::endl;
// outputs 00000001;
uint32_t word2 = boost::endian::endian_reverse(word);
// there's also a `void ::endian_reverse_inplace(...) function
// that reverses the value passed to it in place and returns nothing
std::cout << std::hex << std::setfill('0') << std::setw(8) << word2 << std::endl;
// outputs 01000000
return 0;
}
示范
虽然,看起来c++23最终用std::byteswap解决了这个问题。(我使用的是c++17,所以这不是一个选项。)
如果您这样做是为了在不同平台之间传输数据,请查看ntoh和hton函数。
实现优化器友好的未对齐非就地末端访问器的可移植技术。它们处理每个编译器、每个边界对齐和每个字节排序。这些未对齐的例程被补充或讨论,取决于本机的端序和对齐方式。部分列出,但你懂的。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];
}
如果不与访问器一起使用,这些类型def的好处是会引发编译器错误,从而减少被遗忘的访问器错误。
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 */
我只是想在这里添加我自己的解,因为我在任何地方都没有看到它。它是一个小而可移植的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;
}
如果一个大端位32位无符号整数看起来像0xAABBCCDD,它等于2864434397,那么同样的32位无符号整数在小端位处理器上看起来像0xDDCCBBAA,它也等于2864434397。
如果一个大端序16位无符号空头看起来像0xAABB,它等于43707,那么同一个16位无符号空头在小端序处理器上看起来像0xBBAA,它也等于43707。
这里有两个方便的#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))