Funções criadas pelo programador Prof. André Renato Analogia Imagine que você resolva morar sozinho; No seu novo cotidiano, algumas tarefas precisarão ser executadas: ◦ ◦ ◦ ◦ ◦ ◦ Lavar a roupa; Passá-las; Fazer comida; Limpar a casa; Pagar as contas; ufa!!!! Analogia É fácil perceber que nesse estilo de vida, muitas coisas vão gastar seu tempo e sua energia; O que pode ser feito para que você não precise se ocupar de tantas tarefa? Solução 1 Escolher uma tarefa complexa, que consuma bastante seu tempo (cozinhar, por exemplo); Contratar alguém para realizar esta tarefa; Toda vez que você precisar de comida, basta mandá-la fazer o que você quiser; Seu tempo, agora, pode ser direcionado para outras atividades que você queira realizar, além das outras tarefas restantes; Solução 1 É importante deixar claro que a cozinheira só é capaz de fazer a comida ficar pronta; Outras tarefas como conseguir os ingredientes ainda estão por sua conta!!! Ainda assim, esta solução vai começar a facilitar um pouco a sua vida. Em programação... Obviamente não vamos fazer um programa que cozinhe (por enquanto); No entanto, geralmente enfrentamos problemas semelhantes: ◦ Nossos programas precisam fazer muitas coisas diferentes; ◦ Tentar fazer tudo de uma vez só complica mais do que ajuda; ◦ O que fazer então? Solução 1 (de novo) Escolher uma tarefa complicada; Arrumar alguém (que não seja o colega) para realizá-la; Toda vez que precisarmos executar esta tarefa, chamamos um módulo do programa capaz de realizá-la; Nossa preocupação, agora, fica somente com as outras etapas do programa; Exemplo Nosso programa precisa calcular a média dos vários alunos de uma turma; O cálculo da média, embora não seja muito complicado, é uma pequena etapa do nosso programa que pode não ter nada a ver com o restante do código (leitura das notas, impressão de mensagens para o usuário, indicação de alunos aprovados/reprovados etc); Exemplo O programador lida com este tipo de problema dividindo seu algoritmo diversas partes, geralmente pequenas, chamadas módulos; Este processo se chama modularização do código-fonte; Em C/C++, os pequenos módulos são normalmente chamados de funções; Exemplo sem função #include <stdio.h> float nota1, nota2, nota3, media; int main(){ printf(“Digite as duas primeiras notas do aluno:”); scanf(“%f %f”,¬a1,¬a2); media = (nota1*4 + nota2*6)/10; if (media < 5.0){ printf(“Digite as nota da prova substitutiva:”); scanf(“%f”,¬a3); if (nota3>nota2){ media = (nota1*4 + nota3*6)/10; } } printf(“A média final foi: %f”,media); return 0; } Exemplo com função #include <stdio.h> float nota1, nota2, nota3, media; float CalculaMedia(float notaA, float notaB ){ return (notaA*4 + notaB*6)/10; } int main(){ printf(“Digite as duas primeiras notas do aluno:”); scanf(“%f %f”,¬a1,¬a2); media = CalculaMedia(nota1,nota2); if (media < 5.0){ printf(“Digite as nota da prova substitutiva:”); scanf(“%f”,¬a3); if (nota3>nota2){ media = CalculaMedia(nota1,nota3); } } printf(“A média final foi: %f”,media); return 0; } Exemplo com função (protótipo) #include <stdio.h> float nota1, nota2, nota3, media; float CalculaMedia(float notaA, float notaB ); int main(){ printf(“Digite as duas primeiras notas do aluno:”); scanf(“%f %f”,¬a1,¬a2); media = CalculaMedia(nota1,nota2); if (media < 5.0){ printf(“Digite as nota da prova substitutiva:”); scanf(“%f”,¬a3); if (nota3>nota2){ media = CalculaMedia(nota1,nota3); } } printf(“A média final foi: %f”,media); return 0; } float CalculaMedia(float notaA, float notaB ){ return (notaA*4 + notaB*6)/10; } Vantagens da modularização Facilita a detecção de erros, pois é, em princípio, simples verificar qual é o módulo responsável pelo erro. É mais fácil testar os módulos individualmente do que o programa completo. É mais fácil fazer a manutenção (correção de erros, melhoramentos etc.) módulo por módulo do que no programa total. Além disso, a modularização aumenta a probabilidade dessa manutenção não ter consequências ruins nos outros módulos do programa. Permite o desenvolvimento independente dos módulos. Isto simplifica o trabalho em equipe, pois cada elemento, ou cada subequipe, tem a seu cargo apenas alguns módulos do programa. Porventura a mais evidente vantagem da modularização em programas de pequena escala, mas também nos de grande escala, é a possibilidade de reutilização do código desenvolvido. Outras Elementos de uma função float CalculaMedia(float notaA,float notaB){ return (notaA*4 + notaB*6)/10; } Tipo de valor de retorno: float Nome da função: CalculaMedia Lista de parâmetros: float notaA, float notaB Código da função: return (notaA*4 + notaB*6)/10; Tipo de valor de retorno Pode ser praticamente qualquer tipo de dado da linguagem C; Vetores e matrizes têm uma forma de declaração especial que será visto mais adiante; Existe também o tipo de dado “void”; Este dado não representa valor algum e é utilizado quando não existe um resultado direto dos comandos existentes na função; Nome da função Pode ser dado qualquer nome à função, desde que siga as regras dos nomes para identificadores de cada linguagem: ◦ Não pode começar por número; ◦ Não pode ter nome repetido de funções e/ou variáveis; ◦ Não pode ter símbolos especiais; ◦ Letras maiúsculas/minúsculas; Lista de parâmetros São todos os dados necessários para que a função possa executar seu papel corretamente; Normalmente, esta lista é composta por um ou mais parâmetros, mas também é possível que esta lista esteja vazia; Cada parâmetro da lista deve estar separado por vírgula, ter o tipo de dado representado e o nome do parâmetro (parâmetro formal); Voltando à cozinha... Os parâmetros podem ser comparados aos ingredientes que devem ser dados à cozinheira para que a comida possa ser feito; A parte que chama a função deve entregar a ela os parâmetros corretos na ordem correta definida pela função; Voltando à cozinha... Exemplo: Pão FazPão(Farinha f, Sal s,Óleo o, Fermento fe){ Coloque f,o em uma vasilha; Misture um pouco; Adicione fe; Espere 5 minutos; Acrescente s; Espere 40 minutos; Leve ao forno; Retorne Pão; } Voltando à cozinha... O que aconteceria se eu entregar açúcar ao invés de fermento? E se eu trocar a farinha com o sal? É importante lembrar que a nossa cozinheira só é capaz de misturar os ingredientes e devolver o alimento pronto; A parte da aquisição e da entrega de alimentos ainda é conosco; Um pouco mais sobre parâmetros O nome que é dado ao parâmetro pela função nada mais é do que um apelido para o valor que está sendo entregue; A parte que chama a função deve indicar corretamente o valor que será utilizado; A partir de então, este valor é copiado para a função que passará a chamá-lo pelo nome que ela definiu; Um pouco mais sobre parâmetros float nota1, nota2, nota3, media; float CalculaMedia(float notaA, float notaB ){ Apelidos return (notaA*4 + notaB*6)/10; } int main(){ printf(“Digite as duas primeiras notas do aluno:”); scanf(“%f %f”,¬a1,¬a2); media = CalculaMedia(nota1,nota2); if (media < 5.0){ printf(“Digite as nota da prova substitutiva:”); scanf(“%f”,¬a3); if (nota3>nota2){ media = CalculaMedia(nota1,nota3); } } printf(“A média final foi: %f”,media); return 0; } Valores reais Código da função É, sem dúvida, a parte principal da função; Define o que e como será feito; Tem a aparência de um código comum, como aqueles que escrevemos em qualquer outra parte do programa; Variáveis locais São variáveis declaradas e utilizadas dentro de uma função; Servem para auxiliar na execução do código da função; Têm seu escopo (validade) restritos à função, ou seja, quando a função terminar elas “desaparecem”; Não podem ser acessadas fora da função; Variáveis locais int MaiorDe3(int n1, int n2, int n3){ int maior; if (n1 > n2){ maior = n1; }else{ maior = n2; } if (n3>maior){ maior = n3; } return maior; } Variáveis globais São variáveis declaradas antes de qualquer função; São, portanto, acessáveis de qualquer ponto do programa e deixam de existir quando o próprio programa encerra; Devem ser utilizadas com cuidado, pois o seu excesso dificulta a compreensão e a correção do código; A função em execução Devemos tem em mente que uma função é uma parte auxiliar do bloco principal do programa; Ela pode ser acionada diversas vezes, mas quem é executado primeiro é o bloco principal; Vejamos como isso acontece: A função em execução Bloco principal (primeira parte) Função (primeira chamada) Bloco principal (segunda parte) Bloco principal (terceira parte) Função (segunda chamada) A função em execução #include <stdio.h> float nota1, nota2, nota3, media; float CalculaMedia(float notaA, float notaB ){ Valor copiado return (notaA*4 + notaB*6)/10; } int main(){ printf(“Digite as duas primeiras notas do aluno:”); scanf(“%f %f”,¬a1,¬a2); media = CalculaMedia(nota1,nota2); Valor devolvido if (media < 5.0){ printf(“Digite as nota da prova substitutiva:”); scanf(“%f”,¬a3); if (nota3>nota2){ media = CalculaMedia(nota1,nota3); } } printf(“A média final foi: %f”,media); return 0; } Valor devolvido Tipo de retorno “void” “Void” é um tipo especial de dado que não pode ser processado de forma alguma; Não é possível somar, dividir, comparar escrever, ler ou fazer qualquer outra coisa diretamente com um valor “void”... Logo vem a pergunta: “Pra que serve um troço que não serve pra nada???” Tipo de retorno “void” Uma utilização do “void” se dá exatamente no âmbito das funções; Existem determinados tipos de processamento que não resultam diretamente em um valor específico, mas que são importantes na modularização de um programa; Tipo de retorno “void” Exemplos: ◦ Ler dados do usuário (nome, endereço, telefone, idade, CPF etc); ◦ Escrever resultados na tela; ◦ Zerar um vetor ou uma matriz; ◦ Manipular arquivos; Nem sempre essas operações resultam em um valor, mas pode ser importantes no nosso programa; Tipo de retorno “void” Ao declarar que uma função retorna “void” estamos dizendo que não será gerado nenhum valor correspondente à execução da função, mas que ele será realizada normalmente; A parte que chamou a função não poderá, portanto, atribuir uma função que retorna “void” a uma variável ou fazer qualquer outra coisa que não seja simplesmente chamar a função; Tipo de retorno “void” Exemplo: void EscreveBoletim(float notaA, float notaB){ float media; media = (notaA*4 + notaB*6)/10; printf(“Suas notas foram: %f e %f.”, notaA,notaB); printf(“Sua média final foi: %f.”, media); if (media >= 5.0){ printf(“Você foi aprovado!”); }else{ printf(“Você foi reprovado!”); } } Tipo de retorno “void” A parte que chamar a função não poderá fazer: ... aux = EscreveBoletim(n1,n2); ... A função EscreveBoletim deverá ser executada desta forma: ... EscreveBoletim(n1,n2); ... Coesão e acoplamento São dois conceitos muito importantes no planejamento de um programa e na sua correta modularização; A coesão diz respeito ao conjunto de comandos que são executados dentro de uma função; Já o acoplamento se refere ao conjunto de parâmetros passados para a função; Espera-se uma alta coesão Quando uma função é definida, espera-se que ela execute sua tarefa da forma mais eficiente possível; Por eficiente, entende-se o código capaz de realizar a tarefa com a menor quantidade de comandos e de forma correta; Espera-se uma alta coesão Uma função criada para calcular a média de um aluno precisa do seguinte trecho? printf(“Digite sua idade:”); scanf(“%d”,&idade); Ou então? printf(“Você passou?”); scanf(“%d”,&opcao); Espera-se um baixo acoplamento Para que uma função possa funcionar bem, é necessário um conjunto mínimo de parâmetros; Os parâmetros devem ser definidos de forma que sejam em menor quantidade possível, evitando parâmetros que não servem para nada, ou que podem ser calculados a partir dos outros; Espera-se um baixo acoplamento void EscreveMedia(float notaA,float notaB,float media){ media = (notaA*4 + notaB*6)/10; printf(“Sua média foi: %f”,media); } void EscreveMedia(float notaA, float notaB, int idade){ float media; media = (notaA*4 + notaB*6)/10; printf(“Sua média foi: %f”,media); }