I do the latter thing, because I don't want to depend on gcc (or
rather have to use machines where gcc isn't available).
Here is a SOCKS request structure from this POV:
#define rq_ver(s) ((s)->buf[0])
#define rq_cmd(s) ((s)->buf[1])
#define rq_rsv(s) ((s)->buf[2])
#define rq_atyp(s) ((s)->buf[3])
#define rq_addr(s) (*(struct in_addr *)(((s)->buf)+4))
#define rq_port(s) (*(unsigned short *)(((s)->buf)+8))
where s is a pointer to a struct which contains
unsigned char buf[BUFS];
The code gets rather readable this way.
Beware - both __attribute__((packed)) and this method don't account
for hardware enforced alignment. If it is ever compiled on a system
that requires 8-byte alignment for a struct in_addr (a 4-byte entity)
it will bomb. I don't know if there is such hardware, but I have
already got bus errors on an HP using this technique on a misaligned
short (with the same program). A way around that is something like
#define xx(s) ((((s)->buf[5])<<8)+(s)->buf[6])
if xx is an unsigned short in network order on position 5.
olaf