📚 Capítulo do curso: 08 — Modularização, Funções, Escopo e Linha de Comando (argc/argv) da Especialização em Sistemas com C. Leia o capítulo antes de começar.

🧮 Miniprojeto C 08 — Utilitário CLI Modular com argc/argv

Uma pequena “biblioteca” de funções matemáticas, invocada por linha de comando com argc/argv.

🔒 Regra de Ouro: até o Capítulo 08, você ganha funções (com protótipo, antes de main), passagem de parâmetros por valor, escopo de variáveis, a palavra-chave static e os parâmetros argc/argv de main. Este é o primeiro miniprojeto do curso com mais de uma função além de main — até aqui, tudo era resolvido dentro de um único main.

⏱️ Tempo estimado 📦 Você vai entregar ✅ Pronto quando
50 min main.c ./app 10 20 gera os dois relatórios e ./app sozinho falha com código 1

Diagrama do 🧮 Miniprojeto C 08 — Utilitário CLI Modular com argc/argv

🎯 Objetivos

📋 O problema

Construa uma pequena biblioteca matemática (main.c) com três funções:

O main recebe dois números pela linha de comando (argc/argv), convertidos com atof. Se o programa for executado sem os dois argumentos necessários, ele deve imprimir uma mensagem de uso em stderr e retornar EXIT_FAILURE — sem tentar acessar argv além do que foi fornecido.

ID Requisito
RF01 O programa deve compilar sem nenhum aviso com gcc -Wall -Wextra -Wpedantic -std=c17.
RF02 Deve conter #include <stdio.h> e #include <stdlib.h>, usar int main(int argc, char *argv[]) e terminar com return EXIT_SUCCESS; ou return EXIT_FAILURE;.
RF03 Deve declarar pelo menos 3 funções com protótipo antes de main: media, potencia e relatorio.
RF04 Se argc for menor que 3 (nome do programa + 2 números), deve imprimir a forma de uso em stderr com fprintf e retornar EXIT_FAILURE imediatamente.
RF05 Deve converter argv[1] e argv[2] para double usando atof.
RF06 potencia deve calcular o resultado com um laço for (sem <math.h>).
RF07 relatorio deve usar static int chamadas para contar e exibir o número da chamada atual, demonstrando que o valor persiste entre invocações.
RF08 O programa deve funcionar corretamente ao ser executado como ./app 10 20.
graph LR
    A["main(argc, argv)"] --> B{"argc < 3?"}
    B -->|Sim| C["fprintf(stderr, uso) + EXIT_FAILURE"]
    B -->|Nao| D["atof(argv[1]), atof(argv[2])"]
    D --> E["media(a, b)"]
    D --> F["potencia(b, 2)"]
    E --> G["relatorio(...)  1a chamada"]
    F --> G
    G --> H["static int chamadas = 1"]
    G --> I["relatorio(...)  2a chamada"]
    I --> J["static int chamadas = 2"]

    style G fill:#0284c7,stroke:#38bdf8,color:#ffffff
    style I fill:#0d9488,stroke:#2dd4bf,color:#ffffff
    style C fill:#dc2626,stroke:#f87171,color:#ffffff

🧰 Ambiente

O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.


Passo 1 — Compilar o esqueleto

🎯 Objetivo: partir de um código que compila e roda, antes de escrever qualquer linha.

⌨️ Faça: crie a pasta c_mini_08, salve o código abaixo como main.c e rode:

gcc -Wall -Wextra -Wpedantic -std=c17 main.c -o app
./app          # Linux/macOS
.\app.exe      # Windows (PowerShell)

📄 src/esqueleto.c

/*
 * Miniprojeto 08: Biblioteca Modular de Matematica com argc/argv
 * Regra: funcoes com prototipo, argc/argv, static (Capitulo 08).
 */
#include <stdio.h>
#include <stdlib.h>

// TODO 1: declare os protótipos das tres funcoes:
// double media(double a, double b);
// double potencia(double base, int expoente);
// void relatorio(double a, double b, double valor_media, double valor_potencia);

