Как узнать, какая переменная из Union используется? - PullRequest
15 голосов
/ 16 ноября 2010

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

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

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

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

Когда я заполнил только «Всего» символом «а» и попытался sizeof(TestUnionInstance), он вернул 12 (размер символа равен 1 байт, верно?).Таким образом, я изолировал структуру от нее и обнаружил, что Размер структуры составляет 12 байтов, а не 5 + 2 = 7 байтов .... Странно !!Кто-нибудь может объяснить ??

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

Ответы [ 6 ]

21 голосов
/ 16 ноября 2010

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

Если вам нужно знать, вы можете использовать что-то, что называется теговым объединением. Некоторые языки имеют встроенную поддержку для них, но в 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;

Затем в своем коде вы всегда устанавливаете тег, указывающий, как используется объединение.

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

6 голосов
/ 16 ноября 2010

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


Что касается вас второстепенного вопроса: компилятору разрешено вставлять «байты заполнения» в структуру , чтобы избежать несогласованного доступа иэто более производительный .

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
4 голосов
/ 16 ноября 2010

Краткий ответ: нет другого способа, кроме как добавить enum где-нибудь в вашей структуре вне объединения.

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
2 голосов
/ 16 ноября 2010

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

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

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

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

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

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

1 голос
/ 16 ноября 2010

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

0 голосов
/ 03 июля 2018

Другой пример включения объединения с перечислением для определения того, что хранится. Я нашел это намного более ясным и по существу.

из: https://www.cs.uic.edu/~jbell/CourseNotes/C_Programming/Structures.html

автор: Доктор Джон Т. Белл


Чтобы узнать, какое поле объединения действительно хранится, объединения часто вкладываются в структуры, причем перечислимый тип указывает, что там на самом деле хранится. Например:

typedef struct Flight {
    enum { PASSENGER, CARGO } type;
    union {
        int npassengers;
        double tonnages;  // Units are not necessarily tons.
    } cargo;
} Flight;

Flight flights[ 1000 ];

flights[ 42 ].type = PASSENGER;
flights[ 42 ].cargo.npassengers = 150;

flights[ 20 ].type = CARGO;
flights[ 20 ].cargo.tonnages = 356.78;
...