【问题标题】:Why does GCC allocate separate stack space for local unions in different scopes?为什么 GCC 为不同范围的本地联合分配单独的堆栈空间?
【发布时间】:2012-02-23 15:29:04
【问题描述】:

考虑以下代码:

#include <stdlib.h>

#ifndef TRY
#define TRY struct
#endif

TRY testme
{
  int one;
  int two;
  char three;
  int four;
};

int
main (void)
{
  {
    volatile TRY testme one;

    one.one = 2;
    one.three = 7;
  }

  {
    volatile TRY testme twos;

    twos.one = 3;
  }

  {
    volatile TRY testme one;

    one.one = 4;
  }

  {
    volatile TRY testme twos;

    twos.one = 5;
  }

  {
    volatile TRY testme twos;

    twos.one = 6;
  }

  {
    volatile TRY testme twos;

    twos.one = 6;
  }

  return EXIT_SUCCESS;
}

按原样编译为 x86 是(意思是 testme 是一个结构),编译器分配给 main 的堆栈大小是 16 字节。

$ gcc -g -O2 test.c -o test 
$ objdump -d ./test | ./checkstack.pl i386 | grep main
16 main

但是,使用定义为 union 的 TRY 编译(意味着 testme 是一个 union),编译器为 main 分配的堆栈大小为 32 字节:

$ gcc -DTRY=union -g -O2 test.c -o test 
$ objdump -d ./test | ./checkstack.pl i386 | grep main

此外,在附加作用域中定义的结构/联合的任何附加实例,在使用联合时会产生更大的堆栈分配,但在用作结构时不会扩大堆栈分配。

现在,这没有意义 - 联合应该占用更少的堆栈空间,如果有的话,而不是更多,然后是具有相同字段的结构!

似乎 GCC 将联合视为同时使用,即使在不同的范围内也是如此,但对结构却不这样做。

更多说明:

  1. volatile 用于阻止编译器优化分配。释放 volatile 并在不优化的情况下编译会产生相同的结果。

  2. 即使 testme 是一个结构,其成员之一是联合,也会观察到相同的行为。换句话说 - 一个结构的成员之一是 GCC 的联合就足够了,可以进行单独的堆栈分配。

  3. 编译器是 gcc 版本 4.4.3 (Ubuntu 4.4.3-4ubuntu5),但其他架构的其他 GCC 版本显示相同的行为。

  4. checkstack.pl 只是在 objdump 输出中搜索用于分配堆栈的指令(堆栈指针的子指令)。

我的问题:

  1. 为什么 GCC 会这样做?这是一个错误还是有这种行为的原因?
  2. 假设这不是错误,有没有办法解决这个问题并强制 GCC 为与联合相同的结构分配堆栈。

澄清:我的问题不是为什么结构或联合的大小看起来比它的部分更大。我知道原因是填充对齐。我的问题是编译器为联合的不同实例分配多个堆栈帧,即使它们是在不同的范围内定义的,而对于具有相同字段的结构,它不应该而且确实不这样做。

谢谢!

【问题讨论】:

  • 也许联合在-O2 上没有得到很好的优化(还)?查看生成的汇编代码。也尝试禁用strict-aliasing。哦,试试当前的 GCC。
  • 你看过this questionthis one吗?这似乎是填充和对齐约束的问题
  • @Anony-Mousse -fno-strict-aliasing 产生相同的结果。我正在构建最新的 GCC 进行测试。
  • @Coren 我有,谢谢。我的问题不是为什么结构的大小大于其部分的总和。这就是为什么 GCC 在不同的情况下为不同的联合实例分配不同的堆栈区域的原因
  • 如果你只做一个块,那么分配多少堆栈?无论如何,它应该认识到它可以在每个块的末尾重用堆栈内存。我可以想象编译选择不这样做的唯一原因是它可以一劳永逸地初始化它。所以 6*16 字节 -> 每次重用 16 个字节,6*4 字节 -> 初始化一次,在堆栈上保留 6 个实例,因为它们非常小。在 4 个字节的情况下,堆栈管理可能比为该程序保留额外的 4 个字节更昂贵。

标签: c gcc stack stack-size


【解决方案1】:

显然,至少已经尝试放松 gcc 对联合的严格别名偏执狂。

您可能希望确保您编译的 gcc 源应用了此补丁或等效补丁: http://codereview.appspot.com/4444051/

【讨论】:

  • 这正是我想要的。非常感谢:-)
【解决方案2】:

它看起来像是填充和 Int 的基本默认大小定义的材料。

在 32 位中,内存映射将是: 一个(2 字节) 两个(2 字节) 三(1字节) (1 字节)填充 四(2 字节)

总计 - 8 个字节。

在 64 位中它将是: 一个(4 字节) 两个(4 字节) 三(1字节) (3 字节填充) 四(4 字节)

总计 - 16 个字节。

如果把“int”改成“short int”,内存看起来会不一样。

【讨论】:

  • 但是作为union,无论是32位还是64位,整个东西都应该只占用4个字节。
  • 我对单个结构或联合需要多少没有问题。我的问题是 GCC 似乎为不同范围内定义的联合的不同实例分配不同的堆栈空间,即使它不需要这样做,而且确实没有为 struct 这样做
  • 即使作为一个工会,它也取决于接下来会发生什么。如果它在联合的数组内 - 即使在 64 位以下,它也会占用 32 位。如果它是可变的,那么在 64 位系统上它将被填充到 64 位。这不是范围,而是“接下来会发生什么”。
猜你喜欢
  • 1970-01-01
  • 2023-03-29
  • 2020-11-10
  • 2012-04-09
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
  • 2021-07-17
相关资源
最近更新 更多