Tipo de dado Struct

From Applied Science

Há muitos casos em que várias variáveis são correlacionadas, com algumas guardando valores que são dados que pertencem a um mesmo objeto ou entidade. Seria desejável uma maneira de guardar todas essas informações num "pacote", sem precisar de uma variável para cada dado e mantendo a relação entre elas na base de uma convenção de nomes. Felizmente temos as structs que permitem juntar muitos dados num único "pacote".

Na sua forma mais simples, struct é um grupo ou coleção de diferentes tipos de dados formando um tipo de dado composto. Indo mais longe, combinando structs e ponteiros, conseguimos construir estruturas de dados mais complexas como listas ligadas e pilhas.

Duas perguntas naturais podem surgir: "Membros podem ser adicionados ou removidos de uma struct em tempo de execução?" e "Structs podem herdar membros de outras structs?". Precisamos de novos conceitos que não estão cobertos neste site no momento. Resposta curta: não.

Para evitar confusão

  • Uma struct não é uma variável, mas um tipo de dado composto;
  • Struct também pode ser local ou global.


Para entender structs não precisamos de conhecimento além de variáveis e vetores


  • Declarando uma simples struct para guardar dados sobre uma pessoa:
struct Pessoa {
char PrimeiroNome[20];
char UltimoNome[20];
int  idade;
char Genero[1];
};

Agora temos um novo tipo de dado do tipo "Pessoa"', que contém quatro campos para guardar informações sobre uma pessoa. Cada campo é um membro da struct.

Nota: não podemos declarar e atribuir valores nos campos da struct ao mesmo tempo, porque estamos declarando um novo tipo de dado, não uma variável.


  • Como usar o novo tipo de dado recém-criado:
struct person Maria;
Maria.PrimeiroNome[0] = 'M';
Maria.PrimeiroNome[1] = 'a';
Maria.PrimeiroNome[2] = 'r';
Maria.PrimeiroNome[3] = 'i';
Maria.PrimeiroNome[4] = 'a';
Maria.UltimoNome[0] = 'D';
Maria.UltimoNome[1] = 'o';
Maria.UltimoNome[2] = 'l';
Maria.UltimoNome[3] = 'o';
Maria.UltimoNome[4] = 'r';
Maria.UltimoNome[5] = 'e';
Maria.UltimoNome[6] = 's';
Maria.idade = 20;
Maria.Genero = 'F';

Acabamos de declarar uma nova variável chamada "Maria", com o tipo "Pessoa". A sintaxe é a seguinte: tipo 'ponto' nome do campo.

Atribuição: uma struct só pode ser atribuída à outra se as duas forem do mesmo tipo. Não existe uma conversão automática se não forem iguais. Este comportamento é diferente de, por exemplo, inteiro e ponto flutuante, onde uma conversão é feita implicitamente.

Nota: structs não podem ser usadas em expressões de comparação ou expressões aritméticas. Por exemplo: suponha que temos duas variáveis do tipo "pessoa", "Maria" e "Sara". Comparações como Maria == Sara ou operações como Maria + Sara não tem sentido para o computador. Temos que dizer para o computador o que de "Maria" e "Sara" será comparado ou quais valores serão usados numa expressão. Assim, expressões como "Maria.idade > Sara.idade" são válidas.


  • Struct aninhada:
struct Escola {
char Nome[20];
char Endereco[20];
char Codigo[3];
};

struct Pessoa {
char   PrimeiroNome[20];<br />
char   UltimoNome[20];<br />
int    idade;
char   Genero[1];
struct Escola Arvore;
};

Em C podemos aninhar structs para criar tipos de dado complexos. No exemplo acima declaramos a struct "Escola", aninhada na struct "Pessoa". Note que, dentro de "Pessoa", não estamos declarando a struct "Escola", mas criando um membro "Arvore" que é do tipo "Escola".


  • Acessando structs aninhadas:
struct Person Maria;
Maria.Arvore.Codigo[0] = 'X';
Maria.Arvore.Codigo[1] = '0';
Maria.Arvore.Codigo[2] = '0';

A sintaxe é basicamente igual à qualquer outra struct, com exceção de um ponto adicional.

Nota: não há uma regra que diga quando demos aninhar ou não. No exemplo acima, tanto "Pessoa" quanto "Escola" poderiam vir primeiro, no nível mais alto. Fica a critério do programador.


  • Typedef:
typedef struct Pessoa Pes;
Pe Maria;

Quando lidamos com struct é uma prática comum usar 'typedef' para tornar o código mais fácil de ler. O que o 'typedef' faz é "substituir" a sintaxe da struct por um atalho. No exemplo acima, "subsituímos" a expressão mais comprida da struct por "Pes", um atalho de três letras parecido com 'int', 'float', etc.


  • Declarando e usando um vetor de struct:
struct companhia {
char nome[10];
int  idade;
};

struct companhia pessoa[2];
pessoa[0].nome[0] = 'M';
pessoa[0].nome[1] = 'a';
pessoa[0].nome[2] = 'r';
pessoa[0].nome[3] = 'i';
pessoa[0].nome[4] = 'a';
pessoa[0].idade   =  20;
pessoa[0].nome[0] = 'S';
pessoa[0].nome[1] = 'a';
pessoa[0].nome[2] = 'r';
pessoa[0].nome[3] = 'a';
pessoa[1].idade   =  22;

Assim como um vetor de inteiros ou um vetor de caracteres, também temos vetores de struct. Sem um vetor teríamos que declarar uma variável por pessoa. Com um vetor temos uma maneira conveniente de guardar dados sobre muitas pessoas.


  • Um modo mais rápido de declarar um vetor de struct:
struct companhia {
char nome[10];
int  idade;
} 

pessoa[2];

Assim não precisamos digitar "struct tipo" duas vezes.