Introdução ao conceito de parâmetros para funções

Neste texto, explicamos os princípios a respeito de passagem de parâmetros para funções, particularmente diferenciando as passagens por referência (ou endereço) e por valor.

Por que parâmetros são necessários para funções?

A passagem de parâmetros é essencial para ampliar o potencial de uso de uma função. Uma função sem parâmetro, é apenas um procedimento que executará sempre exatamente os mesmos passos. Por exemplo, uma função para computar o fatorial de um número natural, deve ter um único parâmetro, digamos n, sendo que n deve conter o valor para o qual deseja-se saber o fatorial. Por um lado, na declaração da função deve-se indicar de alguma forma este parâmetro, que neste caso é denominado parâmetro formal (por ser um formalismo necessário para funcionar o conceito).
Por outro lado, o ponto do código que "está interessado" no fatorial de algum valor é quem deve invocar a função passando-lhe esse valor como parâmetro, que neste caso recebe o nome de parâmetro efetivo (pois é efetivamente o valor sobre o qual dever ser "aplicada" a função).

Fig. 1. Ilustração do fluxo de execução ao invocar uma função para computar o fatorial de um natural.

A figura 1 ilustra o exemplo da função fatorial, o código à esquerda está interessado em computar o fatorial de N, de k e de N-k, que são os parâmtros efetivos para a função fat do exemplo. Do lado direito está a código da função, sua declaração (usamos o nome fat) e seu único parâmtro formal, que recebeu o nome n.

Introdução aos parâmetros formal e efetivo

O conceito de funções apresenta 4 palavras-chave principais, que são:

- parâmetros formais: usados na declaração da função (por isso denominados formais);
- parâmetros efetivos: usados na chamada da função (quem invoca é quem deve providenciar os parâmetros efetivamente utilizados);
- a passagem de parâmetros pode ser
  - por valor: como "a" e "b" em `void funcao (<verm>int</verm> a, float b)`
  - por referência: como "a" e "b" em `void funcao (<verm>int</verm> *a, float b[])`
- em C, uma função devolve apenas tipos simples (como "int", "float" e "char"), nunca um vetor!!!

Assim, no caso de passagem de parâmetro por referência (ou por endereço), como o nome indica, será passada uma referência à uma variável externa à função (o parâmetro efetivo). Desse modo, dentro da função, ao alterar o valor do parâmetro formal, quem estará sendo alterado é efetivamente à variável externa. Em C existe uma sintaxe especial para declarar e alterar o parâmetro por referência (usando asterisco '`*`' - vide exemplo abaixo).

Já o uso de parâmetro por valor é mais simples, o parâmetro formal funciona como uma variável local que é inicializada com o valor do parâmetro efetivo, assim que a função em questão é invocada. Qualquer alteração do parâmetro formal, tem influência nula sobre o parâmetro formal associado (ou seja, uma atribuição dentro da função com determinado parâmetro, não provoca alteração alguma no valor da variável que foi passada para a função - parâmetro efetivo).

Nos exemplos abaixo ilustramos os 4 conceitos em exemplos para C e para Python.

C | Python
--- | ---

    <verm>int</verm> soma (<verm>int</verm> a, <verm>int</verm> b) {
      <verm>int</verm> c = a+b; <cyan>// variavel local: sem relacao com 'main.c'!</cyan>
      return a+b; <cyan>// devolve a+b</cyan>
      }
    <verm>int</verm> soma_max (<verm>int</verm> a, <verm>int</verm> b, <verm>int</verm> *max) {
      if (a < b)
        *max = b; <cyan>// altera o valor da referencia (em quem invocou soma_max)</cyan>
      else
        *max = a; <cyan>// em caso de empate tanto faz...</cyan>
      return a+b; <cyan>// devolve a+b</cyan>
      }
    <verm>int</verm> main (void) {
      <verm>int</verm> a, b, c, maximo;
      <verd>scanf</verd>("%d %d", &a, &b); <cyan>// operador '&' pega o endereco da variavel na memoria</cyan>
      c = soma_max(a,b,&maximo);
      <verd>print</verd>("Soma de %d e %d = %d = %d.\n", a, b, soma(a,b), c);
      <verd>print</verd>("O maximo e': %d\n", maximo);
      return 1;
      }

    <verm>def</verm> soma (a, b) :
      c = a+b; <cyan># variavel local: sem relacao com 'main.c'!</cyan>
      return a+b; <cyan># devolve a+b</cyan>

    <verm>def</verm> soma_max (a, b, max) :
      if (a < b) :
        max[0] = b; <cyan># altera o valor da referencia (em quem invocou soma_max)</cyan>
      else :
        max[0] = a; <cyan># em caso de empate tanto faz...</cyan>
      return a+b; <cyan># devolve a+b</cyan>

    <verm>def</verm> main () :
      a = int(<verd>input</verd>());
      b = int(<verd>input</verd>());
      maximo = []; maximo.append(-1); <cyan># truque: usar vetor para param. por ref.</cyan>
      c = soma_max(a, b, maximo);
      <verd>print</verd>("Soma de %d e %d = %d = %d." % (a, b, soma(a,b), c));
      <verd>print</verd>("O maximo e': %d" % (maximo[0]));

    main();

Explicações sobre as diferenças entre parâmetros por referência e por valor

A diferença entre passagem de parâmetro por referência ou valor, no primeiro caso é passado o endereço (ou referência) da variável efetivamente usada (parâmetro efetivo) e no segundo o valor da variável efetivamente usada apenas serve como valor inicial para o parâmetro formal (uma inicialização).

C: No código C acima, o que definiu tratar-se de referência foi o uso de '*' no parâmetro formal. Além disso, para fazer referência ao parâmetro efetivo (a variável `maximo`) também foi preciso usar o '*'.
Por outro, na chamada da função foi preciso passar o endereço da variável `maximo` e isso foi possível usando o operador '&'.
No exemplo não aparece, mas em C, ao passar um vetor como parâmetro, automaticamente ele é passador por referência.
Para examinar mais sobre funções em C clique aqui.

Python: No código Python acima, o que definiu tratar-se de referência foi usar como parâmetro formal um vetor (ou lista). Por isso foi preciso o "truque" de definir um vetor (de nome `maximo`, com um único elemento) e passá-lo como parâmetro efetivo para a função.
Assim, em Python, sempre que passar um vetor como parâmetro, automaticamente ele é passador por referência.
Para examinar mais sobre funções em Python clique aqui.

Leônidas de Oliveira Brandão
http://line.ime.usp.br

Alterações:
2020/08/20: acertos no formato
2020/08/10: revisão de formato e nova introdução
2020/08/09: Sábado, 09 de Agosto 2020, 14:00
2018/04/24: Terça, 24 de Abril 2018, 12:30
2011/06/10: inicial