【问题标题】:C vs C++ struct alignmentC 与 C++ 结构对齐
【发布时间】:2016-02-23 11:10:10
【问题描述】:

在最近的一次采访中,有人问我关于 C++ 结构字段对齐的问题,并且理论上 C 和 C++ 在结构打包中遵循相同的策略。

但是,这是错误的假设。面试官说,通常 C 和 C++ 以不同的方式打包结构,我们不应该期望相反。恕我直言,这是奇怪的说法。 C++ 中的结构没有 pack "C" 限定符,可用于双语 C/C++ 头文件。

因此在实践中,这可能意味着您无法在 C++ 中创建结构并将其传递给 C 库,因为通常它的字段将以不同的方式对齐并具有不同的偏移量。但是,事实上,大多数程序员在使用一些辅助方法将指向 C POD 结构的指针转换为对该结构的 C++ 包装器的引用之前,都非常依赖这种互操作性。你能澄清一下这个问题吗?

【问题讨论】:

  • 多么奇怪的问题。如果 C 和 C++ 之间的结构填充不同,我会被搞砸的。也许面试官在一些晦涩难懂的编译器上发现了一些非常奇怪的问题——或者他在想 C 和 C++ 编译器如何可能完全不同,并且期望两个编译器以相同的方式对齐字段并使用相同的填充可能不是我们应该做的事情有信心依赖(但即使在两个 C 编译器或两个 C++ 编译器之间也是如此——与语言无关)。
  • 你是对的。打包是处理器实现细节,它确保遵守对齐规则。针对同一处理器的 C 编译器和 C++ 编译器必须处理相同的实现细节。
  • @ISanych,什么? g++/gcc 只是前端。调用什么并不重要 - 就编译过程而言,实际驱动程序是基于文件扩展名调用的(链接不同)。
  • @Olaf 但是由于您不能在 C 中继承,因此 C++ 中的相同结构永远不需要 RTTI 或 v-table 指针。
  • @SergeyA:填充是对齐要求的结果,这就是为什么我在中间使用斜线,而不是和号。您通常不会在没有实际需要的情况下进行填充。 CPU 可能无法通过一条指令访问其本机类型。例如。对于某些 ARM CPU,gcc 为 packed structs 发出特殊代码,这些代码按字节读取 int 并将它们组装到寄存器中(反之亦然)。与手动(反)序列化完全相同。而且pack 指令无论如何都不是标准的,并非所有编译器都支持。

标签: c++ c pointers struct object-layout


【解决方案1】:

C 和 C++ 语言标准都没有对结构填充的要求,而是将其作为编译器实现的细节。对此的严格解释意味着不能保证两者之间的结构是相同的。

然而,在实践中,如果需要,可以同时支持 C 和 C++ 的工具链的给定版本(例如 GCC 或 Clang)可以以相同的方式打包相同的结构。没有这个,世界上的许多生产代码根本无法工作。然而,这是工具链提供的保证,不是语言。

值得注意的是,如果您要声明与 C 原始结构类似的结构,但添加了访问说明符(private、public 和 protected),则布局会发生变化,但这有点由于结构不再相同。

【讨论】:

  • 是的,如果你添加虚函数,它也会有所不同。相似就是相似。
  • 当我创建一个 C .h 时,我使用 #ifdef __cplusplus 和 extern "C" ... 来确保 C++ 将以 C 兼容的方式解释事物。
  • @Ike 您可能会想到extern "C" int myfnc(return 0;),但extern "C" { struct mystruct { int y; int z; }; } 保证对齐[对于给定的工具链]。这告诉 C++不 插入一个 vtable 指针,并使用 C 的对齐方式[如果它是不同的]。此外,C++不会将mystruct 定义为type [如果这样做,很多 C .h 会中断]。大多数工具链想要进行互操作(例如 gcc 和 clang,可能是 MS),所以它也可以在那里。这与计算机架构(例如 x86、arm、IBM/370)和对齐的特定硬件限制同样重要
  • 没有编译器错误。一个 extC 块保证其中的 C 语义:没有名称 mangling(!),结构与 C 版本对齐 [if 它不同--IMO,面试官的概念是即使没有 extC,它们也不匹配气味],并且不定义类型。这是一个“未来的证明”。我们都在讨论这个问题,因为一些随机的面试官 [可能是一个一无所知的 LL] 吐出了 BS,然后我们就得清理了。除了规范之外,已知工具的作用是常识性的事情。否则,很多程序都会中断。这是一个拱门的东西(例如,神话拱门需要 16 字节对齐 int)
【解决方案2】:

这显然是错误的(在面试官方面)。很明显,对于任何使用任何处理结构的低级API(例如网络 API)的人来说,结构打包对于 C 和 C++ 都是相同的。所有这些都是 C 函数,它们接受“C”结构,但它们每天被 C++ 代码安全地调用数百万次。

你应该很幸运你有这个问题。这表明你不应该在那里工作。

【讨论】:

  • 我喜欢这个答案(虽然我也喜欢另一个),因为它是一个实用且当之无愧的安慰。听起来面试官脑子里确实有这个奇怪的概念,而不是试图让人们以这种超级理论的方式思考语言标准假设——主要是因为这部分:“C 和 C++ 是包装结构以不同的方式,我们永远不应该期望相反。” 这听起来不像面试官完全掌握了他在说什么。这是我期望从一家拥有...的公司提出的面试问题。
  • ... 基于迷信的奇怪编码标准。至少我见过这种类型。也许我太苛刻了。
  • 是的,不好的问题没有好的答案,应该很高兴不在那里工作
  • 哇。有人刚到那里并批量否决了我所有的答案。干得好!
  • @SergeyA:面试官不喜欢你的回答。
【解决方案3】:

在开发 C++ 时,开发人员发现 C 程序员依赖于 C++ 开发人员不想保证的一些东西,但不保证它们意味着很多 C 代码也是有效的 C++ 代码用作 C++ 代码时损坏。不可取。

这就是他们发明“POD”结构的原因:不使用任何 C++ 功能的结构在 C++ 程序中的行为与在 C 程序中的行为完全相同(除了实现定义的行为可能会改变的事实,因为C 编译器和 C++ 编译器显然不是同一个实现。另一方面,C++ 编译器可能只是从 C 编译器复制实现定义)。

如果您采用任何也是有效 C++ 结构的普通 C 结构(例如,没有名为“class”的成员),然后您只需在左大括号后添加“public:”,然后它的布局顺序为成员,对齐方式等都可以更改。尽管默认情况下所有结构成员都是公共的,所以没有什么真正改变。除了因为“public:”,它不再是 POD。

【讨论】:

  • 你为什么这么认为?当然,你的类的所有公共成员(以及所有私有成员,就此而言)的示例是 POD 好的。
猜你喜欢
  • 1970-01-01
  • 2013-07-24
  • 2014-02-19
  • 2013-06-03
  • 2010-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多