5. Funções

5.1 Introdução.

Um programa em linguagem C pode ser composto por uma (função main()) ou mais funções, distribuídas por um ou mais ficheiros.

As funções são a única forma de se agruparem as instruções que se pretendem executar, constituindo assim uma unidade básica na programação em C.

A linguagem C está feita de modo a que se construam facilmente funções muito eficientes, de tal forma que podem ser utilizadas em diferentes programas sem alterações.

5.2 Definição.

Relembrando do primeiro capítulo, a forma geral da definição de uma função é :

classe_armazenamento tipo_dado nome(declarações_lista_de_parâmetros)
{
declarações das variáveis locais
instruções
}

Sempre que se pretenda que uma função retorne um valor, então deverá ser incluída a instrução return.

As variáveis da lista de parâmetros podem ser consideradas como variáveis locais, que são inicializadas com os valores dos argumentos da chamada da função.

O tipo de dado que precede o nome da função é o tipo do objecto que a função retorna. Por exemplo, a definição double raiz(double num) significa uma função que retorna um double.

Os tipos podem ser portanto os mesmos que os definidos para as variáveis.

Se o tipo de uma função não é declarado explicitamente, então o tipo retornado por defeito é int. E se algum dos parâmetros não tiver a declaração explicita do tipo, este é também assumido como int.

Uma função retorna automaticamente quando atinge o fim do corpo da função, a não ser que antes encontre a instrução return. A sintaxe da instrução return é :

return(expressão);

Os parêntises em torno da expressão são opcionais. O tipo da expressão deverá estar de acordo com o tipo da função. Se uma função retorna um não inteiro então qualquer rotina que a chame deve declarar o tipo do objecto que ela retorna. Eis a distinção entre :

- Definição da função -> Dá o código da função.
- Declaração da função -> Somente informa o compilador do tipo de objecto retornado.

Exemplo : Neste exemplo é calculada a raiz quadrada de um número, pelo método de Newton. Em main() é declarada a função raiz(), para que o compilador saiba qual o tipo de dado que ela retorna.

  /* program raiz.c */
  main()
  {
    float a,r,raiz(); /* Declaração da função raiz() */
    printf (\n Introduza um número:);
    scanf(%f,&a);
    r=raiz(a);
    printf (\nA raiz quadrada de %f é %f,a,r);
  }
  /* Função Raíz Quadrada através da iteracção do método de Newton. */
  float raiz(float x) /* Definição da função 'raiz' que retorna um float . */
  {
    float y,z
    y=x;
    do
    {
      z=y;
      y=(z+x/z)/2;
    }
    while (z!=y);
  return (y);
  }

No contexto das funções existem dois conceitos importantes que por vezes dão origem a alguma confusão:

- Argumentos - Lista de variáveis, constantes ou expressões colocados entre os parêntises da função aquando da chamada desta. No exemplo anterior, na chamada raiz(a), a é o argumento.
- Parâmetros - Lista de declarações de variáveis aquando da definição da função. No exemplo anterior a lista de parâmetros da função raiz(float x) é apenas float x.

Muitas funções, não retornam qualquer valor. A forma de o declarar explicitamente é usando o tipo de dado void. Uma declaração deste tipo, pode ser :

  void inverte(char str[]) /* Função que não retorna qualquer valor */
  {
    ...
  }

Quanto à classe de armazenamento de uma função pode ser :

- static, significando que a função só é visível no ficheiro onde está definida.
- extern, significando que a função já foi definida em outro local ou ficheiro
- não existir classe de armazenamento tratando-se portanto de uma função global.

5.3 Parâmetros de uma Função

Em C, os argumentos para as funções são passados via chamada por valor, ao passo que em Pascal e Fortran são chamados via chamada por referência.

A passagem por valor significa que a uma função (mais concretamente aos seus parâmetros ), são apenas passados os valores dos argumentos, enquanto na chamada por referência é como se fossem passadas as próprias variáveis.

Em Pascal, duas variáveis podem ser trocadas através da chamada ao seguinte procedimento:

  procedure troca(var x,y:integer)
  var
  temp:integer
  begin
    temp:=x
    x:=y;
    y:=temp;
  end

