Pasar por referencia o pasar por valor?

votos
48

Al aprender un nuevo lenguaje de programación, uno de los posibles obstáculos que puede encontrar es la cuestión de si el idioma es, por defecto, pass-by-value o pass-by-reference .

Así que esta es mi pregunta para todos ustedes, en su idioma favorito, ¿cómo se hace realmente? ¿Y cuáles son los posibles escollos ?

Tu lenguaje favorito puede ser, por supuesto, cualquier cosa con la que hayas jugado alguna vez: popular , oscuro , esotérico , nuevo , viejo ...

Publicado el 05/08/2008 a las 09:56
fuente por usuario
En otros idiomas...                            


11 respuestas

votos
31

Aquí está mi propia contribución para el lenguaje de programación Java .

primero un poco de código:

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

llamar a este método resultará en esto:

int pi = 3;
int everything = 42;

swap(pi, everything);

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

"Output:
pi: 3
everything: 42"

incluso el uso de objetos "reales" mostrará un resultado similar:

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"

por lo tanto, está claro que Java pasa sus parámetros por valor , ya que el valor para pi y todo y los objetos MyObj no se intercambian. tenga en cuenta que "por valor" es la única forma en java para pasar parámetros a un método. (por ejemplo, un lenguaje como c ++ le permite al desarrollador pasar un parámetro por referencia usando ' & ' después del tipo del parámetro)

