Pular para conteúdo

Semana 15 — Manipulação de arquivos (simples)

Nesta semana

Unidade 3 · Seg 16/11 · Qua 18/11 · Sex 20/11 · ⚠️ feriado: 20/11 (Consciência Negra)

Chegamos à última semana de conteúdo do semestre. Todo programa que escrevemos até agora tinha um "defeito" silencioso: quando ele terminava, tudo o que estava na memória desaparecia. Nesta semana resolvemos isso. Vamos aprender a gravar dados em arquivos — no disco, de forma permanente — e a lê-los de volta em uma execução futura. É a ideia de persistência, e ela transforma um programa que "esquece tudo" em um programa que guarda o trabalho do usuário.

O tema fecha com chave de ouro a jornada da Unidade 3: depois de entender endereços, ponteiros e memória, os arquivos são a ponte entre o que vive dentro do programa (RAM) e o que precisa sobreviver a ele (disco). Como há feriado na sexta (20/11), aproveite a segunda e a quarta com disciplina — e reserve um tempo próprio para o estudo dirigido.

🎯 Objetivos de aprendizagem

Ao final desta semana, você deverá ser capaz de:

  • explicar o que é persistência e por que dados na RAM são perdidos ao fim do programa, enquanto arquivos no armazenamento sobrevivem;
  • abrir um arquivo com fopen, escolhendo o modo correto ("r", "w", "a" e variantes), e fechar com fclose;
  • verificar sempre se a abertura deu certo, testando o retorno contra NULL;
  • escrever em arquivos-texto com fprintf e fputs;
  • ler de arquivos-texto com fscanf e fgets, detectando corretamente o fim do arquivo (EOF);
  • salvar e recarregar estruturas do curso (vetores e vetores de struct) em disco, integrando tudo o que você aprendeu.

📖 Segunda — Estudo do conteúdo

1. O problema: tudo o que a RAM guarda, ela esquece

Retome a Semana 01. Lá vimos que, quando um programa executa, ele é carregado para a memória RAM, e é ali que vivem todas as suas variáveis, vetores, structs e blocos alocados com malloc. A RAM é rápida — mas é volátil: ao encerrar o programa (ou desligar o computador), o conteúdo se perde.

Isso explica um incômodo que você talvez já tenha sentido: você roda um programa de cadastro, digita 50 alunos com todo o carinho, o programa imprime o relatório, retorna 0… e, na próxima execução, está tudo vazio de novo. Os dados nunca saíram da RAM.

Onde os dados vivem Velocidade Duração Exemplos
Memória RAM muito rápida volátil (some ao terminar) variáveis, vetores, malloc
Armazenamento (HD/SSD) mais lenta permanente (sobrevive) arquivos, fotos, documentos

A solução é gravar os dados importantes em um arquivo no armazenamento. Um arquivo é permanente: ele continua existindo depois que o programa termina, depois que você fecha o editor e mesmo depois de reiniciar a máquina. Essa capacidade de salvar dados que sobrevivem ao fim do programa é o que chamamos de persistência.

flowchart LR
    subgraph exec["Durante a execucao"]
    R["dados na RAM<br/>(volateis)"]
    end
    R -->|"escrita (fprintf)"| A["arquivo no disco<br/>(permanente)"]
    A -->|"leitura (fscanf)"| R2["dados de volta na RAM<br/>(proxima execucao)"]

Curiosidade 💡 — a bancada e a despensa

Na Semana 01 comparamos a RAM com a bancada onde o cozinheiro trabalha, e o armazenamento com a despensa/geladeira. A bancada é perto e prática, mas você não guarda comida ali de um dia para o outro — ela é esvaziada. O que você quer manter vai para a despensa. Gravar um arquivo é exatamente isso: tirar o dado da bancada volátil e guardá-lo na despensa permanente.

2. O tipo FILE * e a função fopen

Em C, um arquivo aberto é representado por um ponteiro para arquivo, do tipo FILE * (o tipo FILE está declarado em <stdio.h>). Você não precisa saber o que há dentro de um FILE — ele é uma "ficha de controle" que o sistema usa para saber qual arquivo é, onde você parou de ler/escrever, etc. Você só guarda o FILE * e o passa para as funções de arquivo.

