Qual é a diferença entre passar por referência e passar por valor?


Respostas:


1079

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 .

    • Agora isso é considerado uma má prática (como uma dependência implícita). Como tal, praticamente todos os idiomas mais novos são exclusivamente, ou quase exclusivamente, passados ​​por valor. A passagem por referência agora é usada principalmente na forma de "argumentos de saída / entrada" em idiomas em que uma função não pode retornar mais de um valor.
  • 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:

  • Se uma referência é apenas tirado de variável de um chamador e passado como um argumento, isso tem o mesmo efeito que a passagem por referência: se o objeto a que se refere é mutado no receptor, o chamador vai ver a mudança.
    • No entanto, se uma variável que contém essa referência for reatribuída, ela irá parar de apontar para esse objeto, portanto, quaisquer operações adicionais nessa variável afetarão o que ela está apontando agora.
  • Para ter o mesmo efeito que a passagem por valor, uma cópia do objeto é feita em algum momento. As opções incluem:
    • O chamador pode apenas fazer uma cópia privada antes da chamada e fornecer ao destinatário uma referência a ela.
    • Em algumas linguagens, alguns tipos de objetos são "imutáveis": qualquer operação que pareça alterar o valor cria um objeto completamente novo sem afetar o original. Portanto, passar um objeto desse tipo como argumento sempre tem o efeito de passar por valor: uma cópia para o chamado será feita automaticamente se e quando precisar de uma alteração, e o objeto do chamador nunca será afetado.
      • Nas linguagens funcionais, todos os objetos são imutáveis.

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:

Digamos que eu queira compartilhar uma página da web com você. Se eu lhe disser o URL, estou passando por referência. Você pode usar esse URL para ver a mesma página da web que posso ver. Se essa página for alterada, ambos veremos as alterações. Se você excluir o URL, tudo o que você está fazendo é destruir sua referência a essa página - você não está excluindo a própria página.

Se eu imprimir a página e fornecer a impressão, estou passando por valor. Sua página é uma cópia desconectada do original. Você não verá nenhuma alteração subsequente e as alterações que você fizer (por exemplo, rabiscar na impressão) não aparecerão na página original. Se você destruir a impressão, na verdade você destruiu sua cópia do objeto - mas a página da Web original permanece intacta.

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.


Eu pessoalmente usaria os termos "novo" ou "indireto" passagem por valor / passagem por referência para as novas técnicas.
ivan_pozdeev 6/03/19

A definição "autêntica" que você fornece não é a definição dada em quase todos os cursos de programação introdutórios. Google que passa por referência e você não receberá essa resposta. A definição autêntica que você fornece é o uso indevido da palavra referência, pois quando você segue essa definição, está usando um alias e não uma referência: você tem duas variáveis ​​que são na verdade a mesma variável, que é um alias e não uma referência. Sua definição autêntica causa confusão em massa sem motivo. Basta dizer que passar por referência significa passar endereço. Faz sentido e evitaria essa confusão sem sentido.
YungGun 12/09/19

@YungGun 1) Forneça um link para uma "definição dada em quase todos os cursos introdutórios de programação". Observe também que isso visa ficar claro nas realidades de hoje, não nas realidades de uma década ou três atrás, quando algum curso de CS foi escrito. 2) "Endereço" não pode ser usado na definição porque abstrai deliberadamente de possíveis implementações. Por exemplo, alguns idiomas (Fortran) não têm ponteiros; eles também diferem em expor o endereço bruto ao usuário (o VB não); ele também não precisa ser um endereço de memória bruto, qualquer coisa que permitiria vincular à variável faria.
ivan_pozdeev

@ivan_podeev no link sorry. Digo "quase todos os cursos introdutórios" porque, pessoalmente, fui para a faculdade e fiz cursos de programação também que me ensinaram isso. Estes cursos eram modernos (há menos de 5 anos). Um "endereço bruto" é sinônimo de "ponteiro" ... Você pode estar tecnicamente correto (de acordo com algum link escolhido), mas o idioma que você está usando é impraticável e confuso para a maioria dos programadores. Se você quiser meus pensamentos completos, escrevi uma postagem no blog de 3500 palavras: medium.com/@isaaccway228/…
YungGun

