为什么GCC为不同作用域的本地联合分配单独的堆栈空间?

发布于 2025-01-07 22:13:46 字数 1759 浏览 5 评论 0原文

考虑以下代码:

#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. 易失性用于阻止编译器优化分配。丢失 易失性 和不进行优化的编译会产生相同的结果。

  2. 即使 testme 是一个具有联合作为成员之一的结构,也会观察到相同的行为。换句话说 - 对于 GCC 来说,结构体的成员之一是联合体就足以进行单独的堆栈分配。

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

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

我的问题:

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

澄清:我的问题不是为什么结构或联合的尺寸看起来比其部分的尺寸更大。我明白原因是为了对齐而填充。我的问题是,编译器为联合的不同实例分配多个堆栈帧,即使它们是在不同的范围中定义的,但它不应该也确实不会对具有相同字段的结构执行相同的操作。

谢谢!

Consider the following code:

#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;
}

Compiled as is for x86 is (meaning testme is a struct), the stack size the compiler allocates for main is 16 bytes.

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

However, compiled with TRY defined to union (meaning testme is a union), the stack size the compiler allocates for main is 32 bytes:

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

Moreover, any additional instances of the struct/union defined in additional scopes, will produce bigger stack allocations when using a union, but will not enlarge the stack allocation when used as a struct.

Now, this does not make sense - the union should take less stack space, if at all, not more, then a struct with the same fields!

It seems as if GCC treats unions as used concurrently even when in different scopes, but does not do the same for structs.

Some more clarifications:

  1. volatile is used to stop the compiler from optimizing away the assignments. Loosing the volatile and compiling with no optimization produces the same results.

  2. Even if testme is a struct that has a union as one of the members, the same behavior is observed. In other words - it is enough that one of the members of a struct is a union for GCC to for separate stack allocations.

  3. Compiler is gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) but other GCC versions for other architectures showed the same behavior.

  4. checkstack.pl simply searches the objdump output for the instructions used to allocate stack (sub to the stack pointer).

My question:

  1. Why does GCC do this? is this a bug or is there a reason for this behavior?
  2. Assuming this is not a bug, is a there a way to work around this and force GCC to allocate stack for stucts same as unions.

Clarification: My question is not why the struct or union appears to be bigger in size from the size of its part. I understand the reason is padding for alignment. My problem is that the compiler allocates multiple stack frames for different instances of the union even though they are defined in different scopes while it shouldn't and indeed doesn't do the same for a struct with the same fields.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

找回味觉 2025-01-14 22:13:47

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

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

Apparently at least an attempt has been made to relax gcc's strict aliasing paranoia regarding unions.

You may wish to make sure that gcc source you compile from has this or equivalent patch applied:
http://codereview.appspot.com/4444051/

无远思近则忧 2025-01-14 22:13:47

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

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

总共 - 8 个字节。

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

总计 - 16 字节。

如果将“int”更改为“short int”,内存看起来会有所不同。

It's looks like mater of padding and basic default size definition of the Int.

In 32 bit the memory map will be:
One (2 bytes)
Two (2 bytes)
Three (1 byte)
(1 bytes) padding
four (2 bytes)

Total - 8 bytes.

in 64 bit it will be:
One (4 bytes)
Two (4 bytes)
Three (1 byte)
(3 bytes padding)
Four (4 bytes)

Total - 16 bytes.

If you change the "int" to "short int", the memory will look different.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文