如何编写(可移植的)反向网络字节顺序
How do you write (portably) reverse network byte order?
背景
在设计二进制文件格式时,通常建议按网络字节顺序写入整数。为此,有像htonhl()
这样的宏。但对于WAV这样的格式,实际上使用的是little-endian格式。
问题
无论代码运行的CPU是大端序还是小端序架构,如何便携地编写小端序值(想法:标准宏ntohl()
和htonl()
可以以某种方式"反向"使用吗?或者,如果代码在小端或大端CPU上运行,则应该测试运行时并选择合适的代码路径吗?(
所以问题并不是关于文件格式,文件格式只是一个例子。它可以是任何类型的串行化,其中需要"在线"的小端序,例如(异端(网络协议
警告:这只适用于无符号整数,因为有符号右移是实现定义的,可能会导致漏洞(https://stackoverflow.com/a/7522498/395029)
C已经提供了对主机端序的抽象:数字†或int†。
在给定的字节序中产生输出可以很容易地完成,而不必试图变得聪明:只需将数字解释为数字,并使用移位来提取每个字节:
uint32_t value;
uint8_t lolo = (value >> 0) & 0xFF;
uint8_t lohi = (value >> 8) & 0xFF;
uint8_t hilo = (value >> 16) & 0xFF;
uint8_t hihi = (value >> 24) & 0xFF;
然后你只需要按照你想要的顺序来写字节。
当你将具有某些字节序的字节序列作为输入时,你可以通过比特运算再次构造数字来在主机的字节序中重建它们:
uint32_t value = (hihi << 24)
| (hilo << 16)
| (lohi << 8)
| (lolo << 0);
&匕首;只有作为字节序列的数字表示才具有端序性;数字(即数量(则不然。
这里有一个基于模板的版本:
#include <iostream>
#include <iomanip>
enum endianness_t {
BIG, // 0x44332211 => 0x44 0x33 0x22 0x11
LITTLE, // 0x44332211 => 0x11 0x22 0x33 0x44
UNKNOWN
};
const uint32_t test_value = 0x44332211;
const bool is_little_endian = (((char *)&test_value)[0] == 0x11) && (((char *)&test_value)[1] == 0x22);
const bool is_big_endian = (((char *)&test_value)[0] == 0x44) && (((char *)&test_value)[1] == 0x33);
const endianness_t endianness =
is_big_endian ? BIG:
(is_little_endian ? LITTLE : UNKNOWN);
template <typename T>
T identity(T v){
return v;
}
// 16 bits values ------
uint16_t swap_(uint16_t v){
return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
}
// 32 bits values ------
uint32_t swap_(uint32_t v){
return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
}
template <typename T, endianness_t HOST, endianness_t REMOTE>
struct en_swap{
static T conv(T v){
return swap_(v);
}
};
template <typename T>
struct en_swap<T, BIG, BIG>{
static T conv(T v){
return v;
}
};
template <typename T>
struct en_swap<T, LITTLE, LITTLE> {
static T conv(T v){
return v;
}
};
template <typename T>
T to_big(T v) {
switch (endianness){
case LITTLE :
return en_swap<T,LITTLE,BIG>::conv(v);
case BIG :
return en_swap<T,BIG,BIG>::conv(v);
}
}
template <typename T>
T to_little(T v) {
switch (endianness){
case LITTLE :
return en_swap<T,LITTLE,LITTLE>::conv(v);
case BIG :
return en_swap<T,BIG,LITTLE>::conv(v);
}
}
int main(){
using namespace std;
uint32_t x = 0x0ABCDEF0;
uint32_t y = to_big(x);
uint32_t z = to_little(x);
cout << hex << setw(8) << setfill('0') << x << " " << y << " " << setw(8) << setfill('0') << z << endl;
}
事实上,MSDN函数ntohl((和htonl((是彼此的逆函数:
htonl函数将u_long从主机转换为TCP/IP网络字节订单(big-endian(。
ntohl函数将u_long从TCP/IP网络顺序转换为主机字节顺序(在英特尔处理器上是小端序(。
是的,运行时检测端序是一件非常明智的事情,基本上是任何现成的宏/函数在某个时候都会做的事情。
若你们想自己进行小-大端转换,请参阅@R-Martinho-Fernandes的回答。
- 将Integer转换为4字节的unsined字符矢量(按大端字节顺序)
- C++ int* 和 char* 字节顺序已交换
- 交换未定义数据类型中的字节顺序
- winsock上的蓝牙,如何删除字节顺序标记
- 我应该考虑网络字节订单吗?
- 将网络字节顺序(大字节序)转换为小字节序
- 将主机字节顺序转换为网络字节顺序有时会导致奇怪的结果
- 当在网络字节顺序中放置双精度时,为什么它是在 4 字节块中完成的
- 使用mysql本机函数进行字节顺序转换
- C++:如何交换wchar_t的字节顺序
- reinterpret_cast接收端的'serializing'数据、字节顺序和对齐方式
- 到Arduino的串行通信的字节顺序
- 使用不带Unicode字节顺序标记的iconv进行字符转换
- 从Byte*到unsigned int的memcpy正在反转字节顺序
- 忽略C++中的字节顺序标记,从流中读取
- 如何连接到 sock4 客户端的网络字节地址?
- iPhone TCP-IP字节顺序映射
- 如何编写(可移植的)反向网络字节顺序
- 网络字节顺序和字节序问题
- UDP 传输和维护网络字节顺序