1. Sub-Rotinas em Linguagem C/C++ Um importante recurso apresentado nas linguagens de programação é a modularização, onde um programa pode ser particionado em sub-rotinas bastante específicas. A linguagem C/C++ possibilita a modularização por meio das funções. Um programa escrito na linguagem C/C++ tem, no mínimo, uma função chamada main, por onde a execução começa. Existem também muitas outras funções predefinidas na linguagem C/C++, por exemplo: clrscr(), gets(), strcmp(), strcpy(), etc. Estas funções são adicionadas aos programas pela diretiva #include, no momento da "linkedição". Além disso, o usuário também pode criar quantas funções quiser, dependendo do problema que estiver sendo resolvido pelo programa. As funções às vezes precisam receber valores externos, chamados parâmetros, e também podem devolver algum valor produzido para o ambiente externo, denominado retorno. Os parâmetros são representados por uma lista de variáveis colocadas dentro de parênteses, logo após o nome da função. Caso haja retorno, a última linha da função deverá incluir o comando return, seguido do valor ou variável que será devolvido a quem chamou a função. O tipo do valor retornado deverá ser exatamente igual ao tipo informado antes do nome da função. Caso não haja retorno, deverá ser digitada a palavra void. Os tipos de funções são apresentados em detalhes a seguir. 1.1 Funções em passagem de parâmetros e sem retorno O tipo mais simples de função é aquele que não recebe nenhuma informação no momento de sua chamada e que também são repassa nenhum valor para quem a chamou. A seguir é apresentado um exemplo (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 3 void soma() 4 { int a, b, s; 5 printf("digite o primeiro numero"); 6 scanf("%d",&a); 7 printf("digite o segundo numero"); 8 scanf("%d",&b); 9 s=a+b; 10 printf("\nsoma = %d",s); 11 getch(); 12 } 13 void main() 14 { 15 soma(); 16 } Como já mencionado na seção anterior, a execução de programa escrito em C/C++ sempre começa pela função main. No exemplo, a execução se inicia na linha 13. Na linha 15, existe uma chamada à função soma. Neste ponto, o fluxo da execução é desviado para 1
a linha 3. Depois são executadas as linhas de 4 até 11. Quando a execução atinge a linha 12, a marca de final de função é encontrada. Neste momento, o fluxo da execução retorna para a linha 16, exatamente abaixo de onde ocorreu o desvio para a função soma. Na linha 16 está a marca de finalização da função main. Assim, a execução do programa é concluída. Devemos destacar que, no momento em que a função soma foi chamada, na linha 15, nenhum valor ou variável foi colocado entre os parênteses, indicando que não houve passagem de parâmetros. Além disso, dentro da função soma não foi utilizado o comando return, sinalizando que ela não retornou valor para quem a chamou. Por esta razão, seu tipo é void. 1.2 Funções com passagem de parâmetros e sem retorno O segundo ripo de função é representado por aquelas que recebem valores no momento em que são chamadas (parâmetros), mas que, no final, não devolvem valor para quem as chamou (retorno). A seguir é mostrado um exemplo (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 3 void calcula_media(int numero1, int numero2) 4 { float media; 5 media = (numero1+numero2)/2; 6 printf("\nmédia = %f",media); 7 getch(); 8 } 9 void main() 10 { int n1, n2; 11 printf("digite o primeiro numero"); 12 scanf("%d",&n1); 13 printf("digite o segundo numero"); 14 scanf("%d",&n2); 15 calcula_media(n1,n2); 16 } Como visto no início deste capítulo, a execução de programa escrito em C/C++ sempre começa pela função main. No exemplo, a execução iniciou-se na linha 9. A partir daí, são executadas sequencialmente as linhas de 10 até 14. Nas linhas 12 e 14, dois valores são recebidos e armazenados nas variáveis n1 e n2. Chegando à linha 15, o fluxo de execução é desviado para a função calcula_media, levando para lá os valores das variáveis n1 e n2. Serão então executadas as linhas 3 a 8, onde está a marca de encerramento da função. O fluxo de execução retorna à função main, na linha 16, imediatamente abaixo do ponto de chamada da função calcula_media. Deste modo, a execução do programa é concluída. Devemos destacar que, no momento em que a função calcula_media foi chamada, na linha 15, duas variáveis foram colocadas entre parênteses, indicando que houve passagem de parâmetros. Os valores delas são copiados para as variáveis numero1 e numero2, descritas no cabeçalho da função, na linha 3. Além disso, dentro da função 2
calcula_media não foi utilizado o comando return, indicando que ela não retornou valor para quem a chamou. Por esta razão, seu tipo foi definido como void. 1.3 Funções sem passagem de parâmetros e com retorno O terceiro tipo de função é representado por aquelas que não recebem valores no momento em que são chamadas (parâmetros), mas que, no final,m devolvem um valor para quem as chamou (retorno). A seguir é apresentado um exemplo (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 3 float multiplicacao() 4 { float multiplicando, multiplicador, produto; 5 printf("digite o primeiro numero"); 6 scanf("%f",&multiplicando); 7 printf("digite o segundo numero"); 8 scanf("%f",&multiplicador); 9 produto=multiplicando*multiplicador; 10 return produto 11 } 12 void main() 13 { float resposta; 14 resposta = multiplicacao(); 15 printf("\no produto é %f",resposta); 16 getch(); 17 } Como já tratado no início do capítulo, a execução do programa escrito em C/C++ sempre começa ela função main. No exemplo, a execução tem início na linha 12. A partir daí, a linha 13 é executada e, ao chegar à linha 14, o fluxo de execução é desviado para função multiplicacao, na linha 3. Assim, as linhas 3 a 10 são executadas. Ao chegar à linha 10, o comando return é encontrado. Isto indica que a execução da função chegou ao fim e que o conteúdo da variável produto será devolvido para quem a chamou. O fluxo de execução, então, retorna à função main, na linha 14, e o valor retornado é atribuído à variável resposta. Depois disso, as linhas 15, 16 e 17 são executadas e o programa chega ao fim. Devemos destacar que, no momento em que a função multiplicacao foi chamada, na linha 14, nenhum valor ou variável foi colocado entre parênteses, o que indica que não houve passagem de parâmetros. Além disso, dentro da função multiplicacao foi utilizado o comando return produto, significando que o valor da variável produto foi devolvido a quem a chamou. Por esta razão, o tipo da função é float, exatamente igual ao tipo do valor retornado. 1.4 Funções com passagem de parâmetros e com retorno 3
O quarto tipo de função é representado por aquelas que recebem valores no momento em que são chamadas (parâmetros) e que, no final, devolvem um valor para quem as chamou (retorno). A seguir é apresentado um exemplo (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 3 float divisao(int dividendo, int divisor) 4 {float q; 5 q=dividendo/divisor; 6 return q; 7 } 8 void main() 9 { int n1, n2; 10 float resposta; 11 printf("digite o primeiro numero"); 12 scanf("%d",&n1); 13 printf("digite o segundo numero"); 14 scanf("%d",&n2); 15 resposta=divisao(n1,n2); 16 printf("\no resultado da divisão é %f",resposta); 17 getch(); 18 } Como já comentado no início do capítulo, a execução do programa escrito em C/C++ sempre começa pela função main. No exemplo, a execução iniciou na linha 8. A partir daí, são executadas sequencialmente as linhas 9 a 14. Nas linhas 12 e 14, dois valores são recebidos e armazenados nas variáveis n1 e n2. Chegando à linha 15, o fluxo de execução é desviado para a função divisao, levando para lá os valores das variáveis n1 e n2. Serão então executadas as linhas 3, 4 e 5. Ao chegar à linha 6, o comando return é encontrado. Isto indica que a execução da função chegou ao fim e que o conteúdo da variável q será devolvido para quem a chamou. O fluxo de execução retorna à função main, na linha 15, e o valor retornado é atribuído à variável resposta. Depois disso, as linhas 17, 18 e 19 são executadas e o programa chega ao fim. Devemos destacar que, no momento em que a função divisao foi chamada, na linha 15, duas variáveis foram colocadas entre parênteses, indicando que houve passagem de parâmetros. Assim, os valores destas variáveis são copiados, respectivamente, para as variáveis dividendo e divisor, descritas no cabeçalho da função, na linha 3. Além disso, dentro da função divisao foi utilizado o comando return q, sinalizando que o valor da variável q será devolvido a quem a chamou. Por esta razão, o tipo da função é, exatamente igual ao tipo do valor retornado. Observação: Em qualquer programa, podemos escrever funções antes ou depois da função main. Se optarmos por escrevê-las antes, nenhum cuidado especial será necessário. Porém, se optarmos por escrevê-las abaixo da função main, deveremos fazer uso dos protótipos de função. Protótipo de uma função é uma linha exatamente igual ao cabeçalho da função (terminando com um ponto-e-vírgula) que sempre deverá ser escrita antes da função main. Esta linha é responsável por informar ao compilador quais outras funções serão encontradas ao término da main. Observe o exemplo a seguir. 4
#include <stdio.h> #include <conio.h> float divisao(int dividendo, int divisor); //esta linha descreve o protótipo da função. void main() { int n1, n2; float resposta; printf("digite o primeiro numero"); scanf("%d",&n1); printf("digite o segundo numero"); scanf("%d",&n2); resposta=divisao(n1,n2); printf("\no resultado da divisão é %f",resposta); getch(); } float divisao(int dividendo, int divisor) {float q; q=dividendo/divisor; return q; } 1.5 Passagem de parâmetros por valor Passagem de parâmetros por valor significa que a função trabalhará com cópias dos valores passados no momento de sua chamada. Para entender melhor este processo, observe o programa a seguir (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 1 #include<stdio.h> 2 #include<conio.h> 3 int soma_dobro(int a, int b); 4 void main() 5 { int x, y, res; 6 printf("digite o primeiro numero"); 7 scanf("%d",&x); 8 printf("digite o segundo numero"); 9 scanf("%d",&y); 10 res=soma_dobro(x,y); 11 printf("\nasoma do dobro dos números %d e %d é %d",x,y,res); 12 getch(); 13 } 14 int soma_dobro(int a, int b) 15 { int soma; 16 a=2*a; 17 b=2*b; 18 soma=a+b; 19 return soma; 20 } 5
Vamos supor que os valores armazenados nas variáveis x e y, por meio da execução das linhas 7 e 9, foram, respectivamente, 5 e 3. Quando a linha 10 é executada, esses valores são copiados para as variáveis a e b (pertencentes à função soma_dobro). Depois disso, os valores de a e b são multiplicados por 2, nas linhas 16 e 17, e depois, na linha 18, é realizada a soma. O resultado dessa soma é devolvido à função main pela execução da linha 19, onde o valor calculado recai sobre a variável res (retorno à linha 10). No momento em que a função soma_dobro chega ao fim, as variáveis, a, b e soma são destuídas e, portanto, as alterações realizadas pelas multiplicações por 2 são perdidas, ou seja, x continua valendo 5 e y continua valendo 3. 1.6 Passagem de parâmetros por referência Passagem de parâmetros por referência significa que os parâmetros passados para uma função correspondem a endereços de memória ocupados por variáveis. Dessa maneira, toda vez que for necessário acessar determinado valor, isso será feito por meio de referência, ou seja, apontamento ao seu endereço. 1 #include<stdio.h> 2 #include<conio.h> 3 int soma_dobro(int *a, int *b); 4 void main() 5 { int x, y, res; 6 printf("digite o primeiro numero"); 7 scanf("%d",&x); 8 printf("digite o segundo numero"); 9 scanf("%d",&y); 10 res=soma_dobro(&x,&y); 11 printf("\nasoma do dobro dos números %d e %d é %d",x,y,res); 12 getch(); 13 } 14 int soma_dobro(int *a, int *b) 15 { int soma; 16 *a=2*(*a); 17 *b=2*(*b); 18 soma=*a+*b; 19 return soma; 20 } Nas linhas 7 e 9 são lidos, respectivamente, os valores das variáveis x e y (como exemplo, estamos supondo que foram digitados 5 e 3). Entretanto, quando a função soma_dobro é chamada, na linha 10, são passados como parâmetros para a função os endereços de memória ocupados pelas variáveis x e y (isso é feito pelo operador & que obtém o endereço de memória de uma variável), ou seja, pelo nosso exemplo, os valores 800 (endereço ocupado por x) e 300 (endereço ocupado por y). Dessa maneira, os valores que recaem sobre as variáveis a e b ( da função) são, respectivamente, 800 e 300 (isto é correto, uma vez que a e b são ponteiros para int). Nas linhas 15 e 16, os valores, 5 e 3, são multiplicados por 2. Neste momento ocorre a 'referência' aos endereços de memória 800 e 300, para que sejam obtidos os valores 6
iniciais e, após a realização das multiplicações, os valores sejam alterados. Dessa maneira, no endereço 800 passamos a ter o valor 10, e no endereço 300 passamos a ter o valor 6. Na linha 17, é realizada a soma dos valores que estão nos endereços especificados por a e b (que já foram multiplicados por 2). Finalmente, na linha 18, o resultado da soma é devolvido à função main, recaindo sobre a variável res (linha 10) e encerrando a função soma_dobro. Quando a função soma_dobro chega ao fim, as variáveis, a, b e soma são destruídas. Entretanto, as alterações decorrentes das multiplicações feitas são mantidas, pois cada alteração fez referência a endereços de memória que estavam fora da área destinada à função. Assim, após a função soma_dobro, o valor de x será 10 e o de y, 6. Observação: A linguagem C/C++ não permite que vetores e matrizes sejam passados na integra como parâmetros para uma função. Para resolver esse problema, deve-se passar apenas o endereço da posição inicial do vetor ou da matriz. Esse endereço é obtido utilizando-se o nome do vetor (ou da matriz) sem o índice entre colchetes. Isto quer dizer que é possível passar um vetor para uma função somente se essa passagem for por referência. Observe o exemplo (a numeração das linhas não faz parte do programa, servindo apenas para facilitar a explicação). 3 void soma_linhas(float m[][5],float v[]) 4 { int i, j; 5 for (i=0;i<3;i++) 6 { for(j=0;j<5;j++) 7 {v[i]=v[i]+m[i][j]; 8 } 9 } 10 } 11 void main() 12 { int i, j; 13 float mat[3][5],vet[3]; 14 clrscr(); 15 for (i=0;i<3;i++) 16 {vet[i]=0; 17 for (j=0;j<5;j++) 18 { printf("\ndigite o elemento %d - %d:", i, j); 19 scanf("%d",&mat[i][j]); 20 } 21 } 22 soma_linhas(mat, vet); 23 for(i=0;i<5;i++) 24 { printf("\nsoma da coluna %d = %d",i, vet[i]); 25 } 26 getch(); 27 } A execução desse programa começa na linha 11, com a função main. Na função main são declaradas algumas variáveis, dentre elas, mat e vet. A variável mat representa uma matriz bidimensional contendo 3 linhas e 5 colunas para armazenar números reais. A 7
variável vet representa um vetor com 3 posições para armazenar a soma dos números de cada linha da matriz mat. Nas linhas 15 a 21, a matriz mat é preenchida com números digitados pelo usuário. Aproveitando essas estruturas de repetição, o vetor vet tem todas as suas posições inicializadas com zero, na linha 16. Duas linhas merecem atenção especial: 22 e 3. A linha 22 está chamando a função soma_linhas, passando como parâmetros a matriz mat e o veto vet. Observe, entretanto, que essas duas variáveis estão acompanhadas de colchetes ([]). Assim, quando o nome de uma matriz ou vetor for usado sem apresentar colchetes contendo um índice, isto significa que estamos usando o endereço de memória ocupado pela posição 0 do vetor ou pela posição 0x0 da matriz. Como endereços de memória só podem ser atribuídos a ponteiros, observe a linha 3, onde está o cabeçalho da função soma_linhas. Nela pode-se ver que a função recebe dois parâmetros: m[][5] e v[]. Assim, toda vez que encontrar um vetor com colchetes vazios ou uma matriz com os colchetes da primeira dimensão vazios, entenda que eles são variáveis ponteiros que guardam os endereços iniciais das variáveis. A partir daí, o programa consegue percorrer o vetor e a matriz normalmente, das linhas 5 a 9. Quando a função soma_linhas chegar ao fim, o fluxo de execução retornará para a linha 23 e o vetor vet, que entrou na função soma_linhas contendo zero em todas as suas posições, voltará com o somatório dos números de cada linha da matriz mat. Estes valores serão mostrados nas linhas 23 a 25. O programa, então é finalizado. Bibliografia Ascencio, Ana Fernanda Gomes; de Campos, Edilene Aparecida Veneruchi. Fundamentos da programação de computadores: Algoritmos, Pascal, C/C++, e Java, 2 ed., Pearson, 2007 Puga, S.; Rissetti, G. Lógica de programação e estruturas de dados com aplicações em Java, 2 ed, Pearson, 2009. C Library. Disponível em: http://www.cplusplus.com/reference/clibrary/ Acessado em: 04/09/2012. 8