Projeto Boletim de Notas
A Ana dá aula de reforço para uma turma pequena e quer um programa que faça o boletim: ela digita o nome e a nota de cada aluno, e o programa mostra a média da turma, quem tirou a maior e a menor nota, quantos passaram e uma tabela bem alinhada. É o projeto para treinar arrays (muitos valores numa variável só) e structs (vários dados juntos de uma pessoa).
- Guardar várias notas num array e percorrê-lo com for.
- Somar e tirar a média (e o cuidado com o índice que passa do fim).
- Juntar nome e nota numa struct e fazer um array de structs.
- Ler texto com
%29ssem estourar o array (Strings). - Achar o maior, o menor e contar quem passou, num laço só.
- Alinhar uma tabela com
%-10se%5.1f(Formatos do printf) e o problema dos acentos.
O que vamos construir
O boletim pronto, na máquina da Ana, com cinco alunos:
$ ./boletim === Boletim da Turma === Quantos alunos (1 a 30)? 5 Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Carla 9 Aluno 3 (nome e nota): Diego 5.5 Aluno 4 (nome e nota): Elisa 8 Aluno 5 (nome e nota): Gabriel 3.5 Aluno Nota Situação ---------- ----- ----------- Bruno 7.5 Aprovado Carla 9.0 Aprovado Diego 5.5 Recuperação Elisa 8.0 Aprovado Gabriel 3.5 Reprovado ---------- ----- ----------- Média 6.7 Maior nota: 9.0 (Carla) Menor nota: 3.5 (Gabriel) Aprovados: 3 de 5
- As notas num array e a média.
- Nome e nota juntos: um array de structs.
- Maior, menor e aprovados.
- A tabela alinhada (
%-10s %5.1f, uma função e a quantidade de alunos).
Antes de começar
Você precisa ter visto Arrays, for e scanf; para a versão 2, Structs. O arquivo vai se chamar boletim.c:
$ mkdir -p c/notas $ cd c/notas
Versão 1 · As notas num array e a média
Cinco alunos, cinco notas. Daria para criar nota1, nota2… nota5, mas e se a turma tiver 30? Um array guarda todas numa variável só: notas[0] é a primeira, notas[4] a quinta. E um for passa por todas, sem repetir código.
#include <stdio.h>
#define QTD 5
int main(void) {
double notas[QTD] = {0};
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Nota do aluno %d: ", i + 1);
if (scanf("%lf", ¬as[i]) != 1) {
printf("\nNota inválida.\n");
return 1;
}
soma += notas[i];
}
printf("Notas: ");
for (int i = 0; i < QTD; i++) {
printf("%.1f ", notas[i]);
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
return 0;
}
| Linha | O que faz |
|---|---|
#define QTD 5 | O tamanho da turma, com nome. Usado no array e nos laços: para mudar a turma, você muda um número só. |
double notas[QTD] = {0}; | Um array de 5 double, todos começando em zero. O {0} zera o array inteiro. |
for (int i = 0; i < QTD; i++) | i vale 0, 1, 2, 3 e 4: as posições do array. Repare no <: a posição 5 não existe (veja o cenário abaixo). |
printf("Nota do aluno %d: ", i + 1); | Para a Ana, o primeiro aluno é o 1, não o 0. Por isso mostramos i + 1. |
scanf("%lf", ¬as[i]) | Lê a nota direto na posição i do array. O & pega o endereço daquela posição. |
soma += notas[i]; | Acumula: soma = soma + notas[i] (veja Operadores de Atribuição). |
printf("%.1f ", notas[i]); | Mostra cada nota com uma casa decimal. |
soma / QTD | A média. Como soma é double, a divisão tem casas decimais (com dois int, 33 / 5 daria 6; veja Conversão de Tipos). |
$ gcc -std=c23 -Wall -Wextra boletim.c -o boletim $ ./boletim === Boletim da Turma === Nota do aluno 1: 7.5 Nota do aluno 2: 9 Nota do aluno 3: 5.5 Nota do aluno 4: 8 Nota do aluno 5: 3.5 Notas: 7.5 9.0 5.5 8.0 3.5 Média da turma: 6.70
i <= QTD: o laço passa do fim do array
Este código tem um erro de propósito: for (int i = 0; i <= QTD; i++) no laço que lê as notas.
$ gcc -std=c23 -Wall -Wextra boletim.c -o boletim $ ./boletim === Boletim da Turma === Nota do aluno 1: 7.5 Nota do aluno 2: 9 Nota do aluno 3: 5.5 Nota do aluno 4: 8 Nota do aluno 5: 3.5 Nota do aluno 6: 10 « pediu uma nota a mais Notas: 7.5 9.0 5.5 8.0 3.5 Média da turma: 8.70 « e a média ficou errada *** stack smashing detected ***: terminated Abortado (imagem do núcleo gravada) ./boletim
O que aconteceu: com <=, o laço também roda com i igual a 5, e o scanf escreveu a sexta nota em notas[5], uma posição que não pertence ao array. O C não confere: ele escreveu por cima do que estava na memória logo depois. A média ficou errada porque a sexta nota também entrou na soma (43.5 / 5 = 8.70). E a escrita fora do array estragou uma "trava" que o gcc coloca na pilha; no fim do main, o programa viu a trava quebrada ("stack smashing detected", pilha esmagada) e se abortou. Em outro programa, poderia não aparecer nada e só dar um resultado errado.
Para achar a linha exata, compile com -g -fsanitize=address (veja Sanitizers):
$ gcc -std=c23 -Wall -Wextra -g -fsanitize=address boletim.c -o boletim
$ ./boletim
=== Boletim da Turma ===
Nota do aluno 1: 7.5
Nota do aluno 2: 9
Nota do aluno 3: 5.5
Nota do aluno 4: 8
Nota do aluno 5: 3.5
Nota do aluno 6: 10
=================================================================
==74118==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x79bf1ddf0058 at pc 0x7dbf202b05dc bp 0x7ffdac6bde10 sp 0x7ffdac6bd598
WRITE of size 8 at 0x79bf1ddf0058 thread T0
#0 0x7dbf202b05db in scanf_common ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:346
#1 0x7dbf202f0e4e in __isoc23_vscanf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1586
#2 0x7dbf202f1634 in __isoc23_scanf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1616
#3 0x60d26256341d in main /home/ana/c/notas/boletim.c:12
… « o relatório continua; cortamos aqui
This frame has 1 object(s):
[48, 88) 'notas' (line 6) <== Memory access at offset 88 overflows this variable
"Estouro de buffer na pilha": uma escrita (WRITE) de 8 bytes (um double) feita pelo scanf chamado em boletim.c:12, logo depois do fim de notas (linha 6).
Como consertar: um array de QTD posições vai de 0 a QTD - 1. O laço é i < QTD. No editor do guia esse erro pode passar calado (a memória do WebAssembly é organizada de outro jeito), por isso o exemplo é do terminal.
Missão (versão 1): o programa lê as notas, mas a média sai 0.00. Dentro do primeiro laço, some cada nota em soma com soma += notas[i];. A entrada da missão é 7.5, 9, 5.5, 8 e 3.5.
#include <stdio.h>
#define QTD 5
int main(void) {
double notas[QTD] = {0};
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Nota do aluno %d: ", i + 1);
if (scanf("%lf", ¬as[i]) != 1) {
printf("\nNota inválida.\n");
return 1;
}
// some a nota em soma
}
printf("Notas: ");
for (int i = 0; i < QTD; i++) {
printf("%.1f ", notas[i]);
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
return 0;
}
#include <stdio.h>
#define QTD 5
int main(void) {
double notas[QTD] = {0};
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Nota do aluno %d: ", i + 1);
if (scanf("%lf", ¬as[i]) != 1) {
printf("\nNota inválida.\n");
return 1;
}
soma += notas[i];
}
printf("Notas: ");
for (int i = 0; i < QTD; i++) {
printf("%.1f ", notas[i]);
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
return 0;
}
Versão 2 · Nome e nota juntos: um array de structs
Notas sem nome não servem para um boletim. Poderíamos ter dois arrays, um de nomes e um de notas, e cuidar para a posição 2 de um combinar com a posição 2 do outro. Mais simples: uma struct, uma "ficha" com os dados de um aluno, e um array de fichas.
#include <stdio.h>
#define QTD 5
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
printf("\n");
for (int i = 0; i < QTD; i++) {
printf("%s: %.1f\n", turma[i].nome, turma[i].nota);
}
printf("Média da turma: %.2f\n", soma / QTD);
return 0;
}
- struct Aluno { char nome[30]; double nota; };
- Cria um tipo novo: cada
struct Alunotem um nome (texto de até 29 letras mais o'\0', veja Strings) e uma nota. O;depois da}é obrigatório. - struct Aluno turma[QTD];
- Um array de 5 fichas.
turma[0]é a ficha do primeiro aluno. - turma[i].nome e turma[i].nota
- O ponto entra na ficha: "o nome da ficha
i". - scanf("%29s %lf", turma[i].nome, &turma[i].nota)
- Lê uma palavra e um número.
%29slê no máximo 29 caracteres: sobra lugar para o'\0'e o nome nunca passa do fim do array. Onomenão leva&: um array já é o endereço do seu começo (veja Ponteiros e Arrays). - != 2
- Agora o
scanftem de ler dois valores.
$ gcc -std=c23 -Wall -Wextra boletim.c -o boletim $ ./boletim === Boletim da Turma === Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Carla 9 Aluno 3 (nome e nota): Diego 5.5 Aluno 4 (nome e nota): Elisa 8 Aluno 5 (nome e nota): Gabriel 3.5 Bruno: 7.5 Carla: 9.0 Diego: 5.5 Elisa: 8.0 Gabriel: 3.5 Média da turma: 6.70 $ ./boletim === Boletim da Turma === Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Ana Paula 8 Entrada inválida.
O último teste mostra um limite do %s: ele lê uma palavra, até o primeiro espaço. Em Ana Paula 8, o nome ficou Ana, e o %lf tentou ler Paula como número e falhou. Para nomes com espaço, seria preciso ler a linha com fgets e separar nome e nota (é um dos desafios). Por enquanto, um nome por aluno.
%s sem o tamanho
Com scanf("%s", nome), alguém que digite um nome de 100 letras escreve 100 bytes num array de 30, e o programa estraga a memória ao lado (como no cenário da versão 1). É o famoso buffer overflow (veja Segurança). Sempre um a menos que o tamanho do array: char nome[30] → %29s.
Missão (versão 2): o programa lê os cinco alunos, mas só mostra a média. Antes dela, escreva um for que mostra cada aluno no formato Bruno: 7.5, usando turma[i].nome e turma[i].nota com %.1f.
#include <stdio.h>
#define QTD 5
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
printf("\n");
// mostre cada aluno no formato "Bruno: 7.5"
printf("Média da turma: %.2f\n", soma / QTD);
return 0;
}
#include <stdio.h>
#define QTD 5
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
printf("\n");
for (int i = 0; i < QTD; i++) {
printf("%s: %.1f\n", turma[i].nome, turma[i].nota);
}
printf("Média da turma: %.2f\n", soma / QTD);
return 0;
}
Versão 3 · Maior, menor e aprovados
Agora as estatísticas. Para achar a maior nota, o jeito clássico é: "a maior até agora é a do primeiro aluno; para cada um dos outros, se a nota dele for maior, ele passa a ser o maior". Guardamos a posição (não a nota), porque assim sabemos também o nome. A menor nota é igual, com <. E os aprovados são um contador que sobe quando a nota é pelo menos 6.
#include <stdio.h>
#define QTD 5
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0; // posição do aluno com a maior nota
int menor = 0; // posição do aluno com a menor nota
int aprovados = 0;
for (int i = 0; i < QTD; i++) {
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
printf("Maior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, QTD);
return 0;
}
- #define MEDIA_APROVACAO 6.0
- A nota mínima para passar, com nome. Ninguém precisa adivinhar o que é aquele
6.0no meio do código. - int maior = 0;
- A posição do aluno com a maior nota. Começa no primeiro (posição 0).
- if (turma[i].nota > turma[maior].nota) maior = i;
- Compara com a maior até agora; se ganhou, troca a posição.
- if (turma[i].nota >= MEDIA_APROVACAO) aprovados++;
- Conta quem passou.
>=: quem tirou exatamente 6 também passa. - turma[maior].nome
- Com a posição, pegamos qualquer dado da ficha.
$ gcc -std=c23 -Wall -Wextra boletim.c -o boletim $ ./boletim === Boletim da Turma === Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Carla 9 Aluno 3 (nome e nota): Diego 5.5 Aluno 4 (nome e nota): Elisa 8 Aluno 5 (nome e nota): Gabriel 3.5 Média da turma: 6.70 Maior nota: 9.0 (Carla) Menor nota: 3.5 (Gabriel) Aprovados: 3 de 5
Com >, o primeiro aluno com a maior nota fica com o título; com >=, ficaria o último. Nenhum dos dois está errado, mas é bom saber qual você escolheu.
Missão (versão 3): o programa já acha a maior nota, mas a menor sai sempre a do Bruno e os aprovados dão 0. No laço, atualize menor (como foi feito com maior, mas com <) e conte os aprovados (nota >= MEDIA_APROVACAO).
#include <stdio.h>
#define QTD 5
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0; // posição do aluno com a maior nota
int menor = 0; // posição do aluno com a menor nota
int aprovados = 0;
for (int i = 0; i < QTD; i++) {
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
// atualize "menor" e conte os aprovados (nota >= MEDIA_APROVACAO)
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
printf("Maior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, QTD);
return 0;
}
#include <stdio.h>
#define QTD 5
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
int main(void) {
struct Aluno turma[QTD];
double soma = 0.0;
printf("=== Boletim da Turma ===\n");
for (int i = 0; i < QTD; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0; // posição do aluno com a maior nota
int menor = 0; // posição do aluno com a menor nota
int aprovados = 0;
for (int i = 0; i < QTD; i++) {
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("\nMédia da turma: %.2f\n", soma / QTD);
printf("Maior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, QTD);
return 0;
}
Versão 4 · A tabela alinhada
A versão final tem três novidades:
- A quantidade de alunos é perguntada no começo (de 1 a 30). O array tem 30 posições, mas o programa só usa as
qtdprimeiras. - Uma função
situacaodevolve o texto"Aprovado","Recuperação"(de 4 a 5.9) ou"Reprovado". - Uma tabela, com as colunas alinhadas pela largura dos formatos do
printf.
#include <stdio.h>
#define MAX_ALUNOS 30
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
const char *situacao(double nota) {
if (nota >= MEDIA_APROVACAO) {
return "Aprovado";
} else if (nota >= 4.0) {
return "Recuperação";
}
return "Reprovado";
}
int main(void) {
struct Aluno turma[MAX_ALUNOS];
int qtd = 0;
printf("=== Boletim da Turma ===\n");
printf("Quantos alunos (1 a %d)? ", MAX_ALUNOS);
if (scanf("%d", &qtd) != 1 || qtd < 1 || qtd > MAX_ALUNOS) {
printf("\nQuantidade inválida.\n");
return 1;
}
double soma = 0.0;
for (int i = 0; i < qtd; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0;
int menor = 0;
int aprovados = 0;
printf("\n%-10s %5s %s\n", "Aluno", "Nota", "Situação");
printf("---------- ----- -----------\n");
for (int i = 0; i < qtd; i++) {
printf("%-10s %5.1f %s\n", turma[i].nome, turma[i].nota, situacao(turma[i].nota));
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("---------- ----- -----------\n");
printf("%-11s %5.1f\n", "Média", soma / qtd); // 11: o "é" ocupa 2 bytes
printf("\nMaior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, qtd);
return 0;
}
| Formato | O que faz | Exemplo |
|---|---|---|
%-10s | Texto numa coluna de 10 posições, encostado à esquerda (o -); o resto é preenchido com espaços. | [Bruno ] |
%10s | O mesmo, encostado à direita. | [ Bruno] |
%5.1f | Número com 1 casa decimal numa coluna de 5 posições, à direita: os pontos decimais ficam um embaixo do outro. | [ 7.5] |
%5s | O título Nota com a mesma largura dos números. | [ Nota] |
- const char *situacao(double nota)
- A função devolve um texto fixo: um ponteiro para o primeiro caractere de
"Aprovado", por exemplo (veja Ponteiros e Strings). Oconstavisa que ninguém deve mudar esse texto. - scanf("%d", &qtd) != 1 || qtd < 1 || qtd > MAX_ALUNOS
- Recusa a quantidade se não for número, ou se for menor que 1 ou maior que 30. Sem isso,
qtd = 50faria o laço escrever além do array. - "%-11s %5.1f\n", "Média"
- Por que 11 e não 10? Veja o cenário logo abaixo.
$ gcc -std=c23 -Wall -Wextra boletim.c -o boletim $ ./boletim === Boletim da Turma === Quantos alunos (1 a 30)? 5 Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Carla 9 Aluno 3 (nome e nota): Diego 5.5 Aluno 4 (nome e nota): Elisa 8 Aluno 5 (nome e nota): Gabriel 3.5 Aluno Nota Situação ---------- ----- ----------- Bruno 7.5 Aprovado Carla 9.0 Aprovado Diego 5.5 Recuperação Elisa 8.0 Aprovado Gabriel 3.5 Reprovado ---------- ----- ----------- Média 6.7 Maior nota: 9.0 (Carla) Menor nota: 3.5 (Gabriel) Aprovados: 3 de 5
Com %-10s na linha da média, a Ana viu isto:
Gabriel 3.5 Reprovado ---------- ----- ----------- Média 6.7
E com alunos de nome acentuado, mesmo com a correção da média:
$ ./boletim === Boletim da Turma === Quantos alunos (1 a 30)? 3 Aluno 1 (nome e nota): Bruno 7.5 Aluno 2 (nome e nota): Fábio 6 Aluno 3 (nome e nota): Lúcia 8 Aluno Nota Situação ---------- ----- ----------- Bruno 7.5 Aprovado Fábio 6.0 Aprovado Lúcia 8.0 Aprovado ---------- ----- ----------- Média 7.2 Maior nota: 8.0 (Lúcia) Menor nota: 6.0 (Fábio) Aprovados: 3 de 3
O que aconteceu: o printf conta bytes, não letras. No UTF-8, letras acentuadas como é, á e ú ocupam 2 bytes. "Fábio" tem 5 letras, mas 6 bytes; o %-10s acha que já usou 6 posições e completa com só 4 espaços. Na tela, a coluna fica um passo para a esquerda.
Como consertar: para um texto fixo, como "Média", aumente a largura no número de letras acentuadas: %-11s. Para nomes digitados, conte as letras de verdade e complete com espaços você mesmo (é o desafio 3). A aula Acentos e UTF-8 explica os bytes.
Missão (versão 4): a tabela sai toda torta: Bruno 7.5 Aprovado. Use larguras nos dois printf da tabela: "\n%-10s %5s %s\n" no título e "%-10s %5.1f %s\n" nas linhas dos alunos (repare nos dois espaços antes da situação). A entrada da missão é a turma da Ana.
#include <stdio.h>
#define MAX_ALUNOS 30
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
const char *situacao(double nota) {
if (nota >= MEDIA_APROVACAO) {
return "Aprovado";
} else if (nota >= 4.0) {
return "Recuperação";
}
return "Reprovado";
}
int main(void) {
struct Aluno turma[MAX_ALUNOS];
int qtd = 0;
printf("=== Boletim da Turma ===\n");
printf("Quantos alunos (1 a %d)? ", MAX_ALUNOS);
if (scanf("%d", &qtd) != 1 || qtd < 1 || qtd > MAX_ALUNOS) {
printf("\nQuantidade inválida.\n");
return 1;
}
double soma = 0.0;
for (int i = 0; i < qtd; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0;
int menor = 0;
int aprovados = 0;
printf("\n%s %s %s\n", "Aluno", "Nota", "Situação");
printf("---------- ----- -----------\n");
for (int i = 0; i < qtd; i++) {
printf("%s %.1f %s\n", turma[i].nome, turma[i].nota, situacao(turma[i].nota));
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("---------- ----- -----------\n");
printf("%-11s %5.1f\n", "Média", soma / qtd); // 11: o "é" ocupa 2 bytes
printf("\nMaior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, qtd);
return 0;
}
#include <stdio.h>
#define MAX_ALUNOS 30
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
const char *situacao(double nota) {
if (nota >= MEDIA_APROVACAO) {
return "Aprovado";
} else if (nota >= 4.0) {
return "Recuperação";
}
return "Reprovado";
}
int main(void) {
struct Aluno turma[MAX_ALUNOS];
int qtd = 0;
printf("=== Boletim da Turma ===\n");
printf("Quantos alunos (1 a %d)? ", MAX_ALUNOS);
if (scanf("%d", &qtd) != 1 || qtd < 1 || qtd > MAX_ALUNOS) {
printf("\nQuantidade inválida.\n");
return 1;
}
double soma = 0.0;
for (int i = 0; i < qtd; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0;
int menor = 0;
int aprovados = 0;
printf("\n%-10s %5s %s\n", "Aluno", "Nota", "Situação");
printf("---------- ----- -----------\n");
for (int i = 0; i < qtd; i++) {
printf("%-10s %5.1f %s\n", turma[i].nome, turma[i].nota, situacao(turma[i].nota));
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("---------- ----- -----------\n");
printf("%-11s %5.1f\n", "Média", soma / qtd); // 11: o "é" ocupa 2 bytes
printf("\nMaior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, qtd);
return 0;
}
O programa completo
A versão final, igual à versão 4. Compile com gcc -std=c23 -Wall -Wextra boletim.c -o boletim:
#include <stdio.h>
#define MAX_ALUNOS 30
#define MEDIA_APROVACAO 6.0
struct Aluno {
char nome[30];
double nota;
};
const char *situacao(double nota) {
if (nota >= MEDIA_APROVACAO) {
return "Aprovado";
} else if (nota >= 4.0) {
return "Recuperação";
}
return "Reprovado";
}
int main(void) {
struct Aluno turma[MAX_ALUNOS];
int qtd = 0;
printf("=== Boletim da Turma ===\n");
printf("Quantos alunos (1 a %d)? ", MAX_ALUNOS);
if (scanf("%d", &qtd) != 1 || qtd < 1 || qtd > MAX_ALUNOS) {
printf("\nQuantidade inválida.\n");
return 1;
}
double soma = 0.0;
for (int i = 0; i < qtd; i++) {
printf("Aluno %d (nome e nota): ", i + 1);
if (scanf("%29s %lf", turma[i].nome, &turma[i].nota) != 2) {
printf("\nEntrada inválida.\n");
return 1;
}
soma += turma[i].nota;
}
int maior = 0;
int menor = 0;
int aprovados = 0;
printf("\n%-10s %5s %s\n", "Aluno", "Nota", "Situação");
printf("---------- ----- -----------\n");
for (int i = 0; i < qtd; i++) {
printf("%-10s %5.1f %s\n", turma[i].nome, turma[i].nota, situacao(turma[i].nota));
if (turma[i].nota > turma[maior].nota) {
maior = i;
}
if (turma[i].nota < turma[menor].nota) {
menor = i;
}
if (turma[i].nota >= MEDIA_APROVACAO) {
aprovados++;
}
}
printf("---------- ----- -----------\n");
printf("%-11s %5.1f\n", "Média", soma / qtd); // 11: o "é" ocupa 2 bytes
printf("\nMaior nota: %.1f (%s)\n", turma[maior].nota, turma[maior].nome);
printf("Menor nota: %.1f (%s)\n", turma[menor].nota, turma[menor].nome);
printf("Aprovados: %d de %d\n", aprovados, qtd);
return 0;
}
O que o projeto juntou: arrays, for, structs, scanf com largura, #define, uma função que devolve texto e os formatos do printf.
Rodando no laboratório
Em vez de digitar os alunos toda vez, a Ana guardou a turma num arquivo de texto e mandou o arquivo para a entrada do programa com < (veja stdin, stdout e stderr). Como ninguém digitou nada, as respostas não aparecem depois das perguntas:
$ cat turma.txt 4 Bruno 7.5 Carla 9 Diego 5.5 Elisa 8 $ ./boletim < turma.txt === Boletim da Turma === Quantos alunos (1 a 30)? Aluno 1 (nome e nota): Aluno 2 (nome e nota): Aluno 3 (nome e nota): Aluno 4 (nome e nota): Aluno Nota Situação ---------- ----- ----------- Bruno 7.5 Aprovado Carla 9.0 Aprovado Diego 5.5 Recuperação Elisa 8.0 Aprovado ---------- ----- ----------- Média 7.5 Maior nota: 9.0 (Carla) Menor nota: 5.5 (Diego) Aprovados: 3 de 4
Desafios extras
- Duas notas por aluno. Troque
double notapordouble notas[2](prova e trabalho) e mostre a média de cada aluno numa coluna nova. - Nomes com espaço. Leia cada aluno com
fgets, ache o último espaço da linha comstrrchr, converta o que vem depois comstrtode copie o que vem antes como nome (veja Funções de String). - Alinhar com acentos. Escreva uma função que conta as letras de uma string UTF-8 (conte só os bytes em que
(c & 0xC0) != 0x80) e use-a para completar a coluna do nome com espaços. - Em ordem de nota. Mostre a tabela da maior para a menor nota com
qsort(veja Ordenação). - Turma de qualquer tamanho. Troque o array fixo de 30 por um array criado com
mallocdo tamanho exato (veja malloc e free).
Exercícios
1. Um array declarado como double notas[5] tem quais posições válidas?
2. Por que o scanf usa %29s para um char nome[30]?
3. O que printf("[%-6s]", "Ana") mostra?
4. Por que o programa guarda a posição da maior nota em vez da nota?
5. Complete o formato que mostra uma nota com uma casa decimal numa coluna de 5 posições:
6. Complete o acesso à nota do terceiro aluno do array turma:
Resumo
- Um array guarda muitos valores do mesmo tipo; as posições vão de
0atamanho - 1, e o laço éi < tamanho. - Passar do fim do array não dá erro de compilação: estraga a memória ao lado. O
-fsanitize=addressmostra a linha. - Uma struct junta os dados de uma coisa (nome e nota); um array de structs guarda a turma.
%29slê uma palavra sem passar de 29 letras;%spara no primeiro espaço.- Maior e menor: comece pelo primeiro e troque quando achar um melhor; guarde a posição.
%-10salinha texto à esquerda,%5.1falinha números; oprintfconta bytes, então acentos desalinham.
Próximo projeto: Agenda de Contatos.