【问题标题】:Questions about memory alignement in structures and portability of the sizeof operator关于结构中内存对齐和sizeof操作符可移植性的问题
【发布时间】:2012-04-01 07:45:08
【问题描述】:

我有一个关于 C 语言结构的结构填充和内存对齐优化的问题。我正在通过网络发送一个结构,我知道,出于运行时优化的目的,结构内的内存不是连续的。我已经在本地计算机上运行了一些测试,实际上 sizeof(my_structure) 与我所有结构成员的总和不同。我进行了一些研究以找出两件事:

  • 首先,sizeof() 运算符检索结构的填充大小(即将存储在内存中的实际大小)。

  • 在声明中指定 __attribute__((__packed__)) 时编译器禁用了此优化的结构,因此 sizeof(my_structure) 将与我的结构的字段总和完全相同。

话虽如此,我想知道sizeof 运算符是否在每个编译器实现和每个架构上都获得了填充大小,换句话说,使用memcpy 复制结构是否总是安全的,例如使用sizeof 运算符如:

memcpy(struct_dest, struct_src, sizeof(struct_src)); 

我也想知道 __attribute__((__packed__) 的真正目的是什么),它是用于在提交结构时在网络上发送不太重要的数据量,还是实际上用于避免一些未指定和平台相关的sizeof 操作员行为?

提前致谢。

【问题讨论】:

  • 如果sizeof 坏了,我们就完蛋了。幸运的是,它不是 - 最糟糕的是,一个本科生在五周内编写的一些半途而废的编译器无法正确实现它。

标签: c memory-management sizeof


【解决方案1】:

不同架构上的不同编译器可以并且确实使用不同的填充。因此,对于有线传输,打包结构以实现一致的二进制布局并不少见。然后,这可以满足在不同架构上运行的线路每一端的代码。

但是,如果您使用这种方法,您还需要确保您的数据类型大小相同。例如,在 64 位系统上,long 在 Windows 上是 4 个字节,而在其他地方几乎是 8 个字节。而且您还需要处理字节顺序问题。标准是以网络字节顺序通过线路传输。实际上,您最好使用专用的序列化库,而不是尝试重新发明所有这些问题的解决方案。

【讨论】:

  • s/Windows/64-bit Windows/ 和 s/almost everywhere else/on most other 64-bit systems/,但是是的。
  • 感谢大卫的回答,我已经解决了字节序问题:)。我同意其余的。
【解决方案2】:
我正在通过网络发送一个结构

停在那里。也许有些人会不同意我的观点(实际上你确实看到很多项目都在这样做),但struct 是一种在内存中布置事物的方式——它不是一种序列化机制。通过使用此工具完成工作,您已经将自己束缚在一堆不可移植的假设中。

当然,您可以使用诸如结构填充 pragma 和属性之类的东西来伪造它,但是 - 真的可以吗?即使使用那些不可移植的机制,您也永远不知道可能会出现什么怪癖。我记得在一个使用“打包”结构的代码库中工作,然后突然将它带到一个访问必须是字对齐的平台......即使它名义上是它产生的相同编译器(因此支持相同的专有扩展)崩溃的二进制文件。你从这条道路上得到的任何痛苦都可能是应得的,我想说,只有当你能 100% 确定它只会在给定的编译器和环境中运行,并且永远不会改变时,才接受它。我会说更安全的选择是编写一个适当的序列化机制,该机制不允许跨进程边界编写结构。

【讨论】:

  • 嗯,它确实不是一种序列化机制,但我没有将它用于此目的,实际上我是使用原始套接字在低级别发送数据包。通过线路发送结构只是发送原始数据的最常见方式,这就是 OSI 堆栈(在应用程序数据下方)中实现的所有协议的实际工作方式。如您所说,结构是在内存中布置事物的常用方法,但它取决于语言,实际上压缩结构只是一组位。
  • 确实,例如,Linux 内核(举个例子)可能会为此目的使用打包结构,但它们 (a) 依赖于特定的编译器和架构,并且 (b) 必须采用非常小心地正确执行此操作。这是一个好主意吗?我仍然会说这符合“使用structs 作为序列化机制”的资格,根据您的回复,我会花更多时间思考这意味着什么。 :-)
  • 您说的对我可能面临的编译器问题是对的,尽管我正在开发的项目是大学作业,可能无法在其他平台上编译。但是,我喜欢您处理此问题的方法。我感谢您对此的经验,我想知道您实际上面临的问题。顺便说一下,我知道什么是序列化机制,谢谢:)
【解决方案3】:

使用 memcpy 复制结构是否总是安全的,例如使用 sizeof 运算符

是的,这就是提供sizeof 运算符的目的。

通常__attribute__((__packed__)) 不用于大小考虑,而是当您想要确保结构的布局完全符合您的要求时使用。

例如: 如果要使用一个结构来匹配硬件或通过线路发送,那么它需要具有完全相同的布局而没有任何填充。这是因为不同的架构通常实现不同种类和数量的填充和对齐,这是确保共同点是使用包装从图片中删除填充。

【讨论】:

    猜你喜欢
    • 2012-05-05
    • 2019-07-03
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 2013-10-24
    • 2011-02-15
    相关资源
    最近更新 更多