Разница между структурой и союзом - PullRequest
381 голосов
/ 06 декабря 2008

Есть ли хороший пример, чтобы дать разницу между struct и union? В основном я знаю, что struct использует всю память своего члена, а union использует наибольшее пространство памяти членов. Есть ли другая разница в уровне ОС?

Ответы [ 15 ]

639 голосов
/ 06 декабря 2008

При объединении вы должны использовать только один из элементов, поскольку все они хранятся в одном месте. Это делает его полезным, когда вы хотите сохранить что-то, что может быть одного из нескольких типов. С другой стороны, структура имеет отдельную ячейку памяти для каждого из своих элементов, и все они могут использоваться одновременно.

Чтобы привести конкретный пример их использования, я некоторое время назад работал над интерпретатором Scheme и по существу накладывал типы данных Scheme на типы данных C. Это включало сохранение в структуре перечисления, указывающего тип значения, и объединения для хранения этого значения.

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

edit: Если вам интересно, что установка x.b на 'c' меняет значение x.a на, технически говоря, оно не определено. На большинстве современных машин значение char составляет 1 байт, а int - 4 байта, поэтому, давая x.b значение 'c', вы получаете первый байт x.a того же значения:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

печать

99, 99

Почему эти два значения одинаковы? Поскольку последние 3 байта int 3 равны нулю, поэтому он также читается как 99. Если мы введем большее число для x.a, вы увидите, что это не всегда так:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

печать

387427, 99

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

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

печать

deadbe22, 22

Вы можете ясно видеть, где 0x22 переписал 0xEF.

НО

В C порядок байтов в int не определен. Эта программа перезаписала 0xEF с 0x22 на моем Mac, но есть другие платформы, где вместо этого перезаписывается 0xDE, потому что порядок байты, составляющие int, были обращены. Поэтому при написании программы никогда не следует полагаться на поведение перезаписи определенных данных в объединении, поскольку оно не переносимо.

Для получения дополнительной информации о порядке байтов, проверьте endianness .

76 голосов
/ 06 декабря 2008

Вот краткий ответ: структура - это структура записи: каждый элемент в структуре выделяет новое пространство. Итак, структура типа

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

выделяет как минимум (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) байтов в памяти для каждого экземпляра. («По крайней мере», поскольку ограничения выравнивания архитектуры могут заставить компилятор дополнять структуру.)

С другой стороны,

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

выделяет один кусок памяти и дает ему четыре псевдонима. Так что sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), опять же с возможностью некоторого дополнения для выравниваний.

49 голосов
/ 08 декабря 2008

Есть ли хороший пример для разграничения между struct и union?

Мнимый протокол связи

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

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

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

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

38 голосов
/ 06 декабря 2008

Как вы уже заявили в своем вопросе, основное отличие между union и struct состоит в том, что union члены перекрывают память друг друга, так что размер объединения равен одному, а struct члены расположены друг за другом (с дополнительным заполнением между ними). Кроме того, профсоюз достаточно большой, чтобы вместить всех его членов, и иметь выравнивание, которое подходит всем его членам. Допустим, что int может храниться только по 2-байтовым адресам и имеет ширину 2 байта, а long может храниться только по 4-байтовым адресам и имеет длину 4 байта. Следующий союз

union test {
    int a;
    long b;
}; 

может иметь sizeof 4 и требование выравнивания 4. И объединение, и структура могут иметь заполнение в конце, но не в их начале. Запись в структуру изменяет только значение записанного члена. Письмо члену профсоюза сделает значение всех остальных членов недействительным. Вы не можете получить к ним доступ, если не писали в них раньше, иначе поведение не определено. GCC предоставляет в качестве расширения, которое вы действительно можете прочитать от членов профсоюза, даже если вы не писали им последнее время. Для операционной системы не должно иметь значения, записывает ли пользовательская программа в объединение или в структуру. На самом деле это только проблема компилятора.

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

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer может указывать на int* или bool*. Если вы приведете адрес типа test к int*, он фактически будет указывать на своего первого члена, a. То же самое верно и для союза. Таким образом, поскольку объединение всегда будет иметь правильное выравнивание, вы можете использовать объединение, чтобы сделать указание на некоторый тип действительным:

union a {
    int a;
    double b;
};

Этот союз действительно может указывать на int и double:

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;    

действительно действительно, как указано в стандарте C99:

Объект должен иметь свое сохраненное значение, доступное только через выражение lvalue, которое имеет один из следующих типов:

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

Компилятор не оптимизирует v->a = 10;, так как это может повлиять на значение *some_int_pointer (и функция вернет 10 вместо 5).

18 голосов
/ 07 сентября 2013

A union полезно в паре сценариев. union может быть инструментом для манипуляций на очень низком уровне, таким как написание драйверов устройств для ядра.

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

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

Взгляните на описание с одинарной точностью в Википедии. Я использовал пример и магическое число 0.15625 оттуда.


union также может использоваться для реализации алгебраического типа данных, который имеет несколько альтернатив. Я нашел пример этого в книге О'Салливана, Стюарта и Гёрзена "Реальный мир Хаскелла". Проверьте это в разделе различаемого союза .

Ура!

11 голосов
/ 16 декабря 2015

Нетехнически означает:

Предположение: стул = блок памяти, люди = переменные

Структура : Если есть 3 человека, они могут сидеть в кресле своего размера соответственно.

Союз : Если есть 3 человека только один стул будет сидеть, все должны использовать один и тот же стул, когда они хотят сидеть.

Технически говоря означает:

Приведенная ниже программа дает глубокое представление о структуре и объединении.

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

Общий размер MAIN_STRUCT = sizeof (UINT64) для bufferaddr + sizeof (UNIT32) для объединения + 32 бита для заполнения (зависит от архитектуры процессора) = 128 бит. Для структуры все члены получают блок памяти непрерывно.

Union получает один блок памяти элемента максимального размера (здесь его 32-битный). Внутри объединения лежит еще одна структура (INNER_STRUCT), члены которой получают блок памяти размером 32 бита (16 + 8 + 8). В объединении можно получить доступ к элементу INNER_STRUCT (32 бита) или (32 бита).

11 голосов
/ 03 января 2013

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

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

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

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

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

11 голосов
/ 06 декабря 2008

" union " и " struct " являются конструкциями языка Си. Говорить о разнице «уровня ОС» между ними неуместно, поскольку компилятор создает другой код, если вы используете то или иное ключевое слово.

10 голосов
/ 06 декабря 2008

У вас это есть, вот и все. Но так, в принципе, какой смысл в профсоюзах?

Вы можете разместить в одном месте контент разных типов. Вы должны знать тип того, что вы сохранили в объединении (так часто вы помещаете это в struct с тегом типа ...).

Почему это важно? Не совсем для космической выгоды. Да, вы можете получить немного битов или сделать несколько отступов, но это уже не главное.

Это для безопасности типов, она позволяет вам выполнять какую-то «динамическую типизацию»: компилятор знает, что ваш контент может иметь разные значения и точное значение того, как вы его интерпретируете, во время выполнения. Если у вас есть указатель, который может указывать на разные типы, вы ДОЛЖНЫ использовать объединение, в противном случае ваш код может быть неправильным из-за проблем с наложением имен (компилятор говорит себе: «О, только этот указатель может указывать на этот тип, поэтому я могу оптимизировать»). эти доступы ... ", и могут случиться плохие вещи).

9 голосов
/ 06 декабря 2008

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

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

...