Почему GCC выделяет отдельное стековое пространство для локальных объединений в разных диапазонах?

Рассмотрим следующий код:

#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 - это struct), размер стека, выделяемого компилятором для main, составляет 16 байт.

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

Однако при компиляции с TRY, определенным как union (то есть testme является union), размер стека, выделяемого компилятором для main, составляет 32 байта:

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

Более того, любые дополнительные экземпляры struct/union, определенные в дополнительных диапазонах, будут давать большее распределение стека при использовании union, но не будут увеличивать распределение стека при использовании в качестве struct.

Это не имеет смысла - объединение должно занимать меньше места в стеке, если вообще занимает, а не больше, чем структура с теми же полями!

Похоже, что GCC рассматривает союзы как одновременно используемые, даже если они находятся в разных диапазонах, но не делает того же для структур.

Еще несколько пояснений:

  1. volatile используется для того, чтобы компилятор не оптимизировал присваивания. Отказ от volatile и компиляция без оптимизации приводит к тем же результатам.

  2. Даже если testme является структурой, одним из членов которой является объединение, наблюдается такое же поведение. Другими словами - достаточно, чтобы один из членов struct был объединением, чтобы GCC выделил отдельный стек.

  3. Компилятор - gcc версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5), но другие версии GCC для других архитектур показали такое же поведение.

  4. checkstack.pl просто ищет в выводе objdump инструкции, использованные для выделения стека (sub к указателю стека).

Мой вопрос:

  1. Почему GCC так делает? Это ошибка или есть причина для такого поведения?
  2. Если предположить, что это не ошибка, есть ли способ обойти это и заставить GCC выделять стек для таких же инструкций, как и для союзов.

Clarification: Мой вопрос не в том, почему структура или объединение кажется больше по размеру, чем размер его части. Я понимаю, что причина в выравнивании. Моя проблема в том, что компилятор выделяет несколько стековых фреймов для разных экземпляров объединения, хотя они определены в разных диапазонах, в то время как он не должен и действительно не делает того же для struct с теми же полями.

Спасибо!

7
задан gby 22 February 2012 в 11:41
поделиться