@YungGun "por muito tempo, não leu". Uma olhada mostra exatamente as confusões descritas na resposta. O passe por referência é uma técnica abstrata, independente da implementação. Não importa o que exatamente é passado sob o capô, importa qual é o efeito no programa.
Ivan_pozdeev 01/10/19

150

É 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:

  • Java suporta apenas passagem por valor. Sempre copia argumentos, mesmo que ao copiar uma referência a um objeto, o parâmetro na função chamada aponte para o mesmo objeto e as alterações nesse objeto sejam vistas no chamador. Como isso pode ser confuso, aqui está o que Jon Skeet tem a dizer sobre isso.
  • O C # suporta passagem por valor e passagem por referência (palavra-chave refusada no chamador e na função chamada). Jon Skeet também tem uma boa explicação sobre isso aqui .
  • O C ++ suporta passagem por valor e passagem por referência (tipo de parâmetro de referência usado na chamada função). Você encontrará uma explicação sobre isso abaixo.

Códigos

Como minha linguagem é C ++, usarei isso aqui

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

E um exemplo em Java não fará mal:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

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


9
por que o voto negativo? se algo estiver errado ou levar a mal-entendidos, por favor deixe um comentário.
Johannes Schaub - litb 17/12/08

1
Não foi o meu voto, mas em um ponto menor (você sabe, o tipo adotado nos debates presidenciais), eu diria que é mais uma "tática" do que uma "estratégia".
harpo 17/12/08

28
+1 para completar. Não se preocupe com votos negativos - as pessoas fazem isso por razões estranhas. Em uma pergunta de opinião sobre calculadoras, todo mundo foi votado por um cara que não achou que os programadores devessem usar calculadoras! Enfim, achei sua resposta muito boa.
22630 Mark Brittingham

1
Os links para as explicações de Skeet estão quebrados.
Programador orientado a dinheiro

Os links para as explicações de Skeet ainda estão quebrados.
Rokit

85

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:

  • chamar por valor significa que você passa valores como argumentos de função
  • chamar por referência significa que você passa variáveis como argumentos de função

Em termos metafóricos:

  • Chamar por valor é onde escrevo algo em um pedaço de papel e entrego a você . Talvez seja uma URL, talvez uma cópia completa de Guerra e Paz. Não importa o que seja, está em um pedaço de papel que eu lhe dei e agora é efetivamente o seu pedaço de papel . Agora você está livre para rabiscar nesse pedaço de papel ou usar esse pedaço de papel para encontrar algo em outro lugar e mexer com ele, qualquer que seja.
  • Chamada por referência é quando eu lhe entrego meu caderno de anotações . Você pode rabiscar no meu caderno (talvez eu queira que você faça, talvez não) e depois guardo meu caderno, com quaisquer rabiscos que você colocou lá. Além disso, se o que você ou eu escrevemos tiver informações sobre como encontrar algo em outro lugar, você ou eu podemos ir lá e mexer com essas informações.

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 Objecte em C # todos os classtipos) 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 exemplo int*).


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:

void foo(int param) {  // line 1
  param += 1;
}

E aqui está um exemplo de chamar esta função:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

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 para foo, também declarado na linha 1
  • argé uma variável , especificamente uma variável local da função bar, declarada e inicializada na linha 2
  • argtambém é um argumento para uma chamada específica da foolinha 3

