如果我在C ++中有struct
,是否无法安全地将其读/写到跨平台/编译器兼容的文件中?
因为如果我理解正确,每个编译器都会根据目标平台“填充”。
答案 0 :(得分:47)
没有。这是不可能的。这是因为在二进制级别缺乏C ++标准化。
Don Box写道(引自他的书Essential COM,章节 COM作为一个更好的C ++ )
C ++和可移植性
一旦做出决定 将C ++类作为DLL分发 面对的根本之一 C ++的弱点,即缺乏 二进制级别的标准化。 虽然是ISO / ANSI C ++草案 工作文件试图编纂哪一个 程序将编译和什么 运行它们的语义效果会 是,它没有尝试标准化 C ++的二进制运行时模型。该 第一次这个问题会变成 很明显,当客户端尝试链接时 针对FastString DLL的导入库 一个C ++开发环境其他 比用于构建的那个 FastString DLL。
不同的编译器以不同的方式完成struct padding。即使您使用相同的编译器,结构的打包对齐也可能因您使用的pragma pack而有所不同。
不仅如果你编写两个成员完全相同的结构,唯一的区别在于它们声明的顺序是不同的,那么大小每个结构的结构可以(通常是)不同。
例如,请参阅此内容,
struct A
{
char c;
char d;
int i;
};
struct B
{
char c;
int i;
char d;
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
}
将其编译为gcc-4.3.4
,您将获得此输出:
8
12
即使两个结构都具有相同的成员,大小也是不同的!
Ideone的代码:http://ideone.com/HGGVl
最重要的是,标准没有谈到应该如何填充,因此编译器可以自由做出任何决定而你不能假设所有编译器做出同样的决定。< / p>
答案 1 :(得分:18)
如果您有机会自己设计结构,那么它应该是可能的。基本思想是你应该设计它,这样就不需要在其中插入填充字节。第二个技巧是你必须处理有关的结果差异。
我将描述如何使用标量构造结构,但是您应该能够使用嵌套结构,只要您为每个包含的结构应用相同的设计。
首先,C和C ++的基本事实是类型的对齐不能超过类型的大小。如果可以,那么就不可能使用malloc(N*sizeof(the_type))
分配内存。
从最大的类型开始布局结构。
struct
{
uint64_t alpha;
uint32_t beta;
uint32_t gamma;
uint8_t delta;
接下来,手动填充结构,以便最终匹配最大类型:
uint8_t pad8[3]; // Match uint32_t
uint32_t pad32; // Even number of uint32_t
}
下一步是确定结构是否应以小端或大端格式存储。如果存储格式与主机系统的字节顺序不匹配,最好的方法是在写入之前或读取结构之后“交换”所有 in situ 元素。
答案 2 :(得分:7)
不,没有安全的方法。除了填充之外,还必须处理不同的字节顺序和不同大小的内置类型。
您需要定义文件格式,并将结构转换为该格式或从该格式转换。序列化库(例如boost :: serialization或google的协议缓冲区)可以为此提供帮助。
答案 3 :(得分:3)
长话短说不。没有与平台无关,符合标准的方法来处理填充。
Padding在标准中被称为“对齐”,它开始在3.9 / 5中讨论它:
对象类型具有对齐方式 要求(3.9.1,3.9.2)。该 对齐完整的对象类型是 实现定义的整数 表示字节数的值; 在一个地址分配一个对象 符合对齐要求 它的对象类型。
但是它从那里开始并逐渐消失到标准的许多黑暗角落。对齐是“实现定义的”,意味着它可以在不同的编译器之间,甚至在相同的编译器下的地址模型(即32位/ 64位)之间不同。
除非您有严格的性能要求,否则您可能会考虑以不同的格式将数据存储到光盘,例如char字符串。当自然格式可能是其他东西时,许多高性能协议使用字符串发送所有内容。例如,我最近处理的低延迟交换提要发送日期为格式如下的字符串:“20110321”和时间类似地发送:“141055.200”。即使这个交换源整天每秒发送500万条消息,它们仍然会使用字符串来处理所有内容,因为这样可以避免出现字符串和其他问题。
答案 4 :(得分:2)
您可以使用类似boost::serialization
的内容。