Professor: Gabriel Soares Baptista
Baixe os casos de teste da aula (11 - abstract data types test cases.zip).
.zip contém apenas inputs/ e outputs por etapa. gcc main.c -o programa
./programa < inputs/case1.in
outputs/. struct, typedef, enum e union em problemas reais. ->. Nesta aula, nós não vamos estudar cada recurso isoladamente.
Vamos construir um pequeno sistema de boletim.
Cada etapa muda a entrada, aumenta a regra de negócio e obriga o código a ficar mais organizado.
struct. enum. .h e .c. ->. union. Entrada:
nome matricula nota1 nota2 nota3
Exemplo:
Ana 1001 8.0 7.0 9.0
float calcular_media(float n1, float n2, float n3){
return (n1 + n2 + n3) / 3.0f;
}
const char *classificar_media(float media){
if(media >= 7.0f) return "APROVADO";
if(media >= 5.0f) return "RECUPERACAO";
return "REPROVADO";
}
O cálculo ainda funciona. O que começa a falhar é a organização dos dados.
structAgora a entrada continua a mesma.
O que muda é a modelagem do programa.
As informações deixam de ser “peças soltas” e passam a representar explicitamente um aluno.
structstruct permite agrupar dados diferentes sob um mesmo nome. nome, matricula, nota1, nota2 e nota3 separadamente, passamos a pensar em um Aluno. struct não existe para "enfeitar" o código. Ela existe para fazer o programa representar melhor o problema.
structstruct NomeDaStruct {
tipo campo1;
tipo campo2;
tipo campo3;
};
struct NomeDaStruct variavel;
structstruct Aluno {
char nome[50];
int matricula;
float nota1;
float nota2;
float nota3;
};
struct Aluno a;
Aluno passa a ser um modelo de dados do programa. struct.struct Aluno a;
strcpy(a.nome, "Ana");
a.matricula = 1001;
a.nota1 = 8.0f;
a.nota2 = 7.0f;
a.nota3 = 9.0f;
. acessa um campo da estrutura. strcpy(). structstruct Aluno a = {"Ana", 1001, 8.0f, 7.0f, 9.0f};
Em struct, a ordem da inicialização importa.
typedef struct {
char nome[50];
int matricula;
float notas[3];
} Aluno;
nome reaproveita a ideia de strings. notas[3] reaproveita a ideia de vetor. Aluno agora vira um tipo de verdade dentro do programa. float aluno_media(const Aluno *a){
return (a->notas[0] + a->notas[1] + a->notas[2]) / 3.0f;
}
Nova entrada:
n
nome matricula nota1 nota2 nota3
nome matricula nota1 nota2 nota3
...
n alunos. Aluno turma[100];
for(i = 0; i < n; i++)
ler_aluno(&turma[i]);
struct. Nova entrada:
n
nome matricula nota1 nota2 nota3 faltas
...
Nova regra:
enum para organizar estadostypedef enum {
REPROVADO_NOTA,
RECUPERACAO,
APROVADO,
REPROVADO_FALTA
} SituacaoAluno;
SituacaoAluno aluno_situacao(const Aluno *a){
float media = aluno_media(a);
if(a->faltas > 18) return REPROVADO_FALTA;
if(media >= 7.0f) return APROVADO;
if(media >= 5.0f) return RECUPERACAO;
return REPROVADO_NOTA;
}
Até aqui, tudo ainda estava em um arquivo só.
Agora isso começa a conflitar com a aula de bibliotecas.
Precisamos separar:
aluno.h
aluno.c
main.c
aluno.h: tipos e protótipos aluno.c: implementação das funções main.c: leitura da entrada e relatório gcc main.c aluno.c -o programa
Nova entrada:
n
... turma ...
m
matricula bonus
matricula bonus
...
O bônus será aplicado apenas à menor nota do aluno.
->Aluno *buscar_aluno_por_matricula(Aluno turma[], int n, int matricula);
void aplicar_bonus(Aluno *a, float bonus);
a->notas[indice_menor] += bonus;
Nova entrada:
n
nome matricula nota1 nota2 nota3 faltas cidade estado
...
m
... bonus ...
estado_consulta
typedef struct {
char cidade[30];
char estado[3];
} Localidade;
typedef struct {
char nome[50];
int matricula;
float notas[3];
int faltas;
Localidade local;
} Aluno;
a->local.estado
union entra como contraste, não como núcleo do sistemaO sistema principal não precisa de union.
Então vamos usá-la em um experimento separado.
Problema:
n
tipo nome valor
tipo nome valor
...
union faz sentidotypedef union {
int inteiro;
float real;
char texto[40];
} Dado;
Mas sozinha ela não basta.
Precisamos combinar com enum para saber qual campo está ativo.
struct, os campos coexistem. union, os campos compartilham memória. struct é a solução natural do projeto principal. union aparece quando só um formato de dado é válido por vez. O objetivo não é apenas fazer funcionar. O objetivo é entender por que a solução anterior começou a ficar insuficiente.
Agora que os dados já podem ser melhor modelados e organizados, surge uma nova pergunta:
e quando a quantidade de dados não é conhecida em tempo de compilação?
Na próxima aula, isso nos levará naturalmente para alocação dinâmica em C.