2009-04-07 3 views
9

Идея этого вопроса, чтобы понять более глубокие понятия, используя объединение и использовать его по-разному, чтобы сэкономить память .. Мой вопрос ко всем is--Союзы против структур в C

допустим есть структура

struct strt 
{ 
    float f; 
    char c; 
    int a; 
} 

и та же структура, представленная в союзе

union unin 
{ 
    float f; 
    char c; 
    int a; 
} 

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

Поэтому мне нужно выяснить метод, который может хранить значения f, c, a, используя объединение, и затем я могу напечатать то же самое , (Применяйте какие-либо операции или что-то еще ..), но я ищу эту технику. Может ли кто-нибудь там руководить мной или давать мне какие-либо идеи?

ответ

38

Я думаю, вы неправильно поняли цель union.

A union, как следует из названия, определяет структуру, где все ее члены занимают одно и то же пространство памяти. В то время как struct помещает каждый из своих членов в отдельную память в единую смежную область.

С вашего союза, когда вы пишете:

union foo; 
foo.c = 3; 

Тогда foo.a и foo.f оба будут изменены. Это связано с тем, что .a, .c и .f хранятся в в том же месте памяти. Таким образом, каждый член объединения является другим «представлением» одной и той же памяти. Это не происходит с struct, потому что все члены отличаются друг от друга и отделены друг от друга.

Существует нет способа обойти это поведение , потому что это намеренно.

+0

Да, я понимаю идею этой логики, но я хочу использовать объединение вместо структуры, чтобы использовать меньшее пространство памяти, и я могу получить все значения (при использовании некоторых операций или так далее). Идея использования объединения заключается в пространственной памяти. – AGeek

+0

Идея использования объединения заключается в том, чтобы сэкономить память и получить результат, эквивалентный структуре, выполнив какую-то или любую операцию. – AGeek

+0

Нет, идея союза состоит в том, чтобы сделать такую ​​же память доступной, как и разные типы. Храните поплавок и, например, прочитайте его битовый шаблон в int. Вы не можете просто «уменьшить использование памяти». – greyfade

8

Объединение содержит набор взаимно исключающих данных.

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

Вы должны вернуться и спросить себя что вы моделирования:

  • Вы действительно хотите значения е, с и быть взаимоисключающими (т.е. только одно значение может существовать сразу)?Если да, рассмотрите возможность использования объединения в сочетании с значением enum (хранимым вне объединения), указывающим, какой член в объединении является «активным» в любой конкретный момент времени. Это позволит вам получить преимущества использования памяти с помощью объединения за счет более опасного кода (так как любой, кто его поддерживает, должен знать, что значения взаимоисключающие - т. Е. Это действительно объединение). Учитывайте эту возможность только в том случае, если вы создаете многие из этих объединений, и сохранение памяти имеет жизненно важное значение (например, для встроенных процессоров). Вы даже не можете сохранить память, потому что вам нужно будет создавать переменные перечисления в стеке, которые также занимают память.

  • Вы хотите, чтобы эти значения были одновременно активными и не мешали друг другу? Если это так, вам нужно будет использовать структуру вместо этого (как вы помещаете в свой первый пример). Это будет использовать больше памяти - когда вы создаете структуру, выделенная память представляет собой сумму всех членов (плюс некоторое дополнение к границе ближайшего слова). Если сохранение памяти имеет первостепенное значение (см. Предыдущий пример), я бы одобрил этот подход.

Edit:

(очень простой) пример того, как использовать перечисления в сочетании с союзом:

typedef union 
{ 
    float f; 
    char c; 
    int a; 
} floatCharIntUnion; 

typedef enum 
{ 
    usingFloat, 
    usingChar, 
    usingInt 
} unionSelection; 