int main(int argc, char *argv[]) {
    // Ponto de partida: mostra o que chegou pela linha de comando
    printf("Argumentos recebidos: %d\n", argc - 1);
    if (argc > 1) {
        printf("Primeiro argumento: %s\n", argv[1]);
    }

    // TODO 2: se argc < 3, imprima a forma de uso com fprintf(stderr, ...)
    // e retorne EXIT_FAILURE

    // TODO 3: converta argv[1] e argv[2] para double usando atof

    // TODO 4: chame media(...) e potencia(...) e guarde os resultados em variaveis

    // TODO 5: chame relatorio(...) pelo menos duas vezes com dados diferentes,
    // para observar o contador static incrementando entre as chamadas

    return EXIT_SUCCESS;
}

// TODO 6: implemente double media(double a, double b)
// (retorna (a + b) / 2.0)

// TODO 7: implemente double potencia(double base, int expoente)
// usando um "for" interno que multiplica um acumulador "resultado" por "base"
// "expoente" vezes, comecando de resultado = 1.0

// TODO 8: implemente void relatorio(double a, double b, double valor_media, double valor_potencia)
// com "static int chamadas = 0;" declarada dentro da funcao,
// incrementando a cada chamada e imprimindo o numero da chamada atual

✅ Checkpoint: zero avisos do gcc, e a saída é:

🖥️ Saída esperada:

Argumentos recebidos: 2
Primeiro argumento: 10

⚠️ A saída acima é a de ./app 10 20 (.\app.exe 10 20 no Windows). Os números depois do nome do programa chegam ao main em argv. Rode também ./app sozinho: deve aparecer Argumentos recebidos: 0.


Passo 2 — Protótipos e validação de argc (TODO 1 e 2)

🎯 Objetivo: declarar as funções antes de usá-las e nunca acessar um argv[i] que não existe.

⌨️ Faça:

  1. Logo depois dos #include, declare os 3 protótipos (media, potencia, relatorio).
  2. No começo do main, se argc < 3, imprima a forma de uso com fprintf(stderr, "Uso: %s <numero1> <numero2>\n", argv[0]); e return EXIT_FAILURE;.
  3. Apague as linhas Argumentos recebidos e Primeiro argumento do esqueleto.

✅ Checkpoint: ./app sozinho imprime o uso e echo $? (ou echo $LASTEXITCODE) mostra 1. ./app 10 20 não imprime nada ainda, e o código de retorno é 0.

🧯 Se der errado: implicit declaration of function 'media' → faltou o protótipo, ou ele está depois do main. Em C17 uma função precisa ser declarada antes do primeiro uso.


Passo 3 — media e potencia (TODO 3, 4, 6 e 7)

🎯 Objetivo: escrever funções puras que recebem cópias dos argumentos e devolvem um valor.

⌨️ Faça:

✅ Checkpoint: ./app 10 20 mostra 15.00 e 400.00.

🧯 Se der errado: atof devolve 0.0 quando o texto não é número. ./app dez 20 dá média 10.00 sem erro nenhum. Validar isso exige strtod, assunto do Desafio.


Passo 4 — relatorio com contador static (TODO 5 e 8)

🎯 Objetivo: ver uma variável local que não é recriada a cada chamada.

⌨️ Faça: implemente relatorio com static int chamadas = 0; no topo, incremente a cada chamada e imprima Relatorio #%d seguido dos 4 valores. No main, troque os printf do passo anterior por duas chamadas: uma com (numero1, numero2, ...) e outra com os números trocados, recalculando media e potencia para a ordem nova.

✅ Checkpoint: aparecem Relatorio #1 e Relatorio #2. No segundo, b = 10.00 e potencia(b, 2) = 100.00.

🧯 Se der errado: se os dois relatórios dizem #1, faltou o static: sem ele, chamadas volta a 0 em toda chamada.


Passo final — Conferir com o gabarito

🎯 Objetivo: validar a saída completa e o código de retorno.

Rode ./app 10 20 (.\app.exe 10 20 no Windows).

