Aula 13 - Revisão da Área II
Esta aula não traz conteúdo novo: é uma revisão rápida de tudo que vimos na Área II (Aulas 8 a 12), seguida de exercícios integradores — problemas maiores, inspirados em questões de provas anteriores, que combinam structs, ponteiros, funções e arquivos ao mesmo tempo, exatamente como costuma ser cobrado em avaliações.
Revisão rápida, aula por aula
Aula 8 — Funções, escopo, parâmetros e retorno.
|
Aula 9 — Ponteiros e passagem por referência.
|
Aula 10 — Structs.
|
Aula 11 — Arquivos texto e binários.
|
Aula 12 — Recursividade, modularização e boas práticas.
|
Exercícios integradores
Os problemas a seguir são inspirados em questões de provas de disciplinas de programação, adaptados para focar nos conceitos da Área II: structs, ponteiros, funções e arquivos.
Exercício 1 — Pontos mais distantes (structs e funções)
Escreva um programa para processar um conjunto de pontos bidimensionais. Cada ponto deve ser representado por uma struct com as coordenadas x e y. O programa deve:
- Ler do usuário a quantidade
nde pontos a considerar, validando quenesteja entre2e100(repita a leitura até um valor válido); - Ler as coordenadas dos
npontos informados; - Determinar a maior distância entre qualquer par de pontos, utilizando uma função
distanciaque recebe duas structs de ponto (por valor) e retorna a distância euclidiana entre elas.
Exercício 2 — Atualização de salários (structs e arquivos)
Escreva um programa para processar informações de funcionários de uma empresa. Cada funcionário deve ser representado por uma struct com nome (string de 80 caracteres), sexo ('M' ou 'F') e salário. O programa deve:
- Ler um valor de salário de referência;
- Ler, de um arquivo binário
"func.bin", uma quantidade indeterminada de funcionários; - Para cada funcionário lido, verificar se o salário é menor que o de referência — se for, atualizá-lo para o valor de referência, usando uma função
atualizaque recebe um ponteiro para a struct do funcionário e o salário de referência, retornando1se atualizou e0caso contrário; - Salvar, em um arquivo de texto
"func.txt", um funcionário por linha (nome e salário, separados por vírgula), apenas para os funcionários que tiveram o salário atualizado.
Exercício 3 — Relatório de produtos por classe (structs e arquivos)
Escreva um programa que gere um relatório textual de produtos armazenados em um arquivo binário "prod.bin". Cada produto deve ser representado por uma struct com nome (100 caracteres), classe ('A', 'B' ou 'C') e preço. O programa deve:
- Obter do usuário a classe de interesse;
- Ler, de
"prod.bin", uma quantidade indeterminada de produtos; - Salvar, em um arquivo de texto
"prod.txt", um produto por linha (apenas nome e preço, separados por vírgula — a classe não precisa ser armazenada), somente para os produtos da classe informada.
Exercício 4 — Catálogo de jogos (texto para binário)
Implemente um programa que leia dados de jogos a partir de um arquivo de texto e os grave em um arquivo binário. Defina uma struct Jogo com int id e char nome[100]. O arquivo "jogos.txt" tem uma linha por jogo, no formato id;nome. Implemente as funções, com exatamente estes protótipos:
int lerJogosTexto(const char *nomeArquivo, Jogo jogos[], int maxJogos);— lê os jogos do arquivo de texto e retorna a quantidade lida.void escreverJogosBinario(const char *nomeArquivo, Jogo jogos[], int quantidade);— grava os jogos no arquivo binário.void exibirJogosBinario(const char *nomeArquivo);— lê o arquivo binário e exibe os jogos no console.
O programa deve ler "jogos.txt", gravar em "jogos.bin", e então exibir o conteúdo gravado.
Exercício 5 — Cadastro de estudantes (passagem por valor, referência e retorno)
Implemente um cadastro simples de estudantes. Defina uma struct Estudante com char nome[50], int matricula, float notas[3] e float media. Implemente as funções:
void cadastrarEstudante(Estudante *estudante);— recebe um ponteiro e preenche nome, matrícula e notas via entrada do usuário, calculando e armazenando a média dentro da própria função.void imprimirEstudante(Estudante estudante);— recebe por valor e exibe todos os dados.float calcularMedia(float notas[3]);— recebe um vetor de notas e retorna a média.Estudante melhorAluno(Estudante estudantes[], int quantidade);— retorna o estudante de maior média.
O programa deve cadastrar ao menos 3 estudantes, exibir todos, e exibir o de maior média.
Exercício 6 — Alunos e alteração de nota
Implemente um programa que gerencie alunos:
- Defina, com
typedef, uma structAlunocom nome e nota; void inicializarAlunos(Aluno *lista, int tamanho);— preenche os dados dos alunos com valores lidos do usuário;void alterarNota(Aluno *aluno, float novaNota);— recebe o endereço de um aluno e altera sua nota;Aluno melhorAluno(Aluno alunos[], int tamanho);— retorna o aluno com melhor nota.
Na main(): declare um vetor de alunos (máximo de 10, via constante), inicialize-os pela função, altere a nota do terceiro aluno para 9.5, e imprima o nome do melhor aluno.
Exercício 7 — Sensores de temperatura (sem main)
Implemente apenas as funções abaixo (sem main) para gerenciar leituras de sensores:
- Defina, com
typedef, uma structSensorcomid(inteiro) etemperatura; int carregarSensoresTexto(char nomeArquivo[], Sensor sensores[], int *tamanho);— lê um arquivo de texto no formatoid leitura(um por linha) e preenche o vetor de sensores. Retorna0em caso de sucesso, ou-1se não conseguir abrir o arquivo.float mediaTemperatura(Sensor sensores[], int qtd);— calcula e retorna a média das leituras.void salvarAcimaMediaBin(const char *nomeArquivo, const Sensor *lista, int qtd, float media);— salva, em arquivo binário, apenas os sensores com temperatura acima da média.
Exercício 8 — Pontos no plano cartesiano (translação)
Implemente um programa que manipule pontos no plano:
- Defina, com
typedef, uma structPontocom coordenadasxey; void lerPontos(Ponto pontos[], int *tamanho);— preenche os pontos via entrada do usuário;float distancia(Ponto p1, Ponto p2);— retorna a distância euclidiana entre dois pontos;void transladarPonto(Ponto *p, float dx, float dy);— altera um ponto (via ponteiro), aplicando uma translação.
Na main(): declare um vetor de pontos (máximo de 10, via constante); use lerPontos para preenchê-lo; calcule a distância entre o primeiro e o segundo pontos; translade o primeiro ponto 3 unidades para a direita e 2 para cima.
Exercício 9 — Veículos (arquivo texto para binário, e consulta)
Implemente um programa que gerencie veículos, lendo de um arquivo texto e gravando/consultando em um arquivo binário:
- Defina, com
typedef, uma structVeiculocom placa e valor de venda; int abrirVeiculos(char nomeArquivo[], Veiculo veiculos[], int *tamanho);— lê um arquivo de texto no formatoplaca,valor(um por linha) e preenche o vetor. Retorna0em caso de sucesso, ou-1se não conseguir abrir.void salvarValoresVeiculos(char nomeArquivo[], Veiculo veiculos[], int tamanho);— grava, em arquivo binário, a quantidade de veículos seguida de seus valores de venda.void recuperaValorVeiculo(char nomeArquivo[], float *valorVenda, int posVeiculo);— recupera, diretamente do arquivo binário (usandofseek, sem precisar ler os anteriores), o valor de venda doposVeiculo-ésimo veículo.
Exercício 10 — Empréstimo de livros (leitura direta na main)
Implemente um programa que controle o empréstimo de livros em uma biblioteca:
- Defina uma struct
Livrocom código, título e número de exemplares disponíveis; - Na
main(), declare um vetor deLivro(atéNposições) e leia os dados diretamente pelo teclado, perguntando após cada livro se o usuário deseja cadastrar mais um; int totalExemplares(Livro livros[], int quantidade);— retorna o total de exemplares na biblioteca. Chame-a e imprima o resultado logo após o cadastro;void emprestaLivro(Livro livros[], int quantidade, int codigo);— reduz em uma unidade os exemplares disponíveis do livro com o código informado (verificando se o código existe e se há unidades a subtrair). Sem retorno;- Ao final, imprima as informações de todos os livros diretamente na
main().
Exercício 11 — Aprovados no ENEM (arquivo binário para texto)
O arquivo "enem.bin" contém dados de estudantes que prestaram o ENEM, cada um com nome (60 caracteres), idade e nota final (real, entre 0 e 100) — a quantidade de registros é desconhecida. Escreva um programa que leia "enem.bin" e grave, em um arquivo de texto "aprovados.txt", apenas o nome e a nota dos alunos com nota maior ou igual a uma nota de corte lida do usuário. Ao final, o programa deve informar a quantidade de alunos aprovados. Não é obrigatório usar funções.
Exercício 12 — Cadastro de imóveis (três funções)
Implemente um programa para gerenciar imóveis para aluguel. Cada imóvel tem um endereço (string), a área em metros quadrados (float) e um indicador de ocupação ('o' para ocupado, 'l' para livre). O programa deve:
- Na
main(), cadastrar dois imóveis, usando uma primeira função que lê do usuário endereço e área, marca a ocupação como livre, e retorna a struct preenchida; - Comparar as áreas dos dois imóveis para identificar o de maior área, e chamar uma segunda função (que recebe um ponteiro para o imóvel) para marcar seu campo de ocupação como
'o'; - Ao final, exibir os dados completos dos dois imóveis com uma terceira função, que recebe o imóvel por valor e imprime seus dados formatados.
Exercício 13 — Reservas de hotel (inadimplentes)
Um hotel armazena reservas em um arquivo binário "reservas.bin", cada registro seguindo a struct:
typedef struct {
char nome_cliente[50];
int numero_quarto;
float valor_total;
int pago; // 1 - Sim, 0 - Nao
} TIPO_RESERVA;
Implemente:
int ler_clientes(TIPO_RESERVA reservas[]);— lê todas as reservas do arquivo binário (quantidade desconhecida, no máximo 100), preenchendo o vetor e retornando quantas foram lidas;void grava_inadimplentes(TIPO_RESERVA reservas[], int quantidade);— gera"inadimplentes.txt", uma linha por reserva não paga (nome, número do quarto e valor total);- Na
main(), exiba o total arrecadado (reservas pagas) e o total pendente (reservas não pagas).
Exercício 14 — Cadastro e busca de músicas (busca direto no arquivo)
Implemente um sistema de cadastro e busca de músicas usando arquivos binários:
- Defina, com
typedef, uma structMusicacom título (50 caracteres), artista (30 caracteres) e ano; - Leia exatamente
N = 5músicas do teclado, em um vetor deMusica; int salvaMusicas(Musica musicas[], int n);— grava o vetor em"cadastroMusicas.bin", retornando1em caso de sucesso e-1em caso de erro;int buscaPorAno(int anoBusca);— lê os dados diretamente do arquivo binário (não do vetor em memória) e imprime as músicas daquele ano, retornando a quantidade encontrada (0se nenhuma,-1em caso de erro de leitura).
A busca deve ser feita exclusivamente lendo o arquivo binário, sem usar o vetor original nem variáveis globais.
Exercício 15 — Concurso de culinária (arquivo texto)
As notas de um concurso de culinária estão em um arquivo de texto "receitas.txt", no formato: uma linha com o nome do prato, seguida de uma linha com duas notas (sabor e apresentação), repetindo para cada prato (no máximo 20). Exemplo:
Bolo de Chocolate 9 8 Lasanha a Bolonhesa 10 8 Mousse de Maracuja 10 9 Feijoada Completa 9 7
Escreva um programa que leia "receitas.txt", armazenando os dados em um vetor de structs Prato (nome, nota de sabor e nota de apresentação); calcule a nota final de cada prato (soma das duas notas); e exiba o nome e a pontuação do prato vencedor (maior nota final).
Exercício 16 — Mínimo, máximo e troca por referência
Escreva uma função em C que analise um vetor de inteiros para encontrar seus elementos de menor e maior valor, e então troque de lugar esses dois elementos dentro do próprio vetor. Os valores mínimo e máximo encontrados devem ser devolvidos através de parâmetros por referência. Não é necessário tratar casos especiais (vetor vazio ou com um único elemento). Utilize obrigatoriamente o protótipo:
void encontrarMinMaxEtrocar(int vetor[], int tamanho, int *min_encontrado, int *max_encontrado);
Sugestões de Soluções
Exercício 1 — Pontos mais distantes
#include <stdio.h> #include <math.h> #define MAX_PONTOS 100 typedef struct { float x; float y; } Ponto; // Protótipo float distancia(Ponto a, Ponto b); int main() { Ponto pontos[MAX_PONTOS]; int n, i, j; float maiorDistancia = 0, d; printf("Quantidade de pontos (2 a 100): "); scanf("%d", &n); while (n < 2 || n > 100) { printf("Valor invalido! Digite novamente (2 a 100): "); scanf("%d", &n); } for (i = 0; i < n; i++) { printf("Ponto %d - x: ", i); scanf("%f", &pontos[i].x); printf("Ponto %d - y: ", i); scanf("%f", &pontos[i].y); } for (i = 0; i < n; i++) { for (j = i + 1; j < n; j++) { d = distancia(pontos[i], pontos[j]); if (d > maiorDistancia) { maiorDistancia = d; } } } printf("Maior distancia entre dois pontos: %.2f\n", maiorDistancia); return 0; } // Implementação float distancia(Ponto a, Ponto b) { float dx = a.x - b.x; float dy = a.y - b.y; return sqrt(dx * dx + dy * dy); }
A validação de n usa um while (Aula 3) porque não sabemos de antemão quantas vezes o usuário vai errar. distancia recebe as duas structs por valor — como só precisamos ler as coordenadas, não há necessidade de ponteiros aqui.
Exercício 2 — Atualização de salários
#include <stdio.h> typedef struct { char nome[80]; char sexo; float salario; } Funcionario; // Protótipo int atualiza(Funcionario *f, float referencia); int main() { float referencia; Funcionario f; FILE *bin, *txt; printf("Salario de referencia: "); scanf("%f", &referencia); bin = fopen("func.bin", "rb"); if (bin == NULL) { return 1; } txt = fopen("func.txt", "w"); if (txt == NULL) { fclose(bin); return 1; } while (fread(&f, sizeof(Funcionario), 1, bin) == 1) { if (atualiza(&f, referencia)) { fprintf(txt, "%s,%.2f\n", f.nome, f.salario); } } fclose(bin); fclose(txt); return 0; } // Implementação int atualiza(Funcionario *f, float referencia) { if (f->salario < referencia) { f->salario = referencia; return 1; } return 0; }
Como a quantidade de funcionários no arquivo é desconhecida, usamos o próprio retorno de fread como condição do while (Aula 11): o laço só continua enquanto conseguirmos ler exatamente 1 struct por vez. atualiza recebe um ponteiro porque precisa alterar de fato o salário do funcionário (Aula 9), e seu retorno (0 ou 1) decide se aquele funcionário é gravado em "func.txt".
Exercício 3 — Relatório de produtos por classe
#include <stdio.h> typedef struct { char nome[100]; char classe; float preco; } Produto; int main() { char classeDesejada; Produto p; FILE *bin, *txt; printf("Classe de interesse (A, B ou C): "); scanf(" %c", &classeDesejada); bin = fopen("prod.bin", "rb"); if (bin == NULL) { return 1; } txt = fopen("prod.txt", "w"); if (txt == NULL) { fclose(bin); return 1; } while (fread(&p, sizeof(Produto), 1, bin) == 1) { if (p.classe == classeDesejada) { fprintf(txt, "%s,%.2f\n", p.nome, p.preco); } } fclose(bin); fclose(txt); return 0; }
O espaço antes de %c em scanf(" %c", &classeDesejada) descarta espaços/quebras de linha deixados no buffer, evitando que a leitura do caractere pegue um '\n' por engano — um cuidado importante ao misturar %d/%f com %c no scanf.
Exercício 4 — Catálogo de jogos
#include <stdio.h> #include <string.h> #define MAX_JOGOS 100 typedef struct { int id; char nome[100]; } Jogo; // Protótipos (assinaturas exigidas pelo enunciado) int lerJogosTexto(const char *nomeArquivo, Jogo jogos[], int maxJogos); void escreverJogosBinario(const char *nomeArquivo, Jogo jogos[], int quantidade); void exibirJogosBinario(const char *nomeArquivo); int main() { Jogo jogos[MAX_JOGOS]; int quantidade; // 1) Le do arquivo texto quantidade = lerJogosTexto("jogos.txt", jogos, MAX_JOGOS); // 2) Grava no arquivo binario escreverJogosBinario("jogos.bin", jogos, quantidade); // 3) Exibe o que foi gravado, lendo de volta do binario exibirJogosBinario("jogos.bin"); return 0; } // Le "id;nome" por linha, ate o fim do arquivo ou ate maxJogos int lerJogosTexto(const char *nomeArquivo, Jogo jogos[], int maxJogos) { FILE *arquivo = fopen(nomeArquivo, "r"); int quantidade = 0; if (arquivo == NULL) { return 0; } while (quantidade < maxJogos && fscanf(arquivo, "%d;%99[^\n]\n", &jogos[quantidade].id, jogos[quantidade].nome) == 2) { quantidade++; } fclose(arquivo); return quantidade; } // Grava o vetor inteiro de uma vez, em modo binario void escreverJogosBinario(const char *nomeArquivo, Jogo jogos[], int quantidade) { FILE *arquivo = fopen(nomeArquivo, "wb"); if (arquivo == NULL) { return; } fwrite(jogos, sizeof(Jogo), quantidade, arquivo); fclose(arquivo); } // Le do binario, um Jogo por vez, e exibe no console void exibirJogosBinario(const char *nomeArquivo) { FILE *arquivo = fopen(nomeArquivo, "rb"); Jogo j; if (arquivo == NULL) { return; } printf("Jogos cadastrados:\n"); while (fread(&j, sizeof(Jogo), 1, arquivo) == 1) { printf("%d - %s\n", j.id, j.nome); } fclose(arquivo); }
O formato de leitura "%d;%99[^\n]\n" lê um inteiro, pula o ; literal, e captura o restante da linha (até \n, no máximo 99 caracteres) como o nome — uma forma de ler campos separados por um caractere que não seja espaço, algo que %s sozinho não conseguiria fazer (pois %s para no primeiro espaço).
Exercício 5 — Cadastro de estudantes
#include <stdio.h> #define NUM_ESTUDANTES 3 typedef struct { char nome[50]; int matricula; float notas[3]; float media; } Estudante; // Protótipos void cadastrarEstudante(Estudante *estudante); void imprimirEstudante(Estudante estudante); float calcularMedia(float notas[3]); Estudante melhorAluno(Estudante estudantes[], int quantidade); int main() { Estudante turma[NUM_ESTUDANTES]; int i; for (i = 0; i < NUM_ESTUDANTES; i++) { printf("--- Estudante %d ---\n", i + 1); cadastrarEstudante(&turma[i]); } printf("\nTodos os estudantes:\n"); for (i = 0; i < NUM_ESTUDANTES; i++) { imprimirEstudante(turma[i]); } printf("\nMelhor aluno:\n"); imprimirEstudante(melhorAluno(turma, NUM_ESTUDANTES)); return 0; } // Implementações void cadastrarEstudante(Estudante *estudante) { int i; printf("Nome: "); scanf("%s", estudante->nome); printf("Matricula: "); scanf("%d", &estudante->matricula); for (i = 0; i < 3; i++) { printf("Nota %d: ", i + 1); scanf("%f", &estudante->notas[i]); } estudante->media = calcularMedia(estudante->notas); } void imprimirEstudante(Estudante estudante) { printf("%s (matricula %d) - notas: %.1f, %.1f, %.1f - media: %.2f\n", estudante.nome, estudante.matricula, estudante.notas[0], estudante.notas[1], estudante.notas[2], estudante.media); } float calcularMedia(float notas[3]) { return (notas[0] + notas[1] + notas[2]) / 3; } Estudante melhorAluno(Estudante estudantes[], int quantidade) { int i; Estudante melhor = estudantes[0]; for (i = 1; i < quantidade; i++) { if (estudantes[i].media > melhor.media) { melhor = estudantes[i]; } } return melhor; }
Note as três formas de passagem em ação: cadastrarEstudante recebe ponteiro (precisa alterar o estudante de quem chamou); imprimirEstudante recebe por valor (só precisa ler); e melhorAluno retorna uma struct inteira (uma cópia do melhor estudante encontrado).
Exercício 6 — Alunos e alteração de nota
#include <stdio.h> #define MAX_ALUNOS 10 typedef struct { char nome[50]; float nota; } Aluno; // Protótipos void inicializarAlunos(Aluno *lista, int tamanho); void alterarNota(Aluno *aluno, float novaNota); Aluno melhorAluno(Aluno alunos[], int tamanho); int main() { Aluno alunos[MAX_ALUNOS]; inicializarAlunos(alunos, MAX_ALUNOS); alterarNota(&alunos[2], 9.5); // terceiro aluno = indice 2 printf("Melhor aluno: %s\n", melhorAluno(alunos, MAX_ALUNOS).nome); return 0; } // Implementações void inicializarAlunos(Aluno *lista, int tamanho) { int i; for (i = 0; i < tamanho; i++) { printf("Nome do aluno %d: ", i + 1); scanf("%s", lista[i].nome); printf("Nota do aluno %d: ", i + 1); scanf("%f", &lista[i].nota); } } void alterarNota(Aluno *aluno, float novaNota) { aluno->nota = novaNota; } Aluno melhorAluno(Aluno alunos[], int tamanho) { int i; Aluno melhor = alunos[0]; for (i = 1; i < tamanho; i++) { if (alunos[i].nota > melhor.nota) { melhor = alunos[i]; } } return melhor; }
Repare como &alunos[2] obtém o endereço de uma posição específica do vetor — combinando o que vimos sobre vetores (Aula 4) e sobre & (Aula 9) — permitindo que alterarNota altere exatamente aquele aluno, e nenhum outro.
Exercício 7 — Sensores de temperatura
#include <stdio.h> typedef struct { int id; float temperatura; } Sensor; // (b) Le "id leitura" por linha, ate o fim do arquivo int carregarSensoresTexto(char nomeArquivo[], Sensor sensores[], int *tamanho) { FILE *arquivo = fopen(nomeArquivo, "r"); if (arquivo == NULL) { return -1; } *tamanho = 0; while (fscanf(arquivo, "%d %f", &sensores[*tamanho].id, &sensores[*tamanho].temperatura) == 2) { (*tamanho)++; } fclose(arquivo); return 0; } // (c) Média simples das temperaturas float mediaTemperatura(Sensor sensores[], int qtd) { int i; float soma = 0; for (i = 0; i < qtd; i++) { soma += sensores[i].temperatura; } return soma / qtd; } // (d) Salva em binario apenas quem esta acima da media void salvarAcimaMediaBin(const char *nomeArquivo, const Sensor *lista, int qtd, float media) { FILE *arquivo = fopen(nomeArquivo, "wb"); int i; if (arquivo == NULL) { return; } for (i = 0; i < qtd; i++) { if (lista[i].temperatura > media) { fwrite(&lista[i], sizeof(Sensor), 1, arquivo); } } fclose(arquivo); }
carregarSensoresTexto recebe tamanho como ponteiro para int (Aula 9) exatamente pelo mesmo motivo de argc/*tamanho em outros exercícios: a função precisa devolver, além dos dados lidos, quantos elementos foram lidos — e uma função só pode ter um valor de return, então o segundo "retorno" (a contagem) sai por referência. O parâmetro const Sensor *lista, em salvarAcimaMediaBin, usa const (Aula 12) para deixar explícito que a função só lê a lista, nunca a altera.
Exercício 8 — Pontos e translação
#include <stdio.h> #include <math.h> #define MAX_PONTOS 10 typedef struct { float x; float y; } Ponto; // Protótipos void lerPontos(Ponto pontos[], int *tamanho); float distancia(Ponto p1, Ponto p2); void transladarPonto(Ponto *p, float dx, float dy); int main() { Ponto pontos[MAX_PONTOS]; int tamanho; lerPontos(pontos, &tamanho); printf("Distancia entre o 1o e o 2o ponto: %.2f\n", distancia(pontos[0], pontos[1])); transladarPonto(&pontos[0], 3, 2); printf("Primeiro ponto transladado: (%.2f, %.2f)\n", pontos[0].x, pontos[0].y); return 0; } // Implementações void lerPontos(Ponto pontos[], int *tamanho) { int i; printf("Quantos pontos deseja informar (max %d)? ", MAX_PONTOS); scanf("%d", tamanho); for (i = 0; i < *tamanho; i++) { printf("Ponto %d - x: ", i); scanf("%f", &pontos[i].x); printf("Ponto %d - y: ", i); scanf("%f", &pontos[i].y); } } float distancia(Ponto p1, Ponto p2) { float dx = p1.x - p2.x; float dy = p1.y - p2.y; return sqrt(dx * dx + dy * dy); } void transladarPonto(Ponto *p, float dx, float dy) { p->x += dx; p->y += dy; }
Note o parâmetro tamanho em lerPontos: dentro da função, usamos *tamanho (ou passamos o próprio tamanho, que já é um ponteiro, direto ao scanf) para ler e alterar o valor de tamanho lá na main() — o mesmo padrão do Exercício 7.
Exercício 9 — Veículos
#include <stdio.h> #define MAX_VEICULOS 100 typedef struct { char placa[10]; float valorVenda; } Veiculo; // (b) Le "placa,valor" por linha int abrirVeiculos(char nomeArquivo[], Veiculo veiculos[], int *tamanho) { FILE *arquivo = fopen(nomeArquivo, "r"); if (arquivo == NULL) { return -1; } *tamanho = 0; while (fscanf(arquivo, "%9[^,],%f\n", veiculos[*tamanho].placa, &veiculos[*tamanho].valorVenda) == 2) { (*tamanho)++; } fclose(arquivo); return 0; } // (c) Grava quantidade + valores em binario void salvarValoresVeiculos(char nomeArquivo[], Veiculo veiculos[], int tamanho) { FILE *arquivo = fopen(nomeArquivo, "wb"); int i; if (arquivo == NULL) { return; } fwrite(&tamanho, sizeof(int), 1, arquivo); for (i = 0; i < tamanho; i++) { fwrite(&veiculos[i].valorVenda, sizeof(float), 1, arquivo); } fclose(arquivo); } // (d) Recupera o n-esimo valor, pulando direto ate ele com fseek void recuperaValorVeiculo(char nomeArquivo[], float *valorVenda, int posVeiculo) { FILE *arquivo = fopen(nomeArquivo, "rb"); long deslocamento; if (arquivo == NULL) { return; } // pula o "int tamanho" inicial, e mais "posVeiculo" floats deslocamento = sizeof(int) + (long) posVeiculo * sizeof(float); fseek(arquivo, deslocamento, SEEK_SET); fread(valorVenda, sizeof(float), 1, arquivo); fclose(arquivo); }
O deslocamento calculado em recuperaValorVeiculo é o ponto-chave (Aula 11): como o arquivo começa com um int (a quantidade) seguido de floats consecutivos, para chegar ao valor de índice posVeiculo é preciso pular sizeof(int) bytes (o cabeçalho) mais posVeiculo * sizeof(float) bytes (os valores anteriores) — sem nunca precisar ler os valores intermediários.
Exercício 10 — Empréstimo de livros
#include <stdio.h> #define MAX_LIVROS 50 typedef struct { int codigo; char titulo[100]; int exemplares; } Livro; // Protótipos int totalExemplares(Livro livros[], int quantidade); void emprestaLivro(Livro livros[], int quantidade, int codigo); int main() { Livro livros[MAX_LIVROS]; int total = 0; char continuar; int codigoEmprestimo, i; // (b) leitura direta na main do { printf("--- Livro %d ---\n", total + 1); printf("Codigo: "); scanf("%d", &livros[total].codigo); printf("Titulo: "); scanf(" %99[^\n]", livros[total].titulo); printf("Exemplares disponiveis: "); scanf("%d", &livros[total].exemplares); total++; printf("Cadastrar mais um livro? (s/n): "); scanf(" %c", &continuar); } while ((continuar == 's' || continuar == 'S') && total < MAX_LIVROS); // (c) printf("Total de exemplares na biblioteca: %d\n", totalExemplares(livros, total)); // (d) printf("Codigo do livro a emprestar: "); scanf("%d", &codigoEmprestimo); emprestaLivro(livros, total, codigoEmprestimo); // (e) printf("\nAcervo atual:\n"); for (i = 0; i < total; i++) { printf("%d - %s (%d exemplares)\n", livros[i].codigo, livros[i].titulo, livros[i].exemplares); } return 0; } // Implementações int totalExemplares(Livro livros[], int quantidade) { int i, total = 0; for (i = 0; i < quantidade; i++) { total += livros[i].exemplares; } return total; } void emprestaLivro(Livro livros[], int quantidade, int codigo) { int i; for (i = 0; i < quantidade; i++) { if (livros[i].codigo == codigo) { if (livros[i].exemplares > 0) { livros[i].exemplares--; printf("Emprestimo realizado!\n"); } else { printf("Nao ha exemplares disponiveis!\n"); } return; } } printf("Codigo nao encontrado!\n"); }
Como um vetor já é, por natureza, passado como ponteiro para uma função (Aula 9), emprestaLivro consegue alterar livros[i].exemplares diretamente, mesmo sem retorno — a alteração já é visível na main() assim que a função termina, sem precisar de return nem de um parâmetro Livro * explícito adicional.
Exercício 11 — Aprovados no ENEM
#include <stdio.h> typedef struct { char nome[60]; int idade; float nota; } Aluno; int main() { Aluno a; float notaCorte; int aprovados = 0; FILE *bin, *txt; printf("Nota de corte: "); scanf("%f", ¬aCorte); bin = fopen("enem.bin", "rb"); if (bin == NULL) { return 1; } txt = fopen("aprovados.txt", "w"); if (txt == NULL) { fclose(bin); return 1; } while (fread(&a, sizeof(Aluno), 1, bin) == 1) { if (a.nota >= notaCorte) { fprintf(txt, "%s,%.2f\n", a.nome, a.nota); aprovados++; } } fclose(bin); fclose(txt); printf("Total de alunos aprovados: %d\n", aprovados); return 0; }
Como o enunciado dispensa o uso de funções, o programa inteiro cabe na main() — mas repare que a lógica é idêntica à do Exercício 2: fread em laço para uma quantidade desconhecida de registros, filtrando e gravando em texto apenas os que atendem à condição.
Exercício 12 — Cadastro de imóveis
#include <stdio.h> typedef struct { char endereco[80]; float area; char ocupacao; } Imovel; // Protótipos Imovel cadastrarImovel(); void ocuparImovel(Imovel *imovel); void exibirImovel(Imovel imovel); int main() { Imovel imovel1, imovel2; printf("--- Imovel 1 ---\n"); imovel1 = cadastrarImovel(); printf("--- Imovel 2 ---\n"); imovel2 = cadastrarImovel(); if (imovel1.area >= imovel2.area) { ocuparImovel(&imovel1); } else { ocuparImovel(&imovel2); } printf("\nSituacao final:\n"); exibirImovel(imovel1); exibirImovel(imovel2); return 0; } // Implementações Imovel cadastrarImovel() { Imovel novo; printf("Endereco: "); scanf(" %79[^\n]", novo.endereco); printf("Area (m2): "); scanf("%f", &novo.area); novo.ocupacao = 'l'; return novo; } void ocuparImovel(Imovel *imovel) { imovel->ocupacao = 'o'; } void exibirImovel(Imovel imovel) { printf("%s - %.2f m2 - status: %c\n", imovel.endereco, imovel.area, imovel.ocupacao); }
As três formas de passagem aparecem lado a lado outra vez: cadastrarImovel retorna a struct pronta (não recebe nenhum imóvel existente, então não faria sentido usar ponteiro); ocuparImovel recebe ponteiro (precisa alterar o imóvel escolhido); exibirImovel recebe por valor (só exibe, nunca altera).
Exercício 13 — Reservas de hotel
#include <stdio.h> #define MAX_RESERVAS 100 typedef struct { char nome_cliente[50]; int numero_quarto; float valor_total; int pago; } TIPO_RESERVA; // Protótipos int ler_clientes(TIPO_RESERVA reservas[]); void grava_inadimplentes(TIPO_RESERVA reservas[], int quantidade); int main() { TIPO_RESERVA reservas[MAX_RESERVAS]; int quantidade, i; float arrecadado = 0, pendente = 0; quantidade = ler_clientes(reservas); grava_inadimplentes(reservas, quantidade); for (i = 0; i < quantidade; i++) { if (reservas[i].pago) { arrecadado += reservas[i].valor_total; } else { pendente += reservas[i].valor_total; } } printf("Total arrecadado: %.2f\n", arrecadado); printf("Total pendente: %.2f\n", pendente); return 0; } // Implementações int ler_clientes(TIPO_RESERVA reservas[]) { FILE *arquivo = fopen("reservas.bin", "rb"); int quantidade = 0; if (arquivo == NULL) { return 0; } while (quantidade < MAX_RESERVAS && fread(&reservas[quantidade], sizeof(TIPO_RESERVA), 1, arquivo) == 1) { quantidade++; } fclose(arquivo); return quantidade; } void grava_inadimplentes(TIPO_RESERVA reservas[], int quantidade) { FILE *arquivo = fopen("inadimplentes.txt", "w"); int i; if (arquivo == NULL) { return; } for (i = 0; i < quantidade; i++) { if (!reservas[i].pago) { fprintf(arquivo, "%s,%d,%.2f\n", reservas[i].nome_cliente, reservas[i].numero_quarto, reservas[i].valor_total); } } fclose(arquivo); }
ler_clientes retorna a quantidade lida (em vez de recebê-la por ponteiro, como em outros exercícios) — ambas as abordagens são válidas; aqui, como o vetor já é alterado diretamente (por já ser passado como ponteiro), sobra apenas um valor para "devolver", o que se encaixa perfeitamente em um return simples.
Exercício 14 — Cadastro e busca de músicas
#include <stdio.h> #define N 5 typedef struct { char titulo[50]; char artista[30]; int ano; } Musica; // Protótipos int salvaMusicas(Musica musicas[], int n); int buscaPorAno(int anoBusca); int main() { Musica musicas[N]; int i, anoBusca, encontradas; for (i = 0; i < N; i++) { printf("--- Musica %d ---\n", i + 1); printf("Titulo: "); scanf(" %49[^\n]", musicas[i].titulo); printf("Artista: "); scanf(" %29[^\n]", musicas[i].artista); printf("Ano: "); scanf("%d", &musicas[i].ano); } if (salvaMusicas(musicas, N) != 1) { printf("Erro ao salvar musicas!\n"); return 1; } printf("Ano de interesse: "); scanf("%d", &anoBusca); encontradas = buscaPorAno(anoBusca); if (encontradas > 0) { printf("Total encontrado: %d\n", encontradas); } else if (encontradas == 0) { printf("Nenhuma musica encontrada para o ano informado.\n"); } else { printf("Erro na leitura do arquivo!\n"); } return 0; } // Implementações int salvaMusicas(Musica musicas[], int n) { FILE *arquivo = fopen("cadastroMusicas.bin", "wb"); if (arquivo == NULL) { return -1; } fwrite(musicas, sizeof(Musica), n, arquivo); fclose(arquivo); return 1; } int buscaPorAno(int anoBusca) { FILE *arquivo = fopen("cadastroMusicas.bin", "rb"); Musica m; int encontradas = 0; if (arquivo == NULL) { return -1; } while (fread(&m, sizeof(Musica), 1, arquivo) == 1) { if (m.ano == anoBusca) { printf("%s - %s (%d)\n", m.titulo, m.artista, m.ano); encontradas++; } } fclose(arquivo); return encontradas; }
O ponto central deste exercício: buscaPorAno recebe apenas o ano como parâmetro — nada do vetor original é passado para ela. A busca é feita inteiramente reabrindo "cadastroMusicas.bin" e lendo Musica por Musica com fread, exatamente como fizemos ao ler qualquer outro arquivo binário de tamanho desconhecido nesta revisão.
Exercício 15 — Concurso de culinária
#include <stdio.h> #define MAX_PRATOS 20 typedef struct { char nome[50]; int notaSabor; int notaApresentacao; } Prato; int main() { Prato pratos[MAX_PRATOS]; int total = 0; int i, notaFinal, melhorIndice, melhorNota; FILE *arquivo = fopen("receitas.txt", "r"); if (arquivo == NULL) { return 1; } while (total < MAX_PRATOS && fscanf(arquivo, " %49[^\n] %d %d", pratos[total].nome, &pratos[total].notaSabor, &pratos[total].notaApresentacao) == 3) { total++; } fclose(arquivo); melhorIndice = 0; melhorNota = pratos[0].notaSabor + pratos[0].notaApresentacao; for (i = 1; i < total; i++) { notaFinal = pratos[i].notaSabor + pratos[i].notaApresentacao; if (notaFinal > melhorNota) { melhorNota = notaFinal; melhorIndice = i; } } printf("O prato vencedor e: %s\n", pratos[melhorIndice].nome); printf("Nota Final: %d\n", melhorNota); return 0; }
O formato " %49[^\n] %d %d" lê, em uma única chamada de fscanf, o nome do prato (até 49 caracteres, parando na quebra de linha) seguido das duas notas na linha seguinte — os espaços entre os especificadores fazem o fscanf pular automaticamente as quebras de linha entre um campo e outro.
Exercício 16 — Mínimo, máximo e troca por referência
#include <stdio.h> // Protótipo (exigido pelo enunciado) void encontrarMinMaxEtrocar(int vetor[], int tamanho, int *min_encontrado, int *max_encontrado); int main() { int numeros[6] = {8, 3, 9, 1, 5, 2}; int min, max, i; encontrarMinMaxEtrocar(numeros, 6, &min, &max); printf("Minimo: %d, Maximo: %d\n", min, max); printf("Vetor apos a troca: "); for (i = 0; i < 6; i++) { printf("%d ", numeros[i]); } printf("\n"); return 0; } // Implementação void encontrarMinMaxEtrocar(int vetor[], int tamanho, int *min_encontrado, int *max_encontrado) { int i, posMin = 0, posMax = 0; int temp; for (i = 1; i < tamanho; i++) { if (vetor[i] < vetor[posMin]) { posMin = i; } if (vetor[i] > vetor[posMax]) { posMax = i; } } *min_encontrado = vetor[posMin]; *max_encontrado = vetor[posMax]; // troca de posicao os elementos min e max dentro do proprio vetor temp = vetor[posMin]; vetor[posMin] = vetor[posMax]; vetor[posMax] = temp; }
Note que guardamos as posições (posMin, posMax) do menor e maior valor, e não apenas os valores — é a posição que permite tanto ler o valor (para devolvê-lo por *min_encontrado/*max_encontrado) quanto trocá-lo de lugar dentro do vetor original, já que o vetor foi passado (como sempre) como um ponteiro para seu primeiro elemento (Aula 9).