Diferença entre uma estrutura e uma união

411

Existe algum bom exemplo para dar a diferença entre a structe a union? Basicamente, eu sei que structusa toda a memória de seu membro e unionusa o maior espaço de memória de membros. Existe alguma outra diferença de nível do sistema operacional?

gagneet
fonte

Respostas:

677

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 .

Kyle Cronin
fonte
1
usando este exemplo, em união, se xb = 'c' o que é armazenado em xa? é o número de referência do caractere?
kylex 6/12/08
1
espero que explica mais detalhadamente o que está armazenado em xa quando você definir xb
Kyle Cronin
1
@KyleCronin Acho que entendi. No seu caso, você tem um grupo dos tipos, sabendo que só precisará usar um, mas não sabe qual até o tempo de execução - portanto, a união permite que você faça isso. Graças
user12345613
2
As uniões @ user12345613 podem ser usadas como uma espécie de classe base para estruturas. Você pode emular uma hierarquia OO usando uniões de estruturas
Morten Jensen
1
A ordem de bytes do @Lazar em tipos de vários bytes depende da disponibilidade. Eu sugiro ler o artigo da Wikipedia sobre ele.
Kyle Cronin
83

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.

Charlie Martin
fonte
53

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.

cygil
fonte
1
Sim. Isso explica bem um caso de uso!
Gideon 25/05
1
suponha que você tenha um número de packetheader ph;solicitação de acesso? ph.request.requestnumber?
usar o seguinte código
Melhor explicação! Obrigado.
84RR1573R 23/10
39

Como você já declarou em sua pergunta, a principal diferença entre unione structé que os unionmembros cobrem a memória um do outro, de modo que o tamanho da união seja o mesmo, enquanto os structmembros 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 intque 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 testpara 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 10vez de 5).

Johannes Schaub - litb
fonte
18

A unioné útil em alguns cenários. unionpode ser uma ferramenta para manipulação de nível muito baixo, como escrever drivers de dispositivo para um kernel.

Um exemplo disso é dissecar um floatnúmero usando uniona structcom campos de bits e a float. Eu guardo um número no floate, posteriormente, posso acessar partes específicas do floatmesmo 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í.


uniontambé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!

Krzysztof Voss
fonte
11

" 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.

Gabriele D'Antona
fonte
11

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.

skanda93
fonte
Ótima explicação. Felicidades!
Prem
11

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.

Ravi Kanth
fonte
10

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).

Piotr Lesnicki
fonte
9

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.

CMS
fonte
2
Você também pode adicionar que os membros da união "se sobreponham", pois todos começam no endereço inicial da "estrutura" da união alocada.
Jim Buck
4

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/

Abdus Sattar Bhuiyan
fonte
3

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

Ahmed
fonte
3

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

Anurag Bhakuni
fonte
2

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.
Aniket Suryavanshi
fonte
1

Uma União é diferente de uma estrutura, uma vez que uma União se repete sobre as outras: ela redefine a mesma memória enquanto a estrutura define uma após a outra sem sobreposições ou redefinições.

Dennis Ng
fonte