domingo, 6 de dezembro de 2009

Dividir para conquistar! Parte I (funções)

Até o presente momento vimos exemplos de códigos fontes pequenos, com uma utilidade muito pequena (coisa muito difícil de encontrar no 'mundo real'), e portanto, muito fácil de acompanhar, compreender o seu funcionamento e eliminar os seus erros.
Eu já disse algumas vezes aqui no blog que é fácil encontrar códigos fonte com mais de 1000 linhas. Imagine a dificuldade para acompanhar um código desses!
E imagine a quantidade de tempo que 1 programador levará para concluir este código!
Para resolver estas questões o C usa uma metodologia chamada de modularidade.

O que é modularidade?
Imagine que você fosse o chefe de um grupo que deveria criar uma maquete de uma fábrica. Lógico que você iria dividir este trabalho de criação em várias partes. Dividindo entre os componentes do grupo cada uma dessas partes de acordo com o que cada um sabe fazer. Não seria inteligente deixar alguém fazer algo que não é sua especialidade tendo na equipe um especialista nesta tarefa.
Isto é modularidade!
Dividir uma tarefa grande em várias partes menores e especializadas. Quando digo especializada, quero dizer que idealmente, cada módulo fará uma única tarefa.(O que você prefere? Ser atendido por um médico especializado na sua doença ou por um clínico geral?)
Cada um desses módulos é chamado de função.
As funções podem ser classificadas de acordo com seus parâmetros e retorno.

O que são parâmetros? E retorno?
Algumas funções precisam de dados para cumprir a tarefa para a qual foram criadas. Esses dados são chamados de parâmetros da função.
Voltando ao exemplo da maquete, se você quiser um prédio em sua maquete, terá que dizer a quem irá fazê-lo quantos andares ele terá, qual a cor dele, se terá entrada lateral, etc. Se essas informações não forem passadas, o responsável não saberá como fazê-lo, ou poderá fazê-lo da forma que ele quiser, que pode não ser a que você desejava.
De posse dessas informações (argumentos), o prédio será construído conforme especificado, seguindo esses parâmetros.
Existem funções que precisam de vários parâmetros, outras que precisam de apenas 1 parâmetro, e ainda, as que não precisam de nenhum paramêtro.
Se você quisesse a sua maquete bem realista, poderia pedir a um componente do grupo que fizesse alguns carros populares para ficar no estacionamento da fábrica. Este componente é especialista em fazer carros populares, portanto, não é necessário passar nenhuma informação para ele. Ele fará os carros e entregará.
Já o retorno é o dado que a função devolve depois de fazer o seu trabalho.
No exemplo da maquete, seria o prédio e os carros.
Diferente dos parâmetros, o retorno só permite duas configurações: 1 ou nenhum.
Você pode se perguntar: Mas não seriam vários carros?
Você está certo. O que acontece na verdade, é que o responsável por fazer os carros, os faz um a um. E também os entrega um a um.
Neste exemplo da maquete, você é o chefe da equipe, o coordenador do projeto. Você passa a tarefa para o componente, e ele devolve o trabalho pronto. Você apenas agrupa o resultado. Pode ser que inclusive, você tenha que passar o resultado final do trabalho de um componente para um outro componente como a matéria-prima deste.
Num programa em C, esta função de coordenação é feita pela função main (sim, a main é uma função!).
Vamos ao exemplo de um programa que faz a soma de 2 números inteiros digitados pelo usuário usando este conceito de modularidade.
Antes de ver o código propriamente dito, vamos pensar nas funções que precisamos para executar essa tarefa.
1o precisamos de uma função que receba os valores inteiros digitados pelo usuário e devolva estes valores. Como sabemos ela não pode devolver os 2 valores de uma só vez. Então ela deve ler um valor e retornar este único valor. Depois ela será executada de novo para o outro valor.
Esta função precisa de algum parâmetro? A resposta é NÃO. Ela é especializada em ler inteiros, e inteiros são sempre inteiros. Acredito que ela não necessite de nenhum. E tem algum retorno? Com certeza. São os valores digitados pelo usuário.

2o precisamos de uma função que faça a soma propriamente dita.
Esta função precisa de parâmetros? Com certeza. São os valores digitados pelo usuário, que vieram da função anterior.
E tem algum retorno? Com certeza. É o resultado da soma.

3o precisamos de uma função que mostre ao usuário o resultado da soma.
Esta função precisa de algum parâmetro? Com certeza. É o resultado da soma retornado pela função anterior.
E tem algum retorno? A resposta é NÃO. Já que o valor vai ser mostrado e nada mais será feito no programa, não existe esta necessidade.

Vamos ver uma representação gráfica disto:



As setas indicam 'o caminho' das variáveis. As vermelhas indicam as variáveis que as funções retornam para a função main, e as pretas indicam as variáveis que a função main passa para as funções. (Eu iria usar outras cores, mas não poderia deixar de fazer esta homenagem ao HEXA DO MENGÃO! rsrs)

Uma função em C, é declarada da seguinte forma:

 <tipo de retorno> nome da função (<tipo do parâmetro> nome do parâmetro, <tipo do parâmetro> nome do parâmetro, ..., <tipo do parâmetro> nome do parâmetro){
    comandos da função
}


