Algoritmos e Programação Departamento de Informática 21 Modularização - Funções e Procedimentos · À medida que aumenta a complexidade de um programa, torna-se cada vez mais imperativo "dividir" o programa em módulos menores, isolando em cada um destes módulos as instruções logicamente relacionadas. A esta tarefa se dá o nome de modularização. · Todo programa em C tem pelo menos uma função, denominada main(), que é sempre a primeira a ser executada. · Ao encontrar a chamada a uma função, o controle do programa é desviado para esta função. Depois de executadas as instruções desta função, o controle volta ao ponto da função que fez a chamada. Vantagens: · Simplificação na programação – os módulos são menores, e mais simples; · Qualidade – projeto, implementação e teste por etapas controladas; · Reusabilidade do código – pode-se usar as mesmas funções em outros programas. Se uma função está bem implementada e testada, não há por que desenvolver novamente; · Legibilidade do programa – cada função tem uma tarefa específica e facilmente reconhecível; · Particionamento do trabalho em equipes – divisão das tarefas por grupos de pessoas; · Encapsulamento da funcionalidade – determina-se O QUE a função deve fazer e QUAL a interface dela com o restante do programa. COMO é feita a implementação. Caso se deseje alterar esta implementação, mantendo-se a funcionalidade e a interface, o programa não será afetado, o que influi em todas as vantagens anteriores. 21.1 Procedimentos · As funções podem ou não retornar valores para o ponto de onde são chamadas. Na linguagem C, quando não retornam valores recebem o nome especial de procedimentos. · Quando não retornam valores, devem ter a palavra void colocada antes do seu nome. Observação: em Algoritmos e em Pascal, procedimentos são módulos que retornam nenhum ou vários valores. Funções retornam somente um valor ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 67 Algoritmos e Programação Departamento de Informática Exemplo: #include <stdio.h> #include <conio.h> void desenha() { int i; for (i =0; i<=10; i++) printf ("-"); } main() { desenha(); printf (" tracejado efetuado pela funcao desenha() "); desenha(); getch(); } Observações: · · Cada vez que é executada uma chamada ao procedimento desenha() são impressos 10 traços na tela. Observa-se que o procedimento não retornou nenhum valor e que também, neste caso, não foi passado ao mesmo nenhum valor. 21.1.1 Procedimentos sem parâmetros · Quando o procedimento não precisa de nenhum valor para executar a sua funcionalidade, a chamada a este procedimento é programada colocando-se na instrução – void + nome da função/procedimento seguida de (). Exemplo 1: Programa com procedimento que imprime valor na tela. Não há envio de valores do main() para o procedimento e nem retorno de valores para o main(). void mostra_numero() { printf("33\n"); } main() { mostra_numero(); getch(); } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 68 Algoritmos e Programação Departamento de Informática Exemplo 2: O mesmo programa do exemplo anterior, com a exceção de que a função/procedimento é colocada após o main(). Neste caso, para que não ocorra erro no momento da compilação, é colocado o cabeçalho da função antes do main(). Isto permite que o compilador, no momento da tradução da instrução de chamada à função verifique se esta chamada está correta quanto ao nome, número e tipo de argumentos/parâmetros, se existirem. void mostra_numero(); main() { mostra_numero(); getch(); } void mostra_numero() { printf("33\n"); } Exemplo 3: Impressão de mensagens no main() antes e depois da chamada à função e na função, antes e depois da impressão do valor numérico. void mostra_numero() { printf("Imprimindo no inicio da funcao \n"); printf("Teste de Procedimentos <----\n"); printf("imprimindo no final da funcao\n"); } main() { printf("\nImprimindo do main(),antes de chamar funcao \n"); mostra_numero(); printf("Imprimindo do main(), apos a chamada da funcao\n"); getch(); } Exemplo 4: Programa que chama 5 vezes a função/procedimento que imprime o alfabeto. Pode-se observar que a variável i, apesar de ter sido declarada como do tipo inteiro, recebe caracteres, sem nenhum problema. Poderia, sem nenhuma alteração para o comportamento do programa, ter sido declarada do tipo char. void mostra_alfabeto() { int i; for (i='A';i<='Z';++i) putchar(i); putchar('\n'); } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 69 Algoritmos e Programação Departamento de Informática main() { int i; for(i=1;i<=5;i++) mostra_alfabeto(); getch(); } 21.1.2 Passagem de parâmetros para procedimentos - por valor · · · Quando o procedimento ou função precisa de um ou vários valores para executar a sua funcionalidade, no seu cabeçalho (primeira linha) são colocadas as variáveis que receberão cópia dos valores enviados nas chamadas, precedidas de suas declarações de tipo. Estas variáveis são chamadas de parâmetros da função/procedimento e devem combinar em número e tipo com os valores ou variáveis colocados nas chamadas, que são chamados de argumentos do procedimento ou função. Algumas bibliografias usam os termos parâmetro formal e parâmetro real para os parâmetros e argumentos, respectivamente. Exemplo 1: Programa com procedimento que recebe e imprime um valor inteiro. O recebimento é efetuado através da variável int digito, que receberá cópia do valor do argumento do tipo int, constante ou variável, na chamada ao procedimento. A primeira chamada à função usa como argumento a constante inteira 5. A segunda chamada usa como argumento a variável inteira i. A declaração do parâmetro digito no cabeçalho da função tem efeito de declaração de variável local. void mostra_digito(int digito) { printf("Valor passado para a funcao = %d\n",digito); } main() { int i=10; mostra_digito(5); mostra_digito(i); getch(); } Exemplo 2: Programa com as mesmas funções do exemplo anterior, porém com a função colocada dentro do main(). Neste caso, com a colocação apenas do tipo do parâmetro da função no cabeçalho, possibilita que o compilador possa conferir se a chamada à função está correta, incluindo nome da função, número e tipo dos parâmetros. ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 70 Algoritmos e Programação Departamento de Informática main() { void mostra_digito(int); mostra_digito(5); getch(); } void mostra_digito(int digito) { printf("%d\n",digito); } Exemplo 3: Este programa executa 10 chamadas à função mostra_digito, cada chamada passando um valor diferente. void mostra_digito(int digito) { printf("%d\n",digito); } main() { int i; for (i=1;i<=10;i++) mostra_digito(i); getch(); } Exemplo 4: Utilização de parâmetros tipo float (reais). Observa-se que um dos argumentos é inteiro. Isto não causa erro nem de compilação nem de execução. Caso ocorresse o contrário – parâmetro do tipo int e argumento do tipo float (coerção). void mostra_soma(float a, float b) { printf("%6.2f +%6.2f =%6.2f\n",a,b,a+b); } main() { mostra_soma(45,53.7); getch(); } 21.1.3 Variáveis locais e globais · As variáveis declaradas dentro de uma função ou procedimento são denominadas locais e somente são visíveis, ou seja, podem ser usadas, dentro do próprio bloco, ou seja, elas são criadas na entrada do bloco e destruídas na saída (automáticas). ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 71 Algoritmos e Programação · Departamento de Informática As variáveis globais são as declaradas fora das funções, geralmente antes do main() e podem ser usadas em qualquer parte do programa, por qualquer função e também ser alterada. O uso de variáveis globais é desaconselhado, por permitir a comunicação entre os módulos, determinada pelos parâmetros e comando de retorno. O uso desapercebido do mesmo nome de variáveis dentro de um módulo pode ter consequências imprevisíveis. Exemplo 1: Programa utilizando somente variáveis locais. As variáveis i do main() e da função mostra_digito são independentes e não tem relação nenhuma uma com a outra. Inclusive o incremento de i dentro da função não terá repercussão nenhuma no conteúdo da variável i do main(): void mostra_digito(int i) { i = i * 2; printf("Valor de i na funcao <mostra_digito> = %d\n",i); } main() { int i; for (i=0; i<10; i++) mostra_digito(i); printf("\nValor de i na funcao <main> = %d",i); getch(); } Exemplo 2: Programa utilizando variável global. O incremento da variável i dentro da função faz com que sejam feitas somente 5 chamadas à esta função e não 10, como se esperaria, em função de que i é incrementado em dois locais. int i; void mostra_digito(int digito) { i++; printf("i= %d digito= %d\n",i, digito); } main() { for (i=0; i<10; i++) mostra_digito(i); getch(); } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 72 Algoritmos e Programação Departamento de Informática 21.2 Funções · Módulos que retornam um valor ao ponto de onde foram chamados. 21.2.1 Comando return · A execução de um comando return em uma função faz com que o controle da execução retorne à função que a chamou. Exemplos: return a; return a+b; return (a+b); 21.2.2 Passagem e retorno de valores da função · A passagem de parâmetros por valor para uma função tem o mesmo comportamento já descrito anteriormente para os procedimentos. · O retorno de valor pode ser feito utilizando passagem de parâmetros por referência, a ser estudado, ou através do comando return. O valor será retornado no local da chamada à função. Exemplo 1: Programa com função que recebe dois valores inteiros e retorna a sua soma. Na primeira chamada são utilizados como argumentos duas constantes inteiras, cujas cópias são passadas (por valor) aos parâmetros a e b, declaradas variáveis locais na função. A execução do comando return força o retorno do controle ao local de chamada, levando consigo o resultado do valor colocado à direita do return, no caso a avaliação da expressão (a+b). Este valor substitui a chamada à função e é atribuído à variável à esquerda. int soma_valores(int a,int b) { return(a+b); } main() { int res1, res2, c=7, d=8; res1=soma_valores(10,53); printf("Resultado = %d\n",res1); res2=soma_valores(c,d); printf("Resultado = %d\n",res2); getch(); } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 73 Algoritmos e Programação Departamento de Informática Exemplo 2: O mesmo programa anterior, com duas alterações principais: 1. Sem os parênteses após o return; 2. Utilizando a chamada diretamente como parte do argumento de outra função, no caso, a printf, o que dispensa a declaração das variáveis res1 e res2, com economia de código: float soma_valores(float a,float b) { return a+b; } main() { float c=7.3, d=8.7; printf("Resultado = %f\n", soma_valores(10,53.7)); printf("Resultado = %f\n", soma_valores(c,d)); getch(); } Exemplo 3: Programa com função que recebe dois valores e retorna a sua média. float media(float a,float b) { return (a+b)/2; } main() { float media(float,float); printf("Media = %f\n",media(7.88,8.37)); getch(); } Exemplo 4: Programa com função que retorna o fatorial de um número inteiro passado como argumento. int fatorial (int n) { int i, resultado = 1; for (i=1; i<=n; i++) resultado *= i; return resultado; } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 74 Algoritmos e Programação Departamento de Informática main() { printf (" o fatorial de 7 = %d", fatorial(7) ); printf (" o fatorial de 8 = %d", fatorial(8) ); getch(); } Exemplo 5: Programa que utiliza uma função para ler um caracter e o transforma em minúscula, caso ele não o seja. char minusculo() { char ch; ch = getche(); if ((ch >= 'A') && (ch <= 'Z')) return (ch + 'a' - 'A'); else return (ch); } main() { char letra; printf ("digite uma letra em maiuscula: "); letra = minusculo(); printf("\n\nletra digitada: %c",letra); getch(); } Tarefas propostas: 1) Modifique a função deste último programa para que ela receba uma letra e a transforme em maíúscula, se ainda não o for. 2) Construa um programa com função que receba a altura e a base de um retângulo e devolva a sua área. 3) Crie uma função que receba base e expoente, inteiros, e calcule a sua potenciação, ou seja, a base elevada ao expoente. Implemente-a em um programa. 21.3 Usando várias funções · Um aplicativo, em sua versão final, pode ter vários módulos, cada um desempenhando uma funcionalidade do programa. Um projeto bem elaborado deve possibilitar que se desfrute de todas as vantagens da modularização. ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 75 Algoritmos e Programação Departamento de Informática Exemplo: Programa que utiliza três funções e calcula a seguinte sequência: S(x, n) = x1/1! + x2/2! + x3/3! + ... + xn/ n! int fat(int n) { int i, resultado=1; for(i=1; i<=n; i++) resultado *= i; return resultado; } float potencia(float base, int expoente) { int i; float resultado=1; if (expoente == 0) return 1; for(i=1; i<=expoente; i++) resultado *= base; return resultado; } float serie (float x, int n) { int i; float resultado=0; for (i=1; i<=n; i++) resultado += potencia(x,i)/fat(i); return resultado; } main() { float x; int termos; printf("entre com o numero de termos: "); scanf("%d", &termos); printf("entre com o valor de X: "); scanf("%f", &x); printf("O valor de serie eh = %f ", serie(x, termos)); getch(); } ____________________________________________________________________________________ Professores: Ivo Mário Mathias e Jeferson Antonio Quimelli 76