Desafio C

Cópia de arquivo com verificação de erros e assert

Enunciado

Implemente um programa que copia o conteúdo de um arquivo de origem para um destino. O programa deve receber dois argumentos de linha de comando: origem e destino. Use assert para garantir que argc == 3. Abra a origem para leitura e o destino para escrita. Se qualquer abertura falhar, use perror e retorne EXIT_FAILURE. Copie os dados em blocos de 4096 bytes, verificando erros de leitura e escrita. Ao final, feche ambos os arquivos e retorne EXIT_SUCCESS.

Requisitos

  • Usar assert(argc == 3) para validar a quantidade de argumentos.
  • Abrir origem com fopen modo "rb" e destino com modo "wb".
  • Verificar erros de fread e fwrite com ferror.
  • Fechar os arquivos com fclose e tratar erros de fechamento.
  • Retornar EXIT_FAILURE em qualquer falha e EXIT_SUCCESS no sucesso.

Código inicial

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main(int argc, char *argv[]) {
    // seu código aqui
    return 0;
}

Saída esperada

Arquivo copiado com sucesso.
Ver dica

Use um buffer de 4096 bytes e um laço com fread. Verifique ferror após cada operação.

Mostrar solução
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define TAM_BUFFER 4096

int main(int argc, char *argv[]) {
    assert(argc == 3); // garante que temos origem e destino

    const char *origem = argv[1];
    const char *destino = argv[2];

    FILE *in = fopen(origem, "rb");
    if (in == NULL) {
        perror("Erro ao abrir origem");
        return EXIT_FAILURE;
    }

    FILE *out = fopen(destino, "wb");
    if (out == NULL) {
        perror("Erro ao abrir destino");
        fclose(in);
        return EXIT_FAILURE;
    }

    unsigned char buffer[TAM_BUFFER];
    size_t lidos;
    while ((lidos = fread(buffer, 1, TAM_BUFFER, in)) > 0) {
        if (fwrite(buffer, 1, lidos, out) != lidos) {
            perror("Erro ao escrever no destino");
            fclose(in);
            fclose(out);
            return EXIT_FAILURE;
        }
    }

    if (ferror(in)) {
        perror("Erro ao ler da origem");
        fclose(in);
        fclose(out);
        return EXIT_FAILURE;
    }

    if (fclose(in) != 0) {
        perror("Erro ao fechar origem");
        fclose(out);
        return EXIT_FAILURE;
    }
    if (fclose(out) != 0) {
        perror("Erro ao fechar destino");
        return EXIT_FAILURE;
    }

    printf("Arquivo copiado com sucesso.\n");
    return EXIT_SUCCESS;
}

Passo a passo

  1. assert(argc == 3) aborta o programa se o número de argumentos estiver errado. Isso é um bug de uso, não uma falha recuperável.
  2. Abrimos a origem em modo binário "rb". Se falhar, perror e EXIT_FAILURE.
  3. Abrimos o destino em modo binário "wb". Se falhar, fechamos a origem e retornamos erro.
  4. No laço, fread lê até 4096 bytes. Enquanto lidos > 0, escrevemos com fwrite.
  5. Se fwrite não escrever todos os bytes, tratamos como erro, fechamos ambos e retornamos falha.
  6. Após o laço, verificamos ferror(in) para detectar erro de leitura que não seja fim de arquivo.
  7. Fechamos ambos os arquivos verificando o retorno de fclose. Se algum falhar, retornamos erro.
  8. Se tudo ocorrer bem, imprimimos a mensagem de sucesso e retornamos EXIT_SUCCESS.

Por que funciona

O uso de assert para validar a linha de comando separa erros de programação (uso incorreto) de erros de execução (arquivo inexistente, disco cheio). As verificações de ferror capturam falhas de I/O que fread/fwrite podem não reportar diretamente. O fechamento verificado garante que os dados sejam realmente gravados no disco.

Erros comuns

  • Não verificar o retorno de fwrite: se o disco encher, a cópia fica incompleta silenciosamente.
  • Esquecer de fechar in quando out falha ao abrir: vazamento de descritor.
  • Usar assert para validar a existência do arquivo: assert não deve ser usado para condições externas, pois pode ser desabilitado com NDEBUG.

Outra forma de resolver

Usar goto cleanup para centralizar o fechamento dos arquivos, evitando repetição. É preferível quando há muitos recursos a liberar.

Saída esperada

Arquivo copiado com sucesso.