首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >便携C二进制序列化原语

便携C二进制序列化原语
EN

Stack Overflow用户
提问于 2012-07-16 08:15:31
回答 3查看 2.4K关注 0票数 10

据我所知,C库在将数值序列化为非文本字节流方面没有提供任何帮助。如果我错了就纠正我。

使用中最标准的工具是来自POSIX的htonl等人。这些职能存在缺陷:

  • 没有64位的支持。
  • 没有浮点支持。
  • 没有签名类型的版本。反序列化时,无符号到签名的转换依赖于有符号的积分溢出,即UB.
  • 它们的名称不说明数据类型的大小。
  • 它们依赖于8位字节和精确大小的uint_N_t的存在。
  • 输入类型与输出类型相同,而不是引用字节流。
    • 这要求用户执行对齐可能不安全的指针类型传送。
    • 在执行了类型转换之后,用户可能会尝试在其本机内存布局中转换和输出结构,这是一种糟糕的做法,会导致意外的错误。

将任意大小的char序列化为8位标准字节的接口将位于C标准之间,C标准并不真正承认8位字节,与其他标准(ITU?)将八进制设为传输的基本单位。但是旧的标准并没有被修改。

既然C11有许多可选组件,就可以在线程的旁边添加二进制序列化扩展,而无需对现有实现提出要求。

这样的扩展是有用的,还是担心非二补机器就那么毫无意义?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-24 01:50:22

我从未使用过它们,但我认为谷歌的协议缓冲器满足了您的需求。

  • 64位类型、有符号/无符号类型和浮点类型为全支持
  • 生成的API是typesafe。
  • 序列化可以对流进行/从流执行。

本教程似乎是个很好的介绍,您可以阅读有关实际二进制存储格式这里

从他们的网页

什么是协议缓冲区? 协议缓冲区是Google的语言中立的、平台中立的、可扩展的序列化结构化数据的机制--考虑XML,但更小、更快、更简单。您可以定义您希望您的数据被构造一次的方式,然后您可以使用特殊生成的源代码来轻松地在各种数据流之间以及使用各种语言-- Java、C++或Python --之间编写和读取结构化数据。

纯C中没有正式的实现(只有C++),但是有两个C端口可能适合您的需要:

  • Nanopb,在http://koti.kapsi.fi/jpa/nanopb/
  • Protobuf-c在http://code.google.com/p/protobuf-c/

我不知道在非8位字节的情况下,它们的表现如何,但是应该比较容易找到。

票数 6
EN

Stack Overflow用户

发布于 2012-07-23 13:56:10

在我看来,像htonl()这样的函数的主要缺点是它们只完成了一半的工作,这就是序列化。它们只在机器是小endian的情况下翻转多字节整数中的字节。序列化时必须做的另一件重要事情是处理对齐,而这些函数不这样做。

许多CPU无法(有效)访问多字节整数,这些整数没有存储在内存位置,而内存位置的地址不是整数大小的倍数(以字节为单位)。这就是为什么从不使用struct覆盖来(反)序列化网络数据包的原因。我不知道这是不是你所说的“就地转换”的意思。

我经常使用嵌入式系统,并且在我自己的库中有一些函数,在生成或解析网络数据包(或任何其他I/O:磁盘、RS232等)时总是使用这些函数:

代码语言:javascript
复制
/* 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);

除了这些函数之外,还有一组宏定义为:

代码语言:javascript
复制
#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)

(反序列化函数逐字节读取或写入值,因此不会出现对齐问题。你也不用担心手语的问题。首先,现在所有的系统都使用2s补码(除了一些ADC之外,但是你不会使用这些功能)。然而,它甚至应该在使用1s补码的系统上工作,因为(据我所知)有符号整数在转换为无符号时被转换为2s补码(并且函数接受/返回无符号整数)。

您的另一个参数是,它们依赖于8位字节和精确大小的uint_N_t。这对我的功能也很重要,但在我看来,这不是一个问题(这些类型总是为我所使用的系统及其编译器定义的)。如果您愿意,可以调整函数原型,使其使用unsigned char而不是uint8_t,也可以使用long longuint_least64_t之类的东西来代替uint64_t

票数 4
EN

Stack Overflow用户

发布于 2012-07-23 14:12:03

参见xdr库和XDR标准RFC-1014 RFC-4506

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11500425

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档