4

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

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

varstr占用什么大小?根据四字节块的6字节或字节。6 × 4

4

5 回答 5

2

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

于 2013-03-24T16:47:59.587 回答
2

数据对齐是 CPU 要求,这意味着对齐量从一个 CPU 更改为另一个,请记住这一点。

谈到堆栈数据对齐,gcc例如使用名为-mpreferred-stack-boundary=nwhere the data will bealigned to的选项保持数据对齐2^n。默认情况下, 的值为n4,这使得堆栈对齐为 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 个字节的数据。好吧,gcc 需要保存 8 个字节leaveret 这使得所需的总内存为 24。
但是,对齐要求是 16 字节,因此 gcc 需要分配堆栈空间,以便它由 16 字节块组成; 将 24 字节变为 32 可以解决问题。
您将有足够的空间来存放变量,retandleave和它由两个 16 字节的块组成。

于 2013-03-24T17:11:37.817 回答
0

如果你有:

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

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

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

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

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

于 2013-03-24T17:32:41.753 回答
0

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

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

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

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

于 2013-03-24T17:01:27.937 回答
0

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

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

例子:

struct X
{
    char field1[6];
};

所以结构 X 大小将是8

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

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

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

于 2013-03-24T17:03:04.060 回答