【问题标题】:Structures in CC中的结构
【发布时间】:2010-08-24 05:17:56
【问题描述】:

我得到了这样的结构:

struct bar {
    char x;
    char *y;
};

我可以假设在 32 位系统上,char 的填充将使其总共 4 个字节,而 32 位的指针是 4,所以总大小将是 8 对吗?

我知道这都是特定于实现的,但我认为如果它在 1-4 内,它应该填充到 4,在 5-8 到 8 和 9-16 在 16 内,对吗?它似乎工作。

我会说结构在 x64 架构中是 12 字节,因为指针是 8 字节吗?或者你认为应该是什么?

【问题讨论】:

  • 为什么不问问编译器——它会用sizeof 运算符告诉你。
  • 你为什么想知道?内存使用?结构布局兼容性?指针访问结构?
  • 你已经知道这是实现定义的。所以问这些问题是愚蠢的。您的任何假设都不能在所有情况下都被认为是有效的,它们只是假设(有时它们会是真实的,而其他情况则不是)。编译器可以随意添加填充,甚至可以使用不同的标志更改填充。
  • 当您的分析器说这是造成瓶颈的原因并且当您在封闭系统中耗尽内存时,请担心字段对齐和大小问题。否则,请关注稳健性和正确性。

标签: c++ alignment byte


【解决方案1】:

我可以假设在 32 位系统上, char 的填充将使它成为 4 字节总数,以及一个 32 位的指针 是 4,所以总大小是 8 对吧?

假设这一点并不安全,但通常情况会如此,是的。对于 x86,字段通常是 ​​32 位对齐的。这样做的原因是以内存使用为代价来提高系统性能(参见here)。

我说得对吗? struct 在 x64 架构中将是 12 个字节, 因为指针是 8 个字节?或者是什么 你觉得应该吗?

同样,对于 x64,字段通常是 ​​64 位/8 字节对齐的,因此 sizeof(bar) 将是 16。

然而,正如 Anders 指出的那样,一旦您开始通过 /Zppack directive 或您的编译器支持的任何其他方式进行对齐,所有这些都会飞出窗口。

【讨论】:

  • 假设是不安全的 ;)
  • 对对齐做出任何假设都是愚蠢的。不要做。即使更改编译器标志也会导致填充/对齐发生变化。
  • @Martin,如果您确实更改了对齐或打包(这会影响结构成员对齐),您必须知道您在做什么,因为您的代码将无法调用标准 C 库和 POSIX 函数.
  • @Maxim:暂时避免使用 POSIX。即使从发布更改为调试也可能会更改包装。许多系统提供两个单独的运行时,一个用于调试,一个用于发布。因此允许在两个不同版本之间进行不同的包装/对齐的可能性。更不用说没有真正运行时的系统 :-) 当使用不同的标志时,编译器不需要具有相同的特性。这就是为什么构建系统倾向于构建具有相同标志的所有文件,并且调试/发布二进制文件被构建到单独的目录中,因此它们不会合并。
  • @Martin。我不知道调试和发布版本有不同对齐方式的任何系统。调试版本关闭了优化,额外的运行时检查和调试符号,这就是所有的区别。此外,具有不同的对齐方式会导致在调试构建中未发现错误,因此它违背了调试构建的目的。您能否提供一个系统/平台示例,其中调试和发布版本具有不同的对齐方式以及原因。
【解决方案2】:

它是一个编译器开关,你不能假设任何东西。如果你认为你可能会遇到麻烦。

例如,在 Visual Studio 中,您可以决定使用 pragma pack(1) 直接在字节边界上使用它。

【讨论】:

    【解决方案3】:

    一般来说,你不能假设任何事情。每个平台都有自己的填充规则。

    也就是说,任何使用“自然”对齐的架构,其中操作数被填充到它们自己的大小(必要且足以避免跨越自然对齐的页面、缓存线等),将使 bar 指针大小的两倍。

    因此,考虑到自然对齐规则,仅此而已,32 位 8 字节,64 位 16 字节。

    【讨论】:

      【解决方案4】:

      $9.2/12-

      a 的非静态数据成员 (非联合)类声明没有 干预访问说明符是 分配给后来的成员 类中的更高地址 目的。分配顺序 非静态数据成员由 访问说明符未指定 (11.1)。实施对齐 要求可能会导致两个相邻 不被分配的成员 紧随其后;所以可能 管理空间要求 虚函数(10.3)和虚函数 基类 (10.1)。

      因此,正如您已经提到的,它是高度特定于实现的。

      【讨论】:

        【解决方案5】:

        不完全是。

        填充取决于下一个成员的对齐要求。内置数据类型的自然对齐方式是它们的大小。

        char 成员之前没有填充,因为它们的对齐要求是 1(假设 char 是 1 个字节)。

        例如,如果一个 char(再次假设它是一个字节)后跟一个 short,比如 2 个字节,则最多可能有 1 个字节的填充,因为一个 short 必须是 2 个字节对齐的。如果 char 后面跟着大小为 8 的 double,则可能有多达 7 个字节的填充,因为 double 是 8 字节对齐的。另一方面,如果一个 short 后跟一个 double,则最多可能有 6 个字节的填充。

        而且结构的大小是对齐要求最大的成员对齐的倍数,所以可能会有尾填充。例如,在以下结构中,

        struct baz {
            double d;
            char c;
        };
        

        对齐要求最大的成员是 d,它的对齐要求是 8,这给出了 sizeof(baz) == 2 * alignof(double)。在成员 c 之后有 7 个字节的尾部填充。

        gcc 和其他现代编译器支持 __alignof() 运算符。 boost中还有便携版。

        【讨论】:

          【解决方案6】:

          正如其他人所提到的,不能在平台之间依赖这种行为。但是,如果您仍然需要这样做,那么您可以使用BOOST_STATIC_ASSERT() 来确保如果违反假设,那么您可以在编译时发现,例如

          #include <boost/static_assert.hpp>
          #if ARCH==x86                             // or whatever the platform specific #define is
            BOOST_STATIC_ASSERT(sizeof(bar)==8);
          #elif ARCH==x64
            BOOST_STATIC_ASSERT(sizeof(bar)==16);
          #else ...
          

          如果alignof() 可用,您也可以使用它来测试您的假设。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-18
            • 2014-12-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多