Какъв е размерът на обединението в C/C++? Това ли е размерът на най-големия тип данни в него? Ако е така, как компилаторът изчислява как да премести указателя на стека, ако един от по-малките типове данни на обединението е активен?
размер на обединение в C/C++
Отговори (8)
Стандартът отговаря на всички въпроси в раздел 9.5 от стандарта C++ или раздел 6.5.2.3, параграф 5 от стандарта C99 (или параграф 6 от стандарта C11, или раздел 6.7.2.1, параграф 16 от стандарта C18):
В обединение най-много един от членовете на данните може да бъде активен по всяко време, т.е. стойността на най-много един от членовете на данните може да се съхранява в обединение по всяко време. [Забележка: прави се една специална гаранция, за да се опрости използването на съюзи: Ако POD-обединение съдържа няколко POD-структури, които споделят обща начална последователност (9.2), и ако обект от този тип POD-обединение съдържа един от POD-структурите, е разрешено да се инспектира общата начална последователност на всеки от членовете на POD-структурата; виж 9.2. ] Размерът на обединението е достатъчен, за да съдържа най-големите от неговите членове с данни. Всеки член на данните се разпределя, сякаш е единствен член на структура.
Това означава, че всеки член споделя една и съща област на паметта. Има има най-много един активен член, но не можете да разберете кой. Ще трябва сами да съхранявате тази информация за текущия активен член някъде другаде. Съхраняването на такъв флаг в допълнение към обединението (например наличието на структура с цяло число като флаг за тип и обединение като хранилище на данни) ще ви даде така нареченото "дискриминирано обединение": Обединение, което знае какъв тип в в момента е "активният".
Една често срещана употреба е в лексери, където можете да имате различни токени, но в зависимост от токена имате различна информация за съхраняване (поставяне на line
във всяка структура, за да се покаже каква е общата начална последователност):
struct tokeni {
int token; /* type tag */
union {
struct { int line; } noVal;
struct { int line; int val; } intVal;
struct { int line; struct string val; } stringVal;
} data;
};
Стандартът ви позволява достъп до line
на всеки член, защото това е общата начална последователност на всеки един.
Съществуват разширения на компилатора, които позволяват достъп до всички членове, без значение кой в момента има съхранена стойност. Това позволява ефективна повторна интерпретация на съхранени битове с различни типове сред всеки от членовете. Например, следното може да се използва за разчленяване на плаваща променлива на 2 неподписани кратки:
union float_cast { unsigned short s[2]; float f; };
Това може да бъде доста полезно, когато пишете код на ниско ниво. Ако компилаторът не поддържа това разширение, но въпреки това го правите, пишете код, чиито резултати не са дефинирани. Така че бъдете сигурни, че вашият компилатор го поддържа, ако използвате този трик.
union
винаги заема толкова място, колкото най-големият член. Няма значение какво се използва в момента.
union {
short x;
int y;
long long z;
}
Екземпляр на горния union
винаги ще вземе поне long long
за съхранение.
Странична бележка: Както е отбелязано от Стефано , действителното пространство, което всеки тип (union
, struct
, class
) ще заеме, зависи от други проблеми, като например подравняване от компилатора. Не минах през това за простота, тъй като просто исках да кажа, че съюзът взема предвид най-големия елемент. Важно е да знаете, че действителният размер наистина зависи от подравняването.
Зависи от компилатора и от опциите.
int main() {
union {
char all[13];
int foo;
} record;
printf("%d\n",sizeof(record.all));
printf("%d\n",sizeof(record.foo));
printf("%d\n",sizeof(record));
}
Това извежда:
13 4 16
Ако си спомням правилно, зависи от подравняването, което компилаторът поставя в разпределеното пространство. Така че, освен ако не използвате някаква специална опция, компилаторът ще постави подложка във вашето пространство за обединение.
редактиране: с gcc трябва да използвате прагма директива
int main() {
#pragma pack(push, 1)
union {
char all[13];
int foo;
} record;
#pragma pack(pop)
printf("%d\n",sizeof(record.all));
printf("%d\n",sizeof(record.foo));
printf("%d\n",sizeof(record));
}
това извежда
13 4 13
Можете също да го видите от разглобяването (премахнах някои printf, за яснота)
0x00001fd2 <main+0>: push %ebp | 0x00001fd2 <main+0>: push %ebp
0x00001fd3 <main+1>: mov %esp,%ebp | 0x00001fd3 <main+1>: mov %esp,%ebp
0x00001fd5 <main+3>: push %ebx | 0x00001fd5 <main+3>: push %ebx
0x00001fd6 <main+4>: sub $0x24,%esp | 0x00001fd6 <main+4>: sub $0x24,%esp
0x00001fd9 <main+7>: call 0x1fde <main+12> | 0x00001fd9 <main+7>: call 0x1fde <main+12>
0x00001fde <main+12>: pop %ebx | 0x00001fde <main+12>: pop %ebx
0x00001fdf <main+13>: movl $0xd,0x4(%esp) | 0x00001fdf <main+13>: movl $0x10,0x4(%esp)
0x00001fe7 <main+21>: lea 0x1d(%ebx),%eax | 0x00001fe7 <main+21>: lea 0x1d(%ebx),%eax
0x00001fed <main+27>: mov %eax,(%esp) | 0x00001fed <main+27>: mov %eax,(%esp)
0x00001ff0 <main+30>: call 0x3005 <printf> | 0x00001ff0 <main+30>: call 0x3005 <printf>
0x00001ff5 <main+35>: add $0x24,%esp | 0x00001ff5 <main+35>: add $0x24,%esp
0x00001ff8 <main+38>: pop %ebx | 0x00001ff8 <main+38>: pop %ebx
0x00001ff9 <main+39>: leave | 0x00001ff9 <main+39>: leave
0x00001ffa <main+40>: ret | 0x00001ffa <main+40>: ret
Където единствената разлика е в main+13, където компилаторът разпределя в стека 0xd вместо 0x10
Няма понятие за активен тип данни за обединение. Вие сте свободни да четете и пишете всеки „член“ на съюза: от вас зависи да тълкувате това, което получавате.
Следователно размерът на един съюз винаги е размерът на неговия най-голям тип данни.
g++
, например, те използват C правилата).
- person underscore_d; 14.06.2016
Размерът ще бъде поне този на най-големия тип композиране. Няма концепция за "активен" тип.
Наистина трябва да гледате на обединението като на контейнер за най-големия тип данни вътре в него, комбиниран с пряк път за каст. Когато използвате един от по-малките членове, неизползваното пространство все още е там, но просто остава неизползвано.
Често виждате това да се използва в комбинация с ioctl() извиквания в Unix, всички ioctl() извиквания ще преминат една и съща структура, която съдържа обединение на всички възможни отговори. напр. този пример идва от /usr/include/linux/if.h и тази структура се използва в ioctl() за конфигуриране/запитване за състоянието на ethernet интерфейс, параметрите на заявката определят коя част от обединението действително се използва:
struct ifreq
{
#define IFHWADDRLEN 6
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
void * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
Какъв е размерът на обединението в C/C++? Това ли е размерът на най-големия тип данни в него?
Да, размерът на съюза е размерът на най-големия му член.
Например :
#include<stdio.h>
union un
{
char c;
int i;
float f;
double d;
};
int main()
{
union un u1;
printf("sizeof union u1 : %ld\n",sizeof(u1));
return 0;
}
Изход:
sizeof union u1 : 8
sizeof double d : 8
Тук най-големият член е double
. И двете са с размер 8
. Така че, както sizeof
правилно ви каза, размерът на съюза наистина е 8
.
как компилаторът изчислява как да премести указателя на стека, ако един от по-малките типове данни на обединението е активен?
Вътрешно се обработва от компилатора. Да предположим, че имаме достъп до един от данните член на обединение, тогава не можем да осъществим достъп до друг член на данни, тъй като можем да осъществим достъп до един член на данни на обединение, защото всеки член на данни споделя една и съща памет. С помощта на Union можем да спестим много ценно пространство.
Размерът на най-големия член.
Ето защо обединенията обикновено имат смисъл в структура, която има флаг, който показва кой е "активният" член.
Пример:
struct ONE_OF_MANY {
enum FLAG { FLAG_SHORT, FLAG_INT, FLAG_LONG_LONG } flag;
union { short x; int y; long long z; };
};