int main() 
{ 
    floatCharIntUnion myUnion; 
    unionSelection selection; 

    myUnion.f = 3.1415; 
    selection = usingFloat; 
    processUnion(&myUnion, selection); 

    myUnion.c = 'a'; 
    selection = usingChar; 
    processUnion(&myUnion, selection); 

    myUnion.a = 22; 
    selection = usingInt; 
    processUnion(&myUnion, selection); 
} 

void processUnion(floatCharIntUnion* myUnion, unionSelection selection) 
{ 

    switch (selection) 
    { 
    case usingFloat: 
     // Process myUnion->f 
     break; 
    case usingChar: 
     // Process myUnion->c 
     break; 
    case usingInt: 
     // Process myUnion->a 
     break; 
    } 
} 
+0

Булево значение вне союза будет работать только для объединения с двумя переменными в нем ... – DeadHead

+0

Да, я понял, что при редактировании.Изменил его на перечисление. – LeopardSkinPillBoxHat

+0

Как я могу использовать перечисление .. может у дать мне один пример .. – AGeek

0

Союзы, как правило, используются, когда только один из ниже будет храниться в экземпляре в любой момент времени. то есть вы можете либо сохранить float, char или int в любой момент. Это необходимо для сохранения памяти - не выделяя дополнительную/отдельную память для float и int, когда вы собираетесь использовать ее для хранения символа. Объем выделенной памяти = наибольший тип в объединении.

union unin 
{ 
    float f; 
    char c; 
    int a; 
} 

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

47

Если вы посмотрите на то, как структура хранит свои ценности, это будет что-то вроде этого:

|0---1---2---3---|4---|5---6---7---8---| 
|ffffffffffffffff| |    | <- f: Where your float is stored 
|    |cccc|    | <- c: Where your char is stored 
|    | |aaaaaaaaaaaaaaaa| <- a: Where your int is stored 

Итак, когда вы измените значение е, вы на самом деле меняются байты 0-3. Когда вы меняете свой символ, вы фактически изменяете байт 4. Когда вы меняете свой int, вы фактически изменяете байты 5-8.

Если вы теперь посмотрите на то, как союзная хранит свои ценности, это будет что-то вроде этого:

|0---1---2---3---| 
|ffffffffffffffff| <- f: where your float is stored 
|cccc------------| <- c: where your char is stored 
|aaaaaaaaaaaaaaaa| <- a: where your int is stored 

Так что теперь, когда я изменить значение е, я меняюсь байты 0-3. Поскольку c хранится в байте 0, когда вы меняете f, вы также меняете c и a! Когда вы меняете c, вы меняете часть f и a - и когда вы меняете a, вы меняете c и f. Вот где происходит «переписывание». Когда вы упаковываете 3 значения в один адрес памяти, вы вообще не «сохраняете место»; вы просто создаете 3 разных способа просмотра и изменения одних и тех же данных. У вас на самом деле нет int, float и char в этом объединении - на физическом уровне у вас есть только 32 бита, которые можно рассматривать как int, float или char. Изменяемый - означает, чтобы изменить остальные. Если вы не хотите, чтобы они меняли друг друга, используйте структуру.

Вот почему gcc сообщает вам, что ваша структура имеет длину 9 байт, а ваш союз - только 4 - это не экономия места - это просто то, что структуры и объединения - это не одно и то же.

+0

Что касается вашего отказа, байт должен быть равен 0, иначе он не будет существовать по тому же адресу, что и float и int. Это не имеет значения в отношении endianness, потому что little-endian и big-endian float или int занимают одинаковое пространство, но все равно будут начинаться с того же адреса, что и char. – dreamlax

+0

Хороший звонок. Я удалил его сейчас. Благодаря! – Smashery

+0

Довольно хорошее объяснение :) – mahesh

1

Это классический пример использования объединения для хранения данных в зависимости от внешнего маркера.

int, float и char * все занимают одно и то же место в объединении, они не являются последовательными, поэтому, если вам нужно сохранить их все, это структура, которую вы ищете, а не союз.

Структура - это размер самой большой вещи в союзе плюс размер этого типа, так как он находится за пределами союза.

