Разница между структурой и союзом


412

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

Ответы:


678

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

Чтобы привести конкретный пример их использования, я некоторое время назад работал над интерпретатором 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

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

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

печать

99, 99

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

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

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


1
используя этот пример в union, если xb = 'c', что хранится в xa? это ссылка номер символа?
kylex

1
надеюсь, это объясняет более подробно, что хранится в xa при установке xb
Кайл Кронин

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

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

1
@Lazar Порядок байтов в многобайтовых типах зависит от порядка байтов. Я предлагаю прочитать статью в Википедии об этом.
Кайл Кронин

83

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

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)), еще раз с возможностью некоторого дополнения для выравнивания.


53

Есть ли хороший пример, чтобы дать разницу между «struct» и «union»?

Воображаемый протокол связи

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

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

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

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


1
Ага. Это хорошо объясняет прецедент!
Гидеон

1
Предположим, у вас есть packetheader ph;как получить доступ к requestnumber? ph.request.requestnumber?
justin.m.chase

Лучшее объяснение! Спасибо.
84RR1573R

39

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

union test {
    int a;
    long b;
}; 

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

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

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

some_test_pointer может указывать на int*или double*. Если вы приведете адрес типа 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

А 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

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


11

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

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

Структура : если есть 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

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

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

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

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

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


10

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

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

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

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


9

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

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


2
Возможно, вы также захотите добавить, что члены объединения «перекрывают» друг друга, так как все они начинаются с начального адреса выделенной «структуры» объединения.
Джим Бак

4

В чем разница между структурой и объединением?

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

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

Здесь есть два члена внутри struct и union: int и long int. Объем памяти для int составляет 4 байта, а объем памяти для длинных int составляет 8 в 32-разрядной операционной системе.

Таким образом, для структуры 4 + 8 = 12 байтов будет создано в то время как 8 байтов будут созданы для объединения

Пример кода:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

Ссылка: http://www.codingpractise.com/home/c-programming/structure-and-union/


3

Использование профсоюзов часто используется, когда требуются специализированные разговоры. Чтобы получить представление о полезности объединения. Стандартная библиотека c / c не определяет функции, специально предназначенные для записи коротких целых чисел в файл. Использование fwrite () влечет за собой чрезмерные накладные расходы для простой операции. Однако, используя объединение, вы можете легко создать функцию, которая записывает двоичный файл с коротким целым числом в файл по одному байту за раз. Я предполагаю, что короткие целые числа имеют длину 2 байта

ПРИМЕР:

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

хотя putw () я вызывал с коротким целым числом, было возможно использовать putc () и fwrite (). Но я хотел показать пример, чтобы доминировать, как профсоюз может быть использован


3

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

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

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

общий объем памяти = 5 байт

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

общий объем памяти = 4 байта


2

Союзы пригодятся при написании функции упорядочения байтов, которая приведена ниже. Это невозможно с помощью структур.

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.

1

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.