Vamos ao código, que eu explico os detalhes depois.


//EXEMPLO DO USO DE FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num1;
 printf("Digite um numero inteiro: ");
 scanf("%d", &num1);
 return num1;
}
 
int soma_int(int val1, int val2){
 return val1 + val2;
}
 
void exibe_result(int resultado){
 printf("O resultado da operacao eh %d.\n", resultado);
}
 
int main(){
 int num1, num2, result;
 
 num1 = ler_int();
 num2 = ler_int();
 result = soma_int(num1, num2);
 exibe_result(result);
 system("pause");
}


A 1a função, que se encontra na linha 5, é a ler_int. Pelo seu cabeçalho, vemos que é uma função que devolve um valor inteiro e não necessita de nenhum parâmetro. Vejamos o que esta função faz.
Na linha 6 é declarada uma variável inteira num1, que será usada para receber o valor digitado pelo usuário e retornar este valor para a função main.
Observe a linha 7 onde é mostrada ao usuário uma mensagem indicando o que ele deve fazer. Poderíamos ter algum código para verificar se o valor digitado está dentro de uma faixa específica, dentre outras coisas.
Na linha 8, o valor digitado pelo usuário é armazenado na variável inteira num1 usando o scanf. Até aqui não há nada que já não vimos antes.
A novidade está na linha 9. Observe o uso da palavra reservada return. É assim que é feito o retorno (será por isso que ele tem esse nome?) do valor para a função main. Neste caso, o valor retornado será o que está armazenado na variável inteira num1.
Na linha 12, temos a nossa 2a função, que é a soma_int. Esta função retorna um valor inteiro e necessita de 2 parâmetros inteiros (não é obrigatório que os parâmetros sejam do mesmo tipo retornado pela função, poderia ser de qualquer tipo e em qualquer quantidade, como já vimos).
Um outro detalhe interessante está na linha 13. Observe que é retornado o resultado da soma dos valores passados como parâmetros. Poderíamos ter declarado uma variável inteira, ter atribuído o resultado desta soma à variável e, só então, retornar o valor armazenado nela. A escolha de uma forma ou outra é exclusiva do programador. Eu, particularmente, prefiro como se encontra no código.
A 3a função, exibe_result, que aparece na linha 16, já começa com uma novidade.
Como vimos acima, esta função não retorna nenhum valor. A forma em C de especificar isto é usando o tipo void (lembra dele?). Isto significa que a função não retorna NADA. Além disto, ela necessita de 1 parâmetro, que é o que vai ser exibido, neste caso o resultado da soma que é retornado pela função soma_int.
A última função deste código é a main, que aparece na linha 20. Como já falei antes é ela que coordena o 'trabalho' das outras funções. Vamos ver como ela faz isto.
Na linha 21 são declaradas 3 variáveis inteiras. Elas são necessárias para receber o valor retornado pelas funções (se uma função devolve um valor, ele tem que ser armazenado em algum lugar ou será perdido).
Observe que uma destas variável tem o nome de num1, o mesmo nome daquela variável usada pela função ler_int, lá na linha 6. Antes que você reclame, deixe-me falar uma coisa: elas tem o mesmo nome, mas são a mesma 'pessoa' (rsrs).
A variável num1 de ler_int, só 'existe' enquanto a função ler_int está em execução. E enquanto isto a variável num1 da main, fica 'desativada'.
Dizemos que essas variáveis são locais. Mais a frente falaremos detalhadamente sobre isto. Por enquanto basta saber isto.
Na linha 23, está a 1a chamada da função ler_int. Este é o nome técnico usando para indicar que uma função será executada. Observe que é feita uma atribuição nesta mesma linha. Como já falamos, é para salvar o retorno da função.
Quando não existe um retorno, a forma da chamada é igual a da linha 26: somente o nome da função e os argumentos (os valores que são passados para preencher os parâmetros de uma função são chamados de argumentos) que ela necessita, se ela necessitar de algum.
Acredito que o restante do código agora ficou fácil de entender.
Uma outra coisa interessante a respeito das funções, é que é possível passar o retorno de uma função diretamente como argumento para outra função, sem precisar armazená-lo em uma variável.
Olhe este código abaixo e veja se consegue entender o que ele faz.


//EXEMPLO DO USO DE FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num1;
 printf("Digite um numero inteiro: ");
 scanf("%d", &num1);
 return num1;
}
 
int soma_int(int val1, int val2){
 return val1 + val2;
}
 
void exibe_result(int resultado){
 printf("O resultado da operacao eh %d.\n", resultado);
}
 
int main(){
 
 exibe_result(soma_int(ler_int(), ler_int()));
 system("pause");
}


Fácil não é?
Até a próxima.

4 comentários:

  1. parabéns, muito boa ajuda

    ResponderExcluir
  2. parabéns pela explicação!

    Tire uma duvida se caso fosse 'x' quantidades de numeros para ser somados.Pois nesse codigo são apenas dois numeros, como seria?

    ResponderExcluir
  3. usa a funçao dentro de um for
    eu acho

    ResponderExcluir