Unions are complex. For many years I could not understand them, then I started to deal with network protocols, and someone showed me the light. Say you have a header, and then after the header there are different types of packages, for example:
| type (4 bytes) | uid (8 bytes) | payload length (2 bytes) | Payload (variable length) |
And then there will be different types of payload packets ... For the argument, there may be greetings, goodbye and message packets ...
Well, you can build a nested set of structures / unions that can accurately represent a package in this protocol, for example ...
struct packet { uint type; char unique_id [8]; ushort payload_length; union payload { struct hello { ushort version; uint status; }; struct goodbye { char reason[20]; uint status; }; struct message { char message[100]; }; }; };
Inevitably, you get this protocol from the operating system through a call to read (), and it's just a mess of bytes. But if you are careful about defining your structure, and all types are of the right size, you can simply make a pointer to the structure, point it to your buffer filled with random data, and ...
char buf[100]; packet *pkt; read(outsideworld,&buf,1000); pkt = (struct packet *)&buf;
and reading your packages is as simple as ...
switch(pkt->type){ case PACKET_MESSAGE: printf("message = %s\n", pkt->payload.message.message); break; case PACKET_HELLO: printf("hello! version = %d status = %d\n", pkt->payload.hello.version, pkt->payload.hello.status); break; case PACKET_GOODBYE: printf("goodbye! reason = %s status = %d\n", pkt->payload.goodbye.reason, pkt->payload.goodbye.status); break; }
Do not cheat, count bytes, etc. You can nest it as deep as you want (create a union for ip addresses that gives you all this as an unsigned int or separate bytes, so it’s easier to type 192.168.0.1).
Unions do not slow down your code, because all this simply translates into offsets into machine code.