Curso de C
Funções e Recursividade



  Funções

  As funções são sub-rotinas que executam em separado do restante do programa. Elas funcionam como uma "caixa-preta", onde recebem dados de entrada, realizam algum processamento e produzem uma resposta.

  A sintaxe para uma função é:
tipo_de_dado_de_retorno  nome_da_função(lista_de_parâmetros)
{
  // Código de processamento
  return dado_de_retorno;
}
  No Pascal, utilizamos o nome da função para atribuir o valor de retorno. No caso do C, utilizamos a palavra reservada "return", seguido do valor (ou expressão) a ser retornado.
  O uso do "return" é obrigatório para funções que retornam valor. Entretanto, é facultativo para funções que não retornam valor.

  Exemplo de função:
int dobro(int n)
{
  return n*2;
}
  Esta função recebe um valor inteiro, passado através da variável "n", e retorna outro valor inteiro. O valor retornado é a expressão "n*2", que é o dobro de "n".

  Para utilizar a função, deve-se declarar o nome da função, passando-se os parâmetros necessários e associar o resultado a uma variável. Ex:
#include <stdio.h>

int x;

int dobro(int n)
{
  return n*2;
}

main()
{
  x = dobro(4); // x recebe o resultado de n*2, que é 8
}
  Na chamada da função, o valor 4 é passado para a função "dobro", que recebe o valor 4 em "n", multiplica por 2 e retorna o valor igual a 8. O resultado da conta é armazenado na variável "x".

  O uso de uma variável para armazenar o resultado não é obrigatório. Por exemplo:
  dobro(5);
  É válido, a função executa, porém o resultado retornado não é armazenado e é "perdido".

  Agora, vamos ver graficamente o funcionamento do programa do exemplo anterior.
     ┌─────────┐
     │  Dobro  │
     ├─────────┤
 +-- │   n*n   │ <-+
 |   └─────────┘   |
 |                 |
 |                 |
 |   ┌──────────┐  |
 |   │   Main   │  |
 |   ├──────────┤  |
 |   │ dobro(4) │ -+
 +-> │ x        │
     └──────────┘
  Obs: Após armazenar o valor em "x", a execução passa imediatamente para a próxima instrução.


  Procedure

  No Pascal, temos a "procedure", que é uma função que não retorna valor. O C também possui um tipo de procedure, que é o retorno de função do tipo "void". Esta função não retorna valor algum e não necessita do "return". Exemplo:
void minha_procedure()
{
  printf("Minha procedure\n");
}

  Quando a função não retorna valor, ela pode ser chamada diretamente (sem a associação com uma variável). Ex:
minha_procedure();


  Lista de parâmetros

  Uma função pode ter a lista de parâmetros com múltiplos tipos de dados, além de poder retornar qualquer tipo de valor. Exemplo:
#include <stdio.h>

double potencia(double valor, int exp)
{
  int i;
  double result = 1;

  for (i=0; i<exp; i++)
    result *= valor;

  return result;
}

main()
{
  printf("O valor de 4.5^3 é: %.2f\n", potencia(4.5, 3)); 
}
  Saída:
  O valor de 4.5^3 é: 91.1250

  Relacionamento das variáveis de chamada e função:

  Suponha a função "exemplo" que recebe três parâmetros "aa", "bb" e "cc", e que outras três variáveis "a", "b" e "c" são passadas como parâmetros.
void exemplo(int aa, int bb, int cc)
{
 
}

main()
{
  int a,b,c;
  exemplo(a,b,c);
}

  O relacionamento entre as variáveis é feito da seguinte maneira:
             a       b       c
exemplo(int ba, int bb, int cc)

  Assim, temos:
#include <stdio.h>

void exemplo(int aa, int bb, int cc)
{
  printf("Valor de aa: %d\n", aa);
  printf("Valor de bb: %d\n", bb);
  printf("Valor de cc: %d\n", cc);
}

main()
{
  int a=1, b=2, c=3;
  exemplo(a,b,c);
}
  Saída:
  Valor de aa: 1
  Valor de bb: 2
  Valor de cc: 3


  Funções que recebem e retornam estruturas

  É possível também passar e receber estruturas nas funções. Veja o exemplo a seguir.
#include <stdio.h>
#include <string.h>

struct func
{
  char nome[10];
  int idade;
  int matricula;
};

void imprime(struct func f)
{
  printf("Dados do funcionário:\n");
  printf("Nome: %s\n", f.nome);
  printf("Idade: %d\n", f.idade);
  printf("Matrícula: %d\n", f.matricula);
}

main()
{
  struct func func1;
  strcpy(func1.nome, "Jaqueline");
  func1.idade = 24;
  func1.matricula = 311;

  imprime(func1);
}
  Saída:
  Dados do funcionário:
  Nome: Jaqueline
  Idade: 24
  Matrícula: 311

  O exemplo a seguir possui uma função que cria o funcionário e devolve a estrutura.
#include 
#include 

struct func
{
  char nome[10];
  int idade;
  int matricula;
};

struct func cria_func(char *nome, int idade, int matricula)
{
  struct func tmp;

  strcat(tmp.nome, nome);
  tmp.idade = idade;
  tmp. matricula = matricula;

  return tmp;
}

