Как узнать, какая переменная используется в Union?

Если я объявляю Союз как:

union TestUnion
{
    struct 
    {
      unsigned int Num;
      unsigned char Name[5];
    }TestStruct;
    unsigned char Total[7];
};

Теперь, откуда я могу узнать, используется ли Total [7] или используется TestStruct?

Я использую C! Я пересматривал союзы и структуры, и этот вопрос пришел мне на ум. "sizeof" не может использоваться, поскольку оба имеют одинаковый размер, т.е. 7 байтов. (И вот еще один вопрос)

Когда я заполнил только "Total" символом "a" и пробовал sizeof(TestUnionInstance), он вернул 12 (размер Char равен 1 байт, правильно?). Поэтому я выделил структуру из нее и обнаружил, что размер структуры составляет 12 байт, а не 5 + 2 = 7 байт.... Странно!! Кто-нибудь может объяснить?

P.S. Я использую Visual Studio 2008.

Ответы

Ответ 1

Вы не можете. Эта часть точки союзов.

Если вам нужно сказать, вы можете использовать что-то, называемое объединенным тегом. Некоторые языки имеют встроенную поддержку для них, но в C вы должны сделать это самостоятельно. Идея состоит в том, чтобы добавить тег вместе с объединением, который вы можете использовать, чтобы определить, какая версия. Как:

enum TestUnionTag {NUM_NAME, TOTAL};

struct {
    enum TestUnionTag tag;
    union {
        struct {
            unsigned int Num;
            unsigned char Name[5];
        } TestStruct;
        unsigned char Total[7];
    } value;
} TestUnion;

Затем в вашем коде вы всегда устанавливаете тег, чтобы сказать, как используется соединение.

О sizeof: struct имеет 12 байт, потому что для int существует 4 байта (большинство современных компиляторов имеют 4-байтовый int, такой же, как long int), затем три байта заполнения и пять байтов для символов (я не знаю, доходит ли дополнение до или после символов). Отступы существуют, так что структура представляет собой целое число слов в длину, так что все в памяти сохраняется на границах слов. Поскольку структура имеет длину 12 байтов, объединение должно быть 12 байтов для ее хранения; объединение не меняет размер в соответствии с тем, что в нем.

Ответ 2

Член, которого вы используете, - это тот, который вы в последний раз писали; другие не имеют пределов. Вы знаете, к кому вы, в конце концов, писали, не так ли? В конце концов, именно вы написали программу: -)


Как для вас вторичный вопрос: компилятору разрешено вставлять "заполняющие байты" в структуру, чтобы избежать несвязанных обращений и сделать его более результативным.

example of a possible distribution of bytes inside your structure

Num    |Name     |pad
- - - -|- - - - -|x x x
0 1 2 3|4 5 6 7 8|9 a b

Ответ 3

Короткий ответ: нет способа, кроме как добавив перечисление где-нибудь в вашей структуре вне союза.

enum TestUnionPart
{
  TUP_STRUCT,
  TUP_TOTAL
};

struct TestUnionStruct
{
  enum TestUnionPart Part;
  union
  {
    struct
    {
      unsigned int Num;
      unsigned char Name[5];
    } TestStruct;
    unsigned char Total[7];
  } TestUnion;
};

Теперь вам нужно будет управлять созданием вашего объединения, чтобы убедиться, что перечисление установлено правильно, например, с функциями, похожими на:

void init_with_struct(struct TestUnionStruct* tus, struct TestStruct const * ts)
{
  tus->Part = TUP_STRUCT;
  memcpy(&tus->TestUnion.TestStruct, ts, sizeof(*ts));
}

Отправка правильных значений теперь представляет собой один переключатель:

void print(struct TestUnionStruct const * tus)
{
  switch (tus->Part)
  {
    case TUP_STRUCT:
      printf("Num = %u, Name = %s\n",
             tus->TestUnion.TestStruct.Num,
             tus->TestUnion.TestStruct.Name);
      break;
    case TUP_TOTAL:
      printf("Total = %s\n", tus->TestUnion.Total);
      break;
    default:
      /* Compiler can't make sure you'll never reach this case */
      assert(0);
  }
}

В качестве примечания я хотел бы упомянуть, что эти конструкции лучше всего обрабатывать на языках семейства ML.

type test_struct = { num: int; name: string }
type test_union = Struct of test_struct | Total of string

Ответ 4

Во-первых, sizeof(int) для большинства архитектур в настоящее время будет 4. Если вы хотите 2, вы должны посмотреть short или int16_t в заголовке stdint.h на C99, если хотите быть конкретным.

Во-вторых, C использует байты заполнения, чтобы каждый struct был привязан к границе слова (4). Итак, ваша структура выглядит следующим образом:

+---+---+---+---+---+---+---+---+---+---+---+---+
|      Num      |   N   a   m   e   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+

В конце 3 байта. В противном случае следующий struct в массиве будет иметь поле Num в неудобно выровненном месте, что сделает его менее эффективным для доступа.

В-третьих, объединение sizeof будет самым большим членом sizeof. Даже если все это пространство не используется, sizeof вернет самый большой результат.

Вам нужно, как говорили другие ответы, каким-то другим способом (например, enum) определить, какое поле вашего объединения используется.

Ответ 5

Невозможно сказать. У вас должны быть дополнительные флаги (или другие средства, внешние по отношению к вашему объединению), говорящие, какая из частей соединения действительно используется.