Existem dois conjuntos de conceitos muito importantes para distinguir aqui. O primeiro é o valor versus a variável :

  • Um valor é o resultado da avaliação de uma expressão no idioma. Por exemplo, na barfunção acima, a seguir à linha int arg = 1;, a expressão argtem o valor 1 .
  • Uma variável é um contêiner para valores . Uma variável pode ser mutável (esse é o padrão na maioria das linguagens do tipo C), somente leitura (por exemplo, declarada usando Java finalou C # readonly) ou profundamente imutável (por exemplo, usando C ++ const).

O outro par importante de conceitos a distinguir é parâmetro versus argumento :

  • Um parâmetro (também chamado de parâmetro formal ) é uma variável que deve ser fornecida pelo chamador ao chamar uma função.
  • Um argumento é um valor que é fornecido pelo chamador de uma função para satisfazer um parâmetro formal específico dessa função

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:

int arg = 1;
int another_variable = arg;

Aqui arge another_variablesão variáveis ​​completamente independentes - seus valores podem mudar independentemente um do outro. No entanto, no ponto em que another_variableé declarado, é inicializado para manter o mesmo valor que argmantém - o que é 1.

Como são variáveis ​​independentes, as alterações another_variablenão afetam arg:

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

É exatamente o mesmo que a relação entre arge paramno nosso exemplo acima, que repetirei aqui por simetria:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

É exatamente como se tivéssemos escrito o código desta maneira:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

Ou seja, a característica definidora do que chamada por valor significa é que o chamado ( fooneste caso) recebe valores como argumentos, mas possui suas próprias variáveis separadas para esses valores das variáveis ​​do chamador ( barneste caso).

Voltando à minha metáfora acima, se eu sou bare 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 papel param. Esse valor é uma cópia do valor que escrevi no meu notebook (minhas variáveis ​​locais), em uma variável que eu chamo arg.

(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:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

Como paramé apenas outro nome para arg- ou seja, eles são a mesma variável , as alterações nas quais paramsão refletidas arg. 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:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

Neste caso, paramnão apenas tem o mesmo valor como arg, ele realmente é arg (apenas por um nome diferente) e assim barpode-se observar que argfoi 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. :-)


Explained Better: Existem dois conjuntos de conceitos muito importantes para distinguir aqui. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
Excelente resposta. Eu acho que gostaria de acrescentar que nenhum novo armazenamento precisa ser criado de passagem por referência. As referências de nome parâmetro o armazenamento original (memória) .graças
drlolly

1
Melhor resposta IMO
Rafael Eyng

59

Antes de entender os 2 termos, você DEVE entender o seguinte. Todo objeto tem duas coisas que podem fazer com que ele se destaque.

  • O seu valor.
  • O seu endereço.

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. Passagem 0x7fd5d258dd00é 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)


3
Ou seja, eu estava procurando, na verdade, deve-se procurar o conceito e não apenas a explicação, polegares para cima mano.
Haisum Usman

Java é sempre transmitido por valor. Passar referências a objetos em java é considerado passagem por valor. Isso contradiz sua declaração "Passar 0x7fd5d258dd00 é conhecido como passar por referência".
chetan raina

53

Aqui está um exemplo:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

1
Acho que há um problema, pois a última linha deve imprimir 0 em vez de 2. Por favor, diga-me se estou perdendo alguma coisa.
Taimoor Changaiz

@TaimoorChangaiz; Qual "última linha"? A propósito, se você pode usar o IRC, acesse a ## programação no Freenode. Seria muito mais fácil explicar as coisas lá. Meu nick existe "pyon".
pyon

1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // prints 2
Taimoor Changaiz 12/03

5
@ TaimoorChangaiz: Por que não imprimir 2? yjá foi definido como 2 pela linha anterior. Por que voltaria a 0?
pyon

@ EduardoLeón meu mal. sim você está certo. Graças para a correção
Taimoor Changaiz

28

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.


Este é o exemplo mais simples e melhor entre todas as outras respostas.
Amit Ray

18

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.


12

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.


7

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):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

Mantenha as coisas simples, espreitadelas. Paredes de texto podem ser um mau hábito.


Isso é realmente útil para entender se o valor do parâmetro foi alterado ou não, obrigado!
Kevin Zhao

5

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


3

Exemplos:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

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.


2

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á.


2

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.


1

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


0

Aqui está um exemplo que demonstra as diferenças entre a passagem por valor - valor do ponteiro - referência :

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

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.


Você declara "se um parâmetro for declarado [como referência], seu parâmetro real correspondente deverá ser uma variável", mas isso não é verdade em geral. Se uma referência estiver vinculada a um temporário (como o valor de retorno de uma função), sua vida útil será estendida para corresponder à referência. Veja aqui para detalhes.
Chris caça
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.