Programação Imperativa

Aula 19

Continuação da matéria de apontadores. Passagem de parâmetros em funções.


Para que servem os apontadores?

Em C, existe uma série de coisas que só se consegue fazer manipulando apontadores. Uma delas é a modificação dos valores dos argumentos durante a execução de uma função. Outra utilidade dos apontadores é o permitir a alocação dinâmica de memória. A alocação dinâmica de memória permite entre outras coisas criar arrays de dimensão variável sem ter de especificar previamente o tamanho máximo do array.

Nesta aula iremos ver a utilidade dos apontadores no que diz respeito à passagem de parâmetros em funções. A alocação dinâmica de memória fica para a próxima aula.

Passagem de parâmetros em funções

Imaginemos que queríamos fazer uma função para trocar o valor de 2 variáveis. Aqui vai uma tentativa:

    void troca( int x, int y )
    {
      int temp; 

      temp = x;
      x = y;
      y = temp;
    }

O programa principal poderia ficar qualquer coisa deste estilo:

    main()
    {
      int a,b;

      a = 3;
      b = 7;
      troca( a, b );
      printf("%d %d", a, b );
    }

O objectivo seria trocar os valores das variáveis. Como tal, o programa deveria escrever no ecrã:

    7 3

mas se executarmos o programa, o computador não irá trocar coisa nenhuma e irá escrever:

    3 7

O problema acontece porque aquilo que é passado quando a função troca é chamada são cópias dos valores dos argumentos a e b. Isto é, x vai receber uma cópia do valor de a (3 no exemplo) e y vai receber uma cópia do valor de b (7 no exemplo). Internamente na função, os valores de x e y vão ser trocados mas os valores de a e b manter-se-ão inalterados (repara que isso aconteceria mesmo que na função tivéssemos chamado x e y em vez de a e b, já que nesse caso, o x da função não teria nada a ver com o x que estava declarado fora da função).

Uma maneira de alterar o valor de variáveis dentro das funções é utilizando variáveis globais (o que torna os programas menos genéricos); outra é passando o endereço das variáveis e depois modificar o seu valor através do operador *. Exemplo:

    void troca( int *x, int *y )
    {
      int temp; 

      temp = *x;
      *x = *y;
      *y = temp;
    }

    main()
    {
      int a,b;

      a = 3;
      b = 7;
      troca( &a, &b );
      printf("%d %d", a, b );
    }

Desta vez, a função troca vai trocar os valores que são apontados por x e y. No exemplo concreto, o programa irá trocar o valor das variáveis a e b. Se não perceberem isto, inventem endereços para as variáveis a e b e façam uns bonecos tal e qual como na aula anterior.

Acerca do velho amigo scanf

Quando vos ensinei a instrução scanf disse-vos que tinham de colocar sempre o sinal & antes da variável. Nunca vos expliquei porque é que isso era preciso, mas agora já devem saber.

    int n;
  
    scanf("%d", &n);

O que acontece é que o scanf é uma função que pretende modificar o valor da variável n (n vai passar a ser o valor que o utilizador introduzir), e a única maneira de o fazer é passando o endereço da variável n.

Porque não usar variáveis globais?

Uma variável definida globalmente pode ser modificada dentro de uma função sem ter de ser passada como argumento. À primeira vista, esta estratégia pode parecer mais simples do que estar a manipular apontadores. No entanto, o uso de variáveis globais deve ser evitado sempre que possível porque as funções que usam variáveis globais deixam de ser genéricas. Uma função deve resolver um sub-problema específico e deve ser feita de tal modo que possa ser reutilizada noutro contexto. Se uma função depende de variáveis globais, não podemos pegar nela e reutilizá-la noutro programa.

Moral da história ...