O equivalente em Fortran,seria :

  SUBROUTINE TROCA(X,Y)
  INTEGER X,Y,TEMP
  TEMP=X
  X=Y
  RETURN
  END

O equivalente em C, seria ineficaz:

  troca(x,y)
  int x,y
  {
    int temp;
    temp=x;
    x=y;
    y=temp;
  }

Esta rotina troca unicamente os valores das variáveis da função na stack, e deixa os valores originais da rotina chamadora inalteráveis. Supondo que troca() era invocada do seguinte modo:

  int a=1,b=2;
  troca(a,b);

seriam trocados os valores de x e y da função mas manter-se-iam inálteráveis os de a e b.
Para que na função se pudessem alterar os valores das variáveis da função chamadora, haveria que passar os endereços dessas variáveis, e os parâmetros da função teriam que ser apontadores. Então a função troca() seria invocada do seguinte modo:

  int a=1,b=2;
  troca(&a,&b);

e teria o seguinte aspecto, com as variáveis X e Y definidas como apontadores para inteiros (ou seja, variáveis que guardam endereços de posições de memória onde estão armazenados inteiros):

  troca (int *x, int*y)
  {
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
  }

Quer isto dizer que X e Y receberam os endereços de a e b respectivamente, e que através do operador de conteúdo de um endereço (*), podem aceder aos conteúdos desses endereços.

5.4 Conversão de Tipos.

O valor de uma expressão numa instrução return é convertido para o tipo da função através das regras de conversão de tipos de dados. Com efeito, um cast para o tipo apropriado é inserido automaticamente em qualquer instrução return. Por exemplo, a função atoi() (que converte uma string num inteiro) pode ser escrita em termos duma rotina de biblioteca atof() (a qual converte uma string para um número de vírgula flutuante) como se segue :

  atoi(char s[])
  {
    double atof();
    return atof(s);
  }

O valor produzido pela função atof() é automaticamente convertido para inteiro na instrução return, em virtude da função atoi() retornar um inteiro.

As únicas conversões de tipo efectuadas durante a avaliação de expressões numa lista de argumentos são as conversões de tipo automático: Assim, nas outras situações há que forçar a conversão, como no exemplo seguinte, em que a função log() espera um argumento do tipo double:

  int n;
  double x,log();
  x=log((double)n);

e em que foi feito o cast da variável n de inteiro para um double.

5.5 Argumentos da Linha de Comandos

É possível em C passar valores para o programa através da linha de comandos. Estes valores são passados à função main(). Nesta função usam-se habitualmente 0 ou 2 parâmetros formais. Esses dois parâmetros formais da main() estão relacionados com os argumentos de linha de comando que são dados na chamada do programa. A maior parte dos programadores escolhe os nomes argc e argv para estes dois parâmetros:

  main( int argc, char argv[])
  {
    ...
  }

O primeiro parâmetro argc é o número de argumentos da linha de comando (isto é, palavras na linha de comando separadas por espaços brancos).

O segundo parâmetro argv é um array de strings (cada string é um array de caracteres) representando os argumentos da linha de comando.

Como o nome do próprio programa que é executado está em argv[0], então o argc é sempre no mínimo 1. O primeiro argumento verdadeiro está em argv[1] e o último está em argv[argc-1].

argv[argc] possui normalmente um valor para distinção (por exemplo NULL).

Compreender-se-á melhor esta secção após a leitura do capítulo 6 sobre vectores.

5.6 Conclusão

Como conclusão, recapitulam-se em seguida alguns aspectos mais importantes sobre funções:

- Permitem dividir o programa em partes funcionais mais pequenas.
- Podem residir em bibliotecas e/ou noutros ficheiros fonte.
- A comunicação do exterior para a função é feita através dos argumentos.
- A comunicação da função para o exterior é feita através de um valor de retorno, sendo este do mesmo tipo da função.
- Todas as funções têm um determinado tipo associado.
- A definição da função pode ser feita em qualquer ponto.
- A função pode invocar-se a si própria (recursividade).
- Uma função cujo tipo é antecedido por static só é visível no ficheiro onde está definida.

Back | Up | Forward

To front page | Pages by Nuno Nunes. |