Aula 12: Programação Modular II:

Funções com entrada e saída

Traduzido por Ana Paula Costa

Na aula anterior (aula 11) vimos funções que não aceitam parâmetros e não retornam valores. Estas eram funções simples. Agora vamos ver funções que aceitam parâmetros de entrada e funções que produzem valores de retorno.

Parâmetros para passar para as funções

Nós podemos passar parâmetros para as funções. As funções podem depois trabalhar com estes parâmetros. Dentro da função, os parâmetros funcionam como variáveis normais. Em C, os parâmetros que a função espera são colocados depois do nome da função entre parêntesis. Para uma função que não tem saída (ou por outras palavras, que retorna o tipo void):
Uma funcção com entrada(input) (mas sem saída(output)):
 void nomedafunção(listadeparâmetros)
 {
 <declaração de variáveis locais>

  instruções;
}

As variáveis na lista de parâmetros são declaradas da mesma maneira que as variáveis normais de um programa ou função, nomeadamente temos que especificar o tipo de cada parâmetro. Dentro da função podemos utilizar o parâmetro como se fosse uma variável normal. Podemos efectuar cálculos com ele, utilizá-lo em condições, e até mesmo alterar o seu valor. Não necessita de ser inicializado, porque a inicialização vem do programa que faz a chamada à função.

Como um exemplo, o programa que se segue irá calcular e mostrar o quadrado de uma variável x: De notar a forma como o parâmetro r é declarado e utilizado.
código do programa


#include <stdio.h>

void write_square(float r)
{
  float y;

  y = r*r;
  printf("The square of %f is %f", r, y); 
}

void main()
{
  float x;

  x = 4;
  write_square(x);
  write_square(3.0);
}

output


The square of 4.0 is 16.0
The square of 3.0 is 9.0

Como se pode ver no programa de cima, as funções com parâmetros podem ser chamadas com uma variável, como é o caso em write_square(x), ou com uma constante, como em write_square(3.0).

Outro exemplo, que utiliza dois parâmetros:
código do programa


#include <stdio.h>

void write_sum(int i1, i2)
/* write the sum of i1 and i2 */
{
 int j;

  j = i1+i2;
  printf("The sum of %d  and %d is %d',
    i1, i2, j); 
}

void main()
{
  int x, y;

  x = 4;
  y = 5;
  write_sum(x, y);
  write_sum(3, 4);
}

output


The sum of 4 and 5 is 9
The sum of 3 and 4 is 7
De notar que temos que passar para a função o tipo de informação que ela espera. Neste caso, a função espera por dois inteiros, e então nós devemos passar-lhe dois inteiros (x e y).
Por fim, um exemplo com uma lista de parâmetros de vários tipos. As variáveis a serem declaradas na lista de parâmetros são separadas por uma vírgula (,).
código do programa


#include <stdio.h>

void write_N_times(float r, int n);
  /* Will write n times the real r */
{
  int i;

  for (i= 1; i<=n; i++)
    printf("%10.3f\n", r);
}

void main()
{
  write_N_times(3.0, 4);
}

output


3.000 
3.000
3.000
3.000


Funções com saída (output)

As funções podem retornar um valor de saída. O tipo do valor de retorno tem que ser especificado no momento da declaração da função, antes do nome da função.
 
 tipo nomedafunção(listadeparâmetros)
  {
  <listadevariáveis>

   instruções;
 }

Funções com saída e com parâmetros 
de entrada

Algures nas instruções temos que especificar o valor de retorno. Fazemos isso com a palavra reservada return
 

 return (valor)

Obviamente, o valor tem que ser do mesmo tipo que o tipo da declaração da função.
De notar que a instrução return sai imediatamente da função e as instruções que se encontrem depois de return não serão executadas.

  double square(double r)
    /* will return the square of of the parameter r */
  {
    r = r*r;
    return (r);
  }

No local onde a função será chamada, podemos atribuir este valor a uma variável (do mesmo tipo do valor retornado pela função!), por exemplo

  y = square(3.0);

utilizá-lo como parte de uma expressão, por exemplo

  y = 4.0 * square(3.0) + 1.0;

ou utilizá-lo noutra função, por exemplo

  printf("%f", square(3.0));


Um exemplo completo:

código do programa


/* example with parameters */
#include <stdio.h>

double square(double r)
  (* will return the square of of the parameter r *) 
{
  r = r*r;
  return(r);
}

void main()
{
  double x, y;
  x = 4.0;
  y = square(x);
  printf("The square of %f is %f\n", x, y);
  printf("The square of %f is %f\n", 3.0, square(3.0));
}

output


The square of 4.0 is 16.0
The square of 3.0 is 9.0
O que está a acontecer com a instrução y = square(x) é o seguinte:
  1. O valor da expressão dentro de parêntesis é calculado. Neste caso é simples. É o valor de x, nomeadamente 4.0;
  2. Este valor (4.0) é passado para a função square(). Dentro da função:
  3. É criada uma variável temporária com nome r.
  4. O valor passado para a função é atribuído a essa variável r. Efectivamente aconteceu uma instrução r=4.0.
  5. r=r*r; O novo valor de r é calculado. r agora tem o valor 16.0.
  6. return(r); O valor da expressão dentro de parêntesis (16) é passado de volta à instrução que fez a chamada (y=square(x);); onde poderá ser utilizado.
  7. Neste caso, o valor retornado (square(4.0) que é 16.0) será atribuído a y. A instrução y=square(x); efectivamente transforma-se em y=16.0;
  8. A linha seguinte (printf...) mostra os valores das variáveis x e y. (De notar que o valor de x se manteve inalterado e continua a ser 4.0. Mais tarde, na aula sobre "passagem por valor / passagem por referência" iremos ver que não é necessariamente assim.)


De notar que em C não temos que utilizar o valor que é retornado pela função. Podemos, por exemplo escrever na nossa função main()
  square(3.0);
Esta construção é muito confusa e deve ser evitada quando possível em programas estruturados; um valor retornado por uma função deve, em princípio, ser utilizado na parte que fez a chamada.


Porquê?

Agora a grande questão é "porquê?". Porquê escrever funções se podemos fazer o mesmo com linhas de instruções normais? De facto, as primeiras linguagens (por exemplo o BASIC) não tinham a possibilidade de escrever funções e mesmo assim podíamos escrever programas para resolver qualquer problema. Existem no entanto três razões importantes que justificam a utilização de módulos.


Teste Rápido:
Para testar os conhecimentos sobre o que aprendeu nesta aula, prima aqui para fazer um teste on-line. De notar que este Não é o formato que será utilizado no teste final!

Peter Stallinga. Universidade do Algarve, 8 November 2002