Passar por referência ou passar por valor?

votos
48

Ao aprender uma nova linguagem de programação, um dos possíveis obstáculos que podem surgir é a questão de saber se a linguagem é, por padrão, passar por valor ou passar por referência .

Então aqui está a minha pergunta a todos vocês, na sua língua favorita, como é realmente feito? E quais são as possíveis armadilhas ?

Sua linguagem favorita pode, naturalmente, ser qualquer coisa que você já jogou com: populares , obscuro , esotérico , novo , velho ...

Publicado 05/08/2008 em 09:56
fonte usuário
Em outras línguas...                            


11 respostas

votos
31

Aqui é a minha própria contribuição para a linguagem de programação Java .

primeiro algum código:

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

chamar esse método irá resultar no seguinte:

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

mesmo usando objetos 'reais' irá mostrar um resultado semelhante:

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public int getNumber() {
        return this.number;
    }


    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}


"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

assim, é claro que Java passa seus parâmetros por valor , como o valor para pi e tudo e os objetos myObj não são trocados. estar ciente de que "por valor" é a única maneira em java para passar parâmetros para um método. (por exemplo, uma linguagem como C ++ permite que o desenvolvedor para passar um parâmetro por referência usando ' e ' depois o tipo do parâmetro)

agora a parte difícil , ou pelo menos a parte que irá confundir a maioria dos novos desenvolvedores java: (emprestado de JavaWorld )
autor original: Tony Sintes

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}


"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

complicado muda com sucesso o valor de pnt1! Isto implicaria que os objetos são passados por referência, este não é o caso! A afirmação correta seria: as referências de objeto são passados por valor.

mais de Tony Sintes:

O método altera com sucesso o valor de pnt1, mesmo que ele é passado por valor; no entanto, uma troca de pnt1 e PNT2 falha! Esta é a principal fonte de confusão. No método principal (), pnt1 e PNT2 são nada mais do que referências de objeto. Quando você passar pnt1 e PNT2 ao método complicado (), Java passa referências por valor como qualquer outro parâmetro. Isto significa que as referências passaram para o método são realmente cópias das referências originais. A Figura 1 mostra duas referências apontando para o mesmo objecto após Java passa um objecto a um método.

figura 1 http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif

Conclusão ou uma longa história curta:

  • Java passa parâmetros por valor
  • "por valor" é a única maneira em java para passar um parâmetro para um método
  • usando métodos do objeto dado como parâmetro irá alterar o objecto das referências apontar para os objectos originais. (se esse método em si muda alguns valores)

Links Úteis:

Respondeu 05/08/2008 em 09:56
fonte usuário

votos
20

Aqui está outro artigo para a linguagem C # programação

c # passa seus argumentos por valor (por padrão)

private void swap(string a, string b) {
  string tmp = a;
  a = b;
  b = tmp;
}

chamando esta versão do swap irá, portanto, não têm resultado:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: foo
y: bar"

No entanto, ao contrário de java c # faz dar o desenvolvedor a oportunidade de passar parâmetros por referência , isso é feito usando a palavra-chave 'ref' antes do tipo do parâmetro:

private void swap(ref string a, ref string b) {
  string tmp = a;
  a = b;
  b = tmp;
} 

esta troca irá alterar o valor do parâmetro de referência:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: bar
y: foo"

c # também tem uma palavra-chave para fora , ea diferença entre ref e fora é sutil. do MSDN:

O chamador de um método que tem um parâmetro de saída não é necessária para atribuir à variável transmitido como o parâmetro para fora antes da chamada; No entanto, o receptor é obrigado a atribuir ao parâmetro fora antes de retornar.

e

Em contraste ref parâmetros são considerados inicialmente atribuído pelo receptor. Como tal, o receptor é não é necessário atribuir à ref parâmetro antes de usar. Ref parâmetros são transmitidos tanto para dentro e para fora de um método.

uma pequena armadilha é, como em Java, que objetos passados por valor ainda pode ser alterada utilizando os métodos internos

conclusão:

  • c # passa seus parâmetros, por padrão, pelo valor
  • mas os parâmetros quando necessário, também pode ser passado por referência usando a palavra-chave ref
  • métodos de interiores de um parâmetro transmitido pelo valor irá alterar o objecto (se esse método em si altera alguns valores)

Links Úteis:

Respondeu 05/08/2008 em 17:40
fonte usuário

votos
19

Python usa passar por valor, mas desde que todos esses valores são referências de objeto, o efeito líquido é algo semelhante a passar por referência. No entanto, os programadores Python pensar mais sobre se um tipo de objeto é mutável ou imutável . Objetos mutáveis podem ser alterados no local (por exemplo, dicionários, listas, objetos definidos pelo usuário), enquanto objetos imutáveis não pode (por exemplo, números inteiros, cordas, linhas).