Para abrir um arquivo, usamos fopen, que recebe o nome do arquivo e o modo de abertura, e devolve um FILE *:

FILE *f = fopen("dados.txt", "r");   // abre "dados.txt" para leitura

O modo (o segundo argumento, uma string) diz o que você pretende fazer com o arquivo. Os três modos essenciais:

Modo Nome O que faz Se o arquivo…
"r" read (leitura) abre para ler não existe → falha (NULL)
"w" write (escrita) abre para escrever existeAPAGA todo o conteúdo!; não existe → cria
"a" append (acréscimo) escreve no fim não existe → cria; existe → preserva e acrescenta

Preste muita atenção ao "w": ele cria o arquivo se não existir, mas se o arquivo já existe, ele zera o conteúdo antes de você escrever a primeira linha. Já o "a" (append) preserva o que estava lá e só acrescenta ao final — perfeito para um log ou um diário.

O modo \"w\" apaga o conteúdo existente

Abrir um arquivo em "w" descarta imediatamente tudo o que havia nele — mesmo que você nunca chegue a escrever nada. Se o objetivo é acrescentar sem perder o que já existe, use "a". Trocar um pelo outro é um erro que "come" dados silenciosamente.

Existem também as variantes com +, para leitura e escrita ao mesmo tempo:

  • "r+" — abre para ler e escrever (o arquivo precisa existir);
  • "w+" — cria/apaga e permite ler e escrever;
  • "a+" — abre para ler e acrescentar no fim.

Por ora, foque nos três básicos ("r", "w", "a"); as variantes ficam de conhecimento para quando você precisar.