ahora la parte difícil , o al menos la parte que confundirá a la mayoría de los nuevos desarrolladores de Java: (tomado 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 cambia con éxito el valor de pnt1! Esto implicaría que los objetos se pasan por referencia, ¡este no es el caso! Una declaración correcta sería: las referencias a Objeto se pasan por valor.

más de Tony Sintes:

El método altera con éxito el valor de pnt1, a pesar de que se pasa por valor; sin embargo, ¡un intercambio de pnt1 y pnt2 falla! Esta es la mayor fuente de confusión. En el método main (), pnt1 y pnt2 no son más que referencias de objeto. Cuando pasa pnt1 y pnt2 al método tricky (), Java pasa las referencias por valor al igual que cualquier otro parámetro. Esto significa que las referencias pasadas al método son en realidad copias de las referencias originales. La Figura 1 a continuación muestra dos referencias que apuntan al mismo objeto después de que Java pasa un objeto a un método.

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

Conclusión o una larga historia corta:

  • Java pasa los parámetros por valor
  • "por valor" es la única forma en java para pasar un parámetro a un método
  • el uso de métodos del objeto dado como parámetro alterará el objeto ya que las referencias apuntan a los objetos originales. (si ese método en sí altera algunos valores)

Enlaces útiles:

Respondida el 05/08/2008 a las 09:56
fuente por usuario

votos
20

Aquí hay otro artículo para el lenguaje de programación c #

c # pasa sus argumentos por valor (por defecto)

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

llamar a esta versión de intercambio no tendrá ningún resultado:

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

"output: 
x: foo
y: bar"

sin embargo, a diferencia de java c # le da al desarrollador la oportunidad de pasar parámetros por referencia , esto se hace usando la palabra clave 'ref' antes del tipo del parámetro:

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

este intercambio se cambie el valor del parámetro de referencia:

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

"output: 
x: bar
y: foo"

c # también tiene una palabra clave out , y la diferencia entre ref y out es sutil. de msdn:

El llamante de un método que toma un parámetro de salida no es necesario para asignar a la variable pasada como el parámetro de salida antes de la llamada; sin embargo, se requiere que el destinatario se asigne al parámetro out antes de regresar.

y

En contraste, los parámetros ref se consideran asignados inicialmente por el destinatario. Como tal, no es necesario que el destinatario se asigne al parámetro ref antes de su uso. Los parámetros de referencia se pasan tanto dentro como fuera de un método.

una pequeña trampa es, como en java, que los objetos pasados ​​por valor todavía se pueden cambiar usando sus métodos internos

conclusión:

  • c # pasa sus parámetros, por defecto, por valor
  • pero cuando los parámetros necesarios también se pueden pasar por referencia usando la palabra clave ref
  • Los métodos internos de un parámetro pasado por valor alterarán el objeto (si ese método en sí altera algunos valores)

Enlaces útiles:

Respondida el 05/08/2008 a las 17:40
fuente por usuario

votos
19

Python usa pass-by-value, pero dado que todos esos valores son referencias a objetos, el efecto neto es algo similar a pasar por referencia. Sin embargo, los programadores de Python piensan más sobre si un tipo de objeto es mutable o inmutable . Los objetos mutables pueden cambiarse en el lugar (por ejemplo, diccionarios, listas, objetos definidos por el usuario), mientras que los objetos inmutables no pueden (por ejemplo, enteros, cadenas, tuplas).

El siguiente ejemplo muestra una función a la que se le pasan dos argumentos, una cadena inmutable y una lista mutable.

>>> 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']

La línea a = "Red"simplemente crea un nombre local a, para el valor de cadena "Red"y no tiene ningún efecto sobre el argumento transferido (que ahora está oculto, ya que adebe hacer referencia al nombre local a partir de ese momento). La asignación no es una operación in situ, independientemente de si el argumento es mutable o inmutable.

El bparámetro es una referencia a un objeto de lista mutable, y el .append()método realiza una extensión in situ de la lista, haciendo tachuelas en el nuevo "Blue"valor de cadena.

(Debido a que los objetos de cadena son inmutables, no tienen ningún método que admita modificaciones in situ).

Una vez que la función retorna, la reasignación de no aha tenido ningún efecto, mientras que la extensión de bmuestra claramente la semántica de llamadas de estilo de paso por referencia.

Como se mencionó anteriormente, incluso si el argumento para aes un tipo mutable, la reasignación dentro de la función no es una operación in situ, y por lo tanto no habría ningún cambio en el valor del argumento pasado:

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

Si no deseaba que su lista fuera modificada por la función llamada, en su lugar usaría el tipo de tupla inmutable (identificado por los paréntesis en la forma literal, en lugar de corchetes), que no es compatible con el .append()método en contexto:

>>> 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'
Respondida el 23/08/2008 a las 17:50
fuente por usuario

votos
7

Ya que no he visto una respuesta Perl sin embargo, pensé que me gustaría escribir una.

Bajo el capó, Perl funciona efectivamente como el paso por referencia. Las variables como argumentos de la función de llamada se pasan referencialmente, las constantes se pasan como valores de sólo lectura, y los resultados de las expresiones se pasan como temporales. Las expresiones habituales para la construcción de listas de argumentos por asignación lista de @_, o por shifttienden a ocultar esta parte del usuario, dando la apariencia de pasar por valor:

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

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

Esto imprimirá Value is now 1porque el $x++ha incrementado la variable léxica declarada dentro de la incr()función, en lugar de la variable que se pasa. Este estilo el paso por valor es por lo general lo que se desea la mayor parte del tiempo, ya que las funciones que modifican sus argumentos son poco frecuentes en Perl, y el estilo debe ser evitado.

Sin embargo, si por alguna razón se desea específicamente este comportamiento, se puede lograr haciendo funcionar directamente sobre los elementos del @_array, ya que será alias para variables pasadas en la función.

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

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

Esta vez va a imprimir Value is now 2, ya que la $_[0]++expresión incrementada de la actual $valuevariable. La forma en que esto funciona es que bajo el capó @_no es un verdadero arsenal como la mayoría de otras matrices (como se obtendría mediante my @array), pero en cambio sus elementos se construyen directamente de los argumentos pasados a una llamada de función. Esto le permite construir la semántica pase por referencia si es que se necesitaría. Argumentos de llamada función de las variables que son lisos se insertan tal cual en esta matriz, y las constantes o resultados de las expresiones más complejas se insertan como de sólo lectura temporales.

Sin embargo, es extremadamente raro para hacer esto en la práctica, porque Perl soporta valores de referencia; es decir, los valores que se refieren a otras variables. Normalmente es mucho más claro para la construcción de una función que tiene un efecto secundario obvio en una variable, mediante el paso en una referencia a esa variable. Esta es una clara indicación para el lector en el callsite, que la semántica pasar por referencia están en vigor.

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

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

Aquí el \operador proporciona una referencia de la misma manera que el &operador de dirección en C.

Respondida el 13/04/2012 a las 16:33
fuente por usuario

votos
6

Aquí hay una buena explicación para .NET.

Muchas personas se sorprenden de que los objetos de referencia pasen realmente por valor (tanto en C # como en Java). Es una copia de una dirección de pila. Esto evita que un método cambie a donde apunta realmente el objeto, pero aún permite que un método cambie los valores del objeto. En C # es posible pasar una referencia por referencia, lo que significa que puede cambiar hacia dónde apunta un objeto real.

Respondida el 06/08/2008 a las 23:43
fuente por usuario

votos
5

No olvide que también hay un pase por nombre , y pase por valor-resultado .

Pasar por valor-resultado es similar a pasar por valor, con el aspecto agregado de que el valor se establece en la variable original que se pasó como parámetro. Hasta cierto punto, puede evitar la interferencia con variables globales. Aparentemente es mejor en la memoria particionada, donde un pase por referencia podría causar un error de página ( Referencia ).

Pasar por nombre significa que los valores solo se calculan cuando se usan realmente, en lugar de al comienzo del procedimiento. Algol usó pass-by-name, pero un efecto secundario interesante es que es muy difícil escribir un procedimiento de intercambio ( Referencia ). Además, la expresión aprobada por nombre se vuelve a evaluar cada vez que se accede, que también puede tener efectos secundarios.

Respondida el 05/08/2008 a las 11:00
fuente por usuario

votos
4

Lo que usted diga que el paso por valor o el paso por referencia debe ser consistente a través de idiomas. La definición más común y coherente utilizado en diferentes idiomas es que con el paso por referencia, puede pasar una variable a una función "normalmente" (es decir, sin tomar explícitamente dirección o algo por el estilo), y la función se puede asignar a (no muta el contenido de) el parámetro dentro de la función y tendrá el mismo efecto que la asignación a la variable en el ámbito de llamar.

Desde este punto de vista, las lenguas se agrupan de la siguiente manera; cada grupo que tiene la misma semántica pasajeras. Si usted piensa que dos lenguas no deben ser puestos en el mismo grupo, los reto a que venga con un ejemplo que los distingue.

La gran mayoría de los idiomas, incluyendo C , Java , Python , Rubí , JavaScript , Esquema , OCaml , Standard ML , Go , Objective-C , Smalltalk , etc, todos ellos pasan por valor única . Al pasar un valor de puntero (algunos idiomas llaman una "referencia") no cuenta como pase por referencia; sólo estamos preocupados por lo pasado, el puntero, no lo señaló.

Idiomas tales como C ++ , C # , PHP son de forma predeterminada el paso por valor como los idiomas anteriores, pero las funciones se declaran explícitamente los parámetros a ser pasar por referencia, usando &o ref.

Perl siempre se pasan por referencia; Sin embargo, en la práctica, las personas casi siempre copian los valores después de conseguir que, por lo tanto el uso de una forma pase por valor.

Respondida el 13/04/2012 a las 21:00
fuente por usuario

votos
4

por valor

  • es más lento que por referencia ya que el sistema tiene que copiar el parámetro
  • usado solo para entrada

por referencia

  • más rápido ya que solo se pasa un puntero
  • utilizado para entrada y salida
  • puede ser muy peligroso si se usa junto con variables globales
Respondida el 05/08/2008 a las 10:10
fuente por usuario

votos
3

En cuanto a J , mientras que solo hay, AFAIK, pasando por valor, hay una forma de pasar por referencia que permite mover una gran cantidad de datos. Simplemente pasa algo conocido como configuración regional a un verbo (o función). Puede ser una instancia de una clase o simplemente un contenedor 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

La desventaja obvia es que necesita saber el nombre de su variable de alguna manera en la función llamada. Pero esta técnica puede mover una gran cantidad de datos sin dolor. Es por eso que, aunque técnicamente no paso por referencia, lo llamo "más o menos eso".

Respondida el 21/11/2009 a las 18:14
fuente por usuario

votos
2

PHP también pasa 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";

Productos:

a b

Sin embargo, en PHP4 los objetos fueron tratados como primitivos . Lo 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"
Respondida el 11/08/2008 a las 03:21
fuente por usuario

votos
-1

Por defecto, la norma ANSI / ISO C utiliza, ya sea - que depende de cómo usted declara su función y sus parámetros.

Si se declara sus parámetros de función como punteros a continuación, la función se pasan por referencia, y si declara sus parámetros de función como variables no-puntero a continuación, la función será pasar 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.)

Puede tener problemas si se crea una función que devuelve un puntero a una variable no estática que se ha creado dentro de esa función. El valor devuelto de la siguiente código sería indefinido - no hay manera de saber si el espacio de memoria asignada a la variable temporal creado en la función se ha sobrescrito o no.

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

Se podría, sin embargo, devolver una referencia a una variable estática o un puntero que fue aprobada en la lista de parámetros.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Respondida el 13/01/2011 a las 21:48
fuente por usuario

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