Qual é a diferença entre
- um parâmetro passado por referência
- um parâmetro passado por valor?
Você poderia me dar alguns exemplos, por favor?
language-agnostic
pass-by-reference
pass-by-value
Desduplicador
fonte
fonte
Respostas:
Em primeiro lugar, a distinção "passar por valor vs. passar por referência", conforme definida na teoria da CS, está agora obsoleta porque a técnica originalmente definida como "passe por referência" caiu em desuso e raramente é usada agora. 1 1
Os idiomas mais recentes 2 tendem a usar um par de técnicas diferente (mas semelhante) para obter os mesmos efeitos (veja abaixo), que é a principal fonte de confusão.
Uma fonte secundária de confusão é o fato de que em "passagem por referência", "referência" tem um significado mais restrito que o termo geral "referência" (porque a frase é anterior a ela).
Agora, a definição autêntica é:
Quando um parâmetro é passado por referência , o chamador e o destinatário usam a mesma variável para o parâmetro. Se o chamado modifica a variável de parâmetro, o efeito é visível para a variável do chamador.
Quando um parâmetro é passado por valor , o responsável pela chamada e o chamado têm duas variáveis independentes com o mesmo valor. Se o chamado modifica a variável de parâmetro, o efeito não é visível para o chamador.
Os pontos a serem observados nesta definição são:
"Variável" aqui significa a própria variável (local ou global) do chamador - ou seja, se eu passar uma variável local por referência e atribuir a ela, mudarei a própria variável do chamador, não o que quer que esteja apontando se for um ponteiro .
O significado de "referência" em "passagem por referência" . A diferença com o termo geral "referência" é que essa "referência" é temporária e implícita. O que o receptor basicamente recebe é uma "variável" que é de alguma forma "a mesma" que a original. A especificidade desse efeito é irrelevante (por exemplo, a linguagem também pode expor alguns detalhes de implementação - endereços, ponteiros, desreferenciamento - tudo isso é irrelevante; se o efeito líquido é esse, é passagem por referência).
Agora, nas linguagens modernas, as variáveis tendem a ser de "tipos de referência" (outro conceito inventado depois de "passar por referência" e inspirado por ele), ou seja, os dados reais do objeto são armazenados separadamente em algum lugar (geralmente, na pilha), e somente "referências" a ele são mantidas em variáveis e passadas como parâmetros. 3
Passar essa referência se enquadra na passagem por valor, porque o valor de uma variável é tecnicamente a própria referência, não o objeto referido. No entanto, o efeito líquido no programa pode ser o mesmo que passar por valor ou passar por referência:
Como você pode ver, esse par de técnicas é quase o mesmo que o da definição, apenas com um nível de indireção: basta substituir "variável" por "objeto referenciado".
Não existe um nome definido para eles, o que leva a explicações distorcidas como "chamar por valor onde o valor é uma referência". Em 1975, Barbara Liskov sugeriu o termo " compartilhamento de chamada por objeto " (ou, às vezes, apenas "chamada por compartilhamento"), embora nunca tenha entendido bem. Além disso, nenhuma dessas frases é paralela ao par original. Não é de admirar que os termos antigos acabassem sendo reutilizados na ausência de algo melhor, levando à confusão. 4
NOTA : Por um longo tempo, esta resposta costumava dizer:
Isso é mais correto, exceto pelo significado mais restrito de "referência" - sendo temporário e implícito (não é necessário, mas ser explícito e / ou persistente são recursos adicionais, não faz parte da semântica de passagem por referência). , como explicado acima). Uma analogia mais próxima seria fornecer uma cópia de um documento versus convidá-lo a trabalhar no original.
1 A menos que você esteja programando no Fortran ou no Visual Basic, esse não é o comportamento padrão e, na maioria dos idiomas em uso moderno, a verdadeira chamada por referência não é possível.
2 Uma boa quantidade de idosos também apóia
3 Em vários idiomas modernos, todos os tipos são tipos de referência. Essa abordagem foi pioneira na linguagem CLU em 1975 e desde então foi adotada por muitas outras linguagens, incluindo Python e Ruby. E muitas outras linguagens usam uma abordagem híbrida, onde alguns tipos são "tipos de valor" e outros são "tipos de referência" - entre eles C #, Java e JavaScript.
4 Não há nada ruim em reciclar um termo antigo adequado por si só, mas é preciso, de alguma forma, deixar claro qual significado é usado a cada vez. Não fazer isso é exatamente o que continua causando confusão.
fonte
É uma maneira de passar argumentos para funções. Passar por referência significa que o parâmetro das funções chamadas será o mesmo que o argumento passado pelos chamadores (não o valor, mas a identidade - a própria variável). Passar por valor significa que o parâmetro das funções chamadas será uma cópia do argumento passado pelos chamadores. O valor será o mesmo, mas a identidade - a variável - é diferente. Assim, as alterações em um parâmetro feito pela função chamada em um caso alteram o argumento passado e, no outro caso, apenas alteram o valor do parâmetro na função chamada (que é apenas uma cópia). Depressa:
ref
usada no chamador e na função chamada). Jon Skeet também tem uma boa explicação sobre isso aqui .Códigos
Como minha linguagem é C ++, usarei isso aqui
E um exemplo em Java não fará mal:
Wikipedia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Esse cara praticamente diz:
http://javadude.com/articles/passbyvalue.htm
fonte
Muitas respostas aqui (e, em particular, a resposta mais votada) são factualmente incorretas, pois entendem mal o que "chamada por referência" realmente significa. Aqui está minha tentativa de esclarecer as coisas.
TL; DR
Em termos mais simples:
Em termos metafóricos:
O que "chamar por valor" e "chamar por referência" não significa
Observe que esses dois conceitos são completamente independentes e ortogonais do conceito de tipos de referência (que em Java são todos os tipos que são subtipos de
Object
e em C # todos osclass
tipos) ou do conceito de tipos de ponteiro, como em C (que são semanticamente equivalentes aos "tipos de referência" do Java, simplesmente com sintaxe diferente).A noção de tipo de referência corresponde a um URL: é ao mesmo tempo uma informação e é uma referência (um ponteiro , se você desejar) a outras informações. Você pode ter muitas cópias de um URL em locais diferentes, e eles não mudam para qual site todos eles vinculam; se o site for atualizado, todas as cópias de URL ainda levarão às informações atualizadas. Por outro lado, alterar o URL em qualquer lugar não afetará nenhuma outra cópia escrita do URL.
Observe que o C ++ possui uma noção de "referências" (por exemplo
int&
) que não é como os "tipos de referência" de Java e C #, mas é como "chamada por referência". Os "tipos de referência" de Java e C # e todos os tipos em Python são como o que C e C ++ chamam de "tipos de ponteiros" (por exemploint*
).OK, aqui está a explicação mais longa e formal.
Terminologia
Para começar, quero destacar alguns trechos importantes da terminologia, para ajudar a esclarecer minha resposta e garantir que todos nós estamos nos referindo às mesmas idéias quando usamos palavras. (Na prática, acredito que a grande maioria da confusão sobre tópicos como esses decorre do uso de palavras de maneira a não comunicar completamente o significado pretendido.)
Para começar, aqui está um exemplo em alguma linguagem semelhante a C de uma declaração de função:
E aqui está um exemplo de chamar esta função:
Usando este exemplo, quero definir alguns bits importantes da terminologia:
foo
é uma função declarada na linha 1 (Java insiste em fazer todos os métodos de funções, mas o conceito é o mesmo sem perda de generalidade; C e C ++ fazem uma distinção entre declaração e definição que não abordarei aqui)param
é um parâmetro formal parafoo
, também declarado na linha 1arg
é uma variável , especificamente uma variável local da funçãobar
, declarada e inicializada na linha 2arg
também é um argumento para uma chamada específica dafoo
linha 3Existem dois conjuntos de conceitos muito importantes para distinguir aqui. O primeiro é o valor versus a variável :
bar
função acima, a seguir à linhaint arg = 1;
, a expressãoarg
tem o valor1
.final
ou C #readonly
) ou profundamente imutável (por exemplo, usando C ++const
).O outro par importante de conceitos a distinguir é parâmetro versus argumento :
Chamada por valor
Na chamada por valor , os parâmetros formais da função são variáveis recém-criadas para a chamada da função e inicializadas com os valores de seus argumentos.
Isso funciona exatamente da mesma maneira que qualquer outro tipo de variável é inicializado com valores. Por exemplo:
Aqui
arg
eanother_variable
são variáveis completamente independentes - seus valores podem mudar independentemente um do outro. No entanto, no ponto em queanother_variable
é declarado, é inicializado para manter o mesmo valor quearg
mantém - o que é1
.Como são variáveis independentes, as alterações
another_variable
não afetamarg
:É exatamente o mesmo que a relação entre
arg
eparam
no nosso exemplo acima, que repetirei aqui por simetria:É exatamente como se tivéssemos escrito o código desta maneira:
Ou seja, a característica definidora do que chamada por valor significa é que o chamado (
foo
neste caso) recebe valores como argumentos, mas possui suas próprias variáveis separadas para esses valores das variáveis do chamador (bar
neste caso).Voltando à minha metáfora acima, se eu sou
bar
e vocêfoo
, quando eu ligo para você, entrego a você um pedaço de papel com um valor escrito. Você chama esse pedaço de papelparam
. Esse valor é uma cópia do valor que escrevi no meu notebook (minhas variáveis locais), em uma variável que eu chamoarg
.(Como um aparte: dependendo do hardware e do sistema operacional, existem várias convenções de chamada sobre como você chama uma função de outra. A convenção de chamada é como se decidíssemos se eu escreveria o valor em um pedaço do meu papel e o entregasse a você , ou se você tem um pedaço de papel em que eu o escrevo, ou se eu o escrevo na parede à nossa frente. Esse também é um assunto interessante, mas muito além do escopo desta resposta já longa.)
Chamada por referência
Na chamada por referência , os parâmetros formais da função são simplesmente novos nomes para as mesmas variáveis que o chamador fornece como argumentos.
Voltando ao nosso exemplo acima, é equivalente a:
Como
param
é apenas outro nome paraarg
- ou seja, eles são a mesma variável , as alterações nas quaisparam
são refletidasarg
. Essa é a maneira fundamental pela qual a chamada por referência difere da chamada por valor.Pouquíssimos idiomas suportam chamadas por referência, mas o C ++ pode fazer o seguinte:
Neste caso,
param
não apenas tem o mesmo valor comoarg
, ele realmente éarg
(apenas por um nome diferente) e assimbar
pode-se observar quearg
foi incrementado.Observe que não é assim que funciona Java, JavaScript, C, Objective-C, Python ou quase qualquer outra linguagem popular. Isso significa que esses idiomas não são chamados por referência, eles são chamados por valor.
Adendo: chamada por compartilhamento de objeto
Se o que você tem é chamado por valor , mas o valor real é um tipo de referência ou ponteiro , o "valor" em si não é muito interessante (por exemplo, em C, é apenas um número inteiro de um tamanho específico da plataforma) - o que é interessante é o que esse valor aponta .
Se o que esse tipo de referência (ponteiro) aponta para é mutável , é possível um efeito interessante: você pode modificar o valor apontado e o chamador pode observar alterações no valor apontado, mesmo que o chamador não possa observar muda para o ponteiro em si.
Para emprestar a analogia do URL novamente, o fato de eu ter lhe dado uma cópia do URL para um site não é particularmente interessante se a única coisa com a qual nos preocupamos é o site, não o URL. O fato de você rabiscar sua cópia da URL não afeta minha cópia da URL não é algo que nos interessa (e, de fato, em linguagens como Java e Python, a "URL" ou o valor do tipo de referência pode não pode ser modificado, apenas a coisa apontada por ele pode).
Barbara Liskov, quando inventou a linguagem de programação CLU (que possuía essa semântica), percebeu que os termos existentes "chamada por valor" e "chamada por referência" não eram particularmente úteis para descrever a semântica dessa nova linguagem. Então, ela inventou um novo termo: chamada por compartilhamento de objetos .
Ao discutir linguagens que são tecnicamente chamadas por valor, mas onde tipos comuns em uso são tipos de referência ou ponteiro (ou seja: quase todas as linguagens de programação imperativas, orientadas a objetos ou com vários paradigmas modernos), acho muito menos confuso para simplesmente evite falar sobre chamada por valor ou chamada por referência . Atenha-se à chamada pelo compartilhamento de objetos (ou simplesmente chamada por objeto ) e ninguém ficará confuso. :-)
fonte
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Antes de entender os 2 termos, você DEVE entender o seguinte. Todo objeto tem duas coisas que podem fazer com que ele se destaque.
Então se você diz
employee.name = "John"
sabe que existem 2 coisas sobre
name
. Seu valor que é"John"
e também a sua localização na memória que é um número hexadecimal talvez assim:0x7fd5d258dd00
.Dependendo da arquitetura da linguagem ou do tipo (classe, estrutura, etc.) do seu objeto, você estaria transferindo
"John"
ou0x7fd5d258dd00
Passar
"John"
é conhecido como passar por valor. Passagem0x7fd5d258dd00
é conhecida como passagem por referência. Qualquer pessoa que esteja apontando para esse local de memória terá acesso ao valor de"John"
.Para saber mais sobre isso, recomendo que você leia sobre o cancelamento de referência de um ponteiro e também por que escolher struct (tipo de valor) sobre a classe (tipo de referência)
fonte
Aqui está um exemplo:
fonte
y
já foi definido como 2 pela linha anterior. Por que voltaria a 0?A maneira mais simples de fazer isso é em um arquivo do Excel. Digamos, por exemplo, que você tenha dois números, 5 e 2 nas células A1 e B1 em conformidade, e deseja encontrar a soma deles na terceira célula, digamos A2. Você pode fazer isso de duas maneiras.
Quer por passagem seus valores para a célula A2 por tipagem = 5 + 2 para essa célula. Nesse caso, se os valores das células A1 ou B1 forem alterados, a soma em A2 permanecerá a mesma.
Ou passando as "referências" das células A1 e B1 para a célula A2 , digitando = A1 + B1 . Nesse caso, se os valores das células A1 ou B1 forem alterados, a soma em A2 também será alterada.
fonte
Ao passar por ref, você está basicamente passando um ponteiro para a variável. Passe por valor, você está passando uma cópia da variável. No uso básico, isso normalmente significa que as alterações passadas por ref na variável serão vistas como o método de chamada e passadas pelo valor que não costumam ser.
fonte
Passagem por valor envia uma CÓPIA dos dados armazenados na variável especificada, passagem por referência envia um link direto para a própria variável. Portanto, se você passar uma variável por referência e depois alterar a variável dentro do bloco em que a passou, a variável original será alterada. Se você simplesmente passar por valor, a variável original não poderá ser alterada pelo bloco em que você a passou, mas você receberá uma cópia do que quer que ela contenha no momento da chamada.
fonte
Passagem por valor - A função copia a variável e trabalha com uma cópia (para que não mude nada na variável original)
Passar por referência - A função usa a variável original; se você alterar a variável na outra função, ela também muda na variável original.
Exemplo (copie e use / tente você mesmo e veja):
Mantenha as coisas simples, espreitadelas. Paredes de texto podem ser um mau hábito.
fonte
Uma grande diferença entre eles é que as variáveis do tipo valor armazenam valores, portanto, especificar uma variável do tipo valor em uma chamada de método passa uma cópia do valor dessa variável para o método. As variáveis do tipo de referência armazenam referências a objetos, portanto, especificar uma variável do tipo de referência como argumento passa ao método uma cópia da referência real que se refere ao objeto. Embora a própria referência seja passada por valor, o método ainda pode usar a referência que recebe para interagir com - e possivelmente modificar - o objeto original. Da mesma forma, ao retornar informações de um método por meio de uma instrução de retorno, o método retorna uma cópia do valor armazenado em uma variável do tipo valor ou uma cópia da referência armazenada em uma variável do tipo referência. Quando uma referência é retornada, o método de chamada pode usar essa referência para interagir com o objeto referenciado. Assim,
Em c #, para passar uma variável por referência para que o método chamado possa modificar a variável, o C # fornece as palavras-chave ref e out. A aplicação da palavra-chave ref a uma declaração de parâmetro permite passar uma variável a um método por referência - o método chamado poderá modificar a variável original no chamador. A palavra-chave ref é usada para variáveis que já foram inicializadas no método de chamada. Normalmente, quando uma chamada de método contém uma variável não inicializada como argumento, o compilador gera um erro. Preceder um parâmetro com a palavra-chave out cria um parâmetro de saída. Isso indica ao compilador que o argumento será passado para o método chamado por referência e que o método chamado atribuirá um valor à variável original no chamador. Se o método não atribuir um valor ao parâmetro de saída em todos os caminhos possíveis de execução, o compilador gerará um erro. Isso também impede que o compilador gere uma mensagem de erro para uma variável não inicializada que é passada como argumento para um método. Um método pode retornar apenas um valor ao seu chamador por meio de uma declaração de retorno, mas pode retornar muitos valores especificando vários parâmetros de saída (ref e / ou out).
veja discussão e exemplos do c # aqui link text
fonte
Exemplos:
const &
geralmente é melhor. Você não incorre na penalidade de construção e destruição. Se a referência não for const, sua interface sugere que alterará os dados passados.fonte
Em resumo, Passado por valor é O QUE É e passado por referência é ONDE está.
Se o seu valor for VAR1 = "Cara Feliz!", Você verá apenas "Cara Feliz!". Se o VAR1 mudar para "Happy Gal!", Você não saberá disso. Se for aprovado por referência e o VAR1 for alterado, você o fará.
fonte
Se você não quiser alterar o valor da variável original depois de passá-la para uma função, a função deve ser construída com um parâmetro " passar por valor ".
Então a função terá APENAS o valor, mas não o endereço da variável passada na. Sem o endereço da variável, o código dentro da função não pode alterar o valor da variável como visto de fora da função.
Mas se você deseja dar à função a capacidade de alterar o valor da variável como vista de fora, é necessário usar o passe por referência . Como o valor e o endereço (referência) são passados e disponibilizados dentro da função.
fonte
passar por valor significa como passar valor para uma função usando argumentos. na passagem por valor, copiamos os dados armazenados na variável especificada e é mais lenta que a passagem por referência, porque os dados são copiados. Se fizermos alterações nos dados copiados, os dados originais não serão afetados. e em passagem por referência ou passagem por endereço, enviamos link direto para a própria variável. ou passando o ponteiro para uma variável. é mais rápido porque menos tempo é consumido
fonte
Aqui está um exemplo que demonstra as diferenças entre a passagem por valor - valor do ponteiro - referência :
O método "passando por referência" tem uma limitação importante . Se um parâmetro é declarado como passado por referência (portanto, é precedido pelo sinal &), o parâmetro atual correspondente deve ser uma variável .
Um parâmetro real referente ao parâmetro formal "passado por valor" pode ser uma expressão em geral, portanto, é permitido usar não apenas uma variável, mas também o resultado de uma invocação literal ou mesmo de uma função.
A função não pode colocar um valor em algo que não seja uma variável. Ele não pode atribuir um novo valor a um literal ou forçar uma expressão a alterar seu resultado.
PS: Você também pode verificar a resposta de Dylan Beattie no tópico atual que explica em palavras simples.
fonte