Estruturas
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: Uma 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:
- struct1.c
#include <stdio.h> #include <string.h> struct paciente_t // a extensão "_t" é uma 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) ; }
Atribuição de structs
Valores de tipo struct
podem ser atribuídos, copiados, usados como parâmetros e retornados por funções diretamente, sem necessidade de manipular cada elemento individualmente. Em uma atribuição, por exemplo, todos os bytes contidos na estrutura de origem serão copiados na estrutura de destino.
As seguintes operações com estruturas são perfeitamente válidas (e muito úteis):
- struct2.c
#include <stdio.h> #include <string.h> struct paciente_t { char nome[100] ; short idade ; short quarto ; } ; // imprime na tela os dados do paciente void imprime_paciente (struct paciente_t p) { printf ("Paciente %s, %d anos, quarto %d\n", p.nome, p.idade, p.quarto) ; } // devolve um paciente "nulo" struct paciente_t paciente_nulo () { struct paciente_t p = {"nulo", 0, 0} ; return (p) ; } int main () { // declaração de variável com valor inicial struct paciente_t pac1 = { "Home Simpson", 47, 501 } ; // cópia de struct struct paciente_t pac2 = pac1 ; // struct como parâmetro de função (passagem por cópia) imprime_paciente (pac2) ; // struct como retorno de função pac2 = paciente_nulo () ; imprime_paciente (pac2) ; return (0) ; }
Structs e vetores
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 struct paciente_t { char nome[100] ; short idade ; short quarto ; } ; // declaração/alocação struct 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 ; }
Structs e ponteiros
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 *
:
- struct-ptr.c
#include <stdio.h> #include <string.h> struct paciente_t { char nome[100] ; short idade ; short quarto ; } ; int main () { // declaração/alocação struct 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:
struct filaInt_t { int valor ; struct filaInt_t *prox ; } ; // ponteiro para o início de uma fila struct 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:
O código que constrói essa estrutura é o seguinte (simplificado, pois não testa o sucesso do malloc
):
fila1 = malloc (sizeof (struct filaInt_t)) ; fila1->valor = 45 ; fila1->prox = malloc (sizeof (struct filaInt_t)) ; fila1->prox->valor = 21 ; fila1->prox->prox = malloc (sizeof (struct filaInt_t)) ; fila1->prox->prox->valor = 4 ; fila1->prox->prox->prox = NULL ;
Exercícios
- Escrever um programa em C para gerenciar uma relação de alunos, implementada como um vetor de structs de tipo
aluno_t
. O programa deve ter funções para:- ler os dados de um aluno (nome, idade, GRR, curso), devolvendo os campos lidos como um struct;
- imprimir os dados de um aluno a partir de seu struct;
- imprimir a relação de alunos;
- imprimir os nomes dos alunos com idade acima de 22 anos;
- ordenar a relação de alunos por idade;
- ordenar a relação de alunos por nome.
- escreva um programa em C para manipular datas e horários:
- structs do tipo
data_t
armazenam datas (dia, mês, ano); - structs do tipo
hora_t
armazenam horários (hora, minuto, segundo); - structs do tipo
datahora_t
armazenam datas e horários (seus campos internos são structs dos tipos acima); - a função
le_data
lê um struct de tipodata_t
; - a função
le_hora
lê um struct de tipohora_t
; - a função
le_datahora
lê um struct de tipodatahora_t
; - as funções
esc_data
,esc_hora
eesc_datahora
escrevem na tela seus tipos respectivos; - a função
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; - a função
hora_segs
retorna (int
) o número de segundos em um horário, a partir do início do dia (00:00:00); - a função
dif_data
retorna (int
) o número de dias entre duas datas; - a função
dif_hora
retorna (int
) o número de segundos entre dois horários; - a função
dif_datahora
retorna (datahora_t
) a diferença entre duas datas e horários.