Suponhamos que é necessário definir uma representação agregada na linguagem C para manipular inscrições de alunos a disciplinas. Numa inscrição é necessário representar o número de aluno, a sigla da disciplina (máx. 6 caracteres), e a nota obtida. Como representar uma inscrição em C? Será que podemos utilizar um vector? No caso de um vector todos os dados precisam de ser do mesmo tipo... A solução passa por utilizarmos estruturas para representar dados agregados de tipos diferentes.
As estruturas permitem definir estruturas de dados sofisticadas, as quais possibilitam a agregação de diferentes tipos de dados. Exemplos:
struct ponto { double x; double y; }; struct data { int dia; int mes; int ano; }; struct inscricao { int numero; char disciplina[7]; int nota; };
Notar que a definição de uma estrutura introduz um novo tipo de dados, e.g., struct ponto
, struct data
, e struct inscricao
. Uma vez definidas as estruturas, podemos declarar variáveis do tipo de uma estrutura:
struct ponto centro;
Podemos também declarar e inicializar em simultâneo uma variável do tipo estrutura:
struct ponto centro = {12.3, 5.2};
Em geral temos <tipo> <variavel> = { <valores> };
E, é claro, podemos manipular os campos de variáveis do tipo estrutura:
centro.x = 12.3; centro.y = 5.2;
Notar que uma estrutura pode incluir outras estruturas. Exemplo:
struct rectangulo { struct ponto a, b; }; struct rectangulo rect; rect.a.x = 3.4; rect.b.y = 6.1;
Operações válidas sobre estruturas:
- cópia:
- atribuição como entidade;
- acesso aos seus membros.
A cópia e atribuição incluem passagem de parâmetros para funções e retorno de valores de funções. As estruturas não podem ser comparadas através do operador de comparação de igualdade. Para determinar se duas estruturas são iguais, é necessário comparar cada campo da estrutura.
Estruturas e funções
As funções em C podem receber e retornar estruturas, no entanto é preciso ter em conta que a passagem é por valor, o que significa que as estruturas são copiadas. Por exemplo, na função
struct ponto adicionaPonto(struct ponto p1, struct ponto p2) { struct ponto res; res.x = p1.x + p2.x; res.y = p1.y + p2.y; return res; }
é feita uma cópia de cada um dos argumentos aquando da invocação da função, e é retornada uma cópia da estrutura res
. Uma vez que a passagem é por valor, a chamada a esta função com argumentos pa
e pb
, adicionaPonto(pa, pb)
, não altera os valores de pa
e de pb
. Podemos em particular redefinir a função como:
struct ponto adicionaPonto(struct ponto p1, struct ponto p2) { p1.x = p1.x + p2.x; p1.y = p1.y + p2.y; return p1; } int main() { struct ponto pa = {1, 3}, pb = {5, 2}; struct ponto pc = adicionaPonto(pa, pb); ... }
Ao invocar a função, adicionaPonto(pa, pb)
, os valores de pa
e pb
também não são alterados neste caso.
Vectores de estruturas
A utilização de vectores de estruturas permite-nos representar conjuntos de dados agregados.
struct ponto { double x; double y; } figura [100]; struct ponto { double x; double y; }; struct ponto figura [100];
Em ambos os casos estamos a declarar a variável figura
como um vector de pontos.
Podemos também declarar e inicializar em simultâneo vectores de estruturas:
struct ponto { double x; double y; }; struct ponto figura [] = { { 1.2, 3.4 }, { 4.5, 12.6 }, { 1.2, 10.8 } };
typedef
Podemos definir novos tipos através do typedef o que permite associar um nome a um tipo de dados já existente, permitindo um grau adicional de abstracção.
typedef int Inteiro; typedef struct { double x; double y; } Ponto; int main() { Inteiro i; Ponto x; ... }
A sintaxe é typedef <tipo> <nome>;
Exemplo: Inscrições e notas a disciplinas
Objectivo:
- Programa para manipular as inscrições de alunos a disciplinas:
- dado um número de aluno, mostra as notas do aluno às disciplinas onde está inscrito;
- os dados de entrada são todas as inscrições às disciplinas;
- suponha que numa inscrição representamos o número de aluno, a disciplina e a nota do aluno na disciplina;
- define-se um limite máximo de inscrições no programa.
- Exemplo dos dados de entrada:
- 1ª linha com o número total de inscrições ($N$);
- $N$ linhas com o formato: número aluno, nota, sigla disciplina
3 36933 16 IAED 12345 14 IAED 23456 8 AC
Implementação:
#include <stdio.h> #include <string.h> #define MAX_COD_DISC 7 #define MAX_INSCRICOES 10000 typedef struct { int numero; /* numero do aluno */ int nota; /* nota 'a disciplina */ char nome[MAX_COD_DISC]; /* nome/codigo da disciplina */ } inscricao; int leTodasInscricoes (inscricao v[]); void mostraNotasAluno(inscricao v[], int n, int aluno); int main() { inscricao insc[MAX_INSCRICOES]; int numInscricoes = 0, aluno; numInscricoes = leTodasInscricoes(insc); scanf("%d", &aluno); while (aluno > 0) { mostraNotasAluno(insc, numInscricoes, aluno); scanf("%d", &aluno); } return 0; } int leTodasInscricoes (inscricao v[]) { int n, i; scanf("%d", &n); for (i = 0; i < n; i++) scanf("%d%d%s", &(v[i].numero), &(v[i].nota), v[i].nome); return n; } void mostraNotasAluno (inscricao v[], int n, int aluno) { int i; for (i = 0; i < n; i++) if (aluno == v[i].numero) printf("%s %d\n", v[i].nome, v[i].nota); } void mostraNotasDisciplina (inscricao v[], int n, char disc[]) { int i; for (i = 0; i < n; i++) if (strcmp(v[i].nome, disc) == 0) printf("%d %d\n", v[i].numero, v[i].nota); }
Notar que na função leTodasInscricoes
utilizámos %s
para ler uma sequência de caracteres até encontrar um espaço, tabulação ou fim de linha. Notar também que o scanf
coloca ‘\0’
no fim do texto e que, no caso da leitura de strings, não é necessário utilizar &
no scanf
, veremos mais tarde porque não é necessário. Na função mostraNotasDisciplina
utilizámos também pela primeira vez a função strcmp
, sendo necessário incluir string.h
, para comparar duas strings, em que esta devolve 0
se as strings são iguais.