Різниця між структурою та союзом


411

Чи є хороший приклад, щоб дати різницю між a structі a union? В основному я знаю, що structвикористовує всю пам'ять свого члена і unionвикористовує найбільший простір для пам'яті учасників. Чи є якась інша різниця в рівні ОС?

Відповіді:


677

При об'єднанні ви повинні використовувати лише один з елементів, оскільки всі вони зберігаються в одному місці. Це робить його корисним, коли ви хочете зберегти щось, що може бути одним із декількох типів. З іншого боку, структура має окреме місце для пам'яті для кожного з її елементів, і всі вони можуть бути використані одночасно.

Щоб навести конкретний приклад їх використання, я трохи раніше працював над інтерпретатором схеми, і по суті я накладав типи даних схеми на типи даних C. Це передбачало зберігання в структурі enum, що вказує тип значення та об'єднання для зберігання цього значення.

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
використовуючи цей приклад, в союзі, якщо xb = 'c', що зберігається в xa? це посилання № char?
kylex

1
сподіваємось, це пояснює більш докладно, що зберігається в xa, коли ви встановлюєте xb
Kyle Cronin

1
@KyleCronin Я думаю, що я розумію. У вашому випадку у вас є група типів, знаючи, що вам потрібно буде використовувати лише один, але ви не знаєте, який саме до виконання, - тому союз дозволяє вам це зробити. Дякую
користувачеві12345613

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 packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

У цьому уявному протоколі було визначено, що на основі "типу повідомлення" наступне розташування у заголовку буде або номером запиту, або чотири символьним кодом, але не обом. Коротше кажучи, профспілки дозволяють одне і те ж місце зберігання представляти більше одного типу даних, де гарантується, що ви захочете зберігати лише один із типів даних у будь-який час.

Профспілки - це значною мірою деталі низького рівня, що базуються на спадщині С як мові системного програмування, де місцями зберігання «перекриваються» сховищ іноді використовуються таким чином. Іноді ви можете використовувати об'єднання для збереження пам’яті там, де у вас є структура даних, де одночасно буде збережено лише один із декількох типів.

Взагалі ОС не хвилює і не знає про структури та об'єднання - вони обидва просто блоки пам’яті. Структура - це блок пам'яті, який зберігає кілька об'єктів даних, де ці об’єкти не перетинаються. Об'єднання - це блок пам'яті, який зберігає кілька об'єктів даних, але має лише сховище для найбільшого з них, і, таким чином, може зберігати лише один із об'єктів даних у будь-який час.


1
Так. Це добре пояснює приклад використання!
gideon

1
припустимо, у вас є, packetheader ph;як ви маєте доступ до кількості запитів? ph.request.requestnumber?
Justin.m.chase

Найкраще пояснення! Дякую.
84RR1573R

39

Як ви вже держава в вашому питанні, головна відмінність між unionі в structтому , що unionчлени накладення пам'яті один з одним так , що SizeOf профспілки є один, в той час як structчлени розкладають один за одним (з додатковою прокладкою між ними). Крім того, об'єднання є досить великим, щоб містити всіх своїх членів і мати вирівнювання, яке відповідає всім його членам. Отже, скажімо, він intможе зберігатися лише в 2-х байтних адресах і шириною 2 байти, а довгий може зберігатися лише в 4-байтних адресах і має 4 байти. Наступний союз

union test {
    int a;
    long b;
}; 

може мати sizeof4, а вимога до вирівнювання 4. І об'єднання, і структура можуть мати набивання в кінці, але не на їх початку. Запис у структуру змінює лише значення члена, записаного на. Повідомлення члену спілки зробить значення всіх інших членів недійсними. Ви не можете отримати доступ до них, якщо ви раніше не писали їм, інакше поведінка не визначена. GCC надає як розширення, яке ви можете насправді читати від членів профспілки, хоча ви останнім часом не писали їм. Для операційної системи не має значення, чи записує програма користувача до об'єднання чи до структури. Це насправді лише питання компілятора.

Ще однією важливою властивістю об'єднання та структури є те, що вони дозволяють, що вказівник на них може вказувати на типи будь-якого з його членів . Отже, дійсне:

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

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

Прикладом цього є розсічення floatчисла за допомогою uniona structз бітними полями та a 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

" союз " і " структура " - це конструкції мови С. Говорити про різницю між рівнем "ОС" між ними недоцільно, оскільки компілятор створює інший код, якщо ви використовуєте те чи інше ключове слово.


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 використовує всю пам’ять свого члена, а об'єднання використовує найбільший простір пам'яті членів.

Але вся різниця полягає у потребі використання пам'яті. Найкраще використання об'єднання можна побачити в процесах unix, де ми використовуємо сигнали. як процес може одночасно діяти лише на один сигнал. Тож загальною декларацією буде:

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

У цьому випадку процес використовує лише найвищу пам'ять усіх сигналів. але якщо ви використовуєте структуру в цьому випадку, використання пам'яті буде сумою всіх сигналів. Багато робить різницю.

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


10

У вас є, ось і все. Але так, в основному, який сенс спілок?

Ви можете розмістити в одному і тому самому вмісті різних типів. Ви повинні знати тип того, що ви зберегли в союзі (так часто ви ставите його в a structз тегом типу ...).

Чому це важливо? Не дуже для космосу. Так, ви можете набрати кілька біт або зробити трохи прокладки, але це вже не головний момент.

Це для безпеки типу, дозволяє вам зробити якийсь "динамічний набір тексту": компілятор знає, що ваш вміст може мати різні значення та точне значення того, як ваш інтерпретатор залежить від вас під час виконання. Якщо у вас є вказівник, який може вказувати на різні типи, ОБОВ'ЯЗКОВО використовувати об'єднання, інакше код може бути невірним через проблеми зі споживанням (компілятор каже собі "о, тільки цей покажчик може вказувати на цей тип, тому я можу оптимізувати з тих доходів ... ", і можуть трапитися погані речі).


9

Структура виділяє загальний розмір усіх елементів у ній.

Союз виділяє лише стільки пам’яті, скільки вимагає його найбільший член.


2
Ви можете також додати, що члени профспілки "накладають один одного" тим, що всі вони починаються з початку адреси виділеної спілки "структури".
Джим Бак

4

в чому різниця між структурою та союзом?

Коротка відповідь: Захист полягає у розподілі пам'яті. Пояснення: У структурі буде створено простір пам'яті для всіх членів всередині структури. У об'єднанні пам’ять буде створено лише для члена, якому потрібен найбільший простір пам'яті. Розглянемо наступний код:

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

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

Тут є два члени всередині структура та об'єднання: 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.