Programação MEAer e LEE Bertinho Andrade da Costa 2010/2011 1º Semestre Instituto Superior Técnico Argumentos da linha de comando Funções recursivas Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 1
Sumário Argumentos da linha de comando Exemplos Recursividade de funções Exemplos Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 2
Argumentos da linha de comando Motivação: Nas aulas de laboratório foi utilizado o compilador de C, gcc, para efectuar a compilação e linkagem. A execução do programa gcc é realizada através da linha de comando, especificando o nome (gcc) e mais um conjunto de opções: gcc -g -Wall -pedantic -ansi factorial.c -o factorial Sendo o gcc um programa executável, cujo programa fonte está escrito na linguagem C, como é que é possível ter acesso às opções que vem a seguir ao nome do programa gcc? A resposta a esta pergunta passa pela utilização da lista de argumentos da função main. Em vez de void, é necessário especificar um conjunto de argumentos. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 3
Argumentos da linha de comando Utilização: Em C o acesso às opções da linha de comando é realizado através de dois argumentos, na lista de argumentos da função main: int main (int argc, char *argv[]) { /* */ return 0; argc - Indica o número de palavras que foram escritos na linha de comando incluindo o nome do programa executável. Cada palavra deve estar separada por um ou mais espaços em branco. argv[] - É um vector de apontadores para caracteres, tendo a dimensão de argc elementos. O primeiro apontador (argv[0]) permite aceder ao nome do programa executável, o apontador (argv[k]) permitem aceder à palavra k+1 da linha de comando. Nota: argc e argv, são variáveis locais da função main. O seu nome pode ser alterado mas não o seu tipo, por tradição utiliza-se argc-argument count, argv- argument value. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 4
Argumentos da linha de comando Exemplo: Programa que mostra as palavras da linha de comando. /* Linha de comando */ #include <stdio.h> int main( int argc, char *argv[] ) { int i; printf("echo da linha de comando \n"); for (i=0; i<argc; ++i) { printf("arg n.%d = %s\n", i, argv[i] ); printf("\nfim."); return 0; Resultado: lincom.exe Primeiro 2. Terceiro Echo da linha de comando Arg n.0 = lincom.exe Arg n.1 = Primeiro Arg n.2 = 2. Arg n.3 = Terceiro FIM. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 5
Argumentos da linha de comando Exemplo: Máquina de calcular elementar O programa deve ter o nome de cal, deve receber no máximo três argumentos que especificam os dados e a operação como se mostra: cal 3 + 5 #include <stdio.h> int main( int argc, char *argv[] ) { float operando1, operando2, resultado; if (argc!= 4) { printf("programa que simula uma calculadora elementar\n"); printf("numero de argumentos errado\n"); printf("exemplo: \n"); printf("cal 4 + 5 <Enter>"); return 0; if (sscanf( argv[1], "%f", &operando1)!= 1) { printf("erro de conversao no 1. argumento\n"); return 0; if (sscanf( argv[3], "%f", &operando2)!= 1) { printf("erro de conversao no 2. argumento\n"); return 0; Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 6
Argumentos da linha de comando Exemplo: Máquina de calcular elementar (cont.) switch( *(argv[2]) ) { case '+': resultado = operando1 + operando2; break; case '-': resultado = operando1 - operando2; break; case '*': resultado = operando1 * operando2; break; case '/': /* Cuidado com a divisao por zero */ resultado = operando1 / operando2; break; default: printf("operador deconhecido\n"); return 0; printf("%s %s %s = %f", argv[1], argv[2], argv[3], resultado); return 0; Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 7
Argumentos da linha de comando Exemplo: Máquina de calcular elementar. Resultados >cal Programa que simula uma calculadora elementar Exemplo: cal 4 + 5 <Enter> >cal 2 + 3 2 + 3 = 5.000000 >cal 2-3 2-3 = -1.000000 >cal 2 * 3 2 * 3 = 6.000000 >cal 2 / 3 2 / 3 = 0.666667 >cal (:-) + 3 Erro de conversao no 1. argumento >cal 2 % 3 Operador deconhecido >cal 2 + :-) Erro de conversao no 2. argumento Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 8
Argumentos da linha de comando Problema: Desenvolva um programa cuja acção seja equivalente ao comando cp do Linux. O programa deve: Apresentar uma ajuda no caso do número de argumentos na linha de comando seja inadequado. Deve verificar a existência dos ficheiros. Se existe o ficheiro fonte. Se não existe o ficheiro destino. Deve copiar o conteúdo do ficheiro fonte para o ficheiro destino. Exemplo: cp tfcadeira.c tfcbackup1.c Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 9
Recursividade A noção de funções recursivas têm a sua origem na Matemática. O exemplo mais conhecido consiste na definição da função factorial, (f(n) = n!). n* f ( n 1) f ( n) 1 se se n 1 n 0 De salientar que tem que existir: Uma condição inicial, valor de n Uma condição de finalização, quando n = 0, caso contrário não será possível calcular o valor da função. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 10
Recursividade No caso da função factorial, recebe um número inteiro e devolve um número inteiro. #include <stdio.h> int factorial(int n) { int valor; if (n > 0) valor = n * factorial(n-1); else valor = 1; return valor; int main( void ) { int x; printf("funcao factorial.\n\n x="); scanf("%d", &x); printf("factorial(%d) = %d", x, factorial(x)); Resultado: Funcao factorial. x=6 factorial(6) = 720 FIM. printf("\nfim."); return 0; Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 11
Recursividade Para compreender melhor o funcionamento das funções recursivas, são intoduzidas algumas alterações no código da função factorial de modo a mostrar no ecrã a evolução das chamadas da função. int factorial(int n) { int valor; if (n > 0) { printf( n = %d, Chamada a factorial(%d)\n", n, n-1); valor = n * factorial(n-1); else { printf("condicao de finalizacao, n = %d\n", n); valor = 1; printf(" Saida de factorial(%d) com valor = %d\n", n, valor); return valor; Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 12
Recursividade O resultado da execução do programa Funcao factorial. x=6 n = 6, Chamada a factorial(5) n = 5, Chamada a factorial(4) n = 4, Chamada a factorial(3) n = 3, Chamada a factorial(2) n = 2, Chamada a factorial(1) n = 1, Chamada a factorial(0) Condicao de finalizacao, n = 0 Saida de factorial(0) com valor = 1 Saida de factorial(1) com valor = 1 Saida de factorial(2) com valor = 2 Saida de factorial(3) com valor = 6 Saida de factorial(4) com valor = 24 Saida de factorial(5) com valor = 120 Saida de factorial(6) com valor = 720 factorial(6) = 720 FIM. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 13
Recursividade Interpretação dos resultados: Sempre que a função se chama a si própria para execução, há a criação de um novo conjunto de variáveis locais, distinto. As variáveis são criadas no stack e como consequência o stack vai crescendo. O processo de criação de variáveis locais termina quando se alcança a condição de finalização, (ou há falta de memória :-( ( ). Após a execução da condição de finalização é executado o return, correspondente à última invocação da função recursiva. Por cada return que é executado, as variáveis locais vão sendo libertadas e a ocupação do stack vai diminuindo. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 14
Recursividade Interpretação dos resultados: x=6 n=6 <- factorial (6) valor = 6 *? n=5 <- factorial (5) valor = 5 *? n=4 <- factorial (4) valor = 4 *? Stack aumenta Stack diminui n=3 <- factorial (3) valor = 3 *? n=2 <- factorial (2) valor = 2 *? n=1 <- factorial (1) valor = 1 *? n=0 <- factorial (0) valor = 1 n=1 <- factorial (1) valor = 1 * 1 n=2 <- factorial (2) valor = 2 * 1 n=3 <- factorial (3) valor = 3 * 2 n=4 <- factorial (4) valor = 4 * 6 n=5 <- factorial (5) valor = 5 * 24 n=6 <- factorial (6) valor = 6 * 120 factorial(6) = 720 Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 15
Recursividade Atenção: Por cada chamada da função factorial são criadas no stack duas novas variáveis (n e valor), para além do endereço de retorno da função. Atendendo ao facto que é possível calcular a função factorial de forma iterativa, utilizando a instrução for com duas variáveis, é fácil concluir a memória do computador não é utilizada da forma mais eficiente, assim deve-se ponderar com cuidado a utilização de funções recursivas. No entanto há problemas cujas soluções são descritas com funções recursivas sendo extremanente difícil transformar a solução recursiva numa solução iterativa. Nestes caso a solução consiste em utilizar funções recursivas. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 16
Recursividade Problema: A divisão inteira de um número x por um númro y por ser obtida por subtracções sucessivas. Considere o algoritmo seguinte, e suponha que y é diferente de zero: divisão( x, y) 1 divisão( x 0 y, y) se se x x y y Desenvolva um programa que permita obter o resultado da divisão inteira de dois números x, e y. Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 17
Programação 2010/2011 DEEC-IST Arg. da linha de comando; Funções recursivas 18