#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) .
61
来自Rob Pyke的 The Byte Order Fallacy :
假设您的数据流具有小端编码的32位整数 . 以下是如何提取它(假设无符号字节):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
如果它是big-endian,这里是如何提取它:
i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);
TL;DR: don 't worry about your platform native order, all that counts is the byte order of the stream your are reading from, and you better hope it'已明确定义 .
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)
说真的......我不明白为什么所有的解决方案都是 complicated ! How about the simplest, most general template function that swaps any type of any size under any circumstances in any operating system????
// 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);
}
// 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
这是我想出的一个通用版本,用于交换值 . 如果性能问题,其他建议会更好 .
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]);
}
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) 块中 . 这是一个common idiom,允许在宏之后使用正常的分号 .
接下来是使用名为 REVERSE_BYTES 的变量作为 for 循环's counter. The name of the macro itself is used as a variable name to ensure that it doesn' t与使用宏的任何其他符号进行冲突 . 由于该名称正在宏_572374中使用,因此在此处用作变量名时将再次展开 .
28 回答
我以为我在这里添加了自己的解决方案,因为我没有在任何地方看到它 . 它是一个小巧便携的C模板化功能,便于使用位操作 .
如果您正在使用 Visual C++ ,请执行以下操作:您包含intrin.h并调用以下函数:
对于16位数字:
对于32位数字:
对于64位数字:
不需要转换8位数字(字符) .
此外,这些仅针对无符号值定义,它们也适用于有符号整数 .
对于浮点数和双精度数,它与普通整数一样困难,因为这些可能与否可能在主机的字节顺序中 . 您可以在big-endian机器上获得little-endian浮点数,反之亦然 .
其他编译器也有类似的内在函数 .
例如,在 GCC 中您可以直接调用:
(不需要包含某些内容) . Afaik bits.h也以非gcc为中心的方式声明了相同的功能 .
16位交换它只是一个位旋转 .
调用内在函数而不是自己编辑可以获得最佳性能和代码密度 .
简单的说:
用法:
swap_endian<uint32_t>(42)
.来自Rob Pyke的 The Byte Order Fallacy :
TL;DR: don 't worry about your platform native order, all that counts is the byte order of the stream your are reading from, and you better hope it'已明确定义 .
注意:注释中没有显式类型转换,重要的是
data
是unsigned char
或uint8_t
的数组 . 使用signed char
或char
(如果已签名)将导致data[x]
被提升为整数,并且data[x] << 24
可能将1移入符号位(UB) .如果您出于网络/主机兼容性的目的这样做,您应该使用:
如果你出于其他原因这样做,这里提出的byte_swap解决方案之一就可以正常工作 .
我从这篇文章中提出了一些建议并将它们组合在一起形成:
有一个名为BSWAP的汇编指令,可以非常快速地为您进行交换 . 你可以阅读它here .
Visual Studio,或者更确切地说是Visual C运行时库,具有此平台内在函数,称为
_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. 其他平台应该存在类似的情况,但我不知道它们会被称为什么 .从big-endian到little-endian的过程与从little-endian到big-endian的过程相同 .
这是一些示例代码:
我们用模板完成了这个 . 你可以这样:
如果您这样做是为了在不同平台之间传输数据,请查看ntoh和hton函数 .
与C中的方式相同:
您还可以声明一个无符号字符向量,将输入值memcpy到其中,将字节转换为另一个向量并将字节记忆输出,但这将比使用64位值的位数长几个数量级 .
在大多数POSIX系统上(通过它不在POSIX标准中)有endian.h,它可用于确定系统使用的编码 . 从那里它是这样的:
这会交换顺序(从big-endian到little endian):
如果您的数字为0xDEADBEEF(在小端系统上存储为0xEFBEADDE),则ptr [0]将为0xEF,ptr [1]为0xBE等 .
但是如果你想将它用于网络,那么htons,htonl和htonll(以及它们的反向ntohs,ntohl和ntohll)将有助于从主机顺序转换为网络顺序 .
请注意,至少对于Windows,htonl()比它们的内部对应物_byteswap_ulong()要慢得多 . 前者是一个DLL库调用ws2_32.dll,后者是一个BSWAP汇编指令 . 因此,如果您正在编写一些与平台相关的代码,请更喜欢使用内在函数来提高速度:
这对于.PNG图像处理尤其重要,其中所有整数都保存在Big Endian中,并解释“一个人可以使用htonl()...”{以减慢典型的Windows程序,如果你没有准备的话} .
大多数平台都有一个系统头文件,可以提供有效的字节交换功能 . 在Linux上它位于
<endian.h>
. 你可以在C中很好地包装它:输出:
我喜欢这个,只是为了风格:-)
说真的......我不明白为什么所有的解决方案都是 complicated ! How about the simplest, most general template function that swaps any type of any size under any circumstances in any operating system????
这是C和C的神奇力量!只需将原始变量字符交换为字符 .
请记住,我没有使用简单赋值运算符“=”,因为当翻转字节序并且复制构造函数(或赋值运算符)不起作用时,某些对象将被搞砸 . 因此,通过char复制它们更可靠 .
要打电话,只需使用
现在
x
在字节序上有所不同 .我有这个代码,允许我从HOST_ENDIAN_ORDER(不管它是什么)转换为LITTLE_ENDIAN_ORDER或BIG_ENDIAN_ORDER . 我使用模板,所以如果我尝试从HOST_ENDIAN_ORDER转换为LITTLE_ENDIAN_ORDER并且它们恰好与我编译的机器相同,则不会生成任何代码 .
这是带有一些注释的代码:
如果big-endian 32位无符号整数看起来像0xAABBCCDD等于2864434397,那么相同的32位无符号整数在小端处理器上看起来像0xDDCCBBAA,它也等于2864434397 .
如果big-endian 16位无符号短路看起来像0xAABB等于43707,则相同的16位无符号短路看起来像小端处理器上的0xBBAA,也等于43707 .
这里有几个方便的#define函数可以将字节从little-endian交换到big-endian,反之亦然 - >
这是我想出的一个通用版本,用于交换值 . 如果性能问题,其他建议会更好 .
Disclaimer: 我还没有尝试编译或测试它 .
如果你采用公共模式来反转一个字中的位的顺序,并且剔除在每个字节内反转位的部分,那么你只剩下一些只能反转字内字节的东西 . 对于64位:
编译器应该清除多余的位掩码操作(我将它们留下来突出显示模式),但如果没有,你可以用这种方式重写第一行:
这通常应简化为大多数体系结构上的单个旋转指令(忽略整个操作可能是一条指令) .
在RISC处理器上,大而复杂的常量可能会导致编译器出现问题 . 但是,您可以轻松地计算前一个常量中的每个常量 . 像这样:
如果你愿意,你可以把它写成一个循环 . 它不会有效,但只是为了好玩:
为了完整起见,这是第一种形式的简化32位版本:
使用下面给出的代码,您可以轻松地在BigEndian和LittleEndian之间进行切换
我真的很惊讶没有人提到htobeXX和betohXX功能 . 它们在endian.h中定义,与网络函数htonXX非常相似 .
哇,我简直不敢相信我在这里读过的一些答案 . 实际上,汇编中的指令比其他任何东西都更快 . BSWAP . 你可以简单地写一个像这样的函数......
它比建议的内在函数快得多 . 我把它们拆开然后看了看 . 上面的功能没有序言/结尾,所以几乎没有任何开销 .
做16位同样容易,除了你使用xchg al,啊 . bswap仅适用于32位寄存器 .
64位有点棘手,但并不过分 . 比上面所有带有循环和模板等的例子要好得多 .
这里有一些注意事项......首先,bswap仅适用于80x486 CPU及以上版本 . 是否有人计划在386上运行它?!?如果是这样,你仍然可以用...替换bswap
内联汇编仅适用于Visual Studio中的x86代码 . 裸函数不能排列,也不能在x64版本中使用 . 那个例子,你将不得不使用编译器内在函数 .
用于实现优化器友好的未对齐非现场端访问器的便携式技术 . 它们适用于每个编译器,每个边界对齐和每个字节排序 . 这些未对齐的例程根据本机字节序和对齐方式进行补充或提出 . 部分列表,但你明白了 . BO *是基于本机字节排序的常量值 .
如果不与访问器一起使用,这些typedef具有引发编译器错误的好处,从而减轻了被遗忘的访问器错误 .
我最近在C中编写了一个宏来执行此操作,但它在C中同样有效:
它接受任何类型并反转传递的参数中的字节 . 示例用法:
哪个印刷品:
以上是完全复制/粘贴功能,但这里有很多内容,所以我将分解它的工作原理:
第一个值得注意的事情是整个宏被封装在
do while(0)
块中 . 这是一个common idiom,允许在宏之后使用正常的分号 .接下来是使用名为
REVERSE_BYTES
的变量作为for
循环's counter. The name of the macro itself is used as a variable name to ensure that it doesn' t与使用宏的任何其他符号进行冲突 . 由于该名称正在宏_572374中使用,因此在此处用作变量名时将再次展开 .在
for
循环中,有两个字节被引用,XOR swapped(因此不需要临时变量名):VA_ARGS表示给宏的任何内容,并用于增加可传递的内容的灵活性(尽管不是很多) . 然后获取该参数的地址并转换为
unsigned char
指针,以允许通过数组[]
下标交换其字节 .最后一个特点是缺乏
{}
大括号 . 它们不是必需的,因为每个交换中的所有步骤都与comma operator连接,使它们成为一个语句 .最后,值得注意的是,如果速度是首要任务,这不是理想的方法 . 如果这是一个重要因素,其他答案中引用的某些特定于类型的宏或特定于平台的指令可能是更好的选择 . 但是,这种方法可以移植到所有类型,所有主要平台以及C和C语言 .
试试
Boost::endian
,不要自己实施!这是link
以下是如何读取以IEEE 754 64位格式存储的双精度,即使您的主机使用不同的系统 .
对于函数套件的其余部分,包括write和整数例程,请参阅我的github项目
https://github.com/MalcolmMcLean/ieee754
查找位移,因为这基本上是你需要做的从小 - >大端交换 . 然后,根据位大小,您可以更改位移的方式 .