C Unions по сути такие же, как C Structures, за исключением того, что вместо того, чтобы содержать несколько переменных, каждая со своей собственной памятью, Union позволяет использовать несколько имен для одной и той же переменной. Эти имена могут обрабатывать память как разные типы (и размер объединения будет размером самого большого типа + любое дополнение, которое компилятор может решить дать ему)
Итак, если вы хотите иметь возможность читать память переменной разными способами, например, читать целое число по одному байту за раз, у вас может быть что-то вроде этого:
union intParts {
int theInt;
char bytes[sizeof(int)];
};
Позволяя вам просматривать каждый байт по отдельности, не применяя указатель и не используя арифметику указателя:
union intParts parts;
parts.theInt = 5968145; // arbitrary number > 255 (1 byte)
printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n",
parts.theInt, parts.bytes[0], parts.bytes[1], parts.bytes[2], parts.bytes[3]);
// vs
int theInt = parts.theInt;
printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n",
theInt, *((char*)&theInt+0), *((char*)&theInt+1), *((char*)&theInt+2), *((char*)&theInt+3));
// or with array syntax which can be a tiny bit nicer sometimes
printf("The int is %i\nThe bytes are [%i, %i, %i, %i]\n",
theInt, ((char*)&theInt)[0], ((char*)&theInt)[1], ((char*)&theInt)[2], ((char*)&theInt)[3]);
Комбинируя это со структурой, вы можете создать «помеченное» объединение, которое можно использовать для хранения нескольких различных типов, по одному за раз.
Например, у вас может быть структура типа «число», но вы не хотите использовать что-то вроде этого:
struct operator {
int intNum;
float floatNum;
int type;
double doubleNum;
};
Поскольку в вашей программе их много и для всех переменных требуется слишком много памяти, вы можете использовать это:
struct operator {
int type;
union {
int intNum;
float floatNum;
double doubleNum;
} types;
};
Таким образом, размер структуры - это просто размер int type
+ размер самого большого типа в объединении (double). Небольшой выигрыш, всего 8 или 16 байт, но эту концепцию можно применить к аналогичным структурам.
использовать:
operator op;
op.type = 0; // int, probably better as an enum or macro constant
op.types.intNum = 352;
Кроме того, если вы не дадите объединению имя, доступ к его членам будет осуществляться непосредственно из структуры:
struct operator {
int type;
union {
int intNum;
float floatNum;
double doubleNum;
}; // no name!
};
operator op;
op.type = 0; // int
// intNum is part of the union, but since it's not named you access it directly off the struct itself
op.intNum = 352;
Другая, возможно, более полезная функция - это когда у вас всегда есть несколько переменных одного и того же типа, и вы хотите иметь возможность использовать как имена (для удобства чтения), так и индексы (для упрощения итерации), в этом случае вы можете сделать что-то вроде это:
union Coins {
struct {
int quarter;
int dime;
int nickel;
int penny;
}; // anonymous struct acts the same way as an anonymous union, members are on the outer container
int coins[4];
};
В этом примере вы можете видеть, что есть структура, которая содержит четыре (обычных) монеты в Соединенных Штатах.
поскольку объединение заставляет переменные совместно использовать одну и ту же память, массив монет совпадает с каждым int в структуре (по порядку):
union Coins change;
for(int i = 0; i < sizeof(change) / sizeof(int); ++i)
{
scanf("%i", change.coins + i); // BAD code! input is always suspect!
}
printf("There are %i quarters, %i dimes, %i nickels, and %i pennies\n",
change.quarter, change.dime, change.nickel, change.penny);
Создайте объединение, в котором хранится массив из 21 символа и 6 целых чисел (6, поскольку 21/4 == 5, но 5 * 4 == 20, поэтому вам понадобится еще 1 для целей этого упражнения), вы установите целые числа равными 6 заданные значения, а затем распечатать массив символов как серию символов, так и строку.