paulo1205
(usa Ubuntu)
Enviado em 19/01/2016 - 00:52h
union é usado em casos em que você pode sobrepor dados em memória, permitindo interpretá-los de mais de um modo diferente.
O tamanho de um
union é igual ao tamanho do maior dos seus membros, e todos os membros são alinhados em memória pelo primeiro endereço. Por exemplo, veja o código abaixo (a saída assumem que se esteja usando uma máquina little-endian).
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
union teste {
uint8_t bytes[4];
uint32_t word;
uint64_t long_word;
char string[40];
} union_var;
int main(void){
printf("sizeof union_var: %zd\n", sizeof union_var); // imprime 40
printf("sizeof union_var.bytes: %zd\n", sizeof union_var.bytes); // imprime 4
printf("sizeof union_var.word: %zd\n", sizeof union_var.word); // imprime 4
printf("sizeof union_var.long_word: %zd\n", sizeof union_var.long_word); // imprime 8
printf("sizeof union_var.string: %zd\n\n", sizeof union_var.string); // imprime 40
printf("&union_var: %p\n", &union_var); // todos vão imprimir o mesmo endereço
printf("&union_var.bytes: %p\n", &union_var.bytes); // todos vão imprimir o mesmo endereço
printf("&union_var.word: %p\n", &union_var.word); // todos vão imprimir o mesmo endereço
printf("&union_var.long_word: %p\n", &union_var.long_word); // todos vão imprimir o mesmo endereço
printf("&union_var.string: %p\n\n", &union_var.string); // todos vão imprimir o mesmo endereço
strcpy(union_var.string, "paulo1205");
printf("union_var.bytes[0]: %02hhX\n", union_var.bytes[0]); // imprime "70" (p)
printf("union_var.word: %08X\n", union_var.word); // imprime "6C756170" (l, u, a, p)
printf("union_var.long_word: %016" PRIX64 "\n", union_var.long_word); // imprime "3032316F6C756170" (0, 2, 1, o, l, u, a, p)
printf("union_var.string: %s\n\n", union_var.string); // imprime "paulo1205"
union_var.bytes[0]++; // incrementa primeiro byte
union_var.word++; // incrementa palavra de 32 bits
union_var.long_word++; // incrementa palavra de 64 bits
printf("union_var.string: %s\n", union_var.string); // imprime "saulo1205"
return 0;
}
Outro exemplo didático, para brincar de examinar como é a forma interna de representar números de ponto flutuante da máquina.
union fp_test_f {
float f;
uint8_t bytes[sizeof(float)];
uint32_t i32s[sizeof(float)/sizeof(uint32_t)>0? sizeof(float)/sizeof(uint32_t): 1];
};
union fp_test_d {
double d;
uint8_t bytes[sizeof(double)];
uint32_t i32s[sizeof(double)/sizeof(uint32_t)>0? sizeof(double)/sizeof(uint32_t): 1];
};
Poucas vezes usei
union em programas meus. No entanto, uma vez eu fiz um programa para controle de um no-break, e ele tinha uma palavra de status a que às vezes era melhor aceder de uma vez (membro do tipo
int value ), e noutras através dos flags individuais (
bit fields de uma estrutura chamada
flags ). Eu acabei definindo uma
union com a seguinte cara (o tratamento de little e big endian é porque o programa rodava tanto em PCs como em um servidor Sun/SPARC).
union UPS_PSW {
int value;
struct {
#if BYTE_ORDER == LITTLE_ENDIAN
int alarm:1; /* Alarm sound enabled */
int shutdown:1; /* Shutdown sequence initiated */
int selftest:1; /* Self test active */
int outputpower:1; /* Power on outputs active (?) */
int reserved:1; /* Reserved (unused) */
int badbat:1; /* Battery is bad or needs to be replaced (?) */
int lowbat:1; /* Battery voltage is low */
int powerfail:1; /* Power failure */
int space:sizeof(int)*8-8; /* padding space */
#elif BYTE_ORDER == BIG_ENDIAN
int space:sizeof(int)*8-8; /* padding space */
int powerfail:1; /* Power failure */
int lowbat:1; /* Battery voltage is low */
int badbat:1; /* Battery is bad or needs to be replaced (?) */
int reserved:1; /* Reserved (unused) */
int outputpower:1; /* Power on outputs active (?) */
int selftest:1; /* Self test active */
int shutdown:1; /* Shutdown sequence initiated */
int alarm:1; /* Alarm sound enabled */
#endif
} flags;
};