如何编写(便携式)反向网络字节顺序?

问题描述 投票:10回答:3

背景

设计二进制文件格式时,通常建议以网络字节顺序写入整数。为此,存在诸如htonhl()的宏。但是对于WAV之类的格式,实际上使用了小尾数格式。

问题

[[无论代码运行的CPU是大端还是小端架构,您如何都可移植地写入小端值?(想法:可以使用标准宏ntohl()htonl()“相反”,还是代码应该只是测试运行时(如果运行在小端或大端CPU上并选择适当的代码路径?)

因此,问题实际上与文件格式无关,文件格式仅是示例。它可以是任何需要“在线上”很少字节序的序列化,例如(异端)网络协议。

c++ c portability endianness
3个回答
19
投票

警告:这仅适用于无符号整数,因为有符号的右移是在实现中定义的,并且可能导致漏洞(https://stackoverflow.com/a/7522498/395029

C已经提供了主机字节序的抽象:

number†或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);


†只有数字表示为字节序列时才具有字节序;数字(即数量)不是。

5
投票
这是基于模板的版本:

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


2
投票
实际上,MSDN函数ntohl()和htonl()

are >>彼此相反:

htonl函数将u_long从主机转换为TCP / IP网络字节 顺序(大端)。

ntohl函数将u_long从TCP / IP网络顺序转换为主机 字节顺序(在Intel处理器上为little-endian)。

是的,runtime detecting endianness是一件非常明智的事情,基本上是任何即时可用的宏/函数在任何时候都将要做的事情。

并且,如果您想自己进行小的字节序转换,请参阅@ R-Martinho-Fernandes的回答。

© www.soinside.com 2019 - 2024. All rights reserved.