【问题标题】:Is there any environment where "int" would cause struct padding?是否存在“int”会导致结构填充的环境?
【发布时间】:2018-02-12 17:11:19
【问题描述】:

具体来说,这是在讨论中提出的:

内存消耗明智,是否有可能使用两个ints 中的struct 比仅使用两个ints 占用更多内存?

或者,在语言方面:

#include <iostream>

struct S { int a, b; };

int main() {
    std::cout << (sizeof(S) > sizeof(int) * 2 ? "bigger" : "the same") << std::endl;
}

是否有任何合理的1(不一定是常见的或当前的)环境可以让这个小程序打印bigger

1澄清一下,我的意思是系统(和编译器)以某种有意义的数量开发和生产,而不是仅仅为了证明这一点而构建的理论示例,或者一次性原型或业余爱好者的创作。

【问题讨论】:

  • 我打赌这适用于 32 位操作系统上的 Turbo C++。
  • @Bathsheba 试试这个值 15 分! :D
  • @BartekBanachewicz -- 语言定义间接指定了INT_MAX (32767) 的最小值和INT_MIN (-32767) 的最大值。 int 必须至少为 16 位宽。
  • 如果对齐但编译器在末尾引入了填充怎么办?
  • @Bathsheba:我刚刚在 Turbo C++ 中测试过,大小是一样的。

标签: c++ memory int alignment


【解决方案1】:

一个只能访问 64 位块内存的系统可能会选择使用 32 位“int”大小来与其他可能会被 uint32_t 提升到的程序兼容,这并非完全不可信。更大的类型。在这样的系统上,具有偶数个“int”值的结构可能没有额外的填充,但具有奇数个值的结构可能会这样做。

从实际的角度来看,具有两个 int 值的结构需要填充的唯一方法是结构的对齐方式超过两倍于“int”。反过来,这将要求结构的对齐方式比 64 位更粗糙,或者 int 的大小小于 32 位。后一种情况本身并不罕见,但是以一种使 struct 对齐方式比 int 对齐方式粗两倍以上的方式结合起来看起来很奇怪。

