Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a diferen

1. Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a diferen

Perfil removido
removido

(usa Nenhuma)

Enviado em 26/01/2019 - 11:12h

Percebi que existem várias maneiras de se declarar e instanciar uma struct ou union em C. Por exemplo: Já observei situações onde uma union foi declarada e instanciada dentro de uma struct, mas também já vi outras casos onde a union foi declarada fora da struct e só depois instanciada dentro dela.

Bem, como ao longo do tempo eu vi vários casos, eu não poderei citar todos e pedir explicação de cada um para vocês. Portanto, venho aqui com a seguinte pergunta: Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a diferença entre elas?



  


2. Re: Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a dif

Hugo Cerqueira
hrcerq

(usa Outra)

Enviado em 26/01/2019 - 16:30h

Olá.

É natural que você encontre várias maneiras de se fazer algo. A declaração e instanciação de uma struct tem uma sintaxe básica, mas que pode ser aplicada em cenários diferentes, de formas diferentes. A sintaxe básica é:

struct tag {
tipo membro1;
tipo membro2;
...
};


Exemplo:

struct gerente {
int idade;
float salario;
const char* departamento;
};

struct empregado {
int idade;
float salario;
struct gerente superior;
};


Eu posso apelidar essas estruturas usando typedef:

typedef struct gerente gerente; 


Assim, em vez de usar struct gerente basta usar o termo gerente. Posso também instanciar essas structs dentro de uma union:

union membro {
struct gerente g;
struct empregado e;
} membro;

// Ou então, tendo usado typedef antes, poderia fazer assim:

union membro {
gerente g;
empregado e;
} membro;


Ou seja, a union membro pode assumir o valor de gerente ou empregado. Mas atenção, nesse caso específico eu não poderia criar os tipos structs dentro da union pois há uma dependência da struct empregado com a struct gerente (o membro superior dentro da struct empregado).

Mas eu poderia criar structs independentes dentro de uma union, por exemplo:

union prato {
struct feito {
int cod_prato;
int cod_bebida;
} feito;

struct montagem {
const char* principal;
const char* acompanhamento1;
const char* acompanhamento2;
int cod_bebida;
} montagem;
} prato;


Note que nesse caso eu tive que adicionar um apelido ao fim da declaração da struct, pra poder acessar os membros dessa struct.

Mas enfim, existem várias possibilidades de declaração e instanciação. Não acho produtivo ficar tentando imaginar todas elas, e sim pensar no que você precisa fazer primeiro pra depois tentar achar a melhor abordagem.

---

Atenciosamente,
Hugo Cerqueira

Devuan - https://devuan.org/


3. Re: Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a dif

Hugo Cerqueira
hrcerq

(usa Outra)

Enviado em 26/01/2019 - 20:31h

Complementando minha resposta anterior, pois esqueci de falar sobre as unions, basicamente uma union é um elemento que pode assumir apenas um dos valores declarados dentro dela.

Então nos exemplos que falei antes, na union membro, ela pode ser um empregado ou um gerente, mas não as duas coisas.

A struct, por outro lado é uma composição de todos os elementos declarados dentro dela. A criação dos tipos struct e union segue a mesma sintaxe.

Atualização: Tinha colocado aqui informações incorretas sobre portabilidade, espero ter corrigido antes que alguém visse. XD

---

Atenciosamente,
Hugo Cerqueira

Devuan - https://devuan.org/


4. Re: Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a dif

Paulo
paulo1205

(usa Ubuntu)

Enviado em 28/01/2019 - 03:11h

Acho que sua pergunta está relacionada à questão dos escopos em que um nome é visível.

Existem quatro escopos de visibilidade em C: arquivo, bloco, função e protótipo de função.

O escopo de função se aplica apenas a rótulos (labels) usados com o comando goto, então não vem muito ao caso aqui (mas a regra diz que todos os rótulos declarados em qualquer ponto dentro da definição de uma função devem ser visíveis em qualquer outro ponto da função, antes ou depois do ponto em que cada rótulo é declarado).

O escopo de protótipo de função diz respeito a declarações de funções que não sejam imediatamente seguidas de sua definição, e permite que os parâmetros usados na declaração enxerguem os parâmetros anteriores, e esse escopo acaba assim que se chega ao ponto-e-vírgula que encerra a declaração. Por exemplo:
bool ordena_array(int n, const char (*strings)[n]);  // reutiliza parâmetro ‘n’ na declaração do parâmetro ‘strings’; escopo acaba no ‘;’. 


O escopo de bloco limita tudo que for declarado dentro de um bloco delimitado por chaves (“{” e “}”) ao interior desse bloco. Um nome passa a ser visível partir do ponto em que o elemento for declarado e deixa de ser visível assim que o bloco termina com o fechamento de chaves. Se o bloco for referente a uma definição de função ou a um comando if, switch ou for, os parâmetros da função ou a(s) variável(is) de controle declarada(s) entre os parênteses dos referidos comandos também são consideradas válidas dentro do bloco.

Por fim, o escopo de arquivo diz que a visibilidade do que for declarado fora de qualquer dos casos acima é visível a partir do ponto da declaração e até o final do arquivo.