O exemplo a seguir mostra uma função que é passado dois argumentos, uma seqüência imutável, e uma lista mutável.

>>> def do_something(a, b):
...     a = "Red"
...     b.append("Blue")
... 
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']

A linha de a = "Red"apenas cria um nome local, apara o valor da cadeia "Red"e não tem efeito sobre o argumento passado-in (que agora está escondido, como adeve se referir ao nome local a partir de então). Atribuição não é uma operação no local, independentemente de se o argumento é mutável ou imutável.

O bparâmetro é uma referência a um objeto lista mutável, eo .append()método executa uma extensão no local da lista, aderência sobre o novo "Blue"valor da cadeia.

(Porque objetos string são imutáveis, eles não têm quaisquer métodos que suportam modificações no local.)

Uma vez que a função retorna, a re-atribuição de anão teve nenhum efeito, enquanto a extensão da bmostra claramente passagem por referência semântica chamada estilo.

Como mencionado anteriormente, mesmo se o argumento para aum tipo mutável, a reafectação dentro da função não é uma operação no local, e assim não haveria nenhuma mudança para o valor do argumento passado:

>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']

Se você não quer que sua lista modificada pela função chamada, você, ao invés, utilizar o tipo imutável tupla (identificado pelos parênteses na forma literal, ao invés de colchetes), que não suporta a in-place .append()método:

>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'
Respondeu 23/08/2008 em 17:50
fonte usuário

votos
7

Desde que eu não vi uma resposta Perl ainda, eu pensei que eu ia escrever um.

Sob o capô, Perl funciona eficazmente como passagem por referência. Variáveis como argumentos de chamada de função são passados referencialmente, constantes são passados como valores somente leitura, e os resultados das expressões são passados como temporários. As expressões usuais para construir listas de argumentos por cessão lista a partir @_, ou shifttendem a esconder isso do usuário, dando a aparência de passar por valor:

sub incr {
  my ( $x ) = @_;
  $x++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Isto irá imprimir Value is now 1porque o $x++tem incrementado a variável lexical declarada dentro da incr()função, em vez da variável passado. Este estilo passar por valor é geralmente o que é procurado na maioria das vezes, como funções que modificam seus argumentos são raros em Perl, e o estilo deve ser evitado.

No entanto, se por algum motivo deste comportamento é especificamente desejada, ela pode ser alcançada por operar directamente sobre os elementos da @_matriz, porque eles serão alias para as variáveis passaram para a função.

sub incr {
  $_[0]++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Desta vez, ele irá imprimir Value is now 2, porque a $_[0]++expressão incrementado o real $valuevariável. A forma como isto funciona é que sob o capô @_não é uma verdadeira matriz como a maioria das outras matrizes (como seria obtida por my @array), mas em vez disso os seus elementos são construídos diretamente dos argumentos passados para uma chamada de função. Isso permite que você construa semântica passados por referência se que seria necessário. Argumentos de chamada de função que são variáveis simples são inseridos como está neste array, e constantes ou resultados de expressões mais complexas são inseridos como temporários somente leitura.

No entanto, é extremamente raro para fazer isso na prática, porque Perl suporta valores de referência; isto é, valores que se referem a outras variáveis. Normalmente é muito mais clara a construção de uma função que tem um efeito colateral evidente em uma variável, por passagem em uma referência a essa variável. Esta é uma clara indicação para o leitor no callsite, que semântica de referência passar-por-são em efeito.

sub incr_ref {
  my ( $ref ) = @_;
  $$ref++;
}

my $value = 1;
incr(\$value);
say "Value is now $value";

Aqui, o \operador gera uma referência em muito da mesma maneira que o &endereço do operador-em C.

Respondeu 13/04/2012 em 16:33
fonte usuário

votos
6

Há uma boa explicação aqui para .NET.

Um monte de pessoas estão surpresa que os objetos de referência são realmente passados ​​por valor (em C # e Java). É uma cópia de um endereço de pilha. Isto impede que um método de transformação, no qual o objecto realmente aponta para, mas ainda permite um método para alterar os valores do objecto. Em C # a sua possível passar uma referência por referência, o que significa que você pode alterar o local onde um real pontos de objeto para.

Respondeu 06/08/2008 em 23:43
fonte usuário

votos
5

Não se esqueça que também é passar pelo nome , e passagem por valor-resultado .

Passagem por valor-resultado é semelhante ao passar por valor, com o aspecto acrescentado que o valor é definido na variável original que foi transmitido como o parâmetro. Ele pode, em certa medida, evitar interferência com variáveis globais. É aparentemente melhor na memória particionado, onde uma passagem por referência poderia causar uma falha de página ( Referência ).

Passe pelo nome significa que os valores só são calculados quando eles são realmente utilizados, em vez de no início do procedimento. Algol usada passagem por nome, mas um efeito colateral interessante é que é muito difícil escrever um procedimento swap ( Referência ). Além disso, a expressão passada pelo nome é re-avaliado cada vez que é acedido, o que também pode ter efeitos secundários.

Respondeu 05/08/2008 em 11:00
fonte usuário

votos
4

Qualquer coisa que diga como passar por valor ou passar por referência deve ser consistente em todos os idiomas. A definição mais comum e consistente usada em línguas é que, com passagem por referência, você pode passar uma variável para uma função "normalmente" (ou seja, sem levar explicitamente endereço ou qualquer coisa assim), e a função pode atribuir a (não sofrer mutação o conteúdo de) o parâmetro dentro da função e terá o mesmo efeito que a atribuição para a variável no escopo de chamada.

Deste ponto de vista, as línguas são agrupadas da seguinte forma; cada grupo tendo os mesmos semântica passageiras. Se você acha que duas línguas não deve ser colocado no mesmo grupo, eu desafio você a vir para cima com um exemplo que os distingue.

A grande maioria dos idiomas, incluindo C , Java , Python , o Ruby , JavaScript , Scheme , OCaml , ML padrão , Go , Objective-C , Smalltalk , etc. são todos passar por valor única . Passando um valor de ponteiro (algumas linguagens chamam de "referência") não conta como passagem por referência; estamos apenas preocupados com a coisa passou, o ponteiro, não a coisa apontado.

Linguagens como C ++ , C # , PHP são por padrão passar por valor como os idiomas acima, mas funções podem declarar explicitamente parâmetros para ser passado por referência, usando &ouref .

Perl é sempre passar por referência; no entanto, na prática, as pessoas quase sempre copiar os valores depois de obter-lo, portanto, usá-lo de uma forma passagem por valor.

Respondeu 13/04/2012 em 21:00
fonte usuário

votos
4

por valor

  • é mais lento do que por referência uma vez que o sistema tem de copiar o parâmetro
  • usado para a entrada única

por referência

  • mais rápido uma vez que apenas um ponteiro é passado
  • usado para a entrada e saída
  • pode ser muito perigoso se usado em conjunto com variáveis ​​globais
Respondeu 05/08/2008 em 10:10
fonte usuário

votos
3

No que diz respeito J , enquanto só existe, AFAIK, passando por valor, não é uma forma de passar por referência que permite mover uma grande quantidade de dados. Você simplesmente passar algo conhecido como um local a um verbo (ou função). Pode ser uma instância de uma classe ou apenas um contêiner genérico.

spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
    $ y
    ''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
    l =. y
    $ big_chunk_of_data__l
    ''
)
exectime 'passbyvalue big_chunk_of_data'
   0.00205586720663967
exectime 'passbyreference locale'
   8.57957102144893e_6

A desvantagem óbvia é que você precisa saber o nome da sua variável, de alguma forma na função chamada. Mas esta técnica pode mover uma grande quantidade de dados de forma indolor. É por isso que, embora tecnicamente não passar por referência, eu chamo-lhe "muito bonito isso."

Respondeu 21/11/2009 em 18:14
fonte usuário

votos
2

PHP também é passar por valor.

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

saídas:

a b

No entanto, em PHP4 objectos foram tratados como primitivas . Que significa:

<?php
$myData = new Holder('this should be replaced');

function replaceWithGreeting($holder) {
    $myData->setValue('hello');
}

replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"
Respondeu 11/08/2008 em 03:21
fonte usuário

votos
-1

Por padrão, ANSI / ISO C usa tanto - isso depende de como você declarar a sua função e seus parâmetros.

Se você declarar seus parâmetros de funções como ponteiros, em seguida, a função será passar por referência, e se você declarar seus parâmetros da função como variáveis ​​não-ponteiro, em seguida, a função será passar por valor.

void swap(int *x, int *y);   //< Declared as pass-by-reference.
void swap(int x, int y);     //< Declared as pass-by-value (and probably doesn't do anything useful.)

Você pode ter problemas se você criar uma função que retorna um ponteiro para uma variável não-estático que foi criado dentro dessa função. O valor retornado do código a seguir seria indefinida - não há nenhuma maneira de saber se o espaço de memória alocada para a variável temporário criado na função foi substituído ou não.

float *FtoC(float temp)
{
    float c;
    c = (temp-32)*9/5;
    return &c;
}

Você poderia, no entanto, retornar uma referência a uma variável estática ou um ponteiro que foi passado na lista de parâmetros.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Respondeu 13/01/2011 em 21:48
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more