Why do we need C Unions?
When should unions be used? Why开发者_运维百科 do we need them?
Unions are often used to convert between the binary representations of integers and floats:
union
{
int i;
float f;
} u;
// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);
Although this is technically undefined behavior according to the C standard (you're only supposed to read the field which was most recently written), it will act in a well-defined manner in virtually any compiler.
Unions are also sometimes used to implement pseudo-polymorphism in C, by giving a structure some tag indicating what type of object it contains, and then unioning the possible types together:
enum Type { INTS, FLOATS, DOUBLE };
struct S
{
Type s_type;
union
{
int s_ints[2];
float s_floats[2];
double s_double;
};
};
void do_something(struct S *s)
{
switch(s->s_type)
{
case INTS: // do something with s->s_ints
break;
case FLOATS: // do something with s->s_floats
break;
case DOUBLE: // do something with s->s_double
break;
}
}
This allows the size of struct S
to be only 12 bytes, instead of 28.
Unions are particularly useful in Embedded programming or in situations where direct access to the hardware/memory is needed. Here is a trivial example:
typedef union
{
struct {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
} bytes;
unsigned int dword;
} HW_Register;
HW_Register reg;
Then you can access the reg as follows:
reg.dword = 0x12345678;
reg.bytes.byte3 = 4;
Endianness (byte order) and processor architecture are of course important.
Another useful feature is the bit modifier:
typedef union
{
struct {
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char reserved:4;
} bits;
unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;
With this code you can access directly a single bit in the register/memory address:
x = reg.bits.b2;
Low level system programming is a reasonable example.
IIRC, I've used unions to breakdown hardware registers into the component bits. So, you can access an 8-bit register (as it was, in the day I did this ;-) into the component bits.
(I forget the exact syntax but...) This structure would allow a control register to be accessed as a control_byte or via the individual bits. It would be important to ensure the bits map on to the correct register bits for a given endianness.
typedef union {
unsigned char control_byte;
struct {
unsigned int nibble : 4;
unsigned int nmi : 1;
unsigned int enabled : 1;
unsigned int fired : 1;
unsigned int control : 1;
};
} ControlRegister;
I've seen it in a couple of libraries as a replacement for object oriented inheritance.
E.g.
Connection
/ | \
Network USB VirtualConnection
If you want the Connection "class" to be either one of the above, you could write something like:
struct Connection
{
int type;
union
{
struct Network network;
struct USB usb;
struct Virtual virtual;
}
};
Example use in libinfinity: http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74
Unions allow data members which are mutually exclusive to share the same memory. This is quite important when memory is more scarce, such as in embedded systems.
In the following example:
union {
int a;
int b;
int c;
} myUnion;
This union will take up the space of a single int, rather than 3 separate int values. If the user set the value of a, and then set the value of b, it would overwrite the value of a since they are both sharing the same memory location.
Lots of usages. Just do grep union /usr/include/*
or in similar directories. Most of the cases the union
is wrapped in a struct
and one member of the struct tells which element in the union to access. For example checkout man elf
for real life implementations.
This is the basic principle:
struct _mydata {
int which_one;
union _data {
int a;
float b;
char c;
} foo;
} bar;
switch (bar.which_one)
{
case INTEGER : /* access bar.foo.a;*/ break;
case FLOATING : /* access bar.foo.b;*/ break;
case CHARACTER: /* access bar.foo.c;*/ break;
}
Here's an example of a union from my own codebase (from memory and paraphrased so it may not be exact). It was used to store language elements in an interpreter I built. For example, the following code:
set a to b times 7.
consists of the following language elements:
- symbol[set]
- variable[a]
- symbol[to]
- variable[b]
- symbol[times]
- constant[7]
- symbol[.]
Language elements were defines as '#define
' values thus:
#define ELEM_SYM_SET 0
#define ELEM_SYM_TO 1
#define ELEM_SYM_TIMES 2
#define ELEM_SYM_FULLSTOP 3
#define ELEM_VARIABLE 100
#define ELEM_CONSTANT 101
and the following structure was used to store each element:
typedef struct {
int typ;
union {
char *str;
int val;
}
} tElem;
then the size of each element was the size of the maximum union (4 bytes for the typ and 4 bytes for the union, though those are typical values, the actual sizes depend on the implementation).
In order to create a "set" element, you would use:
tElem e;
e.typ = ELEM_SYM_SET;
In order to create a "variable[b]" element, you would use:
tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b"); // make sure you free this later
In order to create a "constant[7]" element, you would use:
tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;
and you could easily expand it to include floats (float flt
) or rationals (struct ratnl {int num; int denom;}
) and other types.
The basic premise is that the str
and val
are not contiguous in memory, they actually overlap, so it's a way of getting a different view on the same block of memory, illustrated here, where the structure is based at memory location 0x1010
and integers and pointers are both 4 bytes:
+-----------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-----+-----+
0x1014 | | |
0x1015 | str | val |
0x1016 | | |
0x1017 | | |
+-----+-----+
If it were just in a structure, it would look like this:
+-------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-------+
0x1014 | |
0x1015 | str |
0x1016 | |
0x1017 | |
+-------+
0x1018 | |
0x1019 | val |
0x101A | |
0x101B | |
+-------+
I'd say it makes it easier to reuse memory that might be used in different ways, i.e. saving memory. E.g. you'd like to do some "variant" struct that's able to save a short string as well as a number:
struct variant {
int type;
double number;
char *string;
};
In a 32 bit system this would result in at least 96 bits or 12 bytes being used for each instance of variant
.
Using an union you can reduce the size down to 64 bits or 8 bytes:
struct variant {
int type;
union {
double number;
char *string;
} value;
};
You're able to save even more if you'd like to add more different variable types etc. It might be true, that you can do similar things casting a void pointer - but the union makes it a lot more accessible as well as type safe. Such savings don't sound massive, but you're saving one third of the memory used for all instances of this struct.
Many of these answers deal with casting from one type to another. I get the most use from unions with the same types just more of them (ie when parsing a serial data stream). They allow the parsing / construction of a framed packet to become trivial.
typedef union
{
UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
// the entire set of fields (including the payload)
struct
{
UINT8 size;
UINT8 cmd;
UINT8 payload[PAYLOAD_SIZE];
UINT8 crc;
} fields;
}PACKET_T;
// This should be called every time a new byte of data is ready
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);
void packet_builder(UINT8* buffer, UINT8 data)
{
static UINT8 received_bytes = 0;
// All range checking etc removed for brevity
buffer[received_bytes] = data;
received_bytes++;
// Using the struc only way adds lots of logic that relates "byte 0" to size
// "byte 1" to cmd, etc...
}
void packet_handler(PACKET_T* packet)
{
// Process the fields in a readable manner
if(packet->fields.size > TOO_BIG)
{
// handle error...
}
if(packet->fields.cmd == CMD_X)
{
// do stuff..
}
}
Edit The comment about endianness and struct padding are valid, and great, concerns. I have used this body of code almost entirely in embedded software, most of which I had control of both ends of the pipe.
It's difficult to think of a specific occasion when you'd need this type of flexible structure, perhaps in a message protocol where you would be sending different sizes of messages, but even then there are probably better and more programmer friendly alternatives.
Unions are a bit like variant types in other languages - they can only hold one thing at a time, but that thing could be an int, a float etc. depending on how you declare it.
For example:
typedef union MyUnion MYUNION;
union MyUnion
{
int MyInt;
float MyFloat;
};
MyUnion will only contain an int OR a float, depending on which you most recently set. So doing this:
MYUNION u;
u.MyInt = 10;
u now holds an int equal to 10;
u.MyFloat = 1.0;
u now holds a float equal to 1.0. It no longer holds an int. Obviously now if you try and do printf("MyInt=%d", u.MyInt); then you're probably going to get an error, though I'm unsure of the specific behaviour.
The size of the union is dictated by the size of its largest field, in this case the float.
Unions are used when you want to model structs defined by hardware, devices or network protocols, or when you're creating a large number of objects and want to save space. You really don't need them 95% of the time though, stick with easy-to-debug code.
union are used to save memory, especially used on devices with limited memory where memory is important. Exp:
union _Union{
int a;
double b;
char c;
};
For example,let's say we need the above 3 data types(int,double,char) in a system where memory is limited.If we don't use "union",we need to define these 3 data types. In this case sizeof(a) + sizeof(b) + sizeof(c) memory space will be allocated.But if we use onion,only one memory space will be allocated according to the largest data t ype in these 3 data types.Because all variables in union structure will use the same memory space. Hence the memory space allocated accroding to the largest data type will be common space for all variables. For example:
union _Union{
int a;
double b;
char c;
};
int main() {
union _Union uni;
uni.a = 44;
uni.b = 144.5;
printf("a:%d\n",uni.a);
printf("b:%lf\n",uni.b);
return 0;
}
Output is: a: 0 and b:144.500000
Why a is zero?. Because union structure has only one memory area and all data structures use it in common. So the last assigned value overwrites the old one. One more example:
union _Union{
char name[15];
int id;
};
int main(){
union _Union uni;
char choice;
printf("YOu can enter name or id value.");
printf("Do you want to enter the name(y or n):");
scanf("%c",&choice);
if(choice == 'Y' || choice == 'y'){
printf("Enter name:");
scanf("%s",uni.name);
printf("\nName:%s",uni.name);
}else{
printf("Enter Id:");
scanf("%d",&uni.id);
printf("\nId:%d",uni.id);
}
return 0;
}
Note:Size of the union is the size of its largest field because sufficient number of bytes must be reserved to store the larges sized field.
In school, I used unions like this:
typedef union
{
unsigned char color[4];
int new_color;
} u_color;
I used it to handle colors more easily, instead of using >> and << operators, I just had to go through the different index of my char array.
In early versions of C, all structure declarations would share a common set of fields. Given:
struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};
a compiler would essentially produce a table of structures' sizes (and possibly alignments), and a separate table of structures' members' names, types, and offsets. The compiler didn't keep track of which members belonged to which structures, and would allow two structures to have a member with the same name only if the type and offset matched (as with member q
of struct x
and struct y
). If p was a pointer to any structure type, p->q would add the offset of "q" to pointer p and fetch an "int" from the resulting address.
Given the above semantics, it was possible to write a function that could perform some useful operations on multiple kinds of structure interchangeably, provided that all the fields used by the function lined up with useful fields within the structures in question. This was a useful feature, and changing C to validate members used for structure access against the types of the structures in question would have meant losing it in the absence of a means of having a structure that can contain multiple named fields at the same address. Adding "union" types to C helped fill that gap somewhat (though not, IMHO, as well as it should have been).
An essential part of unions' ability to fill that gap was the fact that a pointer to a union member could be converted into a pointer to any union containing that member, and a pointer to any union could be converted to a pointer to any member. While the C89 Standard didn't expressly say that casting a T*
directly to a U*
was equivalent to casting it to a pointer to any union type containing both T
and U
, and then casting that to U*
, no defined behavior of the latter cast sequence would be affected by the union type used, and the Standard didn't specify any contrary semantics for a direct cast from T
to U
. Further, in cases where a function received a pointer of unknown origin, the behavior of writing an object via T*
, converting the T*
to a U*
, and then reading the object via U*
would be equivalent to writing a union via member of type T
and reading as type U
, which would be standard-defined in a few cases (e.g. when accessing Common Initial Sequence members) and Implementation-Defined (rather than Undefined) for the rest. While it was rare for programs to exploit the CIS guarantees with actual objects of union type, it was far more common to exploit the fact that pointers to objects of unknown origin had to behave like pointers to union members and have the behavioral guarantees associated therewith.
Unions are great. One clever use of unions I've seen is to use them when defining an event. For example, you might decide that an event is 32 bits.
Now, within that 32 bits, you might like to designate the first 8 bits as for an identifier of the sender of the event... Sometimes you deal with the event as a whole, sometimes you dissect it and compare it's components. unions give you the flexibility to do both.
union Event { unsigned long eventCode; unsigned char eventParts[4]; };
What about VARIANT
that is used in COM interfaces? It has two fields - "type" and a union holding an actual value that is treated depending on "type" field.
I used union when I was coding for embedded devices. I have C int that is 16 bit long. And I need to retrieve the higher 8 bits and the lower 8 bits when I need to read from/store to EEPROM. So I used this way:
union data {
int data;
struct {
unsigned char higher;
unsigned char lower;
} parts;
};
It doesn't require shifting so the code is easier to read.
On the other hand, I saw some old C++ stl code that used union for stl allocator. If you are interested, you can read the sgi stl source code. Here is a piece of it:
union _Obj {
union _Obj* _M_free_list_link;
char _M_client_data[1]; /* The client sees this. */
};
- A file containing different record types.
- A network interface containing different request types.
Take a look at this: X.25 buffer command handling
One of the many possible X.25 commands is received into a buffer and handled in place by using a UNION of all the possible structures.
A simple and very usefull example, is....
Imagine:
you have a uint32_t array[2]
and want to access the 3rd and 4th Byte of the Byte chain.
you could do *((uint16_t*) &array[1])
.
But this sadly breaks the strict aliasing rules!
But known compilers allow you to do the following :
union un
{
uint16_t array16[4];
uint32_t array32[2];
}
technically this is still a violation of the rules. but all known standards support this usage.
Use a union when you have some function where you return a value that can be different depending on what the function did.
精彩评论