Se C não suporta a passagem de uma variável por referência, por que isso funciona?
#include <stdio.h>
void f(int *j) {
(*j)++;
}
int main() {
int i = 20;
int *p = &i;
f(p);
printf("i = %d\n", i);
return 0;
}
Resultado:
$ gcc -std=c99 test.c
$ a.exe
i = 21
&
) antes de chamar a função e desreferê-la explicitamente (com*
) na função.f(&i);
isto é uma implementação de passagem por referência, que não existe puramente em C. passagem por referênciaRespostas:
Como você está passando o valor do ponteiro para o método e desreferenciando-o para obter o número inteiro apontado.
fonte
func
:func(&A);
Isso passaria um ponteiro para A para a função sem copiar nada. É passagem por valor, mas esse valor é uma referência; portanto, você está 'passando por referência' a variável A. Nenhuma cópia é necessária. É válido dizer que é passagem por referência.Isso não é passar por referência, é passar por valor, como outros declararam.
A regra é a seguinte:
Vamos tentar ver as diferenças entre os parâmetros escalar e ponteiro de uma função.
Variáveis escalares
Este pequeno programa mostra a passagem por valor usando uma variável escalar.
param
é chamado de parâmetro formal e,variable
na chamada de função, é chamado de parâmetro real. A nota incrementadaparam
na função não mudavariable
.O resultado é
Ilusão de passagem por referência
Mudamos um pouco o código.
param
é um ponteiro agora.O resultado é
Isso faz você acreditar que o parâmetro foi passado por referência. Não era. Foi passado por valor, o valor do parâmetro sendo um endereço. O valor do tipo int foi incrementado, e esse é o efeito colateral que nos faz pensar que era uma chamada de função de passagem por referência.
Ponteiros - passados por valor
Como podemos mostrar / provar esse fato? Bem, talvez possamos tentar o primeiro exemplo de variáveis escalares, mas, em vez de escalares, usamos endereços (ponteiros). Vamos ver se isso pode ajudar.
O resultado será que os dois endereços são iguais (não se preocupe com o valor exato).
Resultado de exemplo:
Na minha opinião, isso prova claramente que os ponteiros são passados por valor. Caso contrário,
ptr
seriaNULL
após a chamada da função.fonte
Fonte: www-cs-students.stanford.edu
fonte
Porque não há passagem por referência no código acima. O uso de ponteiros (como
void func(int* p)
) é passado por endereço. Isso é passado por referência em C ++ (não funcionará em C):fonte
Seu exemplo funciona porque você está passando o endereço da sua variável para uma função que manipula seu valor com o operador de desreferência .
Embora C não suporte tipos de dados de referência , você ainda pode simular a passagem por referência passando explicitamente os valores do ponteiro, como no seu exemplo.
O tipo de dados de referência C ++ é menos poderoso, mas considerado mais seguro que o tipo de ponteiro herdado de C. Este seria o seu exemplo, adaptado para usar referências C ++ :
fonte
Você está passando um ponteiro (local do endereço) por valor .
É como dizer "aqui é o lugar com os dados que eu quero que você atualize".
fonte
p é uma variável de ponteiro. Seu valor é o endereço de i. Quando você chama f, passa o valor de p, que é o endereço de i.
fonte
Nenhuma passagem por referência em C, mas p "refere-se" a i e você passa p por valor.
fonte
Em C, tudo passa por valor. O uso de ponteiros nos dá a ilusão de que estamos passando por referência porque o valor da variável muda. No entanto, se você imprimir o endereço da variável ponteiro, verá que ela não é afetada. Uma cópia do valor do endereço é passada para a função. Abaixo está um trecho que ilustra isso.
fonte
Porque você está passando um ponteiro (endereço de memória) para a variável p na função f. Em outras palavras, você está passando um ponteiro, não uma referência.
fonte
Resposta curta: Sim, C implementa passagem de parâmetro por referência usando ponteiros.
Ao implementar a passagem de parâmetros, os designers de linguagens de programação usam três estratégias diferentes (ou modelos semânticos): transferir dados para o subprograma, receber dados do subprograma ou fazer as duas coisas. Esses modelos são comumente conhecidos como modo in, out e inout, respectivamente.
Vários designers foram criados por designers de linguagem para implementar essas três estratégias de passagem de parâmetros elementares:
Passagem por valor (semântica no modo) Passagem por resultado (semântica no modo de saída) Passagem por valor (semântica no modo de entrada) Passagem por referência (semântica no modo de entrada) Passagem por nome (modo de entrada semântica)
Passagem por referência é a segunda técnica para a passagem de parâmetros no modo de entrada. Em vez de copiar dados entre a rotina principal e o subprograma, o sistema de tempo de execução envia um caminho de acesso direto aos dados para o subprograma. Nesta estratégia, o subprograma tem acesso direto aos dados, compartilhando efetivamente os dados com a rotina principal. A principal vantagem dessa técnica é que ela é absolutamente eficiente no tempo e no espaço, porque não há necessidade de duplicar o espaço e não há operações de cópia de dados.
A implementação de passagem de parâmetro em C: C implementa a semântica de passagem por valor e também de passagem por referência (modo inout) usando ponteiros como parâmetros. O ponteiro é enviado para o subprograma e nenhum dado real é copiado. No entanto, como um ponteiro é um caminho de acesso aos dados da rotina principal, o subprograma pode alterar os dados na rotina principal. C adotou esse método da ALGOL68.
Implementação de passagem de parâmetro no C ++: O C ++ também implementa a semântica de passagem por referência (modo inout) usando ponteiros e também usando um tipo especial de ponteiro, chamado tipo de referência. Os ponteiros do tipo de referência são desreferenciados implicitamente dentro do subprograma, mas sua semântica também é passada por referência.
Portanto, o principal conceito aqui é que a passagem por referência implementa um caminho de acesso aos dados em vez de copiá-los no subprograma. Os caminhos de acesso a dados podem ser ponteiros desreferenciados explicitamente ou ponteiros desreferenciados automaticamente (tipo de referência).
Para mais informações, consulte o livro Concepts of Programming Languages, de Robert Sebesta, 10a Ed., Capítulo 9.
fonte
Você não está passando um int por referência, está passando um ponteiro para um int por valor. Sintaxe diferente, mesmo significado.
fonte
void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }
comint main(){ int value=0; func(&value); printf("%i\n",value); return 0; }
, imprime 111 em vez de 500. Se você está passando por referência, deve imprimir 500. C não suporta passagem de parâmetros por referência.ptr = &newvalue
não será permitido. Independentemente da diferença, acho que você está apontando que "o mesmo significado" não é exatamente verdadeiro porque você também possui funcionalidade extra em C (a capacidade de reatribuir a "referência" em si).ptr=&newvalue
se fosse passado por referência. Em vez disso, escrevemosptr=newvalue
Aqui está um exemplo em C ++:void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }
O valor do parâmetro passado para func () se tornará500
.param = new Class()
dentro de uma função não terá efeito para o chamador se for passada por valor (ponteiro). Separam
for passado por referência, as alterações serão visíveis para o chamador.Em C, para passar por referência, você usa o endereço do operador
&
que deve ser usado em uma variável, mas no seu caso, desde que você usou a variável ponteirop
, não é necessário prefixá-la com o operador do endereço do endereço. Teria sido verdade se você utilizado&i
como parâmetro:f(&i)
.Você também pode adicionar isso à desreferencia
p
e ver como esse valor correspondei
:fonte
ponteiros e referências são dois thigngs diferentes.
Algumas coisas que eu não vi mencionadas.
Um ponteiro é o endereço de alguma coisa. Um ponteiro pode ser armazenado e copiado como qualquer outra variável. Assim, tem um tamanho.
Uma referência deve ser vista como um ALIAS de alguma coisa. Ele não tem tamanho e não pode ser armazenado. DEVE referenciar algo, ie. não pode ser nulo ou alterado. Bem, às vezes o compilador precisa armazenar a referência como um ponteiro, mas isso é um detalhe de implementação.
Com referências, você não tem problemas com ponteiros, como manipulação de propriedade, verificação nula, cancelamento de referência no uso.
fonte
'Passar por referência' (usando ponteiros) está em C desde o início. Por que você acha que não é?
fonte
j
( not*j
) inf()
não tem efeito emi
inmain()
.Eu acho que C de fato suporta passagem por referência.
A maioria das línguas exige que o açúcar sintático passe por referência em vez de valor. (C ++, por exemplo, requer & na declaração de parâmetro).
C também requer açúcar sintático para isso. É * na declaração do tipo de parâmetro e & no argumento. Então * e & é a sintaxe C para passar por referência.
Agora, pode-se argumentar que a passagem real por referência deve exigir apenas sintaxe na declaração do parâmetro, não no lado do argumento.
Mas agora vem C #, que faz apoio pela passagem de referência e requer açúcar sintático em ambos os parâmetros e argumentos lados.
O argumento de que C não tem passagem por referência faz com que os elementos sintáticos para expressá-lo exibam a implementação técnica subjacente não é um argumento, pois isso se aplica mais ou menos a todas as implementações.
O único argumento restante é que passar por ref em C não é um recurso monolítico, mas combina dois recursos existentes. (Pegue ref do argumento por &, espere ref digitar por *.) C #, por exemplo, requer dois elementos sintáticos, mas eles não podem ser usados um sem o outro.
Este é obviamente um argumento perigoso, porque muitos outros recursos em idiomas são compostos por outros recursos. (como suporte a cadeias de caracteres em C ++)
fonte
O que você está fazendo é passar por valor, não por referência. Como você está enviando o valor de uma variável 'p' para a função 'f' (em principal como f (p);)
O mesmo programa em C com passagem por referência será semelhante a (!!! este programa apresenta 2 erros, pois a passagem por referência não é suportada em C)
Resultado:-
fonte