Curiosidade 💡 — texto × binário (\"rb\", \"wb\")

Você verá modos com um b, como "rb" ou "wb". O b significa binário: os dados são gravados byte a byte, exatamente como estão na memória (usando fread/fwrite, que veremos de leve na seção 8). Sem o b, o arquivo é tratado como texto: legível em qualquer editor, com números e letras escritos como caracteres. Nesta semana trabalhamos com arquivos de texto, que são mais simples de entender e depurar (você abre no Bloco de Notas e lê).

3. SEMPRE verifique se abriu: o teste == NULL

fopen pode falhar. Motivos comuns: o arquivo não existe (no modo "r"), você não tem permissão para acessá-lo, o disco está cheio, o caminho está errado. Quando falha, fopen não trava o programa — ele apenas devolve NULL, aquele ponteiro "para lugar nenhum" que estudamos na Semana 10.

Por isso, todo fopen deve ser seguido de uma verificação:

FILE *f = fopen("dados.txt", "r");
if (f == NULL) {
    printf("Erro: nao foi possivel abrir o arquivo.\n");
    return 1;   // encerra sinalizando erro (valor != 0)
}
// ... aqui em diante, f e' seguro de usar ...

Se você não verificar e o arquivo não abriu, f valerá NULL, e usar fprintf(f, ...) ou fscanf(f, ...) sobre um ponteiro nulo provoca comportamento indefinido — normalmente um travamento (o famoso segmentation fault da Semana 10). Este teste é tão importante quanto o & no scanf: é um reflexo que você deve treinar.

Não confie que o arquivo abriu

Nunca escreva fscanf(f, ...) sem antes ter checado if (f == NULL). Um caminho errado, uma letra a mais no nome, um arquivo que não existe — tudo isso resulta em NULL, e ignorar isso é um dos bugs mais comuns (e mais frustrantes) com arquivos.

4. Ao terminar, fclose

Quando você não precisa mais do arquivo, feche-o com fclose:

fclose(f);

Fechar um arquivo faz duas coisas importantes:

  1. Libera recursos — o sistema operacional tem um limite de arquivos abertos ao mesmo tempo; deixar arquivos abertos "vaza" esse recurso.
  2. Garante a gravação — o que você "escreveu" pode ainda estar num buffer temporário (veja a curiosidade adiante). O fclose descarrega esse buffer para o disco. Esquecer o fclose pode fazer com que parte do que você escreveu nunca chegue ao arquivo.

A regra é simétrica e fácil de lembrar: para cada fopen que deu certo, um fclose.

Curiosidade 💡 — o buffer e o fflush

Escrever no disco a cada caractere seria lento. Por isso, a stdio acumula o que você escreve em uma área temporária na memória, o buffer, e só descarrega de fato no disco de tempos em tempos (quando o buffer enche, quando você chama fflush(f), ou quando faz fclose). Isso explica um fenômeno curioso: se o programa trava antes do fclose, as últimas linhas "escritas" podem sumir — elas estavam no buffer, não no disco. Fechar o arquivo (ou chamar fflush) resolve isso.

5. Escrita em arquivo-texto: fprintf e fputs

Escrever em arquivo é praticamente idêntico a imprimir na tela. A função principal é fprintf, que é igual ao printf — só que o primeiro argumento é o FILE * de destino:

fprintf(f, "Idade: %d, altura: %.2f\n", idade, altura);

Compare:

printf("Media: %.2f\n", media);       // escreve na TELA
fprintf(f, "Media: %.2f\n", media);   // escreve no ARQUIVO f

Mesmíssima string de formato, mesmos especificadores (%d, %f, %c, %s…), mesmas sequências de escape (\n, \t). A única diferença é para onde o texto vai.

Há também fputs, que escreve uma string já pronta (sem formatação):

fputs("Cabecalho do relatorio\n", f);

Um exemplo salvando vários registros de uma vez:

salva_notas.c
#include <stdio.h>

int main(void) {
    FILE *f = fopen("notas.txt", "w");   // CRIA (ou apaga) e abre para escrita
    if (f == NULL) {
        printf("Erro ao abrir o arquivo para escrita.\n");
        return 1;
    }

    // grava tres registros: nome e nota
    fprintf(f, "Ana %.1f\n", 9.5);
    fprintf(f, "Bruno %.1f\n", 7.0);
    fprintf(f, "Carla %.1f\n", 8.25);

    fclose(f);   // fecha: garante que tudo foi para o disco
    printf("Arquivo notas.txt gravado com sucesso.\n");
    return 0;
}

Depois de rodar, abra notas.txt em qualquer editor de texto: você verá, gravadas em disco, as três linhas. Elas continuarão lá quando o programa terminar — este é o momento em que a persistência acontece de verdade.

6. Leitura de arquivo-texto: fscanf, fgets e o fim do arquivo

Para ler, o espelho do scanf é o fscanf — mesma ideia, com o FILE * na frente:

fscanf(f, "%d", &n);         // le um inteiro DO arquivo f

Assim como o scanf na leitura de double usa %lf, o fscanf também:

double nota;
fscanf(f, "%lf", &nota);     // le um double: %lf (nao %f!)

O ponto novo — e essencial — é: como saber que o arquivo acabou? Você não sabe de antemão quantas linhas ele tem. A resposta é o valor EOF (End Of File, "fim do arquivo"), uma constante especial de <stdio.h>.

O fscanf devolve a quantidade de itens que conseguiu ler. Quando chega ao fim do arquivo (não há mais o que ler), ele devolve EOF. Esse é o jeito mais seguro de fazer um laço de leitura: ler no próprio teste do laço e parar quando o retorno for EOF.

le_notas.c
#include <stdio.h>

int main(void) {
    FILE *f = fopen("notas.txt", "r");   // abre para leitura
    if (f == NULL) {
        printf("Erro: arquivo notas.txt nao encontrado.\n");
        return 1;
    }

    char nome[50];
    double nota;
    // le um par (nome nota) por vez; para quando fscanf nao ler os 2 itens
    while (fscanf(f, "%s %lf", nome, &nota) == 2) {
        printf("%s tirou %.2f\n", nome, nota);
    }

    fclose(f);
    return 0;
}

Repare no teste == 2: pedimos ao fscanf para ler dois itens (um %s e um %lf). Enquanto ele conseguir ler os dois, o laço continua; quando o arquivo acaba (ou o formato não bate), ele devolve algo diferente de 2 (no fim do arquivo, EOF), e o laço para. Comparar o retorno com a quantidade esperada é uma técnica robusta.

Também é muito comum ler linha a linha com fgets, que lê uma linha inteira (incluindo espaços) para dentro de um vetor de char:

char linha[100];
while (fgets(linha, 100, f) != NULL) {   // le ate uma linha; NULL no fim
    printf("Linha lida: %s", linha);      // (a propria linha ja traz o \n)
}

O fgets devolve NULL quando não há mais nada para ler — daí o teste != NULL no laço. Ele é ótimo quando cada linha é um "texto livre" que você quer processar depois.

O cuidado clássico com feof

Existe a função feof(f), que devolve verdadeiro depois que uma leitura "bateu" no fim do arquivo. É tentador escrever while (!feof(f)) { fscanf(...); ... }, mas isso costuma processar o último registro DUAS vezes. O motivo: o feof só acusa fim após uma leitura falhar — então, no fim, o fscanf falha, mas o corpo do laço ainda roda uma vez com os valores antigos. A regra de ouro: teste o retorno da própria leitura (fscanf(...) == 2, fgets(...) != NULL), e não confie em feof como condição do laço.

Curiosidade 💡 — stdin, stdout e stderr já são FILE *

Você deve estar pensando: "como o fscanf/fprintf se parecem tanto com scanf/printf?". Não é coincidência. O teclado (stdin), a tela (stdout) e a saída de erros (stderr) são, na verdade, arquivos já abertos para você — três FILE * prontos, definidos em <stdio.h>. Ou seja: printf("oi\n") é essencialmente fprintf(stdout, "oi\n"), e scanf("%d", &x) é fscanf(stdin, "%d", &x). Os scanf/printf são apenas atalhos que já usam esses arquivos-padrão. Você conhece arquivos desde a Semana 01 sem saber!

7. Exemplo completo: salvar e recarregar um vetor de struct

Agora vamos juntar tudo com o que você viu na Semana 14: um vetor de struct. Vamos cadastrar alguns alunos na memória, gravá-los em um arquivo e, em seguida, ler de volta — provando que os dados sobreviveram.

alunos_arquivo.c
#include <stdio.h>

#define MAX 100

typedef struct {
    char nome[50];
    int matricula;
    double media;
} Aluno;

// grava um vetor de alunos em um arquivo-texto
void salvar(const char *caminho, Aluno v[], int n) {
    FILE *f = fopen(caminho, "w");        // "w": cria/apaga o arquivo
    if (f == NULL) {
        printf("Erro ao abrir %s para escrita.\n", caminho);
        return;
    }
    fprintf(f, "%d\n", n);                // 1a linha: quantos alunos
    for (int i = 0; i < n; i++) {
        // um aluno por linha: nome matricula media
        fprintf(f, "%s %d %.2f\n", v[i].nome, v[i].matricula, v[i].media);
    }
    fclose(f);
}

// le os alunos de volta; devolve quantos foram lidos
int carregar(const char *caminho, Aluno v[]) {
    FILE *f = fopen(caminho, "r");        // "r": leitura
    if (f == NULL) {
        printf("Erro ao abrir %s para leitura.\n", caminho);
        return 0;
    }
    int n = 0;
    fscanf(f, "%d", &n);                  // le a quantidade
    for (int i = 0; i < n; i++) {
        fscanf(f, "%s %d %lf", v[i].nome, &v[i].matricula, &v[i].media);
    }
    fclose(f);
    return n;
}

int main(void) {
    Aluno turma[MAX] = {
        {"Ana",   2026001, 9.5},
        {"Bruno", 2026002, 7.0},
        {"Carla", 2026003, 8.25}
    };
    int n = 3;

    salvar("turma.txt", turma, n);        // grava no disco
    printf("Turma gravada em turma.txt.\n\n");

    // simula uma "nova execucao": le tudo de volta para outro vetor
    Aluno recarregada[MAX];
    int m = carregar("turma.txt", recarregada);

    printf("Alunos recarregados do arquivo (%d):\n", m);
    for (int i = 0; i < m; i++) {
        printf("  %-8s mat %d  media %.2f\n",
               recarregada[i].nome,
               recarregada[i].matricula,
               recarregada[i].media);
    }
    return 0;
}

O padrão aqui é muito comum e vale memorizar: gravar primeiro a quantidade (n) e depois cada registro. Na leitura, lemos n e então repetimos a leitura n vezes. Assim o programa que lê sabe exatamente quantos registros esperar — não precisa nem detectar o EOF. (Alternativamente, você poderia ler em laço até fscanf devolver algo diferente de 3, como fizemos na seção 6.)

Note ainda que, ao ler os double, usamos %lf (&v[i].media), e que passamos v[i].nome sem & — porque um vetor de char (string) já é um endereço, como você viu ao estudar vetores.

8. Uma palavra sobre arquivos binários (fread/fwrite)

Tudo o que vimos usa texto: números e nomes gravados como caracteres legíveis. Existe outra família de funções — fwrite e fread — que grava e lê dados em binário, copiando os bytes da memória diretamente para o arquivo. Com elas, você poderia gravar o vetor de struct de uma vez, sem formatar campo a campo. É mais rápido e compacto, mas o arquivo deixa de ser legível em um editor de texto e fica dependente de detalhes da máquina. Fica como conhecimento de fronteira — no nosso curso, ficamos com o texto, que é mais simples de entender, depurar e conferir "a olho".

9. Boas práticas desta semana

  • Sempre verifique o retorno do fopen (if (f == NULL) ...) antes de usar o arquivo. Sem exceção.
  • Sempre fclose ao terminar — libera recursos e garante que o buffer foi para o disco.
  • Cuidado com "w": ele apaga o conteúdo existente. Para acrescentar sem perder, use "a".
  • Na leitura de double, use %lf no fscanf (como no scanf).
  • Detecte o fim do arquivo pelo retorno da leitura, não confiando só em feof.
  • Grave a quantidade de registros no início do arquivo quando isso simplificar a releitura.

10. Erros comuns desta semana (resumão)

Checklist de armadilhas

  • Usar o arquivo sem checar if (f == NULL) depois do fopen.
  • Abrir em "w" um arquivo que você queria preservar (apagou tudo!).
  • Esquecer o fclose (dados podem ficar presos no buffer).
  • Usar %f em vez de %lf ao ler double com fscanf.
  • Usar while (!feof(f)) e processar o último registro em dobro.
  • Esquecer o FILE * como primeiro argumento de fprintf/fscanf.
  • Colocar & antes de um vetor de char (nome) no fscanf — ele já é um endereço.
  • Caminho/nome de arquivo errado → fopen devolve NULL.

👥 Quarta — Encontro

Chegue ao encontro tendo estudado o material acima e tendo executado os exemplos (rode salva_notas.c e depois le_notas.c, e abra os arquivos gerados no seu editor para ver o resultado no disco). O encontro é para consolidar e integrar — é a última aula prática do semestre. Roteiro previsto:

  1. Revisão relâmpago: persistência, FILE *, fopen/fclose, os modos "r"/"w"/"a", e o teste == NULL.
  2. Escrita e leitura ao vivo: gravar dados e lê-los de volta, discutindo o laço de leitura correto (retorno de fscanf × feof).
  3. Integração com a Unidade 3: salvar e recarregar um vetor de struct, fechando o ciclo memória → disco → memória.
  4. Galeria de erros: provocamos de propósito um NULL (arquivo inexistente) e a perda de dados com "w", para você reconhecer os sintomas.

Problema-guia do encontro — agenda de contatos persistente: um programa que mantém uma agenda (nome + telefone) em um arquivo. Ao iniciar, ele carrega os contatos já salvos; permite adicionar um novo; e, ao sair, regrava tudo — de modo que, na próxima execução, os contatos continuem lá.

agenda.c
#include <stdio.h>

#define MAX 200

typedef struct {
    char nome[50];
    char telefone[20];
} Contato;

// carrega os contatos do arquivo; devolve quantos leu (0 se nao existir ainda)
int carregar(const char *caminho, Contato v[]) {
    FILE *f = fopen(caminho, "r");
    if (f == NULL) {
        return 0;               // arquivo ainda nao existe: agenda vazia
    }
    int n = 0;
    // le pares "nome telefone" ate o fim do arquivo
    while (n < MAX && fscanf(f, "%s %s", v[n].nome, v[n].telefone) == 2) {
        n++;
    }
    fclose(f);
    return n;
}

// regrava todos os contatos (modo "w": reescreve o arquivo inteiro)
void salvar(const char *caminho, Contato v[], int n) {
    FILE *f = fopen(caminho, "w");
    if (f == NULL) {
        printf("Erro ao salvar a agenda.\n");
        return;
    }
    for (int i = 0; i < n; i++) {
        fprintf(f, "%s %s\n", v[i].nome, v[i].telefone);
    }
    fclose(f);
}

int main(void) {
    Contato agenda[MAX];
    int n = carregar("agenda.txt", agenda);

    printf("Agenda carregada com %d contato(s).\n", n);
    for (int i = 0; i < n; i++) {
        printf("  %-10s %s\n", agenda[i].nome, agenda[i].telefone);
    }

    // adiciona um novo contato (nome sem espacos, por causa do %s)
    if (n < MAX) {
        printf("\nNovo contato (nome telefone): ");
        if (scanf("%s %s", agenda[n].nome, agenda[n].telefone) == 2) {
            n++;
        }
    }

    salvar("agenda.txt", agenda, n);   // persiste tudo de volta
    printf("Agenda salva com %d contato(s). Rode de novo para conferir!\n", n);
    return 0;
}

Perguntas para pensar antes do encontro: por que carregar devolve 0 (em vez de dar erro) quando o arquivo não existe? O que aconteceria se usássemos "a" em vez de "w" na função salvar? Como o while (... == 2) sabe que a agenda acabou?


✍️ Sexta — Estudo dirigido

Feriado em 20/11 — organize seu tempo

A sexta desta semana (20/11) é feriado (Dia da Consciência Negra). Não haverá encontro nem atividade síncrona. Reserve um horário próprio, ainda esta semana, para fazer o estudo dirigido abaixo — é o fechamento prático do curso.

Arquivos e o Beecrowd: por que quase não aparecem

Diferentemente das semanas anteriores, manipulação de arquivos raramente é cobrada no Beecrowd. O motivo é técnico: o juiz online alimenta seu programa pela entrada padrão (stdin) e confere a saída padrão (stdout) — ele não deixa você criar arquivos no disco dele. Por isso, aqui o foco não é a tabela de exercícios do juiz, e sim os desafios do zero abaixo, que você roda e confere na sua própria máquina, abrindo os arquivos gerados no editor de texto.

(Curiosidade útil: como scanf/printf são fscanf(stdin,...)/fprintf(stdout,...), tudo o que você aprendeu de E/S para o Beecrowd é a mesma máquina de arquivos — só que apontada para os arquivos-padrão.)

Desafios para escrever do zero

Todos abaixo criam e/ou leem arquivos no seu computador. Depois de rodar, abra o arquivo gerado no editor e confira o conteúdo com os próprios olhos.

  1. Soma de N números salvos: peça N números ao usuário e grave-os em numeros.txt (um por linha). Em seguida, abra o arquivo de novo para leitura, some todos e imprima o total. (Prove que a soma veio do arquivo, não da memória.)
  2. Agenda simples: grave vários pares nome telefone em contatos.txt (modo "w") e, num segundo momento, leia e liste todos. Adapte a partir do agenda.c do encontro.
  3. Contador de linhas e palavras: dado um arquivo-texto qualquer, leia-o e conte quantas linhas e quantas palavras ele tem. (Dicas: fgets para contar linhas; fscanf(f, "%s", palavra) == 1 num laço para contar palavras.)
  4. Vetor de struct em disco: crie um vetor de Aluno (nome, matrícula, média), salve em alunos.txt e depois recarregue para outro vetor, imprimindo os dados lidos. (Baseie-se em alunos_arquivo.c.)
  5. Diário incremental: um programa que, a cada execução, acrescenta (modo "a") uma linha em diario.txt (por exemplo, uma frase digitada pelo usuário). Rode várias vezes e observe o arquivo crescendo sem perder as linhas anteriores — o oposto do "w".

Para investigar (curiosidade prática)

  • O NULL na prática: tente abrir, em modo "r", um arquivo que não existe (invente um nome). Teste if (f == NULL) e imprima uma mensagem. Confirme que o programa não trava — ele apenas detecta a falha. Relacione com a Semana 10.
  • O "w" que apaga: crie um arquivo com algum conteúdo (pelo editor, ou com um programa). Depois, abra-o em "w" no seu programa, escreva uma única linha nova e feche. Abra o arquivo no editor: onde foi parar o conteúdo antigo? Agora repita o experimento trocando "w" por "a" e compare.

✅ Checklist de autoavaliação

Marque com sinceridade — se algum item não estiver ✅, volte à seção correspondente.

  • Explico o que é persistência e por que a RAM perde os dados ao fim do programa.
  • Sei declarar um FILE *, abrir com fopen e escolher o modo certo ("r", "w", "a").
  • Sempre verifico if (f == NULL) antes de usar o arquivo.
  • Fecho o arquivo com fclose e entendo por que isso importa (buffer/recursos).
  • Escrevo com fprintf/fputs e leio com fscanf/fgets.
  • Detecto o fim do arquivo pelo retorno da leitura (e sei por que while(!feof(f)) é arriscado).
  • Uso %lf ao ler double com fscanf.
  • Consigo salvar e recarregar um vetor de struct em disco.

Avaliação 3 (A3)

A Avaliação 3 cobre o conteúdo da Unidade 3, das semanas S10 a S15: ponteiros e memória, alocação dinâmica, listas, struct e, agora, manipulação de arquivos. A data e o formato estão no plano de curso — confira com antecedência e organize sua revisão. Refaça os exemplos e os desafios "do zero" que ficaram difíceis: é a melhor preparação possível.

Encerramento do semestre — parabéns pela jornada!

Chegamos ao fim do conteúdo de Introdução às Técnicas de Programação. Vale a pena olhar para trás e ver o quanto você caminhou. Começamos na Semana 01 escrevendo o primeiro printf, entendendo variáveis, tipos e o que a máquina faz "por baixo". Passamos por decisões, repetição e funções — a arte de dividir para conquistar. Fomos além dos tipos simples com vetores, matrizes, strings e estruturas de dados. E, na Unidade 3, encaramos o coração do C: endereços, ponteiros, alocação dinâmica de memória e o modelo real de como os dados vivem na RAM. Hoje, com arquivos, fechamos o ciclo: seus programas já não "esquecem tudo" ao terminar — eles guardam e recuperam o trabalho.

Fundamentos → funções → estruturas de dados → memória e ponteiros → arquivos: essa é uma base sólida que serve para qualquer linguagem que você aprender daqui em diante. Foi um semestre de esforço real, de erros de compilação, de segmentation fault teimosos e de aquela alegria quando o programa enfim roda. Parabéns por chegar até aqui. Agora é revisar com carinho para a A3 — e seguir programando. O melhor jeito de honrar tudo isso é: continue escrevendo código.

🔗 Referências e para se aprofundar

  • Material 2026.1 — Aula 14: Manipulação de arquivos.
  • BACKES, André. Linguagem C: completa e descomplicada (arquivos: fopen, fprintf/fscanf, fgets, modos de abertura).
  • KERNIGHAN, B.; RITCHIE, D. The C Programming Language (o clássico "K&R"), cap. 7 (entrada/saída, incluindo arquivos).
  • MIZRAHI, Victorine V. Treinamento em Linguagem C (arquivos).
  • Referência: cppreference — fopen / FILE (detalhes dos modos e do tipo FILE).
  • Prática: Beecrowd — lembre que arquivos quase não são cobrados; use-o para revisar o conteúdo geral do semestre.