在我看来,类似函数的主要缺点htonl()
是它们只完成了序列化工作的一半。如果您的机器是小端序,它们只会翻转多字节整数中的字节。序列化时必须完成的另一件重要事情是处理对齐,而这些函数不这样做。
许多 CPU 无法(有效地)访问未存储在地址不是整数字节大小的倍数的内存位置的多字节整数。这就是永远不要使用结构覆盖来(反)序列化网络数据包的原因。我不确定这是否是您所说的“就地转换”。
我经常使用嵌入式系统,并且我在自己的库中使用了这些函数,我在生成或解析网络数据包(或任何其他 I/O:磁盘、RS232 等)时总是使用这些函数:
/* Serialize an integer into a little or big endian byte buffer, resp. */
void SerializeLeInt(uint64_t value, uint8_t *buffer, size_t nrBytes);
void SerializeBeInt(uint64_t value, uint8_t *buffer, size_t nrBytes);
/* Deserialize an integer from a little or big endian byte buffer, resp. */
uint64_t DeserializeLeInt(const uint8_t *buffer, size_t nrBytes);
uint64_t DeserializeBeInt(const uint8_t *buffer, size_t nrBytes);
除了这些函数之外,还定义了一堆宏,例如:
#define SerializeBeInt16(value, buffer) SerializeBeInt(value, buffer, sizeof(int16_t))
#define SerializeBeUint16(value, buffer) SerializeBeInt(value, buffer, sizeof(uint16_t))
#define DeserializeBeInt16(buffer) DeserializeBeType(buffer, int16_t)
#define DeserializeBeUint16(buffer) DeserializeBeType(buffer, uint16_t)
(de)serialize 函数逐字节读取或写入值,因此不会发生对齐问题。您也不必担心签名。首先,如今所有系统都使用 2s 补码(可能除了一些 ADC,但您不会使用这些功能)。然而,它甚至应该在使用 1s 补码的系统上工作,因为(据我所知)有符号整数在转换为无符号时会转换为 2s 补码(并且函数接受/返回无符号整数)。
您的另一个论点是它们取决于 8 位字节和存在的确切大小uint_N_t
。这对我的函数也很重要,但在我看来这不是问题(这些类型总是为我使用的系统及其编译器定义的)。如果您愿意,您可以调整函数原型以使用unsigned char
代替uint8_t
和类似long long
或uint_least64_t
代替的东西uint64_t
。