Os métodos de passagem de parâmetros pode ser entendido como a maneira pelas quais transmitimos um o valor ou um caminho de acesso de uma variável para um subprograma. Podemos destacar três métodos de passagem de parâmetros:
- Passagem por valor
- Passagem por referência
- Passagem por nome
Passagem por valor
O principal método de passagem de parâmetro utilizado na linguagem C é a passagem por valor. Neste método de passagem, o valor do parâmetro real é copiado para o parâmetro forma correspondente no subprograma. Este, age como uma variável local do subprograma.
A principal desvantagem deste modelo de passagem de parâmetro é o custo de armazenamento e transferência das informações dos parâmetros reais para os parâmetros formais.
Passagem por referência
Passagem por valor x Passagem por referência
A linguagem C consegue "emular" a passagem por referência utilizando ponteiros. Podemos alterar os valores das variáveis não-locais utilizando um ponteiro para essas variáveis e no subprograma utilizamos a operação de desreferenciamento do ponteiro.
void troca(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; }
Na linguagem C, os vetores são passados implicitamente como ponteiros. Esta escolha pode ser justificada para garantir a eficiência de subprograma que utilizam vetores.
int fun(int a, int v[5]){ a++; v[0]++; } int main(){ int a; int v[5]; a = 2; v[0] = 3; fun(a,v); printf("%d %d",a,v[0]); }
Neste exemplo, o valor esperado para este programa se fosse utilizado a passagem de parâmetro por valor seria 2 e 3. Mas o resultado obtido é 2 e 4.
A operação v[0] é substituída por *(v+0) que utiliza aritmética de ponteiros + a operação de desreferenciamento.
A linguagem C++ implementa o modelo de passagem por referência utilizando o tipo de referência.
void troca(int &a, int &b){ int temp; temp = a; a = b; b = temp; }
Passagem por referência x efeito colateral
Uma função ou expressão é dita ter um efeito colateral se além de produzir um valor, ela também modifica o estado do programa. Na presença de efeitos colaterais, o comportamento do programa depende da história, isto é, da ordem em que as expressões são avaliadas.
Observe que o valor de b depende da ordem que a expressão é avaliada. Se a avaliação for avaliada da esquerda para a direita, o valor da expressão 20. Se a expressão for avaliada da direita para a esquerda, o valor da expressão será 15.
Passagem de parâmetro por nome
Uma função ou expressão é dita ter um efeito colateral se além de produzir um valor, ela também modifica o estado do programa. Na presença de efeitos colaterais, o comportamento do programa depende da história, isto é, da ordem em que as expressões são avaliadas.
int f(int & a ){ a = 2*a; return a; } a = 5; b = f(a) + a;
Observe que o valor de b depende da ordem que a expressão é avaliada. Se a avaliação for avaliada da esquerda para a direita, o valor da expressão 20. Se a expressão for avaliada da direita para a esquerda, o valor da expressão será 15.
Passagem de parâmetro por nome
A passagem por nome é o nome dos parâmetros reais são substituídos textualmente pelos valores dos parâmetros formais.
Este método é bem mais lento que os métodos anteriores e pode confundir bastante o leitor do código.
A passagem de parâmetro por nome está bastante relacionada com substituição de macros. A substituição dos macros compartilha alguns dos problemas da passagem de parâmetro por nome.
Considere o seguinte exemplo:
#define mult(a,b) a*b mult(3+4,6)
O valor dessa expressão é 27. Quando o esperado é 42. Neste caso, a substituição do macro troca a expressão mult(3+4,6) por 3+4*6 que dá 27.
#define mult(a,b) ((a)*(b)) mult(3+4,6)
Agora, a substituição da macro não vai provocar o efeito indesejável.
Considere o seguinte exemplo:
6 a = 7 b = 3
Note que o valor de a é incrementado duas vezes, a substituição do macro gera uma expressão com dois efeitos colaterais. O duplo efeito colateral pode ser evitado dessa maneira:
Saída
3 a = 6 b = 3
Passagem por referência x Passagem por nome
Considere o seguinte exemplo:
Passagem por referência
Saída
valor = 2 lista = { 6, 3, 3, 1, 4}
Passagem por nome
Saída
valor = 2 lista = { 6, 3, 4, 1, 2}
Compilando o código acima com a flag gcc -E podemos interromper o processo de compilação após a expansão dos macros obtendo o seguinte código.
Considere o seguinte exemplo:
#define max(a,b) a>b?a:b int main(){ int a = 5; int b = 3; printf("%d a = %d b = %d\n", max(a++,b), a, b); }Saída
6 a = 7 b = 3
Note que o valor de a é incrementado duas vezes, a substituição do macro gera uma expressão com dois efeitos colaterais. O duplo efeito colateral pode ser evitado dessa maneira:
#define max(X, Y) \ ({ typeof (X) __x = (X), __y = (Y); \ (__x < __y) ? __x : __y; }) int main(){ int a = 5; int b = 3; printf("%d a = %d b = %d\n", max(a++,b), a, b); }
Saída
3 a = 6 b = 3
Passagem por referência x Passagem por nome
Considere o seguinte exemplo:
Passagem por referência
void troca(int & a, int & b){ int temp; temp = a; a = b; b = temp; } int main(){ int valor = 3, lista[5] = {4,6,3,1,2},i; troca(valor, lista[0]); troca(lista[0], lista[1]); troca(valor, lista[valor]); }
Saída
valor = 2 lista = { 6, 3, 3, 1, 4}
Passagem por nome
#define troca(X, Y) \ ({ typeof (X) temp; \ temp = X; X = Y; Y = temp; } ) int main(){ int valor = 3, lista[5] = {4,6,3,1,2},i; troca(valor, lista[0]); troca(lista[0], lista[1]); troca(valor, lista[valor]); }
Saída
valor = 2 lista = { 6, 3, 4, 1, 2}
Compilando o código acima com a flag gcc -E podemos interromper o processo de compilação após a expansão dos macros obtendo o seguinte código.
int main(){ int valor = 3, lista[5] = {4,6,3,1,2},i; ({ typeof (valor) temp; temp = valor; valor = lista[0]; lista[0] = temp; } ); ({ typeof (lista[0]) temp; temp = lista[0]; lista[0] = lista[1]; lista[1] = temp; } ); ({ typeof (valor) temp; temp = valor; valor = lista[valor]; lista[valor] = temp; } ); }
2 comentários:
Cara, não e mais simples você mostrar apenas como e feito a passagem por referencia em sequencia ?
no código você usou ponteiros e tipos de dados comuns, desse jeito prejudica né fera ;)
Oi Jorge,
A sua sugestão é mostrar apenas a passagem por referência e mostrar os casos em que a passagem de referência é realizada implicitamente?
Postar um comentário