具有不同编译器的C ++类型的大小

时间:2014-09-26 07:32:05

标签: c++ gcc g++ clang clang++

我想避免落入XY trap所以这是最初的问题:

我们有一个小程序,可以在PC上创建共享内存段。该程序通过从其头文件(一堆个体和嵌套结构定义)中读取其结构来创建它。基本上只是一个.h和一个.cpp文件。该程序将由g ++编译。

我们想创建另一个程序,一个共享内存查看器,它在树视图中显示该内存的布局。为此,我们必须解析前面提到的头文件并计算偏移量以读取/操纵共享内存的特定部分的内容。如果没有必要,我们不想编写解析器,特别是因为头文件也包含其他声明和定义。该程序将使用与前一个程序相同的g ++版本进行编译。

最初,我们想在第二个程序中使用gccxml来解析头文件,但它基于4.2 gcc,并且无法解析包含C ++ 11代码的包含头文件。另一个想法是使用libclang来获取该头文件的结构。 libclang也包含大小信息,但我不知道在g ++和clang的情况下类型和填充/对齐的大小是否相同。

我的问题是:当你用clang和g ++编译代码时,你能否假设C ++类型的大小和结构的填充/对齐方式是一样的?环境(PC,OS)是一样的。恐怕我们不能,因为C ++标准没有指定类型的确切大小。

你知道原问题的另一种解决方案吗?

5 个答案:

答案 0 :(得分:2)

简短回答:因为clang的目标是"与gcc"兼容(对于C和C ++),我会说你可以期望它为相同的代码生成相同的偏移量和大小。

答案很长: 假设您只使用基本类型(intshortdoublechar以及指向这些类型的指针),我们将限制为gcc和clang(保持相同的OS和相同的位(32位或64位on"双方"),然后受制于编译器中的实际错误,它应该具有相同的结构布局。

当然,这是一长串的限制,当然"受制于实际的错误"在这些案件中,这是一个永无止境的问题。

如果您使用定义的大小类型(例如uint32_t而不是int),则可以使您的案例更容易一些 - 相反,如果您在结构中放置具有虚拟成员的类成员,你会遇到严重的麻烦 - 但无论如何这对共享内存都不起作用,因为它不能保证在不同的应用程序中处于相同的位置。

警惕STL功能 - 您可能无法为两个编译器获得相同的C ++库(您可能会,也可能不会,取决于您的安装方式)。

我会仔细检查,通过添加一些代码来打印重要成员的偏移量和大小(当然也可以使用两个编译器运行) - 不要忘记为某些结构内部的成员执行此操作,因为很可能结构的总体大小可能相同,内容可能处于不同的偏移量。

(正如其他人所说,我见过一些项目,其中一些代码是使用脚本生成的,该脚本打印结构成员的偏移量,这用作项目中其他程序的输入)

答案 1 :(得分:1)

实际上,在这种特殊情况下,你应该没事。

数据结构的内存布局是ABI(应用程序二进制接口)的一部分,gcc和clang都遵循x86(和x86_64)上的Itanium ABI。因此,如果它们都编译为x86或x86_64,它们应该以二进制兼容类型结束。


在一般情况下,您通常会作弊:

  1. 使用压缩数据结构:struct X { ... } __attribute__((packed)) __attribute__((aligned (8)));并完全控制结构内存布局

  2. 如Alf所述,让一个编译器调出每个成员的偏移量并使用它来为第二个编译器提供结构生成

  3. 其他?

答案 2 :(得分:0)

数据类型的大小因平台而异。使用sizeof运算符来找出适用于目标平台的适当大小,而不是硬编码,例如,

sizeof(int)
sizeof(char)
sizeof(double) 

答案 3 :(得分:0)

如果在C风格的结构中使用固定宽度整数类型(http://en.cppreference.com/w/cpp/types/integer)并按大小递减顺序排列成员(即最大的成员优先),那么它应该是非常安全的。

答案 4 :(得分:0)

我想我理解你的问题。这就是Chrome所做的事情

COMPILE_ASSERT(sizeof(double) == 8, Double_size_not_8);

它假设尺寸匹配,但只是为了确保。

COMPILE_ASSERT是一个宏。 You can find the definition here但简短的版本就是它所说的。在编译时发生的断言。

如果大小不匹配,那么处理它的一种方法是仅以字节为单位定义标头。而不是例如

struct SomeBinaryFileHeader {
   int version;
   int width;
   int height;
};

你可以这样做

struct SomeBinaryFileHeaderReadWriteVersion {
   uint_8 version_0;
   uint_8 version_1;
   uint_8 version_2;
   uint_8 version_3;
   uint_8 width_0;
   uint_8 width_1;
   uint_8 width_2;
   uint_8 width_3;
   uint_8 height_0;
   uint_8 height_1;
   uint_8 height_2;
   uint_8 height_3;
}

等。然后从一个转换为另一个甚至可以在字节序上工作