#define TYP_INT 0 
#define TYP_FLT 1 
#define TYP_STR 2 

typedef struct { 
    int type; 
    union data { 
     int a; 
     float b; 
     char *c; 
    } 
} tMyType; 

static void printMyType (tMyType * x) { 
    if (x.type == TYP_INT) { 
     printf ("%d\n", x.data.a; 
     return; 
    } 
    if (x.type == TYP_FLT) { 
     printf ("%f\n", x.data.b; 
     return; 
    } 
    if (x.type == TYP_STR) { 
     printf ("%s\n", x.data.c; 
     return; 
    } 
} 

Функция printMyType корректно определит, что хранится в структуре (если не лежит к нему) и распечатать соответствующее значение.

При заполнении одного из них, вы должны сделать:

x.type = TYP_INT; 
x.data.a = 7; 

или

x.type = TYP_STR; 
x.data.c = "Hello"; 

и данный x может быть только одна вещь в то время.

Горе кто пытается:

x.type = TYP_STR; 
x.data.a = 7; 

Они рожон.

12

Я думаю, что вы недооцениваете Союзы.

Идея использования Союзов схождение сохранить память ...

да, это одна из причин

... и получить результат эквивалентен структуре ...

no

Это не эквивалент. Они выглядят похожими в исходном коде, но это совершенно другая вещь. Как яблоки и самолеты.

Союзы - это очень и очень низкоуровневая конструкция, которая позволяет вам видеть часть памяти, как будто она хранит любой из ее «членов», но вы можете использовать только по одному за раз в. Даже использование слова «член» крайне вводит в заблуждение. Их следует называть «взглядами» или чем-то, а не членами.

Когда вы пишете:

union ABCunion 
{ 
    int a; 
    double b; 
    char c; 
} myAbc; 

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

В этой памяти, теперь вы можете хранить либо ИНТ, или двойной, или полукокса. Если вы храните int, а затем сохраняете double, int уходит навсегда.

Какой смысл?

Для Союзов существует два основных вида использования.

а) дискриминированных хранения

Это то, что мы делали выше. Я выбираю часть памяти, и я даю ей разные значения в зависимости от контекста. Иногда контекст явственен (вы сохраняете некоторую переменную, которая указывает, какой «вид» переменной вы сохранили), а иногда она может быть неявной (на основе раздела кода вы можете указать, какой из них должен использоваться). В любом случае, код должен быть в состоянии понять это, или вы не сможете сделать ничего разумного с переменной.

Типичный (явный) пример может быть:

struct MyVariantType 
{ 
    int typeIndicator ; // type=1 -> It's an int, 
         // type=2 -> It's a double, 
         // type=3 -> It's a char 
    ABCunion body; 
}; 

Например, VB6 в «Варианты» являются профсоюзы не в отличии от выше (но более сложного).

b) Разделение изображения Иногда это полезно, когда вам нужно видеть переменную как «целую» или как комбинацию деталей. Легче объяснить на примере:

union DOUBLEBYTE 
{ 
    struct 
    { 
     unsigned char a; 
     unsigned char b; 
    } bytes; 
    short Integer;   
} myVar; 

Вот короткий int "объединенный" с парой байтов. Теперь вы можете просмотреть то же значение, что и короткий int (myVar.Integer), или просто так же легко изучить отдельные байты, которые составляют часть значения (myVar.bytes.a и myVar.bytes.b).

Обратите внимание, что это второе использование не переносится (я уверен,); это означает, что он не гарантирует работу на разных машинных архитектурах; но это использование абсолютно необходимо для задач, для которых был разработан C (реализация ОС).

+0

Яблоки и самолеты не выглядят * ничего * как друг друга :-) – paxdiablo

+0

Нет, но слова вроде бы, у них обоих есть A..pl..es, так что аналогия на самом деле неплохая. – Eclipse

Смежные вопросы