Prática: Tipos Personalizados
Você já aprendeu o suficiente — agora é hora de construir algo. Como aprender a cozinhar: só ler receitas não basta; você tem que entrar na cozinha. Combine structs, memória dinâmica e E/S de arquivos para criar programas funcionais.
Projeto 1: Sistema de Gerenciamento de Notas
Requisitos
- Adicionar alunos (nome, matrícula, três notas de disciplinas)
- Exibir informações de todos os alunos
- Buscar por matrícula
- Calcular total e média de cada aluno
- Ordenar por nota total
- Salvar em arquivo / carregar de arquivo
- Gerenciamento dinâmico de memória — quantidade de alunos não é fixa
Design da Estrutura de Dados
typedef struct {
char name[32];
char id[16];
int chinese;
int math;
int english;
} Student;
typedef struct {
Student *data;
int count;
int capacity;
} StudentList;
StudentList gerencia alunos com um array dinâmico — capacidade inicial é 4, expandindo quando cheio.
Implementação Principal
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[32];
char id[16];
int chinese;
int math;
int english;
} Student;
typedef struct {
Student *data;
int count;
int capacity;
} StudentList;
void list_init(StudentList *list) {
list->capacity = 4;
list->count = 0;
list->data = (Student *)malloc(sizeof(Student) * list->capacity);
}
void list_free(StudentList *list) {
free(list->data);
list->data = NULL;
list->count = 0;
list->capacity = 0;
}
void list_expand(StudentList *list) {
if (list->count < list->capacity) return;
list->capacity *= 2;
Student *tmp = (Student *)realloc(list->data, sizeof(Student) * list->capacity);
if (tmp) list->data = tmp;
}
void list_add(StudentList *list, const Student *s) {
list_expand(list);
list->data[list->count++] = *s;
}
int total_score(const Student *s) {
return s->chinese + s->math + s->english;
}
double avg_score(const Student *s) {
return total_score(s) / 3.0;
}
Student *find_by_id(StudentList *list, const char *id) {
for (int i = 0; i < list->count; i++) {
if (strcmp(list->data[i].id, id) == 0) {
return &list->data[i];
}
}
return NULL;
}
int cmp_by_total(const void *a, const void *b) {
int ta = total_score((const Student *)a);
int tb = total_score((const Student *)b);
return tb - ta;
}
void sort_by_total(StudentList *list) {
qsort(list->data, list->count, sizeof(Student), cmp_by_total);
}
void print_student(const Student *s) {
printf("%-6s %-10s Portugues:%-3d Matematica:%-3d Ingles:%-3d Total:%-4d Media:%.1f\n",
s->id, s->name, s->chinese, s->math, s->english,
total_score(s), avg_score(s));
}
void print_all(const StudentList *list) {
printf("Matr Nome Portugues Matematica Ingles Total Media\n");
printf("----------------------------------------------------------\n");
for (int i = 0; i < list->count; i++) {
print_student(&list->data[i]);
}
}
int save_to_file(const StudentList *list, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
fprintf(fp, "%d\n", list->count);
for (int i = 0; i < list->count; i++) {
Student *s = &list->data[i];
fprintf(fp, "%s %s %d %d %d\n", s->id, s->name,
s->chinese, s->math, s->english);
}
fclose(fp);
return 0;
}
int load_from_file(StudentList *list, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
int n;
fscanf(fp, "%d", &n);
for (int i = 0; i < n; i++) {
Student s;
fscanf(fp, "%s %s %d %d %d", s.id, s.name,
&s.chinese, &s.math, &s.english);
list_add(list, &s);
}
fclose(fp);
return 0;
}
Exemplo
int main(void) {
StudentList list;
list_init(&list);
Student s1 = {"001", "Zhang", 85, 92, 78};
Student s2 = {"002", "Li", 90, 88, 95};
Student s3 = {"003", "Wang", 72, 65, 80};
list_add(&list, &s1);
list_add(&list, &s2);
list_add(&list, &s3);
printf("--- Todos os Alunos ---\n");
print_all(&list);
sort_by_total(&list);
printf("\n--- Ordenados por Total ---\n");
print_all(&list);
Student *found = find_by_id(&list, "001");
if (found) {
printf("\nEncontrado 001: %s Total=%d\n", found->name, total_score(found));
}
save_to_file(&list, "students.dat");
printf("\nSalvo em students.dat\n");
list_free(&list);
return 0;
}
--- Todos os Alunos ---
Matr Nome Portugues Matematica Ingles Total Media
----------------------------------------------------------
001 Zhang Portugues:85 Matematica:92 Ingles:78 Total:255 Media:85.0
002 Li Portugues:90 Matematica:88 Ingles:95 Total:273 Media:91.0
003 Wang Portugues:72 Matematica:65 Ingles:80 Total:217 Media:72.3
--- Ordenados por Total ---
Matr Nome Portugues Matematica Ingles Total Media
----------------------------------------------------------
002 Li Portugues:90 Matematica:88 Ingles:95 Total:273 Media:91.0
001 Zhang Portugues:85 Matematica:92 Ingles:78 Total:255 Media:85.0
003 Wang Portugues:72 Matematica:65 Ingles:80 Total:217 Media:72.3
Encontrado 001: Zhang Total=255
Salvo em students.dat
Projeto 2: Gerenciamento de Contatos
Requisitos
- Adicionar contatos (nome, telefone, email, grupo)
- Buscar por nome
- Filtrar por grupo
- Remover um contato
- Modificar informações do contato
- Salvar em arquivo / carregar de arquivo
Design da Estrutura de Dados
typedef struct {
char name[32];
char phone[16];
char email[40];
char group[16];
} Contact;
typedef struct {
Contact *data;
int count;
int capacity;
} ContactList;
Implementação Principal
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[32];
char phone[16];
char email[40];
char group[16];
} Contact;
typedef struct {
Contact *data;
int count;
int capacity;
} ContactList;
void clist_init(ContactList *list) {
list->capacity = 4;
list->count = 0;
list->data = (Contact *)malloc(sizeof(Contact) * list->capacity);
}
void clist_free(ContactList *list) {
free(list->data);
list->data = NULL;
list->count = 0;
list->capacity = 0;
}
void clist_expand(ContactList *list) {
if (list->count < list->capacity) return;
list->capacity *= 2;
Contact *tmp = (Contact *)realloc(list->data, sizeof(Contact) * list->capacity);
if (tmp) list->data = tmp;
}
void clist_add(ContactList *list, const Contact *c) {
clist_expand(list);
list->data[list->count++] = *c;
}
int clist_remove(ContactList *list, const char *name) {
for (int i = 0; i < list->count; i++) {
if (strcmp(list->data[i].name, name) == 0) {
list->data[i] = list->data[list->count - 1];
list->count--;
return 0;
}
}
return -1;
}
Contact *clist_find(ContactList *list, const char *name) {
for (int i = 0; i < list->count; i++) {
if (strcmp(list->data[i].name, name) == 0) {
return &list->data[i];
}
}
return NULL;
}
void clist_filter_by_group(const ContactList *list, const char *group) {
printf("--- Grupo: %s ---\n", group);
for (int i = 0; i < list->count; i++) {
if (strcmp(list->data[i].group, group) == 0) {
printf("%s %s %s\n", list->data[i].name,
list->data[i].phone, list->data[i].email);
}
}
}
void print_contact(const Contact *c) {
printf("%-8s %-14s %-24s %s\n", c->name, c->phone, c->email, c->group);
}
void print_all_contacts(const ContactList *list) {
printf("%-8s %-14s %-24s %s\n", "Nome", "Telefone", "Email", "Grupo");
printf("---------------------------------------------------------\n");
for (int i = 0; i < list->count; i++) {
print_contact(&list->data[i]);
}
}
int save_contacts(const ContactList *list, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
fprintf(fp, "%d\n", list->count);
for (int i = 0; i < list->count; i++) {
Contact *c = &list->data[i];
fprintf(fp, "%s %s %s %s\n", c->name, c->phone, c->email, c->group);
}
fclose(fp);
return 0;
}
int load_contacts(ContactList *list, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
int n;
fscanf(fp, "%d", &n);
for (int i = 0; i < n; i++) {
Contact c;
fscanf(fp, "%s %s %s %s", c.name, c.phone, c.email, c.group);
clist_add(list, &c);
}
fclose(fp);
return 0;
}
Exemplo
int main(void) {
ContactList list;
clist_init(&list);
Contact c1 = {"Zhang", "13800001111", "zhangsan@mail.com", "Colega"};
Contact c2 = {"Li", "13900002222", "lisi@mail.com", "Amigo"};
Contact c3 = {"Wang", "15000003333", "wangwu@mail.com", "Colega"};
Contact c4 = {"Zhao", "18600004444", "zhaoliu@mail.com", "Familia"};
clist_add(&list, &c1);
clist_add(&list, &c2);
clist_add(&list, &c3);
clist_add(&list, &c4);
printf("--- Todos os Contatos ---\n");
print_all_contacts(&list);
clist_filter_by_group(&list, "Colega");
Contact *found = clist_find(&list, "Li");
if (found) {
printf("\nEncontrado Li: %s %s\n", found->phone, found->email);
}
clist_remove(&list, "Wang");
printf("\n--- Apos Remover Wang ---\n");
print_all_contacts(&list);
save_contacts(&list, "contacts.dat");
printf("\nSalvo em contacts.dat\n");
clist_free(&list);
return 0;
}
--- Todos os Contatos ---
Nome Telefone Email Grupo
---------------------------------------------------------
Zhang 13800001111 zhangsan@mail.com Colega
Li 13900002222 lisi@mail.com Amigo
Wang 15000003333 wangwu@mail.com Colega
Zhao 18600004444 zhaoliu@mail.com Familia
--- Grupo: Colega ---
Zhang 13800001111 zhangsan@mail.com
Wang 15000003333 wangwu@mail.com
Encontrado Li: 13900002222 lisi@mail.com
--- Apos Remover Wang ---
Nome Telefone Email Grupo
---------------------------------------------------------
Zhang 13800001111 zhangsan@mail.com Colega
Li 13900002222 lisi@mail.com Amigo
Zhao 18600004444 zhaoliu@mail.com Familia
Salvo em contacts.dat
Revisão dos Padrões-Chave
O Padrão de Array Dinâmico
Ambos os projetos usam o mesmo padrão de array dinâmico:
- Struct contém
ponteiro de dados + contagem + capacidade initaloca memória inicialaddverifica a capacidade e usareallocpara expandir quando cheiofreelibera a memória e define como NULL
Esta é a forma mais comum de implementar coleções dinâmicas em C. Domine uma vez e você pode reutilizar em qualquer cenário.
O Padrão de E/S de Arquivo
fprintf(fp, "%d\n", count);
for (int i = 0; i < count; i++) {
fprintf(fp, "campo1 campo2 ...\n", ...);
}
Ao ler, primeiro leia a contagem, depois percorra os registros. Note que %s para no espaço em branco — garanta que os campos não contenham espaços, ou use fscanf com formato fixo.
O Truque de Remoção
Ao remover um elemento do meio de um array, sobrescreva-o com o último elemento, depois count--. Isso evita mover muitos elementos mas não preserva a ordem. Se a ordem importa, use memmove para deslocar os elementos subsequentes para frente.
❓ Perguntas Frequentes
P: Por que dobrar a capacidade em vez de adicionar 1? R: Dobrar é uma estratégia de custo amortizado O(1) — o número total de cópias é muito menor do que incrementar 1 a cada vez. Este é um resultado clássico da análise de algoritmos, e o vector do C++ usa a mesma estratégia.
P: Devo salvar arquivos em formato binário ou texto? R: Formato texto é legível, editável manualmente e multiplataforma. Binário é mais compacto e rápido para ler/escrever, mas não é legível por humanos. Texto é mais intuitivo para projetos de aprendizado.
P: E se eu precisar preservar a ordem ao usar a remoção por troca-com-o-último? R: Use
memmovepara deslocar elementos para frente:memmove(&data[i], &data[i+1], (count-i-1)*sizeof(T)). Ou mude para uma estrutura de lista ligada.**
P: E se um nome contiver espaços? R:
scanf/fscanfcom%spara no espaço em branco. Usefgetspara ler uma linha inteira, ou concorde em usar sublinhados em vez de espaços, ou use um formato de largura fixa como%31[^\n].**
📖 Resumo
- Arrays dinâmicos usam uma struct
ponteiro + contagem + capacidade, com estratégia de expansão 2x - CRUD (criar, ler, atualizar, remover) é o núcleo do gerenciamento de dados — todo projeto gira em torno dessas quatro operações
- Salvamento em arquivo escreve a contagem de registros primeiro, depois cada registro; leitura carrega a contagem primeiro, depois cada registro
- Remover um elemento do meio pode usar troca-com-o-último (rápido mas desordenado) ou deslocamento (preserva ordem)
- Combinar structs, memória dinâmica e E/S de arquivos é o padrão fundamental para projetos em C
📝 Exercícios
- Adicione um recurso de "editar notas" ao sistema de gerenciamento de notas: digite uma matrícula e novas notas para atualizar o registro do aluno correspondente
- Adicione um recurso de "buscar por número de telefone" ao gerenciador de contatos, e mude o armazenamento para manter os contatos ordenados por nome (mantenha a ordem na inserção)
- Divida ambos os projetos em estruturas multi-arquivo: cada projeto deve ter pelo menos um arquivo de declaração
.he um arquivo de implementação.c, com um Makefile correspondente



