Respostas:
Com uma união, você deve usar apenas um dos elementos, porque todos eles são armazenados no mesmo local. Isso o torna útil quando você deseja armazenar algo que pode ser de vários tipos. Uma estrutura, por outro lado, possui um local de memória separado para cada um de seus elementos e todos eles podem ser usados ao mesmo tempo.
Para dar um exemplo concreto de seu uso, eu estava trabalhando em um intérprete de Scheme há pouco tempo e estava essencialmente sobrepondo os tipos de dados Scheme nos tipos C. Isso envolvia armazenar em uma estrutura uma enum indicando o tipo de valor e uma união para armazenar esse valor.
union foo {
int a; // can't use both a and b at once
char b;
} foo;
struct bar {
int a; // can use both a and b simultaneously
char b;
} bar;
union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!
struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK
edit: Se você está se perguntando para que configuração xb como 'c' altera o valor de xa, tecnicamente falando é indefinido. Na maioria das máquinas modernas, um caractere é de 1 byte e um int é de 4 bytes, portanto, fornecer a xb o valor 'c' também fornecerá o primeiro byte de xa com o mesmo valor:
union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
impressões
99, 99
Por que os dois valores são os mesmos? Como os últimos 3 bytes do int 3 são todos zero, também é lido como 99. Se colocarmos um número maior para xa, você verá que esse nem sempre é o caso:
union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
impressões
387427, 99
Para ter uma visão mais detalhada dos valores reais da memória, vamos definir e imprimir os valores em hexadecimal:
union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);
impressões
deadbe22, 22
Você pode ver claramente onde o 0x22 substituiu o 0xEF.
MAS
Em C, a ordem dos bytes em um int não está definida. Este programa substituiu o 0xEF por 0x22 no meu Mac, mas há outras plataformas em que ele substituiria o 0xDE porque a ordem dos bytes que compõem o int foi revertida. Portanto, ao escrever um programa, você nunca deve confiar no comportamento de substituir dados específicos em uma união porque não é portátil.
Para mais informações sobre a ordenação de bytes, consulte endianness .
Aqui está a resposta curta: uma estrutura é uma estrutura de registro: cada elemento na estrutura aloca novo espaço. Então, uma estrutura como
struct foobarbazquux_t {
int foo;
long bar;
double baz;
long double quux;
}
aloca pelo menos (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))
bytes na memória para cada instância. ("Pelo menos" porque as restrições de alinhamento da arquitetura podem forçar o compilador a preencher a estrutura.)
Por outro lado,
union foobarbazquux_u {
int foo;
long bar;
double baz;
long double quux;
}
aloca um pedaço de memória e fornece quatro aliases. Então sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))
, novamente com a possibilidade de alguma adição para alinhamentos.
Existe algum bom exemplo para dar a diferença entre uma 'estrutura' e uma 'união'?
Um protocolo de comunicação imaginário
struct packetheader {
int sourceaddress;
int destaddress;
int messagetype;
union request {
char fourcc[4];
int requestnumber;
};
};
Nesse protocolo imaginário, foi especificado que, com base no "tipo de mensagem", o seguinte local no cabeçalho será um número de solicitação ou um código de quatro caracteres, mas não ambos. Em resumo, os sindicatos permitem que o mesmo local de armazenamento represente mais de um tipo de dados, onde é garantido que você deseje armazenar apenas um dos tipos de dados a qualquer momento.
Os sindicatos são em grande parte um detalhe de baixo nível baseado na herança de C como uma linguagem de programação do sistema, onde locais de armazenamento "sobrepostos" são às vezes usados dessa maneira. Às vezes, você pode usar uniões para economizar memória, onde você possui uma estrutura de dados em que apenas um dos vários tipos será salvo ao mesmo tempo.
Em geral, o sistema operacional não se importa ou conhece estruturas e uniões - ambos são simplesmente blocos de memória para ele. Uma estrutura é um bloco de memória que armazena vários objetos de dados, nos quais esses objetos não se sobrepõem. Uma união é um bloco de memória que armazena vários objetos de dados, mas possui apenas armazenamento para o maior deles e, portanto, pode armazenar apenas um dos objetos de dados a qualquer momento.
packetheader ph;
solicitação de acesso? ph.request.requestnumber
?
Como você já declarou em sua pergunta, a principal diferença entre union
e struct
é que os union
membros cobrem a memória um do outro, de modo que o tamanho da união seja o mesmo, enquanto os struct
membros são dispostos um após o outro (com preenchimento opcional no meio). Além disso, um sindicato é grande o suficiente para conter todos os seus membros e ter um alinhamento adequado a todos os seus membros. Então, digamos int
que só pode ser armazenado em endereços de 2 bytes e tem 2 bytes de largura, e o comprimento só pode ser armazenado em endereços de 4 bytes e tem 4 bytes. A seguinte união
union test {
int a;
long b;
};
poderia ter um sizeof
de 4 e um requisito de alinhamento de 4. Tanto uma união quanto uma estrutura podem ter preenchimento no final, mas não no início. Gravar em uma estrutura altera apenas o valor do membro gravado. Escrever para um membro de um sindicato invalidará o valor de todos os outros membros. Você não pode acessá-los se não tiver escrito antes, caso contrário, o comportamento é indefinido. O GCC fornece como uma extensão que você pode realmente ler dos membros de um sindicato, mesmo que não tenha escrito para eles mais recentemente. Para um sistema operacional, não é necessário importar se um programa do usuário grava em uma união ou em uma estrutura. Na verdade, isso é apenas uma questão do compilador.
Outra propriedade importante de união e estrutura é que eles permitem que um ponteiro para eles possa apontar para tipos de qualquer um de seus membros . Portanto, o seguinte é válido:
struct test {
int a;
double b;
} * some_test_pointer;
some_test_pointer pode apontar para int*
ou double*
. Se você converter um endereço do tipo test
para int*
, ele apontará para o primeiro membro a
, na verdade. O mesmo vale para uma união também. Portanto, como uma união sempre terá o alinhamento correto, você pode usar uma união para tornar válido apontar para algum tipo:
union a {
int a;
double b;
};
Essa união será capaz de apontar para um int e um double:
union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;
é realmente válido, conforme declarado pelo padrão C99:
Um objeto deve ter seu valor armazenado acessado apenas por uma expressão lvalue que possui um dos seguintes tipos:
- um tipo compatível com o tipo efetivo do objeto
- ...
- um tipo agregado ou de união que inclua um dos tipos mencionados acima entre seus membros
O compilador não otimizará o valor, v->a = 10;
pois isso pode afetar o valor de *some_int_pointer
(e a função retornará em 10
vez de 5
).
A union
é útil em alguns cenários.
union
pode ser uma ferramenta para manipulação de nível muito baixo, como escrever drivers de dispositivo para um kernel.
Um exemplo disso é dissecar um float
número usando union
a struct
com campos de bits e a float
. Eu guardo um número no float
e, posteriormente, posso acessar partes específicas do float
mesmo struct
. O exemplo mostra como union
é usado para ter ângulos diferentes para examinar os dados.
#include <stdio.h>
union foo {
struct float_guts {
unsigned int fraction : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} fg;
float f;
};
void print_float(float f) {
union foo ff;
ff.f = f;
printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);
}
int main(){
print_float(0.15625);
return 0;
}
Dê uma olhada na descrição de precisão única na wikipedia. Eu usei o exemplo e o número mágico 0,15625 a partir daí.
union
também pode ser usado para implementar um tipo de dados algébrico que possui várias alternativas. Encontrei um exemplo disso no livro "Real World Haskell" de O'Sullivan, Stewart e Goerzen. Confira na seção A união discriminada .
Felicidades!
" union " e " struct " são construções da linguagem C. Falar de uma diferença no "nível do sistema operacional" entre eles é inadequado, pois é o compilador que produz código diferente se você usar uma ou outra palavra-chave.
Não tecnicamente falando significa:
Suposição: cadeira = bloco de memória, pessoas = variável
Estrutura : Se houver 3 pessoas, elas poderão sentar-se na cadeira de seu tamanho correspondente.
União : Se houver 3 pessoas, apenas uma cadeira estará lá para sentar, todas precisam usar a mesma cadeira quando quiserem se sentar.
Tecnicamente falando significa:
O programa abaixo mencionado dá um mergulho profundo na estrutura e união.
struct MAIN_STRUCT
{
UINT64 bufferaddr;
union {
UINT32 data;
struct INNER_STRUCT{
UINT16 length;
UINT8 cso;
UINT8 cmd;
} flags;
} data1;
};
Total MAIN_STRUCT size = sizeof (UINT64) para bufferaddr + sizeof (UNIT32) para união + 32 bits para preenchimento (depende da arquitetura do processador) = 128 bits. Para estrutura, todos os membros obtêm o bloco de memória contíguo.
Union obtém um bloco de memória do membro de tamanho máximo (aqui são 32 bits). Dentro da união, há mais uma estrutura (INNER_STRUCT), seus membros obtêm um bloco de memória de tamanho total de 32 bits (16 + 8 + 8). Em união, o membro INNER_STRUCT (32 bits) ou os dados (32 bits) podem ser acessados.
Sim, a principal diferença entre struct e union é a mesma que você afirmou. O Struct usa toda a memória de seu membro e o union usa o maior espaço de memória de membros.
Mas toda a diferença está na necessidade de uso da memória. O melhor uso da união pode ser visto nos processos do unix, onde fazemos uso de sinais. como um processo, pode agir apenas com um sinal de cada vez. Portanto, a declaração geral será:
union SIGSELECT
{
SIGNAL_1 signal1;
SIGNAL_2 signal2;
.....
};
Nesse caso, o processo utiliza apenas a memória mais alta de todos os sinais. mas se você usar struct nesse caso, o uso da memória será a soma de todos os sinais. Faz muita diferença.
Para resumir, o Union deve ser selecionado se você souber que acessa qualquer membro de cada vez.
Você tem isso, é tudo. Mas então, basicamente, qual é o sentido dos sindicatos?
Você pode colocar no mesmo local o conteúdo de tipos diferentes. Você precisa saber o tipo do que armazenou na união (tantas vezes você o coloca em umstruct
com uma tag de tipo ...).
Por que isso é importante? Não é realmente para ganhos de espaço. Sim, você pode ganhar alguns bits ou fazer algum preenchimento, mas esse não é mais o ponto principal.
É para segurança de tipos, permite que você faça algum tipo de 'digitação dinâmica': o compilador sabe que seu conteúdo pode ter significados diferentes e o significado preciso de como sua interpretação depende de você em tempo de execução. Se você tem um ponteiro que pode apontar para tipos diferentes, você DEVE usar uma união, caso contrário, seu código pode estar incorreto devido a problemas de alias (o compilador diz para si mesmo "oh, apenas esse ponteiro pode apontar para esse tipo, para que eu possa otimizar fora esses acessos ... ", e coisas ruins podem acontecer).
Uma estrutura aloca o tamanho total de todos os elementos nela.
Uma união aloca apenas a quantidade de memória que o maior membro exige.
qual é a diferença entre estrutura e união?
A resposta curta é: A deferência está na alocação de memória. Explicação: Na estrutura, o espaço de memória será criado para todos os membros dentro da estrutura. Na união, o espaço de memória será criado apenas para um membro que precise de maior espaço de memória. Considere o seguinte código:
struct s_tag
{
int a;
long int b;
} x;
union u_tag
{
int a;
long int b;
} y;
Aqui existem dois membros dentro de struct e union: int e long int. O espaço de memória para int é: 4 bytes e o espaço de memória para int longo é: 8 no sistema operacional de 32 bits.
Portanto, para struct 4 + 8 = 12 bytes serão criados, enquanto 8 bytes serão criados para união
Exemplo de código:
#include<stdio.h>
struct s_tag
{
int a;
long int b;
} x;
union u_tag
{
int a;
long int b;
} y;
int main()
{
printf("Memory allocation for structure = %d", sizeof(x));
printf("\nMemory allocation for union = %d", sizeof(y));
return 0;
}
Ref: http://www.codingpractise.com/home/c-programming/structure-and-union/
Os usos das uniões sindicais são usados com freqüência quando são necessárias conversas especializadas do tipo. Para se ter uma idéia da utilidade da união. A biblioteca padrão c / c não define nenhuma função projetada especificamente para gravar números inteiros curtos em um arquivo. O uso de fwrite () incorre em sobrecarga excessiva para operação simples. No entanto, usando uma união, você pode criar facilmente uma função que grava o binário de um número inteiro curto em um arquivo, um byte por vez. Suponho que inteiros curtos tenham 2 bytes de comprimento
O EXEMPLO:
#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}
embora putw () chamei com número inteiro curto, era possível usar putc () e fwrite (). Mas eu queria mostrar um exemplo para dominar como uma união pode ser usada
estrutura é uma coleção de tipos de dados diferentes, onde diferentes tipos de dados podem residir nela e cada um obtém seu próprio bloco de memória
geralmente usamos união quando temos certeza de que apenas uma das variáveis será usada de uma só vez e você deseja utilizar totalmente a memória presente, porque ela obtém apenas um bloco de memória que é igual ao maior tipo.
struct emp
{
char x;//1 byte
float y; //4 byte
} e;
memória total que obtém => 5 bytes
union emp
{
char x;//1 byte
float y; //4 byte
} e;
memória total que obtém = 4 bytes
As uniões são úteis ao escrever uma função de ordenação de bytes, que é fornecida abaixo. Não é possível com estruturas.
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
} un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
} else
printf("sizeof(short) = %d\n", sizeof(short));
exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.