18 - Funções e Procedimentos em C Programação Modular Unesp Campus de Guaratinguetá Curso de Programação Computadores Prof. Aníbal Tavares Profa. Cassilda Ribeiro Ministrado por: Prof. André Amarante
18.3 Funções e Procedimentos: Protótipos tipos Foi visto que na linguagem C a declaração da função deve ser colocada antes do ponto onde ela é chamada. Por esta razão, até agora, sempre escrevemos as funções antes da função principal main (). Para grandes projetos isto não é prático e algumas vezes é inviável determinar qual função deve preceder as demais. Este problema pode ser contornado com a utilização de protótipos tipos. O protótipo tipo de uma função consiste na repetição da linha de sua definição (cabeçalho) seguida do caractere (;). O protótipo tipo da função deve então ser colocado antes desta ser chamada. Vejamos o exemplo 18.12 a seguir: 13/10/2013 Funções e Procedimentos 2
18.3 Funções e Procedimentos: Protótipos tipos //programa que lê um número e imprime seu fatorial #include <stdio.h> #include <stdlib.h> //Função que calcula e imprimi o fatorial void fat (int n) { int i; int f = 1; for (i = 1; i <= n; i++) f *= i; printf("fatorial = %d\n", f); /* Função principal */ main () { int n; scanf("%d", &n); fat(n); system("pause"); Exemplo 18.12 A função está colocada antes do local onde ela é chamada 13/10/2013 Funções e Procedimentos 3
18.3 Funções e Procedimentos: Protótipos tipos A utilização de protótipo tipo permite que a função seja colocada em qualquer parte do programa, inclusive depois da função main. Os protótipos servem para indicar ao compilador quais são os parâmetros de entrada e saída da função sem ser necessário detalhar sua implementação. Reescrevendo o exemplo anterior utilizando protótipo tipo: 13/10/2013 Funções e Procedimentos 4
//programa que lê um numero e imprime seu fatorial #include <stdio.h> #include <stdlib.h> void fat (int n); /* Função principal */ main () { int n; scanf("%d", &n); fat(n); system("pause"); //Função que calcula e imprimi o fatorial void fat (int n) { int i; int f = 1; for (i = 1; i <= n; i++) f *= i; printf("fatorial = %d\n", f); 18.3 Funções e Procedimentos: Protótipos tipos Protótipo da Função fat Chamada da Função fat A função está colocada depois do local onde ela é chamada, porque está sendo usado protótipo 13/10/2013 Funções e Procedimentos 5
18.3 Funções e Procedimentos: Protótipos tipos Exemplo 18.13: Função que imprime um vetor Cabeçalho e corpo da função Chamada da função dentro da função main. #include <stdio.h> #include <stdlib.h> // Função que imprime um vetor. void imprimev(int v[], int n) { int i; for(i=0; i < n; i++) printf(" [%2d] ",v[i]); printf("\n"); // Função principal chama imprimev. main() { int v[10] = {0; // Imprime elementos do vetor. imprimev(v,10); 13/10/2013 Funções e Procedimentos 6
18.3 Funções e Procedimentos: Protótipos tipos Refazendo o exemplo 18.13 com Protótipo Protótipo da função Chamada da função Declaração e corpo da função #include <stdio.h> #include <stdlib.h> void imprimev(int v[], int n); // Função principal chama imprimev. main() { int v[10] = {0; // Imprime elementos do vetor. imprimev(v,10); // Função que imprime um vetor. void imprimev(int v[], int n) { int i; for(i=0; i < n; i++) printf(" [%2d] ",v[i]); printf("\n"); 13/10/2013 Funções e Procedimentos 7
18.3 Funções e Procedimentos: Protótipos tipos Exemplo 18.14 Ping-Pong #include <stdio.h> #include <stdlib.h> #include <time.h> void ping(int a, int b, int val1, int cont); void pong(int a, int b, int val2, int cont); // Função ping. void ping(int a, int b, int val1, int cont) { int val2 = (rand()%(b-a+1))+a; printf( Ping: %d \n,val2); if (val2 > val1) cont++; if (cont!= 4) pong(a,b,val2,cont); else printf( Ping ganhou: %d! \n,cont); // Função pong. void pong(int a, int b, int val2, int cont) { int val1 = (rand()%(b-a+1))+a; printf( Pong: %d \n,val1); if (val1 > val2) cont--; if (cont!= 0) ping(a,b,val1,cont); else printf( Pong ganhou:%d! \n,cont); // Função Principal. main() { srand(time(0)); ping(0,4,2,2); 13/10/2013 Funções e Procedimentos 8
18.3 Funções e Procedimentos: Protótipos tipos Observe que no exemplo anterior o programa não teria como ser compilado sem o uso de protótipos, pois a função ping pode chamar pong e pong pode chamar ping. ping pong Ou seja, em qualquer ordem que sejam colocadas as funções ping e pong, sempre uma delas estará indefinida em relação a outra. O uso de protótipos não é uma opção e sim uma necessidade neste caso. 13/10/2013 Funções e Procedimentos 9
18.4 Funções e Procedimentos: Recursão Exemplo 18.15 Pilha de Execução #include <stdio.h> #include <stdlib.h> #include <math.h> int f1(int x) { return (x/3); int f2(int x) { return ((cos(x)*cos(x))/(1-sin(x))); O programa ao lado equivale a resolver a expressão: y=(cos(x/3)*cos(x/3))/(1-sin(x/3)) f1 f1 f1 main() {int a, b; a = f1(2); b = f2(a); printf( b = f2(f1(2)) = %d \n, b); 13/10/2013 Funções e Procedimentos 10 f2 b
18.4 Funções e Procedimentos: Recursão xemplo 18.15 Pilha de Execução #include <stdio.h> #include <stdlib.h> #include <math.h> int f1(int x) { return (x/3); A forma como isto é feito é melhor explicado com o conceito de pilha de execução: Início a=f1(2) int f2(int x) { return ((cos(x)*cos(x))/(1-sin(x))); main() main() main() {int a, b; a = f1(2); b = f2(a); printf( b = f2(f1(2)) = %d \n, b); printf(b) main() main() Fim b=f2(a) main() 13/10/2013 Funções e Procedimentos 11
18.4 Funções e Procedimentos: Recursão Definição função recursiva: Exemplos funções recursivas: Uma função é denominada recursiva se ela chama a si mesma. Toda função recursiva é estabelecida a partir de uma relação de recorrência que é calculada usando: (i) O cálculo do n-ésimo termo de uma função a partir de termos menores que n. (ii) Um caso base ao qual é associado um valor. Fatorial Fat(n) = n*fat(n-1) Fat(0) = 1 Fibonacci F(n) = F(n-1)+F(n-2) F(0) = F(1) = 1 13/10/2013 Funções e Procedimentos 12
18.4 Funções e Procedimentos: Recursão Exemplo 18.16 Fatorial recursiva #include <stdio.h> #include <stdlib.h> int fat(int n) {int resp; if (n == 0) return 1; else resp = n*fat(n-1); return resp; // Função Principal. main() { int n; printf( Entre com n >= 0: ); scanf( %d,&n); printf( Fatorial(n) = %d \n,fat(n)); Caso base Fat(n) = 0 Fat(n) = n*fat(n-1) Chamada recursiva 13/10/2013 Funções e Procedimentos 13
18.4 Funções e Procedimentos: Recursão Fatorial: n = 3 int fat(int n = 3) {int resp; if (n == 0) return 1; else resp = 3*fat(2); return resp; int fat(int n=2) {int resp; if (n == 0) return 1; else resp = 2*fat(1); return resp; Obtido o valor do caso base, a expressão de cada chamada pode ser resolvida do caso base até o caso n. A recursão cria chamadas a uma mesma função até que o caso base seja obtido. int fat(int n=1) {int resp; if (n == 0) return 1; else resp = 1*fat(0); return resp; int fat(int n=0) {int resp; if (n == 0) return 1; else resp = n*fat(n-1); return resp; 13/10/2013 Funções e Procedimentos 14
18.4 Funções e Procedimentos: Recursão Exemplo 18.16 Fatorial Recursiva Usando pilha de execução: #include <stdio.h> #include <stdlib.h> int fat(int n) {int resp; if (n == 0) return 1; else resp = n*fat(n-1); return resp; // Função Principal. main() { int n; printf( Entre com n >= 0: ); scanf( %d,&n); printf( Fatorial(n) = %d \n,fat(n)); 0! = 1 1! = 1*0! 2! = 2*1! 3! = 3*2! fat(0)! = 1 fat(1) = 1*fat(0) fat(2) = 2*fat(1) fat(3) = 3*fat(2) 13/10/2013 Funções e Procedimentos 15
18.4 Funções e Procedimentos: Recursão Exemplo 18.17 Fatorial com Laço #include <stdio.h> #include <stdlib.h> int fat(int n) { int resp = n; if (n == 0) resp = 1; else for (i=n-1; i >= 1; i--) resp = resp*i; return resp; // Função Principal. main() { int n; printf( Entre com n >= 0: ); scanf( %d,&n); printf( Fatorial(n) = %d \n,fat(n)); Caso base Fat(n) = 0 Fat(n) = n*fat(n-1) Chamada recursiva 13/10/2013 Funções e Procedimentos 16
18.4 Funções e Procedimentos: Recursão Exemplo 18.17 Fatorial com Laço Observação importante: #include <stdio.h> #include <stdlib.h> int fat(int n) { int i, resp = n; if (n == 0) resp = 1; else for (i=n-1; i >= 1; i--) resp = resp*i; return resp; // Função Principal. main() { int n; printf( Entre com n >= 0: ); scanf( %d,&n); printf( Fatorial(n) = %d \n,fat(n)); Para toda função recursiva existe uma função equivalente que utiliza laço e fornece a mesma solução. Porém, a função com recursão sempre fornece um código mais simples e nem sempre é trivial encontrar o laço que equivale a recursão (vide Exemplo 18.18: Fibonacci e Exemplo 18.19: O problema da Torre de Hanói). 13/10/2013 Funções e Procedimentos 17
18.4 Funções e Procedimentos: Recursão Exemplo 18.18 Fibonacci #include <stdio.h> #include <stdlib.h> int Fib(int n) {int resp; if (n == 0 n == 1) resp = 1; else resp = Fib(n-1)+Fib(n-2); return resp; main() { int n; printf( Entre com n >= 0: ); scanf( %d,&n); printf( Fibonacci(n) = %d \n,fib(n)); system( pause ); Casos base F(1) = F(0) = 0 F(n) = F(n-1)+F(n-2) Chamada recursiva 13/10/2013 Funções e Procedimentos 18
18.4 Funções e Procedimentos: Recursão Uma outra forma de representar as sucessivas chamadas a uma função recursiva é utilizar uma árvore de recursão: F(3) F(3) = F(2)+F(1) Nível 3 F(2) F(2) = F(1)+F(0) Nível 2 F(1) F(1) = 1 F(1) F(1) = 1 Nível 1 F(0) F(0) = 1 Nível 0 13/10/2013 Funções e Procedimentos 19
18.4 Funções e Procedimentos: Recursão O problema da Torre de Hanói consiste em transportar todos os discos do pino 1 para o pino 3 e mover um disco por vez e sem colocar um disco maior sobre um menor. 1 2 3 1 2 3 A resolução deste problema utilizando recursão é: (i) Mover n-1 discos de 1 para 2. (ii) Mover o n-ésimo disco de 1 para 3. (iii) Mover n-1 discos de 2 para 3. 13/10/2013 Funções e Procedimentos 20
18.4 Funções e Procedimentos: Recursão 1 2 3 A resolução deste problema utilizando recursão é: (i) Mover n-1 discos de 1 para 2. (ii) Mover o n-ésimo disco de 1 para 3. (iii) Mover n-1 discos de 2 para 3. 13/10/2013 Funções e Procedimentos 21
18.4 Funções e Procedimentos: Recursão 1 2 3 A resolução deste problema utilizando recursão é: (i) Mover n-1 discos de 1 para 2. (ii) Mover o n-ésimo disco de 1 para 3. (iii) Mover n-1 discos de 2 para 3. 13/10/2013 Funções e Procedimentos 22
18.4 Funções e Procedimentos: Recursão 1 2 3 A resolução deste problema utilizando recursão é: (i) Mover n-1 discos de 1 para 2. (ii) Mover o n-ésimo disco de 1 para 3. (iii) Mover n-1 discos de 2 para 3. 13/10/2013 Funções e Procedimentos 23
18.4 Funções e Procedimentos: Recursão 1 2 3 A resolução deste problema utilizando recursão é: (i) Mover n-1 discos de 1 para 2. (ii) Mover o n-ésimo disco de 1 para 3. (iii) Mover n-1 discos de 2 para 3. 13/10/2013 Funções e Procedimentos 24
18.4 Funções e Procedimentos: Recursão Exemplo 18.19 Torre de Hanói #include <stdio.h> #include <stdlib.h> int mover(int n, char a, char b, char c) { if (n > 0) { mover(n-1,a,c,b); printf( %c -> %c \n,a,b); mover(n-1,c,b,a); // Função Principal. main() { int n; printf( Entre com n: ); scanf( %d,&n); printf( Solucao Hanoi %d discos\n,n); mover(n, A, C, B ); 13/10/2013 Funções e Procedimentos 25
18.4 Funções e Procedimentos: Recursão Para n = 3, temos: (i) Mover(2,A,B,C) (ii) Mover(1,A,C,B) (iii) Mover(2,B,C,A) (i) Mover(2,A,B,C) (i.1) Mover(1,A,C,B): A->C (i.2) A->B (i.3) Mover(1,C,B,A): C->B A B C A B C A B C A B C 13/10/2013 Funções e Procedimentos 26
18.4 Funções e Procedimentos: Recursão Para n = 3, temos: (i) Mover(2,A,B,C) (ii) Mover(1,A,C,B) (iii) Mover(2,B,C,A) (ii) Mover(1,A,C,B): A->C A B C A B C 13/10/2013 Funções e Procedimentos 27
18.4 Funções e Procedimentos: Recursão Para n = 3, temos: (i) Mover(2,A,B,C) (ii) Mover(1,A,C,B) (iii) Mover(2,B,C,A) (iii) Mover(2,A,B,C) (iii.1) Mover(1,B,A,C): B->A (iii.2) B->C (iii.3) Mover(1,A,C,B): A->C A B C A B C A B C A B C 13/10/2013 Funções e Procedimentos 28
18.4 Funções e Procedimentos: Recursão Nível 3 Nível 2 Nível 1 Mover(3, A, C, B ) Mover(2, A, B, C ) Mover(1, A, C, B ) A -> C A -> B Mover(1, C, B, A ) C -> B A -> C Mover(2, B, C, A ) Mover(1, B, A, C ) B -> A B -> C Mover(1, A, C, B ) A -> C 13/10/2013 Funções e Procedimentos 29
18.4 Aplicações Problema 1: Construir um programa que simule um jogo da velha. O programa deve cumprir os seguintes requisitos: (i) Permitir movimentos alternados de dois jogadores A e B. (ii) Identificar se um movimento pode ser realizado ou não. (iii) Identificar o término de um jogo, indicando as 3 possíveis situações: (1) A ganhou, (2) B ganhou, (3) Empate. (iv) Construir um tabuleiro que permita a representação das jogadas tal como dado abaixo. Jogada de A Jogada de B x... o... 13/10/2013 Funções e Procedimentos 30. Espaço livre
18. Funções O Problema do Caminho Mínimo Os nós do grafo, apresentados abaixo, representam cidades, e os arcos, a presença de uma estrada ligando duas cidades. Os números ao lado dos arcos representam a distância medida em quilômetros. 180 2 4 100 101 200 1 40 6 45 15 120 3 90 5 13/10/2013 Funções e Procedimentos 31
18. Funções O Problema do Caminho Mínimo Pode-se representar este grafo através de uma variável composta bidimensional D, na qual a existência de conexão entre duas cidades i e j é indicada pelo elemento D[ i, j ] diferente de zero. Desta forma tem-se: 1 2 3 4 5 6 1 0 100 15 0 0 0 2 100 0 40 180 200 0 3 15 40 0 45 90 0 4 0 180 45 0 0 101 5 0 200 90 0 0 120 6 0 0 0 101 120 0 Matriz D de distância entre as cidades 13/10/2013 Funções e Procedimentos 32
18. Funções O Problema do Caminho Mínimo O problema consiste, então em achar o caminho mais curto entre duas cidades quaisquer. Este problema foi resolvido por Dijkstra (1971) e tem uma série de aplicações na resolução de problemas de otimização. Para resolvê-lo; atribui-se rótulos as cidades, contendo as seguintes informações: 1. Distância acumulada da origem até a cidade C, DA[C]; 2. Um apontador indicando qual a cidade antecessora de C, PAI[C]; 3. Um indicador do estado de C, ESTADO[ C ], que pode ser expandida, não-expandida, não-rotulada. 13/10/2013 Funções e Procedimentos 33
18. Funções O Problema do Caminho Mínimo Partindo da origem, determinam-se as cidades adjacentes (aquelas que têm um caminho em comum) a ela. Estas cidades são rotuladas da seguinte forma: DA[ cidade adjacente ] DA[ origem ] + D[ origem,cidade adjacente ] PAI[ cidade adjacente ] origem ESTADO[ cidade adjacente ] Não expandida A cidade geradora da expansão, no caso a origem, tem seu estado alterado: ESTADO[ origem ] expandida Escolhe-se a seguir, a cidade não expandida cuja distância acumulada seja menor. Repete-se o processo anterior, determinando-se e rotulando-se as cidades adjacentes a escolhida. 13/10/2013 Funções e Procedimentos 34
18. Funções O Problema do Caminho Mínimo Na hipótese de que uma cidade adjacente já esteja rotulada não expandida, um teste se faz necessário com vistas a comparação da distância acumulada dos dois caminhos, prevalecendo aquele de menor distância acumulada. Garante-se que as cidades rotuladas expandidas já foram alcançadas pelo caminho mais curto da origem até ela. Este procedimento é repetido até que o destino seja rotulado ou até que não existirem cidades rotuladas não expandidas. 13/10/2013 Funções e Procedimentos 35
18. Funções O Problema do Caminho Mínimo #include <stdlib.h> #include <stdio.h> main() { //Programa principal int PAI [100], C, ORIGEM, DESTINO, i, j,n,k; float D[100][100], DA[100], MIN; int ESTADO[100]; // Leitura da matriz de distância printf("digite o numero de cidades \n"); scanf("%d",&n); for (i=1;i<=n;i++) { for (j=1;j<=n;j++) { printf("digite a distancia entre a cidade %d e %d ",i,j); scanf("%f",&d[i][j]); //impressao matriz de distancia printf("a matriz de distancia lida eh \n"); for (i=1;i<=n;i++) { for (j=1;j<=n;j++) printf("%4.f", D[i][j]); printf("\n"); printf("\n"); // Inicialização de ESTADO for (i=0; i<=n;i=i+1) ESTADO[i]=0;// 0=não rotulada, //1=não expandida, 2= expandida. // Ler nó) origem e nó destino printf("digite o no. da cidade de origem \n"); scanf("%d",&origem); printf("digite o no. da cidade de destino \n"); scanf("%d",&destino); 13/10/2013 Funções e Procedimentos 36
18. Funções O Problema do Caminho Mínimo //Rotular origem C = ORIGEM; PAI[C] = 0; DA[C] = 0.0; k=1; while (( C!= DESTINO) && (C!= 0)) { //rotular cidades adjacentes for (i=1;i <= N;i++ ) { if((estado[i]== 1) && (D[C][i]!= 0) ) { if (DA[i] > DA[C] + D[C][i]) { PAI[i] = C; DA[i] = DA[C] + D[C][i]; else if(( D[C][i]!= 0 ) && (ESTADO[i] == 0)) { ESTADO[i] = 1; //nao expandido PAI[i] = C; DA[i] = DA[C] + D[C][i]; ESTADO [C] = 2; //Expandido //Escolha de um novo C MIN = 100000.0; for(j=1;j<= N;j++) {//inicio if(estado[j]== 1)// não expandido {/ if(da[j] < MIN) { //então MIN=DA[j]; C=j; //fim então //fim for j if (MIN == 100000.0) C = 0; //fim while rotular cidades adjacentes 13/10/2013 Funções e Procedimentos 37
18. Funções O Problema do Caminho Mínimo if(c == 0) { printf(" Nao existe Caminho unindo"); printf(" as cidades %d e %d\n", ORIGEM, DESTINO); else { printf("\n Caminho mais curto ente %d e %d eh:\n",origem, DESTINO); while(c!= ORIGEM) { printf("%d ",C); C = PAI[C]; printf("%d ",ORIGEM); printf("\n A distancia eh %.2f:\n",DA[DESTINO]); system("pause"); 13/10/2013 Funções e Procedimentos 38
18. Funções O Problema do Caminho Mínimo 13/10/2013 Funções e Procedimentos 39
Unesp-Campus de Guaratinguetá 18 - Funções e Procedimentos FIM Aula 18 Referências dos slides Curso de Programação de Computadores Prof. Aníbal Tavares Profa. Cassilda Ribeiro Prof. Décio Mourão 13/10/2013 Funções e Procedimentos Prof. Galeno Sena 40