Passagem de Parâmetros por Valor (uma breve revisão)
Passagem de Parâmetros por Referência (Ponteiros)
Ponteiros x Matrizes

Recapitulando....
Utilização de Parâmetros
O uso de parâmetros em uma função tem por finalidade efetuar uma comunicação entre
rotinas de programa. Muito útil quando se trabalha apenas com variáveis locais, que
são mais vantajosas por ocuparem um espaço menor em memória do que as variáveis globais,
pois as variáveis locais só ocupam espaço em memória enquanto estão sendo utilizadas.
Quando se trabalha com funções em linguagem C, é possível passar valores de uma
função para outra de duas formas: Passagem de parâmetros por valor e passagem de
parâmetros por referência.
Passagem de Parâmetros por Valor
Caracteriza-se por funcionar apenas como um mecanismo de entrada de valor para uma determinada função. Desta forma, o processamento é executado somente dentro da função chamada, ficando o resultado obtido "preso" dentro da função.
O exemplo abaixo calcula o fatorial, passando o valor da variável limite para a função.
#include "stdio.h"
#include "conio.h"
int fatorial(int);
void main()
{
int limite;
clrscr();
printf("\nQual fatorial: ");
scanf("%d", &limite);
//chamando a funcao/passagem de parametros por valor
int fat=fatorial(limite);
printf("\nO fatorial de %d ‚ = %d",limite,fat);
getche();
}
int fatorial(n)
{
int i,fat;
fat=1;
for(i=1;i<=n;i++)
fat=fat*i;
return(fat);
}
Por exemplo, se o usuário solicitar o fatorial de 4, a saída na tela será a seguinte:
O fatorial de 4 = 24
Observe, que a variável N da função fatorial recebe o valor fornecido pela variável limite. Este valor estabelece o número de vezes que o looping deve ser executado. O uso da instrução return na função fatorial envia o valor da variável fat para a função chamadora (main).
O comando return poderá ser escrito das seguintes formas:
return (); retorna o valor da expressão
return(); não retorna valor
return; não retorna valor
Passagem de Parâmetro por Referência
Caracteriza-se por funcionar como um mecanismo de entrada e saída de valor para uma determinada função. Desta forma, a alteração efetuada é devolvida para a rotina chamadora.
A linguagem C permite que em uma variável seja armazenado o endereço de uma outra variável. Para que isso seja possível, é necessário trabalhar com uma variável como sendo um ponteiro, um apontador. A declaração de um ponteiro ocorre de forma semelhante à de uma variável simples, tendo como diferencial o asterisco antes da variável.
Compreendendo a Chamada por Referência
Como você aprendeu, C passa parâmetros para as funções usando chamada por valor
por padrão. Usando a chamada por valor, as funções não podem modificar
o valor de uma variável passada para uma função. No entanto, na maioria
dos programas, suas funções modificarão as variáveis de um modo ou de
outro. Por exemplo, uma função que lê informações de um arquivo
precisa colocar as informações em uma matriz de strings de caracteres.
Da mesma forma, uma função tal como
strupr precisa converter as letras em uma string de caractere para
maiúsculas. Quando suas funções alteram o valor de um parâmetro, seus
programas precisam passar o parâmetro para a função usando chamada por referência. A diferença entre chamada por valor e
chamada por referência é que, usando a chamada por valor, as funções
recebem uma cópia do valor de um parâmetro. Por outro lado, com a
chamada por referência, as funções recebem o endereço de memória da
variável. Portanto, as funções podem alterar o valor armazenado na
posição de memória específica (em outras palavras, o valor da
variável); alterações essas que permanecem após a função terminar.
Para usar a chamada por referência, seu programa precisa usar ponteiros.
No entanto, pense em um ponteiro simplesmente como um endereço de
memória. Para atribuir o endereço de uma variável a um ponteiro, use o
operador de endereço de C (&). Para acessar posteriormente o valor na
posição de memória para a qual o ponteiro aponta, use o operador de
redireção (*).
Obtendo um endereço
Uma variável é essencialmente um nome atribuído a uma ou mais posições de memória.
Quando seu programa roda, cada variável reside em seu próprio endereço
de memória. Seu programa localiza as variáveis na memória usando o endereço
de memória da variável. Para determinar o endereço de uma
variável, use o operador de endereço para exibir o endereço (em
hexadecimal) das variáveis a, b
e c:
#include <stdio.h>
void main(void)
{
int a = 1, b = 2, c = 3;
printf(“O endereço de a é %x o valor de a é %d\n”, &a, a);
printf(“O endereço de b é %x o valor de b é %d\n”, &b, b);
printf(“O endereço de c é %x o valor de c é %d\n”, &c, c);
}
Quando você compilar e executar este programa, ele exibirá uma saída similar a
esta (os valores dos endereços reais serão diferentes):
O endereço de a é fff4 o valor de a é 1
O endereço de b é fff2 o valor de b é 2
O endereço de c é fff0 o valor de c é 3
Quando seus programas mais tarde passarem parâmetros para funções cujos
valores a função precisa alterar, seus programas passarão as variáveis
por referência (endereço de memória), usando o operador de endereço,
como mostrado aqui:
alguma_funçao (&a, &b, &c);
Usando um Endereço de Memória
Quando você passa um endereço para uma função, precisa dizer ao compilador C
que a função estará usando um ponteiro (o endereço de memória) de uma
variável, e não o valor da variável. Para fazer isso, você precisa
declarar uma variável ponteiro.
Declarar uma variável ponteiro é muito similar à declaração de uma
variável padrão, em que você especifica um tipo e o nome da variável.
A diferença, entretanto, é que um asterisco (*) precede os nomes das
variáveis ponteiro. As declarações a seguir criam variáveis ponteiro
do tipo int, float e char:
int *i_ponteiro;
float *f_ponteiro;
char *c_ponteiro;
Após você declarar uma variável ponteiro, precisa atribuir um endereço de
memória a ela. Por exemplo, o comando a seguir, atribui o endereço da
variável inteira a à variável ponteiro i_ponteiro:
i_ponteiro = &a;
Em seguida, para usar o valor apontado pela variável ponteiro, seus
programas precisam usar o operador de redireção de C – o asterisco
(*). Por exemplo, o comando a seguir atribui o valor 5 à variável a
(cujo endereço está contido na variável i_ponteiro):
*i_ponteiro = 5;
De um modo similar, o comando a seguir atribui à variável b
o valor ao qual a variável i_ponteiro aponta atualmente:
b = *i_ponteiro;
Quando você quiser usar o valor apontado por uma variável ponteiro, use o
operador de redireção (*). Quando quiser atribuir o endereço de uma
variável a uma variável ponteiro, use o operador de endereço (&). O
programa a seguir, ilustra o uso de uma variável ponteiro. O programa
atribui à variável ponteiro i_ponteiro o endereço da variável a. O
programa então usa a variável ponteiro para alterar, exibir e atribuir o
valor da variável:
#include "stdio.h"
void main(void)
{
int a=1, b=2;
int *i_ponteiro;
i_ponteiro = &a; // atribui um endereço
*i_ponteiro = 5; // altera o valor apontado por i_ponteiro para 5
// exibe valor
printf(“O valor apontado por i_ponteiro é %d a variável a é %d\n”,
*i_ponteiro, a);
b = *i_ponteiro; // atribui o valor
printf(“O valor de b é %d\n”, b);
printf(“Valor de i_ponteiro %x\n”, i_ponteiro);
}
Lembre-se de que um ponteiro não é nada mais que um endereço de memória. Seu
programa precisa atribuir o valor que o ponteiro (o endereço) contém. No
programa acima, o programa atribui ao ponteiro o endereço da variável a.
O programa poderia ter também atribuído o endereço da variável b.
Nota: Quando você usa ponteiros, precisa ainda ter em mente os tipos de valores,
tais como int, float e char. Seus programas somente devem atribuir o endereço de valores inteiros às variáveis
ponteiro, e assim por diante.
Alterando o Valor de um Parâmetro
Para alterar o valor de um parâmetro dentro de uma função, seus programas
precisam usar a chamada por referência, passando o endereço da
variável. Dentro da função, você precisa usar ponteiros. O programa a
seguir, usa ponteiros e endereços (chamada por referência) para exibir
e, depois, alterar os parâmetros que o programa passa para a função exibe_e_altera:
#include <stdio.h>
void exibe_e_altera(int *primeiro, int *segundo, int *terceiro)
{
printf(“Valores originais da função %d %d %d\n”, *primeiro, *segundo,
*terceiro);
*primeiro += 100;
*segundo += 100;
*terceiro += 100;
printf(“Valores finais da função %d %d %d\n”, *primeiro, *segundo,*terceiro);
}
void main(void)
{
int a = 1, b = 2, c = 3;
exibe_e_altera(&a, &b, &c);
printf(“Valores finais em main %d %d %d\n”, a, b, c);
}
Já vimos que, quando o programa chama a função, ele passa como parâmetros os endereços das variáveis a, b e c.
Dentro de exibe_e_altera, a função usa variáveis ponteiro e o operador de redireção para
alterar e exibir os valores dos parâmetros. Quando você compilar e
executar o programa acima sua tela exibirá a seguinte saída:
Valores originais da função 1 2 3
Valores finais da função 101 102 103
Valores finais em main 101 102 103
Alterando Somente Parâmetros Específicos
Como você aprendeu, suas funções podem modificar o valor de um parâmetro usando a chamada por referência.
Por exemplo, a dica anterior, apresentou a função exibe_e_altera,
que usou chamada por referência para alterar o valor de cada um de seus
parâmetros. No entanto, em muitos casos, suas funções podem alterar o
valor de um parâmetro e ao mesmo tempo deixar o valor de um segundo
parâmetro inalterado. Por exemplo, o programa a seguir, usa a função muda_primeiro para atribuir ao parâmetro
primeiro o valor do parâmetro segundo:
#include <stdio.h>
void muda_primeiro(int *primeiro, int segundo)
{
*primeiro = segundo; // Atribui o valor de segundo a primeiro
}
void main(void)
{
int a = 0, b = 5;
muda_primeiro(& a, b);
printf(“Valor de a %d valor de b %d\n”, a, b);
}
Como você pode ver, a função muda_primeiro usa a chamada por referência para alterar o valor do
parâmetro primeiro, e chamada por valor para o parâmetro segundo.
Quando seus programas usam ambas as técnicas – e eles usarão –,
você precisa ter em mente quando usar ponteiros e quando referenciar
diretamente a variável. Como regra, os parâmetros cujos valores você
quer alterar irão requerer chamada por referência. Para compreender
melhor o impacto da chamada por referência versus
chamada por valor, modifique a função muda_primeiro,como mostrado aqui:
void muda_primeiro(int *primeiro, int segundo)
{
*primeiro = segundo; // atribui o valor de segundo a primeiro
segundo = 100;
}
A seguir é apresentado o programa de cálculo do fatorial, mas agora usando passagem de parâmetro
por referência
#include "stdio.h"
#include "conio.h"
void fatorial(int *,int);
void main()
{
int limite;
int fat=1;
clrscr();
printf("\nQual fatorial: ");
scanf("%d", &limite);
//chamando a funcao/passagem de parametros por referencia
fatorial(&fat,limite);
printf("\nO fatorial de %d ‚ = %d",limite,fat);
getche();
}
void fatorial(int *fat2,int n)
{
int i;
for(i=1;i<=n;i++)
*fat2 =*fat2 *i;
}
Por exemplo, se o usuário solicitar o fatorial de 4, a saída na tela será a seguinte:
O fatorial de 4 = 24
Observe que o endereço da variável fat é passado, por referência, para a variável fat2 da função fatorial.
Com isso todas as alterações feitas em fat2, alterarão o valor da variável fat. Já o valor da variável
limite é passada por valor para a variável n da função fat. Qualuqer mudança na variável n, não irá refletir
na variável limite, pois elas pertencem a funções diferentes, as quais utilizam pilhas diferentes.
Um Exemplo com Vetor
Como demonstração, considere a leitura de 10 elementos do tipo inteiro em um
vetor e a colocação dos mesmos em ordem crescente.
#include "stdio.h"
#include "conio.h"
void ordena(int *, int *);
void main()
{
int vetor[10], i, j;
clrscr();
//entrada de dados
for(i=0;i<10;i++)
{
printf("Entre o %2d. elemento: ",i+1);
scanf("%d",&vetor[i]);
}
//ordenacao do vetor
for (i=0;i<=8;i++)
for(j=i+1;j<=9;j++)
ordena(&vetor[i], &vetor[j]);
//apresentacao do vetor ordenado
printf("\n");
for (i=0;i<=9;i++)
printf("Agora o %2do. elemento ‚: %d\n",i+1, vetor[i]);
getch();
}
void ordena(int *a, int *b)
{
int x;
if(*a > *b)
{
x= *a;
*a = *b;
*b = x;
}
}
Comprendendo Como C Trata Matrizes Como Ponteiros
O compilador C trata as matrizes como ponteiros. Por exemplo, quando
seu programa passa uma matriz para uma função, o compilador passa o endereço
inicial da matriz. O programa a seguir, exibe o endereço inicial de várias matrizes diferentes:
#include <stdio.h>
void main (void)
{
int conta[10];
float salarios[5];
printf("O endereço da matriz conta é %x\n", conta);
printf("O endereço da matriz salários é %x\n",salarios);
}
Quando você compilar e executar o programa, sua tela exibirá a seguinte saída:
O endereço da matriz conta é ffe2
O endereço da matriz salarios é ffce
Aplicando o Operador de Endreço & a uma Matriz
O compilador C trata uma matriz como um ponteiro para o primeiro elemento da matriz.
C usa o operador de endereço (&) para retornar o endereço de uma variável.
Se você aplicar o operador de endereço a uma matriz, C retornará o endereço inicial
da matriz. Portanto, aplicar o operador de endereço a uma matriz é redundante. O
programa a seguir, exibe o endereço inicial de uma matriz, seguido pelo ponteiro
que o operador de endereço de C retorna:
#include <stdio.h>
void main (void)
{
int conta[10];
float salarios[5];
printf("O endereço da matriz conta é %x &conta é %x\n",
conta, &conta);
printf("O endereço da matriz salários é %x &salários é %x\n",
salários, &salarios);
}
Quando você compilar e executar o programa, sua tela exibirá o seguinte resultado:
O endereço da matriz conta é ffe2 &conta é ffe2
O endereço da matriz salarios é ffce &salarios é ffce
Compreendendo a Aritmética de Ponteiros
Um ponteiro é um endereço que aponta para um valor de um determinado
tipo de memória. Nos termos mais simples possíveis, um ponteiro é um valor que aponta
para uma posição de memória especifica. Se você somar o valor 1 a um ponteiro, o
ponteiro apontará para a próxima posição de memória. Se você somar 5 ao valor de um
ponteiro, o ponteiro apontará para a posição de memória cinco posições adiante do
endereço atual. No entanto, a aritmética de ponteiro não é tão simples quanto você
pode imaginar. Por exemplo, assuma que um ponteiro contenha o endereço 1000. Se você
somasse 1 ao ponteiro, poderia esperar que o resultado fosse 1001. No entanto, o endereço
resultante depende do tipo de ponteiro. Por exemplo, se você somar 1 a uma variável
ponteiro do tipo char (que contém 1000), o endereço será 1001. Se você somar 1 a um
ponteiro do tipo int (que requer dois bytes na memória), o endereço resultante será
1002. Quando você efetuar aritmética de ponteiro, tenha em mente o tipo do ponteiro.
Além de somar valores aos ponteiros, seus programas poderão subtrair valores ou somar e
subtrair dois ponteiros.
Incrementando e Decrementando um Ponteiro
À medida que seus programas trabalhem com ponteiros, uma das
operações mais comuns que eles efetuarão será incrementar e decrementar o valor de um
ponteiro para apontar para a próxima posição ou para posição anterior na memória. O
programa a seguir, atribui o endereço inicial de uma matriz de valores inteiros ao
ponteiro iptr. O programa depois incrementa o valor do ponteiro para exibir os cinco
elementos que a matriz contém:
#include <stdio.h>
void main (void)
{
int valores[5] = {1, 2, 3, 4, 5};
int contador;
int *iptr;
iptr = valores;
for (contador = 0; contador<5; contador++)
{
printf("%d\n", *iptr);
iptr++;
}
}
Quando você compilar e executar o programa sua tela exibirá os
valores de 1 a 5. O programa inicialmente atribui o endereço inicial da matriz ao
ponteiro. Em seguida, o programa incrementa o ponteiro para apontar para cada elemento.
Combinando Uma Referência e Incremento de Ponteiro
Você usou o ponteiro iptr para exibir o conteúdo de uma matriz. Para
exibir o conteúdo da matriz, o ponteiro usou um laço for, como mostrado aqui:
for (contador = 0; contador<5; contador++)
{
printf("%d\n", *iptr);
iptr++;
}
Como você pode ver, o laço for acessa o valor do ponteiro em uma
linha, e, depois, incrementa o ponteiro na próxima linha. Você já aprendeu que pode
usar o operador de incremento de sufixo de C para usar o valor de uma variável, e,
depois, incrementar o valor.
O laço for a seguir usa o operador de incremento de sufixo para
referenciar o valor apontado pela variável ponteiro e depois incrementa o valor do
ponteiro, fazendo a mesma coisa que o exemplo acima, mas usando menos linhas de programação.:
for (contador = 0; contador<5; contador++)
printf("%d\n", *iptr);
|