As estruturas (structs), também chamadas registros (records) são variáveis compostas pelo agrupamento de diversas variáveis e vistas/tratadas pelo programa como uma única variável. As variáveis que compõem uma estrutura podem ser simples (int, float, pointer) ou compostas (arrays e outras estruturas).
Estruturas são geralmente utilizadas para agrupar variáveis correlatas, ou seja, que fazem parte de um mesmo conceito lógico. Por exemplo: Um a estrutura pode representar um paciente em uma aplicação médica e conter todos os dados relativos a ele.
Uma estrutura é definida através da palavra reservada struct
, da seguinte forma:
struct <nome da estrutura> { type variable ; type variable ; ... } ;
A declaração e manipulação de variáveis desse novo tipo é simples:
#include <stdio.h> #include <string.h> struct Paciente_t // a extensão "_t" é convenção usual para um novo tipo { char nome[100] ; short idade ; short quarto ; } ; int main () { // declaração/alocação struct Paciente_t pac1, pac2 ; // atribuição de valor aos campos pac1.idade = 53 ; pac1.quarto = 417 ; // uso como parâmetro strcpy (pac1.nome, "Homer Simpson") ; // atribuição de structs (o conteúdo é copiado) pac2 = pac1 ; // uso dos valores dos campos printf ("Paciente %s, %d anos, quarto %d\n", pac1.nome, pac1.idade, pac1.quarto) ; printf ("Paciente %s, %d anos, quarto %d\n", pac2.nome, pac2.idade, pac2.quarto) ; return (0) ; }
A palavra reservada typedef
permite definir (ou redefinir) um tipo de dado. Ela pode ser aplicada a qualquer tipo da linguagem C, mas é particularmente útil com structs, pois simplifica a declaração de variáveis e parâmetros de tipo struct.
Forma geral:
typedef <tipo existente> <novo tipo> ;
Exemplo com escalares:
typedef unsigned long int uint32_t ; // novo tipo inteiro positivo 32 bits uint32_t a, b ; // aloca duas variáveis do tipo uint32_t
Exemplo com structs:
struct Pac_t { char nome[100] ; short idade ; short quarto ; } ; typedef struct Pac_t Paciente_t ; // struct Pac_t -> Paciente_t // declaração e alocação Paciente_t pac1, pac2 ;
Ou redefinindo o próprio tipo Paciente_t
;
struct Paciente_t { char nome[100] ; short idade ; short quarto ; } ; typedef struct Paciente_t Paciente_t ; // declaração e alocação Paciente_t pac1, pac2 ;
Ou ainda mais “enxuto”:
typedef struct Paciente_t { char nome[100] ; short idade ; short quarto ; } Paciente_t ; // declaração/alocação Paciente_t pac1, pac2 ;
O uso de vetores de structs permite criar estruturas de dados sofisticadas, com grande poder de expressão. A forma de declaração e acesso é similar ao uso convencional de vetores e structs visto até agora:
#define VECSIZE 1000 typedef struct Paciente_t { char nome[100] ; short idade ; short quarto ; } Paciente_t ; // declaração/alocação Paciente_t paciente[VECSIZE] ; // vetor com VECSIZE pacientes // inicializa vetor for (i=0; i<VECSIZE; i++) { strcpy (paciente[i].nome, "") ; paciente[i].idade = 0 ; paciente[i].quarto = 0 ; }
Uma variável de tipo struct é alocada na memória da mesma forma que as demais variáveis, então é possível obter o endereço da variável usando o operador &
e também criar ponteiros para variáveis struct, usando *
:
#include <stdio.h> #include <string.h> typedef struct Paciente_t { char nome[100] ; short idade ; short quarto ; } Paciente_t ; int main () { // declaração/alocação Paciente_t pac, *ptr ; // atribuição de valor aos campos pac.idade = 53 ; pac.quarto = 417 ; strcpy (pac.nome, "Homer Simpson") ; // atribuição do ponteiro ptr = &pac ; // acesso pela variável struct printf ("Paciente %s, %d anos, quarto %d\n", pac.nome, pac.idade, pac.quarto) ; // acesso pelo ponteiro printf ("Paciente %s, %d anos, quarto %d\n", (*ptr).nome, (*ptr).idade, (*ptr).quarto) ; return (0) ; }
O acesso ao campo interno do struct feito através do desreferenciamento do ponteiro também pode ser feito através do operador ponteiro->campo. Assim, as operações abaixo são equivalentes:
(*ptr).idade = 45 ; ptr->idade = 45 ;
O uso conjunto de structs e ponteiros permite construir estruturas de dados complexas, como filas, pilhas, árvores e grafos. Por exemplo, podemos definir um elemento de uma fila de inteiros da seguinte forma:
// definição do tipo FilaInt_t typedef struct FilaInt_t { int valor ; struct FilaInt_t *prox ; } FilaInt_t ; // ponteiro para o início de uma fila FilaInt_t *fila1 ;
O ponteiro prox
dentro do struct permite “apontar” para outras variáveis de mesmo tipo, que por sua vez podem apontar para outras variáveis de mesmo tipo e assim sucessivamente. Isso permite criar uma fila de inteiros, com uma estrutura similar à da figura a seguir:
aluno_t
. O programa deve ter funções para:data_t
armazenam datas (dia, mês, ano);hora_t
armazenam horários (hora, minuto, segundo);datahora_t
armazenam datas e horários (seus campos internos são structs dos tipos acima);le_data
lê um struct de tipo data_t
;le_hora
lê um struct de tipo hora_t
;le_datahora
lê um struct de tipo datahora_t
;esc_data
, esc_hora
e esc_datahora
escrevem na tela seus tipos respectivos;data_dias
retorna (int
) o número de dias em uma data, a partir do início do calendário (1/1/1); para simplificar, desconsiderar anos bissextos e outros ajustes de calendário;hora_segs
retorna (int
) o número de segundos em um horário, a partir do início do dia (00:00:00);dif_data
retorna (int
) o número de dias entre duas datas;dif_hora
retorna (int
) o número de segundos entre dois horários;dif_datahora
retorna (datahora_t
) a diferença entre duas datas e horários.