2010-11-16 3 views
14

Если я объявляю Союз:Как узнать, какая переменная используется в Union?

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

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

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

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

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

ответ

20

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

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

+0

a Специальная благодарность за «заполняющую деталь» .... Я не знал этого! Большой!! – Swanand

+2

Заполнение происходит после 'char', а не раньше. Строго говоря, реализация * могла * поместить перед ним прокладку, но тогда она также должна была бы поместить туда ту же величину прокладки, если вы изменили ее с «char [5]» на «char [6]», что было бы довольно бессмысленным , C требует, чтобы структуры с общей исходной последовательностью элементов были совместимы. –

1

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

6

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


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

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 
+1

+1 за то, что вы нашли время для отображения макета памяти объекта. –

+1

На самом деле, иногда вам просто нужно получить доступ к другим участникам. Одной из важных функций профсоюзов является предоставление разных представлений тем же данным. – thkala

+1

Что касается комментария «off-limits», следует отметить, что в отношении компилятора ничего не выходит за пределы. Программист должен вручную применять любые такие политики. – thkala

2

Во-первых, 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), чтобы определить, какое поле вашего союза используется.

4

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

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

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

type test_struct = { num: int; name: string } 
type test_union = Struct of test_struct | Total of string 
Смежные вопросы