Diferenças
Aqui você vê as diferenças entre duas revisões dessa página.
Ambos lados da revisão anterior Revisão anterior | |||
c:unioes [2023/08/15 14:57] – maziero | c:unioes [2023/08/15 14:57] (atual) – maziero | ||
---|---|---|---|
Linha 1: | Linha 1: | ||
+ | ====== Uniões ====== | ||
+ | |||
+ | {{ progc_unions.mkv |Video desta aula}} | ||
+ | |||
+ | Uma **união** ('' | ||
+ | |||
+ | A figura a seguir ilustra a diferença entre '' | ||
+ | |||
+ | {{ union-struct.png |Union vs. Struct}} | ||
+ | |||
+ | Uniões são uma forma eficiente de armazenar valores de diferentes tipos em uma mesma posição de memória. Obviamente, somente um valor pode ser armazenado a cada instante. A quantidade de memória ocupada por uma união corresponde ao tamanho de seu **maior campo**. | ||
+ | |||
+ | No exemplo abaixo, uma união permite " | ||
+ | |||
+ | <code c union.c> | ||
+ | #include < | ||
+ | |||
+ | // guarda um inteiro OU um vetor de 4 bytes | ||
+ | typedef union | ||
+ | { | ||
+ | int value ; | ||
+ | unsigned char byte[sizeof(int)] ; | ||
+ | } intParts ; | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | intParts a ; | ||
+ | int i ; | ||
+ | |||
+ | a.value = 653459 ; | ||
+ | |||
+ | printf ("%d: ", a.value) ; | ||
+ | | ||
+ | for (i = 0; i < sizeof (int); i++) | ||
+ | printf ("%02x ", a.byte[i]) ; | ||
+ | printf (" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Os campos '' | ||
+ | |||
+ | ^ // | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | Uniões podem ser usadas para armazenar valores de diversos tipos em uma única locação de memória. Por exemplo, a união a seguir pode ser usada para armazenar números de diversos tipos: | ||
+ | |||
+ | <code c> | ||
+ | typedef union | ||
+ | { | ||
+ | short shortVal ; | ||
+ | int intVal ; | ||
+ | long | ||
+ | float floatVal ; | ||
+ | double doubleVal ; | ||
+ | } numeric_t ; | ||
+ | </ | ||
+ | |||
+ | Entretanto, como saber qual o tipo do último valor armazenado, ou seja, qual o valor corrente? | ||
+ | |||
+ | <note warning> | ||
+ | Se armazenarmos um '' | ||
+ | </ | ||
+ | |||
+ | <code c union-error.c> | ||
+ | #include < | ||
+ | |||
+ | typedef union | ||
+ | { | ||
+ | short shortVal ; | ||
+ | int intVal ; | ||
+ | long | ||
+ | float floatVal ; | ||
+ | double doubleVal ; | ||
+ | } numeric_t ; | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | numeric_t a ; | ||
+ | |||
+ | a.shortVal = 741 ; | ||
+ | printf (" | ||
+ | printf (" | ||
+ | |||
+ | a.floatVal = 327.5432 ; | ||
+ | printf (" | ||
+ | printf (" | ||
+ | |||
+ | a.doubleVal = 327.5432 ; | ||
+ | printf (" | ||
+ | printf (" | ||
+ | printf (" | ||
+ | return 0 ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Para resolver esse problema pode ser usada uma estrutura contendo a união e uma variável que indique o tipo do último valor armazenado: | ||
+ | |||
+ | <code c union-type.c> | ||
+ | #include < | ||
+ | |||
+ | typedef struct | ||
+ | { | ||
+ | union // ATTENTION: " | ||
+ | { | ||
+ | short shortVal ; | ||
+ | int intVal ; | ||
+ | long | ||
+ | float floatVal ; | ||
+ | double doubleVal ; | ||
+ | } ; | ||
+ | enum { SHORT, INT, LONG, FLOAT, DOUBLE } type ; | ||
+ | } numeric_t ; | ||
+ | |||
+ | // imprime tipo numérico | ||
+ | void print_num (numeric_t n) | ||
+ | { | ||
+ | switch (n.type) | ||
+ | { | ||
+ | case SHORT : printf (" | ||
+ | case INT : printf (" | ||
+ | case LONG : printf (" | ||
+ | case FLOAT : printf (" | ||
+ | case DOUBLE : printf (" | ||
+ | default | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | numeric_t a ; | ||
+ | |||
+ | a.shortVal = 119 ; | ||
+ | a.type = SHORT ; | ||
+ | print_num (a) ; | ||
+ | printf (" | ||
+ | |||
+ | a.longVal = 3451212796756 ; | ||
+ | a.type = LONG ; | ||
+ | print_num (a) ; | ||
+ | printf (" | ||
+ | |||
+ | a.doubleVal = 3.141592653589793 ; | ||
+ | a.type = DOUBLE ; | ||
+ | print_num (a) ; | ||
+ | printf (" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | O exemplo acima traz um exemplo de **união anônima**, ou seja, sem nome. Nesse caso, os membros da união são considerados como membros da estrutura externa que contém a união. Estruturas também podem ser anônimas. | ||
+ | </ | ||
+ | |||
+ | A linguagem C respeita a ordem de declaração dos campos de uma estrutura, ou seja, eles são colocados na memória na mesma ordem em que são declarados. | ||
+ | |||
+ | Isso permite outro exemplo interessante do uso de uma união, no qual as moedas podem ser acessadas com nomes individuais ou como elementos de um vetor: | ||
+ | |||
+ | <code c> | ||
+ | typedef union { | ||
+ | struct | ||
+ | { | ||
+ | int quarter; | ||
+ | int dime; | ||
+ | int nickel; | ||
+ | int penny; | ||
+ | }; | ||
+ | int coins[4]; | ||
+ | } Coins_t ; | ||
+ | |||
+ | Coins_t a ; | ||
+ | |||
+ | a.dime = 34 ; // são operações equivalentes | ||
+ | a.coins[1] = 34 ; | ||
+ | </ | ||
+ | |||
+ | Outro exemplo interessante é a definição de números reais segundo o padrão [[https:// | ||
+ | |||
+ | <code c> | ||
+ | // extraído (e simplificado) de / | ||
+ | |||
+ | union ieee754_float | ||
+ | { | ||
+ | float f; | ||
+ | |||
+ | /* This is the IEEE 754 single-precision format. | ||
+ | struct | ||
+ | { | ||
+ | #if __BYTE_ORDER == __BIG_ENDIAN | ||
+ | unsigned int negative:1; | ||
+ | unsigned int exponent:8; | ||
+ | unsigned int mantissa: | ||
+ | # | ||
+ | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
+ | unsigned int mantissa: | ||
+ | unsigned int exponent:8; | ||
+ | unsigned int negative:1; | ||
+ | # | ||
+ | } ieee; | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | ===== Exercícios ===== | ||
+ | |||
+ | - Utilizando uma única variável '' | ||
+ | - Variáveis de 32 bits do tipo '' | ||
+ | - Utilizando unions, crie um programa capaz de receber um determinado valor e calcular o módulo de 256 desse valor (dica: utilize '' | ||