quarta-feira, 2 de abril de 2008

Programando em C para console - parte 1

(Depois de um fecundo primeiro de Abril... )

Cada vez mais eu me espanto com a quantidade de novatos que tentam fazer programas para o "modo console" que não fazem o menor sentido.

Programas com menuzinho (1 - saque , 2 - deposito, 3 - sair) ou do estilo "digite um numero (q para sair)" são totalmente contra-produtivos. O novato perde TEMPO fazendo um monte de código desnecessário quando esquece o que deveria fazer (o tal do algoritmo).

Veja um exemplo: um programa que receba uma quantidade variavel de números e que imprima a soma dos mesmos. IMHO a forma mais interessante para fazer isso é pegar os números como argumentos do programa principal. Vc cria um programa console absolutamente limpo e facil de scriptar, por exemplo.

Veja só:

#include <stdio.h>

int main(int argc, char *argv[]){
double soma = 0.0;

printf("A soma dos numeros eh %g\n",soma);

return 0;
}


Feito esse simples arquivo, vamos compilar
$ gcc -Wall soma.c
$ ./a.out 1 2 3 4 5
A soma dos numeros eh 0


Ai vc fala "po, ta errado!", mas claro, eu primeiro escrevi a base do meu programa. Perceba que eu compilei com a opção -Wall, que me informa todos os warnings que a compilação pode gerar (o que sempre é uma excelente pratica, eu procuro compilar sempre com 0 warnings).

Feito isso, vamos definir 2 coisas:
- O algoritmo da soma e
- Possiveis fluxos de excessão.

Vou partir do suposto que, se vc digitar alguma coisa que não seja um numero, eu vou ignorar e considerar como 0. Caso eu não informe nenhum numero ao programa, isso significa que estou faltando com alguma coisa, nesse caso vou informar uma mensagem elucidativa sobre o uso do programa.

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

int main(int argc, char *argv[]){
int i;
double soma = 0.0;

if(argc == 1){
fprintf(stderr,"Faltando Argumentos!\n");
fprintf(stderr,"Uso: %s x1 [ x2 .. xN ]\n",argv[0]);
return 1;
}

for(i=1;i<argc;i++)
soma += atof(argv[i]);

printf("A soma dos numeros eh %g\n",soma);

return 0;
}


Todos os argumentos que eu passo para o programa, incluindo o nome do mesmo, ficam no vetor argv, e o número de argumentos fica na variavel arcc. Dessa forma, o nome do programa é o argv[0] e, se eu não passar nenhum argumento adicional, argc será 1. A função atof converte a string para um número de ponto flutuante e retorna 0 se não for possivel converter.

Simples, não? Agora vamos executar:

$ ./a.out          
Faltando Argumentos!
Uso: ./a.out x1 [ x2 .. xN ]
$ echo $?
1
$ ./a.out 1 2 3 4 5
A soma dos numeros eh 15
$ echo $?
0


A mensagem de uso utiliza uma notação no manual de alguns programas do mundo unix: colocar os argumentos opcionais entre colchetes. Eu informo que preciso de pelo menos um argumento. Caso eu entre no fluxo de excessão, o meu main retorna um valor diferente de 0 para o sistema operacional que pode ser capturado pela variavel de ambiente $? (nesse caso estou em um linux utilizando o bash, em outros sistemas mais exotéricos como o Windows eu não imagino como vc poderia capturar essa informação, tampouco imagino se ela seria util).

Perceba que eu posso criar um script extremamente simples para utilizar esse programa, assim como testar o resultado é uma tarefa de um grep (ou diff). Eu poderia juntar um script de teste e um makefile nesse post mas estou esperando um exemplo um pouco mais interessante.

Eu criei um programa sucinto, facil de entender e muito util, sem ter que perguntar para o usuario nada. transformar esse programa num que calcula a média, por exemplo, é uma tarefa de colocar apenas uma linha (na verdade uma subtração e uma divisão) a mais. Espero que os programadores novatos se inspirem nessa forma de pensar e que utilizem os programas básicos do unix como o grep, cat, diff, cp, etc, para seus futuros trabalhos.

4 comentários:

Tiago Peczenyj disse...

Duas coisas que esqueci de comentar:

1) As mensagens de erro vão para a saida de erro (stderr)

2) A mensagem "A soma dos numeros eh " é totalmente desnecessária. Inclusive seria mais sucinto se a resposta fosse apenas o valor numérico.

fica para a proxima então: scripts de teste, makefile, getopt (posso querer utilizar uma opção -v [verbose]) e, se der tempo, localização!

Felipe disse...

Poucos sabem o que é um UNIX. Programam no DOS usando um while(1) para não fechar a tela do DOS, não sabem nem ir no cmd e digitar o nome do programa, eles clicam no executável.

O C é uma linaguagem que os programadores mais comuns não sabem/se interessam. Ler o TCPL sem entender o que é um wc, grep, sort... é IMHO impossível.

Aguardo os posts sobre getopt e GDB(ah sim GDB não está na lista de "promessas", mas vai ter, vai?)

Tiago Peczenyj disse...

Felipem, o GDB por si só precisaria de um post exclusivo. Tenho que pensar bem no exemplo para que seja util.

Estou pensando num algoritmo de ordenação com algum errinho, ou algum processo recursivo como calcular fibonacci, etc.

Felipe disse...

Aguardando....

OFF: As mentiras de ontem forma muito engraçadas, a do Java mesmo :-)