✅ Checkpoint: a saída é esta, e o código de retorno é 0 (echo $? no Linux/macOS ou echo $LASTEXITCODE no PowerShell):

🖥️ Saída esperada:

---------------------------------------------
Relatorio #1 (chamada persistente via static)
  a = 10.00 | b = 20.00
  media(a,b)      = 15.00
  potencia(b, 2)  = 400.00
---------------------------------------------
---------------------------------------------
Relatorio #2 (chamada persistente via static)
  a = 20.00 | b = 10.00
  media(a,b)      = 15.00
  potencia(b, 2)  = 100.00
---------------------------------------------

Só abra o gabarito depois de chegar até aqui:

🔑 Ver gabarito: src/main.c

📄 src/main.c

/*
 * Miniprojeto 08: Biblioteca Modular de Matematica com argc/argv
 * Especializacao em Engenharia de Sistemas em C
 * Regra: funcoes com prototipo, argc/argv, static (Capitulo 08).
 */
#include <stdio.h>
#include <stdlib.h>

double media(double a, double b);
double potencia(double base, int expoente);
void relatorio(double a, double b, double valor_media, double valor_potencia);

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Uso: %s <numero1> <numero2>\n", argv[0]);
        fprintf(stderr, "Exemplo: %s 10 20\n", argv[0]);
        return EXIT_FAILURE;
    }

    double numero1 = atof(argv[1]);
    double numero2 = atof(argv[2]);

    double valor_media = media(numero1, numero2);
    double valor_potencia = potencia(numero2, 2);

    relatorio(numero1, numero2, valor_media, valor_potencia);
    // Segunda chamada com os numeros trocados: o contador static passa a 2
    relatorio(numero2, numero1, media(numero2, numero1), potencia(numero1, 2));

    return EXIT_SUCCESS;
}

double media(double a, double b) {
    return (a + b) / 2.0;
}

double potencia(double base, int expoente) {
    double resultado = 1.0;
    for (int i = 0; i < expoente; i++) {
        resultado = resultado * base;
    }
    return resultado;
}

void relatorio(double a, double b, double valor_media, double valor_potencia) {
    static int chamadas = 0;
    chamadas = chamadas + 1;

    printf("---------------------------------------------\n");
    printf("Relatorio #%d (chamada persistente via static)\n", chamadas);
    printf("  a = %.2f | b = %.2f\n", a, b);
    printf("  media(a,b)      = %.2f\n", valor_media);
    printf("  potencia(b, 2)  = %.2f\n", valor_potencia);
    printf("---------------------------------------------\n");
}

Por que isso importa: a variável chamadas é declarada com static dentro de relatorio, não em main nem fora de qualquer função. Isso significa que ela é inicializada uma única vez (com 0, na primeira execução do programa) e preserva seu valor entre chamadas — algo que uma variável local comum (int chamadas = 0; sem static) jamais conseguiria, pois seria recriada do zero a cada chamada. Repare também que media, potencia e relatorio recebem cópias dos valores passados (passagem por valor): alterar a ou b dentro de qualquer uma dessas funções nunca afetaria numero1 ou numero2 em main.


🏆 Desafio

Adicione uma quarta função double soma_quadrados(double a, double b) que devolve a² + b² (reaproveitando potencia duas vezes) e imprima o resultado no relatório. Se quiser ir além: aceite um terceiro argumento opcional argv[3] como o expoente de potencia() em vez do valor fixo 2 — sempre validando argc >= 4 antes de acessar argv[3], para nunca ler memória fora dos limites do array de argumentos.

✅ Checkpoint: com ./app 3 4, a soma dos quadrados é 25.00. Com ./app 2 3 5, potencia(3, 5) dá 243.00.

Extra: troque atof por strtod(argv[1], &fim) e recuse com EXIT_FAILURE quando *fim != '\0', ou seja, quando sobrou texto que não é número.


🧭 Navegação

📋 Projetos do curso de C | 📚 Teoria do Capítulo 08 | 🗂️ Hub de projetos