Algumas observações quanto a escopos:

  • Nomes declarados no escopo de arquivo ou num nível de bloco exterior são visíveis em blocos interiores.

  • Um bloco pode declarar um ente de mesmo nome que outro que já tenha sido declarado no escopo de arquivo ou num escopo de bloco que lhe seja exterior. Nesse caso, o nome externo será ocultado no bloco interno a partir do ponto em que ocorrer a declaração com o mesmo nome, e assim permanecerá até o final desse bloco interno.

  • Tanto no escopo de protótipo de funções quanto no de bloco, é inútil (embora sintaticamente aceitável) definir uma nova struct ou union como tipo de um parâmetro da função, em vez de usar uma que já tenha sido declarada em nível de arquivo, porque como o escopo é limitado apenas àquela declaração ou bloco, quem estiver fora não terá como chamar tal função, pois não terá como declarar um argumento do mesmo tipo do parâmetro (mesmo que tenha a mesma forma e até o mesmo especificador de nome, ainda será um tipo distinto porque vai existir em outro escopo).

  • Não existe um escopo específico para structs e unions. Assim sendo, se você tiver uma declaração ou definição de uma struct ou union chamada Y dentro da definição de outra struct ou union chamada X, então o escopo de Y será o mesmo escopo de X, como se a definição de Y tivesse ocorrido antes da declaração de X nesse mesmo escopo. Isso significa que os dois trechos de código seguintes são sinônimos.
void f(){
// Note o escopo não apenas para as variáveis, mas para as estruturas e uniões.
struct A {
struct B {
union IC {
int i;
char c;
} ic;
} b;
} a;
// Dentro da função, posso usar a.b.ic.i ou a.b.ic.c.
} // Fora da função não existem mais a, nem struct A, struct B ou union IC.
void f(){
// Note o escopo não apenas para as variáveis, mas para as estruturas e uniões.
union IC {
int i;
char c;
};
struct B { union IC ic; };
struct A { struct B b; };
struct A a;
// Dentro da função, posso usar a.b.ic.i ou a.b.ic.c.
} // Fora da função não existem mais a, nem struct A, struct B ou union IC.

  • Contudo, é bom evitar definições e declarações aninhadas: em C, elas dificultam a visão de quais nomes estão visíveis no escopo.

  • Outra boa razão para evitar o aninhamento de tipos em C é que se o mesmo código for compilado em C++ (como não raramente acontece, principalmente com cabeçalhos), a equivalência mencionada acima não vale, pois cada struct, union ou class introduz seu próprio escopo. O primeiro exemplo acima, em lugar de produzir três tipos no mesmo escopo, como em C, produziria os seguintes três tipos: A, A::B e A::B::IC (o operador ::, que não existe em C, é chamado de operador de seleção de escopo).



Além da questão de escopo, existe também a de espaços de nomes (name spaces), que opera de modo praticamente ortogonal aos escopos. Há quatro espaços de nome em C, a saber:

  • o de rótulos para uso com goto;

  • o de especificadores (tags) de structs, unions e enums;

  • o de nomes dos campos de structs e unions (cada estrutura ou união declarada tem o seu próprio espaço de nomes, de modo que é possível que tipos distintos tenham campos com mesmos nomes, sem provocar confusão entre eles); e

  • o geral, que contém todos os demais identificadores, que incluem nomes de variáveis e funções, nomes de tipos atribuídos com typedef, nomes das constantes definidas em uma enum.

Os espaços de nomes diferentes são o que permite que haja, num mesmo escopo, um identificador de estrutura como mesmo nome de uma função, ou uma definição de tipo X que o faz equivalente a uma struct X, ou ainda um campo de uma estrutura com o mesmo nome que uma variável ou que o próprio especificador dessa mesma estrutura. Contudo, não é possível ter, num mesmo escopo e ao mesmo tempo, uma variável e um tipo de mesmo nome que ela definido com typedef ou uma constante de enumeração e uma função com mesmo nome.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


5. Re: Quais são as maneiras possíveis de se declarar e instanciar uma struct ou union em C? Qual a diferen

Paulo
paulo1205

(usa Ubuntu)

Enviado em 28/01/2019 - 10:15h

Complementando um ponto que pode ter ficado confuso, structs e unions podem ter três momentos distintos: o de declaração, o de definição e o de instanciação de objeto daquele tipo. O código baixo mostra exemplos desses três momentos distintos.
struct my_data;  // Declaração apenas: reserva o nome “my_data” no espaço de nomes de structs no escopo corrente.

struct weather_data {
double temp, humidity, uv_index, wind_direction, wind_speed;
}; // Definição do conteúdo de struct weather_data, com cinco campos do tipo double com seus respectivos nomes e em ordem.
// Se struct weather_data ainda não tiver sido declarado, a definição também faz o papel de declaração.

struct weather_data station1, station2; // Instanciação: station1 e station2 são dois objetos palpáveis do tipo struct weather_data.

Como você provavelmente já sabe, é possível aglutinar instanciação com definição, simplesmente colocando nomes de objetos a serem instanciados logo após o fechamento das chaves e antes do ponto-e-vírgula que encerra o comando.

É possível, e em muitos casos até desejável, que uma estrutura que foi apenas declarada não tenha sua definição em visível em nenhum lugar do programa. Nesse caso, você também não vai poder instanciar objetos desse tipo, mas pode usar livremente typedefs e ponteiros para objetos desse tipo, que não contam como instanciações.
struct my_data *make_data(const char *name, time_t birth_date);

typedef struct __IO_FILE FILE; // O tipo FILE de <stdio.h> é parecido com isso. Sò a implementação da biblioteca
// sabe o que essa estrutura tem por dentro e pode manipular esse conteúdo.



... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts