不同编译器编译的应用程序能否在共享内存中共享C结构体?

3
我知道通常情况下,C和C++标准给编译器写手很大的自由度。但是特别地,它保证像C结构体成员这样的POD类型必须按照在结构体定义中列出的顺序在内存中排列,并且大多数编译器提供扩展来让你固定成员的对齐方式。因此,如果你有一个头文件定义了一个结构体并手动指定其成员的对齐方式,然后使用该头文件编译两个不同编译器的应用程序,那么其中一个应用程序是否能够将该结构体的实例写入共享内存,并且另一个应用程序能够读取它而不会出现错误呢?
我假设所包含的类型的大小在相同架构的两个编译器上是一致的(因为我们正在谈论共享内存,所以已经是相同的平台)。我知道对于某些类型,这并不总是正确的(例如,在GCC和MSVC 64位上,long与long long),但现在有uint16_t、uint32_t等类型,并且float和double是由IEEE标准规定的。

普通的动态链接库不是每天都这样做吗?它不一定要使用共享内存,两个库之间或库和应用程序之间独立编译的结构体面临同样的问题。该库可以使用PIC进行编译,但这并不会改变结构体的布局。 - Ioan
当库共享相同的ABI时。这通常适用于使用不同编译器编译的C库,但不适用于C ++库。 - Joseph Garvin
5个回答

3
只要您能保证两个编译器具有完全相同的内存布局,包括偏移量,并且数据类型大小也相同,则可以使用此方法。这是因为在这种情况下,该结构体与数据访问相关方面是完全相同的。

@Trent,“手势模糊/”这要看情况。如果你能用可怕的编译指示或语言的边角情况来保证这些,那当然可以工作。但是,在我看来,做出这样的保证非常困难。 - JaredPar
@Trent:使用结构体读写网络数据时,基本上是相同的“是但是”回答。使用结构体表示序列化/编组数据是应用程序的细节,如果您可以保证结构体正确,则可能很方便。不同C实现之间的二进制数据应该被视为“序列化”:无论是在网络上共享还是在共享RAM中,都应考虑到类型和布局可能不匹配的问题。数组和流都可以被认为是char序列。 - Steve Jessop
至少在同一主机上使用共享内存时,您不会遇到字节序问题。 - Mark B

2
是的,当编译和链接混合代码或在不同机器之间传输格式为结构体的数据时,问题和解决方案都是相同的。在早些年,当集成MS C和其他编译器(如Borland Turbo C、DEC VAX C、Greenhills C)时,这种情况经常发生。
让各种数据类型的字节数保持一致是最简单的部分。例如,在一个32位编译器上使用short与在另一端使用16位编译器上的int相同。由于通用源代码声明结构体通常是有益的,一些简洁的声明会很有帮助:
typedef  signed long     s32;
typedef  signed short    s16;
typedef  signed char     s8;
typedef  unsigned long   u32;
typedef  unsigned short  u16;
typedef  unsigned char   u8;
...

Microsoft C是最令人讨厌的。它的默认设置是将成员填充到16位对齐,64位代码可能会有更多的填充。在x86上,其他编译器不会填充成员。

struct {
    int   count;
    char  type;
    char  code;
    char  data [100];
} variable;

看起来,code的偏移量应该是type之后的下一个字节,但实际上可能会插入填充字节。解决方法通常是

#ifdef _MSC_VER    // if it's any Microsoft compiler
 #pragma pack(1)   // byte align structure members--that is, no padding
#endif

还有一个编译器命令行选项可以执行相同的操作。


0

请参考您的编译器手册。

大多数编译器提供扩展,让您可以修复成员的对齐方式

您是否限制自己使用那些编译器和相互兼容的#pragma align样式?如果是这样,安全性由它们的规范决定。

为了实现可移植性,您可能最好放弃#pragma align并依赖于您的ABI,该ABI可能为您平台上所有编译器的合规性提供“合理”的标准。

由于C和C++标准允许任何确定性结构布局方法,因此它们基本上是无关紧要的。


0

内存布局的方式很重要,除了数据类型大小之外,如果您需要在由编译器1编译的库1中使用结构体,并在由编译器2编译的库2中使用,则必须考虑到这一点。


0

确实是可能的,您只需要确保所有涉及的编译器从相同的代码生成相同的数据结构。一种测试方法是编写一个创建结构体并将其写入二进制文件的示例程序。在十六进制编辑器中打开结果文件并验证它们是否相同。或者,您可以将结构体转换为uint8_t数组,并将单个字节转储到屏幕上。

确保数据大小相同的一种方法是使用像int16_t(来自stdint.h)这样的数据类型,而不是普通的int,后者可能会在编译器之间更改大小(尽管在运行在同一平台上的两个编译器上很少发生)。

这并不像听起来那么困难。有许多预编译库可用于多个编译器。关键是构建一个测试程序,让您验证两个编译器是否平等地处理结构。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接