【问题标题】:Is there a pragma for not caring about struct/class layout? If not, why?是否有不关心结构/类布局的编译指示?如果不是,为什么?
【发布时间】:2025-02-23 05:55:02
【问题描述】:

有多种pragmas 用于控制结构/类布局,例如pragma pack。但据我所知,没有pragma 说“我不关心布局。它是内部的,代码不依赖它。重新排序以获得最佳性能/大小。”。 AFAIK,这是典型的案例,它可以在许多情况下提高性能/大小。此外,即使程序员足够小心地根据性能/大小对其进行重新排序,不同的目标架构也可能具有不同的最佳布局。

编辑:澄清一下,我说的是成员的顺序。填充已经是可控的。

另外,PVS-Studio 有一个相关的message。这就是我要说的——为什么这不能由带有pragma 的编译器来完成?

【问题讨论】:

  • 您的意思是允许重新排序数据成员吗?
  • 这个问题没有明确说明,因为 C 和 C++ 都没有提到 pragma。也许您正在使用的编译器有一些特定于实现的细节,但它们将特定于该编译器。
  • 我认为pragmas 可以违反规范?例如__declspec(novtable),我认为规范不允许。
  • 这是存在的,只是它在 C 或 C++ 编译器中不常见。例如,.NET 会这样做,CLR 会优化对象的布局并重新排序成员以获得尽可能小的布局。但它也在运行时强制执行此操作,布局是不可发现的,并且映射到定义的布局需要编组。 C/C++ 的方式是让它成为程序员的工作。
  • 一些旧版本(可能是 4.4 或 4.5)的 GCC 有一个优化通道来重新排序 struct-s 的成员,但这并不总是很好,后来被删除了。

标签: c++ c struct memory-layout


【解决方案1】:

语言标准允许这样的编译指示,但我不知道有任何编译器实现了这样的东西。

在 C 中,#pragma 的行为在the standard 的第 6.10.6 节中指定(链接指向最新草案):

一种预处理指令,格式为
# pragma pp-tokensopt换行
其中预处理令牌 STDC 不会立即 按照指令中的 pragma (在任何宏替换之前) 导致实现以实现定义的方式运行 方式。该行为可能会导致翻译失败或导致 翻译器或生成的程序以不合格的方式运行 方式。实施不认可的任何此类编译指示 被忽略。

所以#pragma 可能实际上违反了语言规则。

这种情况下的相关规则是结构成员按照它们声明的顺序排列。 6.7.2.1 第 15 段:

在结构对象中,非位域成员和单元 哪些位域的地址按顺序增加 他们被宣布。指向结构对象的指针,适当地 转换后,指向其初始成员(或者如果该成员是 位域,然后到它所在的单元),反之亦然。 结构对象内可能有未命名的填充,但在其 开始。

坏消息:C 标准要求结构成员按照声明的顺序排列。第一个成员必须在偏移量 0 处。成员之间可以有任意填充,或者在最后一个之后,但它们不能重新排序。

好消息:该语言允许实现定义一个#pragma,它指定了一个违反上述规则的布局。

坏消息:据我所知,实际上没有任何实现这样做。即使有,也有其他实现没有,所以任何使用这种#pragma 的代码都是不可移植的。 (尽管至少如果 #pragma 的名称是唯一的,任何不识别它的编译器都需要忽略它,因此您的代码仍然可以编译。)

这是针对 C 的。#pragma 的 C++ 规则与 C 规则非常相似。我有理由确定结构布局的 C++ 规则也类似于 C;继承使事情变得更加复杂。

【讨论】:

    【解决方案2】:

    该语言明确指出,类成员在内存中的排序方式与它们在每个访问级别中的顺序相同(如private)。编译指示无法覆盖此行为。

    见 9.2/14:

    具有相同访问权限的(非联合)类的非静态数据成员 分配控制权(第 11 条)以便后来的成员拥有更高的 类对象中的地址。非静态的分配顺序 未指定具有不同访问控制的数据成员

    请记住,重新排序成员会改变调用子对象构造函数和析构函数的顺序,可能还有其他事情。即使给编译器提供编译指示以在幕后进行这些类型的更改,这似乎也极具风险(如果您的成员依赖于另一个成员的初始化怎么办)。

    【讨论】:

    • 请注意此答案中的两个属性:*.com/a/11770476/47453 这两个属性都以 C 和 C++ 标准禁止的方式更改结构布局。但它们仍然存在。它们只会导致非标准行为。
    • 我认为这是它们在类中声明的顺序,无论访问级别如何。
    • @neilkirk 这也是我的理解 - private publicprotected 是纯粹的高级概念,它们基本上是对其他程序员执行良好(并且容易绕过)的提示。
    • 答案是正确的。允许重新排列具有不同访问级别的块。
    最近更新 更多