main()
{
  struct func func1;

  func1 = cria_func("Jaqueline", 24, 311);

  printf("Dados do funcionário:\n");
  printf("Nome: %s\n", func1.nome);
  printf("Idade: %d\n", func1.idade);
  printf("Matrícula: %d\n", func1.matricula);
}
  Saída:
  Dados do funcionário:
  Nome: Jaqueline
  Idade: 24
  Matrícula: 311


  Chamadas de funções em cascata

  As funções também podem chamar outras funções. Exemplo:
#include <stdio.h>

void func1()
{
  printf("Olá.\n");
}

void func2()
{
  func1();
}

main()
{
  func2();
}
  Saída:
  Olá.

  Não há limites para chamadas em cascata. Entretanto, a cada chamada de função, a função chamadora é colocada na pilha. O programa pára a execução, quando essa pilha estoura.


  Passagem por referência e valor

  A passagem por valor é quando uma variável dos parâmetros da função recebe uma CÓPIA do valor da variável utilizada no chamamento da função. Nesse caso, qualquer alteração na variável dentro da função não irá refletir na variável utilizada no chamamento da função.
  Exemplo:
void testa(int b)
{
  b=15;
}

main()
{
  int a=4;
  testa(a);
}
  O valor de "a" permanece 4, pois "b" trabalha com uma cópia de "a".

  A passagem por referência é quando uma variável dos parâmetros da função faz REFERÊNCIA à variável utilizada no chamamento da função, passando a se comportar como ela. No exemplo acima, "b" passa a se comportar como "a". Assim, qualquer alteração em "b" irá refletir em "a".

  Veja os exemplos a seguir:
/* Passagem por VALOR */

#include <stdio.h>

int a;

void teste(int b)
{
  b=5;
}

main()
{
  a = 4;
  teste(a);
  printf("O valor de a é: %d",a);
}
  Saída:
  O valor de a é: 4

  Uma vez que a variável "b" da função "teste" recebe uma cópia de "a", a variável global "a" NÃO É ALTERADA!!

/* Passagem por REFERÊNCIA */

#include <stdio.h>

int a;

void teste(int *b)
{
  *b = 5;
}

main()
{
  a = 4;
  teste(&a);
  printf("O valor de a é: %d",a);
}
  Saída:
  O valor de a é: 5

  Uma vez que a variável "b" da função "teste" é a própria variável "a", a variável global "a" É ALTERADA!!

  Na passagem por referência, é obrigatório chamar a função uilizando variáveis, conforme no exemplo acima.
  Já na passagem por valor, você pode tanto passar os dados utilizando variáveis, como com valores diretos. Por exemplo: soma(2,3).

  Como diferenciar esses mecanismos na declaração da função ?

  A declaração normal de uma variável da lista de parâmetros indica que ela irá fazer a passagem por valor. Ex:
void teste(int a)

  Quando é declarada uma variável do tipo ponteiro (símbolo de asterisco "*"), ela irá fazer a passagem por referência. Ex:
void teste(int *a)

  Quando uma variável comum (não ponteiro) é utilizada como dado de entrada na passagem por referência, deve-se utilizar o símbolo "&" antes do nome dela. Essa é a razão da função "scanf" exigir o símbolo "&" antes do nome da variável. Já no caso de uma string, não é necessário o símbolo "&", uma vez que ela é do tipo ponteiro. Ex:
#include <stdio.h>

char nome[20];

main()
{
  printf("Nome: ");
  scanf("%s",nome);

  printf("Seu nome é: %s\n", nome);
}


  Recursividade

  Após trabalhar com funções, você já deve ter se perguntado: posso chamar uma função dentro dela mesmo? A resposta é sim. Podemos chamar uma função dentro dela infinitamente (em tese). A esse mecanismo, de chamar uma função dentro dela mesmo, damos o nome de recursividade.

  Sintaxe:
 void nome_funcao()
 {
   nome_funcao();
 }

  O grande problema da recursividade é saber quando parar, ou até mesmo, como parar, visto que esse tipo de chamada é infinito.
  Então, parece claro que um teste lógico deverá ser feito antes de se atingir o chamamento da função, de modo que a recursividade seja interrompida.

  O teste poderá ser feito de duas formas:
  Teste antes:
#include <stdio.h>

int i;

void loop()
{
  printf("%d\n",i);

  if (i == 5)
    return;

  i++;
  loop();
}

void main(void)
{
  i=1;
  loop();
}
  Saída:
  1
  2
  3
  4
  5

  Obs:
  Teste durante:
#include <stdio.h>

int i;

void loop()
{
  printf("%d\n",i);

  if (i<5)
  {
    i++;
    loop();
  }
}

void main(void)
{
  i=1;
  loop();
}
  Saída:
  1
  2
  3
  4
  5

  O capítulo de recursividade do Pascal apresenta uma ilustração do comportamento de um programa em caso de recursividade.

  A seguir, um exemplo prático de recursividade: o cálculo de um número fatorial.
#include <stdio.h>

int fatorial(int n)
{
  if (n==0)
    return 1;
  else
    return n*fatorial(n-1); 
}

int i;

void main(void)
{
  i = 4;
  printf("%d! = %d\n", i, fatorial(i));
}
  Saída:
  4! = 24


<< Anterior Linguagem C Próxima >>


/MARMSX/CURSOS/C