【讨论】:

    【解决方案2】:

    在您的具体示例中,struct S { int a, b; };,我看不到任何合理的填充参数。 int 应该已经自然对齐,如果是,int * 可以而且应该是指针的自然表示,S * 没有必要有任何不同。但总的来说:

    一些罕见的系统具有不同表示的指针,例如int * 仅表示为表示“字”地址的整数,char * 是字地址和该字的字节偏移量的组合(其中字节偏移量存储在字地址的其他不需要的高位中)。取消引用 char * 在软件中通过加载字,然后进行屏蔽和移位以获得正确的字节。

    在这样的实现中,确保所有结构类型具有最小对齐可能是有意义的,即使结构的成员不需要它,只是为了使指向该结构的指针不需要字节偏移混乱。这意味着给定struct S { char a, b; };sizeof(S) &gt; 2 是合理的。具体来说,我希望sizeof(S) == sizeof(int)

    我从未亲自使用过这样的实现,所以我不知道它们是否确实产生了这样的填充。但是这样做的实现是合理的,并且至少非常接近现有的实际实现。

    【讨论】:

    • 我使用过(例如 Cray 超级计算机可以使用 64 位字进行字寻址),但它比这更复杂一些。首先,硬件是总是字可寻址的(仅!),因此在布局 OS 数据结构等方面没有任何逻辑试图与之抗衡。其次,char* 的字地址部分不是“普通”整数——它被限制在一个较小的范围内(例如 Cray 1 上只有 24 位),因为它覆盖了硬件的最大地址范围——但它存储在内存中的完整 64 位字,3 位“字中字符”指针存储在同一个字中。
    • @alephzero 谢谢,编辑了我的答案以提及表示。同意操作系统数据结构,但你是否记得用户数据结构发生了什么?尽管struct S { char a, b; }; 可能不是该实现的最佳选择,但可移植用户代码可能正在使用它。你还记得实现对这些类型做了什么吗?
    【解决方案3】:

    我知道这不是您所要求的,这不符合您的问题的精神(因为您可能考虑到标准布局类),但严格回答这部分:

    内存消耗明智,是否有可能使用结构 两个整数比两个整数占用更多的内存?

    答案有点……是的:

    struct S
    {
        int a;
        int b;
    
        virtual ~S() = default;
    };
    

    带有迂腐的注释,即 C++ 没有结构,它有类。 struct 是引入类的声明/定义的关键字。

    【讨论】:

    • 关于迂腐的注释:有趣的是,是的,标准中有一些结构eel.is/c++draft/class#8(这是标准中最愚蠢的段落之一)
    【解决方案4】:

    理论上填充用于提供访问内存区域的有效方式。如果将填充添加到 2 个整数变量会比是它可以有填充增加效率。但实际上我没有遇到任何具有 2 个整数的结构有填充位.

    【讨论】:

    • 这并不能真正回答问题,是吗?如果你找不到这样的环境,“我还没有偶然发现一个”并不是一个很好的论点。
    • 一个明显的候选者(具有 32 位整数和浮点数)将是 struct S {double d1; int i; double d2; }; 当然编译器可以将其实现为 struct S {double d1; double d2; int i; }; 以消除填充 - 但你仍然需要在 struct S 数组的元素之间进行填充以对齐 64 位边界上的所有双精度值。
    • @alephzero 否,编译器无法对标准布局结构中的成员重新排序。
    【解决方案5】:

    是否有任何合理的(不一定是常见的或当前的)环境可以让这个小程序打印得更大?

    我不知道。我知道那是not completely reassuring,但我有理由相信由于 C++ 标准的要求,没有这样的环境。

    在符合标准的†编译器中,以下保持:

    • (1) 数组元素之间不能有任何填充,因为它们可以通过指针访问ref;
    • (2) 标准布局结构在每个成员之后可能有也可能没有填充,但没有在开头,因为它们与“更短”但相等的标准布局结构的布局兼容ref;
    • (3) 数组元素和结构成员正确对齐ref;

    从 (1) 和 (3) 可以得出,类型的对齐方式小于或等于其大小。如果它更大,则数组将需要添加填充以使其所有元素对齐。出于同样的原因,类型的大小始终是其对齐方式的整数倍。

    这意味着在一个给定的结构中,第二个成员将始终正确对齐 - 无论 int 的大小和对齐方式如何 - 如果放置在第一个成员之后,即不需要间隙填充。在这种布局下,结构体的大小也已经是其对齐方式的倍数,因此也不需要尾部填充。

    没有我们可以选择的符合标准的(大小、对齐)值集使该结构需要任何形式的填充。

    任何这样的填充都需要不同的目的。然而,这样的目的似乎难以实现。假设有一个环境出于某种原因需要这个填充。无论填充的原因是什么,它都可能......也适用于数组的情况,但从(1)我们知道它不能。

    但是假设这样的环境确实存在,并且我们需要一个 C++ 编译器来支持它。它可以通过简单地使 int 变得更大,即通过将填充 inside 放在 int 中来支持数组中这种额外需要的填充。反过来,这将再次允许结构与两个 int 大小相同,并且让我们没有理由添加填充。


    † 一个编译器——即使是一个不符合标准的编译器——如果出现任何这些错误,都可以说是有问题的,所以我会忽略这些。

    &ddagger;我猜想,在数组和结构都是基元的环境中,可能存在一些潜在的区别,使我们可以使用未填充的数组和填充的结构,但同样,我在使用 don't know of any such thing

    【讨论】:

    • 来自您的 (3) 参考:“对齐是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。”这与您的“类型的对齐方式小于或等于其大小”不兼容,因为让对齐方式小于 允许重叠对象。
    • @Eric 这不是不兼容的。这里不必禁止重叠的对象;它们可以通过其他地方的措辞来禁止。考虑问题中给出的结构,其大小和对齐方式为 4(例如我的 x64 机器上的 clang)。该结构的大小为 8,对齐方式为 4。对齐方式小于其大小的类型有很多(任何具有两个或更多成员的结构,或任何具有两个或更多元素的数组)
    • 如果结构的大小为 8 且对齐方式为 4,则“可以分配给定对象的连续地址”与连续对象的最后和第一个成员重叠,因此不是“之间的字节数可以分配给定对象的连续地址。"没有条件支持您的“类型的对齐方式小于或等于其大小”。
    • 呃,是的,有。就在那段中:“如果它更大,数组将需要添加填充以使其所有元素对齐”。由于数组不能有填充,所以对齐不能更大。说它不能大于等于说它必须小于或等于。
    • 如果您声称对齐不能小于大小,则该声明不受支持。请注意,仅仅因为一个结构可以放置在地址 0 和 4 并不意味着两个对象可以同时放置在该地址。由于我们正在检查支持哪些声明,因此不允许重叠对象的隐含假设也不受支持。
    猜你喜欢
    • 2021-09-18
    • 1970-01-01
    • 2015-11-07
    • 2012-01-22
    • 2019-06-20
    • 2014-11-20
    • 1970-01-01
    • 2021-07-28
    • 1970-01-01
    相关资源
    最近更新 更多