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.