【问题标题】:What memory space is occupied by auto variables in stack堆栈中的自动变量占用了多少内存空间
【发布时间】:2013-03-14 03:04:02
【问题描述】:

我读到 C 中的函数可以使用本地基于堆栈的变量,并且只需将堆栈指针减少所需的空间量即可分配它们。这总是在 四字节块 中完成(如果我没记错的话)。但是,如果运行如下代码会怎样:

 void foo(void)
{
   char str[6];
   ......
}

var str 占用多大的空间? 6 字节或 6 × 4 字节按四字节块。

【问题讨论】:

    标签: c function compiler-construction stack local-variables


    【解决方案1】:

    四字节块规则只是意味着堆栈指针必须指向一个地址是四的倍数。在这种情况下,分配 8 个字节就满足了该规则,并且这样的块足够大,可以容纳一个只有 2 个字节填充的 6 字符数组。

    【讨论】:

      【解决方案2】:

      数据对齐是 CPU 要求,这意味着对齐量从一个 CPU 到另一个 CPU 会发生变化,请记住这一点。

      谈到堆栈数据对齐,gcc 例如使用名为-mpreferred-stack-boundary=n 的选项保持数据对齐,其中数据将与2^n 对齐。 默认情况下,n 的值为 4,这使得堆栈对齐为 16 字节。

      这意味着您会发现自己在堆栈内存中分配了 16 个字节,尽管您明确分配的只是一个整数。

      int main()
      {
              char ar[6] = {1,2,3,4,5,6};
              int x = 10;
              int y = 12 + (int) ar[1] + x;
              return y;
      }
      

      在我的 CPU 上使用 gcc 编译此代码会生成以下程序集(仅发布堆栈分配指令):

      subl    $32, %esp
      

      但为什么是 32?我们正在分配恰好适合 16 个字节的数据。 好吧,leaveret 需要保存 8 个字节的 gcc,这使得所需的总内存为 24。
      但是,对齐要求是 16 字节,因此 gcc 需要分配堆栈空间,以便它由 16 字节块组成;将 24 字节设置为 32 即可解决问题。
      您将有足够的空间用于变量,retleave,它由两个 16 字节的块组成。

      【讨论】:

        【解决方案3】:

        分配 4 字节块的规则并非在所有情况下都有效。例如,ARM eabi 要求对齐 64 位整数并在 8 字节边界上加倍。

        通常分配的空间与数据打包成结构的规则相匹配。所以char[6] 实际上会占用 6 个字节(通常),但数据的填充(用于下一个字段)可以多占用几个字节。

        例子:

        struct X
        {
            char field1[6];
        };
        

        所以结构 X 的大小将是 8

        structure Y
        {
            char field1[2];
            double field2;
        };
        

        结构 Y 通常类似于 81216 字节,具体取决于架构。

        同样的规则适用于自动堆栈变量:通常填充不是由您正在使用的类型决定,而是由您将要使用的下一个类型决定。而且规则有时有点模糊。

        【讨论】:

        • 可以强制编译器不填充您的数据结构,尽管据我了解这会导致您产生性能成本。
        • @dingrite 一些编译器允许在堆栈上的数据上定义某些对齐。但这是一个例外,因为规则是由平台文档定义的,并且会导致应用程序的某些部分与标准库和其他现有代码不兼容。
        • @dingrite 我忘了提到压缩结构。您可以使用它们,但便携性的副作用和限制可能会超过好处。
        • 我实际上是指命令编译器在不填充的情况下处理结构(甚至可能是类),而不管它分配在哪里。实际上,当我想做一些“有组织的”内存 asm 修补时,我自己曾经使用过一次 - 我需要一个没有填充的结构,所以我使用了“#pragma pack(push, 1)”并在 Visual Studio 上弹出。
        • @dingrite 是的,这会起作用,但它仍然会在边界上对齐结构本身。因此,您可以调整结构成员的对齐方式,但堆栈对齐方式保持预定义。
        【解决方案4】:

        我猜你对数据大小数据对齐感到困惑。没有一般规则,但在现代计算机上,您的变量将以 6 个字节存储。另一方面,下一个元素不一定会存储在下一个字节中。这称为数据结构填充

        字对齐架构(每个变量都必须从字长倍数的地址开始)变得越来越少见。对于 SPARC 或 x86 等新处理器,变量是自对齐。这意味着它们必须从一个是其类型大小倍数的地址开始。

        因此,在非外来计算机上没有“四字节卡盘规则”。在您的示例中,str 将存储 6 个字节。例如,如果您声明一个对齐为 8 个字节的变量(例如 x86 上的 double),您的编译器将插入 2 个填充字节。

        对齐方式由编译器根据您的体系结构固定。所以标准没有定义任何关于它的东西。您可以在Wikipedia 上找到更多信息。

        【讨论】:

          【解决方案5】:

          如果你有:

          char str[6];
          int a;
          char b;
          char c;
          

          堆栈将有足够的大小来包含所有这些变量并且可以被 4 整除(或任何需要的对齐方式)。但是每个变量不需要在同一边界上对齐(尽管可能有硬件要求)。

          在我的系统上,编译上述内容并打印出堆栈变量的地址(为简洁起见,前导数字被删除):

          &str    -- 18
          &a      -- 12
          &b      -- 10
          &c      -- 11
          

          即编译器会安排堆栈对齐,但变量不需要填充。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-10-18
            • 2018-05-02
            • 2018-05-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-22
            相关资源
            最近更新 更多