¿Cuál es la diferencia entre pasar por referencia versus pasar por valor?

Respuestas:

1080

En primer lugar, la distinción "pasar por valor frente a pasar por referencia" tal como se define en la teoría CS ahora es obsoleta porque la técnica originalmente definida como "pasar por referencia" ha caído en desgracia desde entonces y rara vez se usa ahora. 1

Los lenguajes más nuevos 2 tienden a usar un par de técnicas diferentes (pero similares) para lograr los mismos efectos (ver más abajo), que es la principal fuente de confusión.

Una fuente secundaria de confusión es el hecho de que en "pasar por referencia", "referencia" tiene un significado más limitado que el término general "referencia" (porque la frase lo precede).


Ahora, la definición auténtica es:

  • Cuando se pasa un parámetro por referencia , la persona que llama y la persona que llama utilizan la misma variable para el parámetro. Si la persona que llama modifica la variable del parámetro, el efecto es visible para la variable de la persona que llama.

  • Cuando se pasa un parámetro por valor , la persona que llama y la persona que llama tienen dos variables independientes con el mismo valor. Si la persona que llama modifica la variable del parámetro, el efecto no es visible para la persona que llama.

Las cosas a tener en cuenta en esta definición son:

  • "Variable" aquí significa la variable del llamante (local o global) , es decir, si paso una variable local por referencia y la asigno, cambiaré la variable de la persona que llama, no, por ejemplo, a lo que esté apuntando si es un puntero .

    • Esto ahora se considera una mala práctica (como una dependencia implícita). Como tal, prácticamente todos los idiomas más nuevos son exclusivos, o casi exclusivamente de paso por valor. Pass-by-reference ahora se usa principalmente en forma de "argumentos de salida / entrada" en lenguajes donde una función no puede devolver más de un valor.
  • El significado de "referencia" en "pasar por referencia" . La diferencia con el término general de "referencia" es que esta "referencia" es temporal e implícita. Lo que el destinatario básicamente obtiene es una "variable" que de alguna manera es "la misma" que la original. La forma específica en que se logra este efecto es irrelevante (por ejemplo, el lenguaje también puede exponer algunos detalles de implementación - direcciones, punteros, desreferenciación - todo esto es irrelevante; si el efecto neto es este, es paso por referencia).


Ahora, en los lenguajes modernos, las variables tienden a ser de "tipos de referencia" (otro concepto inventado más tarde que "pasar por referencia" e inspirado en él), es decir, los datos del objeto real se almacenan por separado en algún lugar (generalmente, en el montón), y solo las "referencias" a él se mantienen en variables y se pasan como parámetros. 3

Pasar dicha referencia cae bajo el valor de paso por valor porque el valor de una variable es técnicamente la referencia en sí misma, no el objeto referido. Sin embargo, el efecto neto en el programa puede ser el mismo que el de pasar por valor o pasar por referencia:

  • Si se toma una referencia de la variable de un llamante y se pasa como argumento, esto tiene el mismo efecto que pasar por referencia: si el objeto referido está mutado en la persona que llama, la persona que llama verá el cambio.
    • Sin embargo, si se reasigna una variable que contiene esta referencia , dejará de apuntar a ese objeto, por lo que cualquier otra operación en esta variable afectará a lo que esté apuntando ahora.
  • Para tener el mismo efecto que el paso por valor, se realiza una copia del objeto en algún momento. Las opciones incluyen:
    • La persona que llama solo puede hacer una copia privada antes de la llamada y, en su lugar, puede proporcionarle una referencia.
    • En algunos idiomas, algunos tipos de objetos son "inmutables": cualquier operación en ellos que parezca alterar el valor en realidad crea un objeto completamente nuevo sin afectar el original. Por lo tanto, pasar un objeto de ese tipo como argumento siempre tiene el efecto de pasar por valor: se realizará automáticamente una copia para la persona que llama si necesita un cambio, y el objeto de la persona que llama nunca se verá afectado.
      • En lenguajes funcionales, todos los objetos son inmutables.

Como puede ver, este par de técnicas es casi igual a las de la definición, solo que con un nivel de indirección: simplemente reemplace "variable" con "objeto referenciado".

No hay un nombre acordado para ellos, lo que lleva a explicaciones retorcidas como "llamar por valor donde el valor es una referencia". En 1975, Barbara Liskov sugirió el término " llamada por compartir objetos " (o, a veces, simplemente "llamada por compartir"), aunque nunca se dio cuenta. Además, ninguna de estas frases dibuja un paralelo con el par original. No es de extrañar que los viejos términos terminaron siendo reutilizados en ausencia de algo mejor, lo que lleva a la confusión. 4 4


NOTA : Durante mucho tiempo, esta respuesta solía decir:

Digamos que quiero compartir una página web con usted. Si te digo la URL, estoy pasando por referencia. Puede usar esa URL para ver la misma página web que puedo ver. Si se cambia esa página, ambos vemos los cambios. Si elimina la URL, todo lo que está haciendo es destruir su referencia a esa página; no está eliminando la página en sí.

Si imprimo la página y le doy la impresión, estoy pasando por valor. Su página es una copia desconectada del original. No verá ningún cambio posterior, y los cambios que realice (por ejemplo, garabateando en su impresión) no se mostrarán en la página original. Si destruye la impresión, realmente ha destruido su copia del objeto, pero la página web original permanece intacta.

Esto es principalmente correcto, excepto el significado más restringido de "referencia", ya que es temporal e implícito (no tiene que ser así, pero ser explícito y / o persistente son características adicionales, no una parte de la semántica de paso por referencia , como se explicó anteriormente). Una analogía más cercana sería darle una copia de un documento en lugar de invitarlo a trabajar en el original.


1 A menos que esté programando en Fortran o Visual Basic, no es el comportamiento predeterminado, y en la mayoría de los idiomas en el uso moderno, la verdadera llamada por referencia ni siquiera es posible.

2 Una buena cantidad de personas mayores también lo respaldan

3 En varios idiomas modernos, todos los tipos son tipos de referencia. Este enfoque fue iniciado por el lenguaje CLU en 1975 y desde entonces ha sido adoptado por muchos otros idiomas, incluidos Python y Ruby. Y muchos más lenguajes utilizan un enfoque híbrido, donde algunos tipos son "tipos de valor" y otros son "tipos de referencia", entre ellos C #, Java y JavaScript.

4 No hay nada malo en reciclar un término antiguo apropiado per se, pero uno debe aclarar de alguna manera qué significado se usa cada vez. No hacer eso es exactamente lo que sigue causando confusión.

ivan_pozdeev
fuente
Yo personalmente usaría los términos "nuevo" o "indirecto" paso por valor / paso por referencia para las nuevas técnicas.
ivan_pozdeev
La definición "auténtica" que proporciona no es la definición dada en casi todos los cursos introductorios de programación. Busca en Google lo que es por referencia y no obtendrás esa respuesta. La definición auténtica que proporciona es el mal uso de la palabra referencia, ya que cuando sigue esa definición está usando un alias, no una referencia: tiene dos variables que en realidad son la misma variable, es decir, un alias y no una referencia. Su definición auténtica causa confusión masiva sin ninguna razón. Solo di pasar por referencia significa pasar la dirección. Tiene sentido y evitaría esta confusión sin sentido.
YungGun
@YungGun 1) Proporcione un enlace a una "definición dada en casi todos los cursos introductorios de programación". También tenga en cuenta que esto pretende ser claro en las realidades de hoy, no en las realidades de hace una década o tres cuando se escribió algún curso de CS. 2) "Dirección" no se puede utilizar en la definición porque abstrae deliberadamente de posibles implementaciones. Por ejemplo, algunos idiomas (Fortran) no tienen punteros; también difieren en si exponen la dirección sin procesar al usuario (VB no); tampoco tiene que ser una dirección de memoria sin procesar, cualquier cosa que permita vincular a la variable lo haría.
ivan_pozdeev
@ivan_podeev sin enlace lo siento. Digo "casi todos los cursos introductorios" porque personalmente fui a la universidad y también tomé programas de programación que me enseñaron eso. Estos cursos eran modernos (hace menos de 5 años). Una "dirección sin formato" es sinónimo de "puntero" ... Puede ser técnicamente correcto (según algún enlace seleccionado) pero el lenguaje que está utilizando no es práctico y confuso para la mayoría de los programadores. Si quieres mis pensamientos completos sobre esto, he escrito una publicación de blog de 3500 palabras: medium.com/@isaaccway228/…
YungGun
@YungGun "demasiado tiempo, no leí". Una mirada muestra exactamente las confusiones descritas en la respuesta. Pasar por referencia es una técnica abstracta independiente de la implementación. No importa qué se pasa exactamente debajo del capó, importa cuál es el efecto en el programa.
ivan_pozdeev
150

Es una forma de pasar argumentos a funciones. Pasar por referencia significa que el parámetro de las funciones llamadas será el mismo que el argumento pasado de los llamantes (no el valor, sino la identidad, la variable en sí misma). Pasar por valor significa que el parámetro de las funciones llamadas será una copia del argumento pasado de los llamantes. El valor será el mismo, pero la identidad, la variable, es diferente. Por lo tanto, los cambios en un parámetro realizado por la función llamada en un caso cambian el argumento pasado y en el otro caso solo cambian el valor del parámetro en la función llamada (que es solo una copia). En un apuro rápido:

  • Java solo admite pasar por valor. Siempre copia los argumentos, aunque al copiar una referencia a un objeto, el parámetro en la función llamada apuntará al mismo objeto y los cambios a ese objeto se verán en la persona que llama. Como esto puede ser confuso, esto es lo que Jon Skeet tiene que decir al respecto.
  • C # admite pasar por valor y pasar por referencia (palabra clave refutilizada en la persona que llama y llamada función). Jon Skeet también tiene una buena explicación de esto aquí .
  • C ++ admite pasar por valor y pasar por referencia (tipo de parámetro de referencia utilizado en la función llamada). Encontrará una explicación de esto a continuación.

Códigos

Como mi lenguaje es C ++, lo usaré aquí

// 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);
}

Y un ejemplo en Java no hará daño:

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

Este tipo casi lo clava:

http://javadude.com/articles/passbyvalue.htm

Johannes Schaub - litb
fuente
99
¿Por qué el voto negativo? Si algo está mal o conduce a malentendidos, por favor deje un comentario.
Johannes Schaub - litb
1
No fue mi voto, pero en un punto menor (ya sabes, el tipo abordado en los debates presidenciales) diría que es más una "táctica" que una "estrategia".
harpo
28
+1 por lo completo. No se preocupe por los votos negativos: la gente lo hace por razones extrañas. En una pregunta de opinión sobre calculadoras, ¡todos fueron rechazados por un tipo que no creía que los programadores debieran usar calculadoras! De todos modos, pensé que tu respuesta fue muy buena.
Mark Brittingham el
1
Los enlaces a las explicaciones de Skeet están rotos.
Programador orientado al dinero
Los enlaces a las explicaciones de Skeet todavía están rotos.
Rokit
85

Muchas respuestas aquí (y en particular la respuesta más votada) son objetivamente incorrectas, ya que no entienden lo que realmente significa "llamar por referencia". Aquí está mi intento de aclarar las cosas.

TL; DR

En términos más simples:

  • llamar por valor significa que pasa valores como argumentos de función
  • llamar por referencia significa que pasa variables como argumentos de función

En términos metafóricos:

  • La llamada por valor es donde escribo algo en un pedazo de papel y se lo entrego . Tal vez sea una URL, tal vez sea una copia completa de Guerra y paz. No importa lo que sea, está en una hoja de papel que te he dado, y ahora es efectivamente tu hoja de papel . Ahora es libre de garabatear en ese pedazo de papel, o usar ese pedazo de papel para encontrar algo en otro lugar y jugar con él, lo que sea.
  • Llamada por referencia es cuando te doy mi cuaderno que tiene algo escrito . Puedes garabatear en mi cuaderno (tal vez quiero que lo hagas, tal vez no), y luego guardo mi cuaderno, con los garabatos que hayas puesto allí. Además, si lo que usted o yo escribimos hay información sobre cómo encontrar algo en otro lugar, usted o yo podemos ir allí y manipular esa información.

¿Qué significa "llamar por valor" y "llamar por referencia" ?

Tenga en cuenta que ambos conceptos son completamente independientes y ortogonales del concepto de tipos de referencia (que en Java son todos los tipos que son subtipos de Object, y en C # todos los classtipos), o el concepto de tipos de puntero como en C (que son semánticamente equivalentes a los "tipos de referencia" de Java, simplemente con una sintaxis diferente).

La noción de tipo de referencia corresponde a una URL: es a la vez una pieza de información, y es una referencia (un puntero , si lo desea) a otra información. Puede tener muchas copias de una URL en diferentes lugares, y no cambian a qué sitio web enlazan; si el sitio web se actualiza, cada copia de la URL seguirá conduciendo a la información actualizada. Por el contrario, cambiar la URL en cualquier lugar no afectará a ninguna otra copia escrita de la URL.

Tenga en cuenta que C ++ tiene una noción de "referencias" (p int&. Ej. ) Que no es como los "tipos de referencia" de Java y C #, sino que es como "llamada por referencia". Los "tipos de referencia" de Java y C #, y todos los tipos en Python, son como lo que C y C ++ llaman "tipos de puntero" (por ejemplo int*).


OK, aquí está la explicación más larga y más formal.

Terminología

Para empezar, quiero resaltar algunas partes importantes de la terminología, para ayudar a aclarar mi respuesta y asegurarme de que todos nos estamos refiriendo a las mismas ideas cuando usamos palabras. (En la práctica, creo que la gran mayoría de la confusión sobre temas como estos proviene del uso de palabras de manera que no se comunique completamente el significado que se pretendía).

Para comenzar, aquí hay un ejemplo en algún lenguaje tipo C de una declaración de función:

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

Y aquí hay un ejemplo de llamar a esta función:

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

Usando este ejemplo, quiero definir algunos bits importantes de terminología:

  • fooes una función declarada en la línea 1 (Java insiste en hacer todos los métodos de funciones, pero el concepto es el mismo sin pérdida de generalidad; C y C ++ hacen una distinción entre declaración y definición en la que no entraré aquí)
  • parames un parámetro formal para foo, también declarado en la línea 1
  • arges una variable , específicamente una variable local de la función bar, declarada e inicializada en la línea 2
  • argtambién es un argumento para una invocación específica de fooen la línea 3

Hay dos conjuntos de conceptos muy importantes para distinguir aquí. El primero es el valor versus la variable :

  • Un valor es el resultado de evaluar una expresión en el lenguaje. Por ejemplo, en la barfunción anterior, después de la línea int arg = 1;, la expresión argtiene el valor 1 .
  • Una variable es un contenedor de valores . Una variable puede ser mutable (este es el valor predeterminado en la mayoría de los lenguajes tipo C), de solo lectura (por ejemplo, declarada utilizando Java finalo C # 's readonly) o profundamente inmutable (por ejemplo, utilizando C ++' s const).

El otro par importante de conceptos para distinguir es parámetro versus argumento :

  • Un parámetro (también llamado parámetro formal ) es una variable que el llamador debe proporcionar al llamar a una función.
  • Un argumento es un valor que proporciona el llamador de una función para satisfacer un parámetro formal específico de esa función

Llamada por valor

En la llamada por valor , los parámetros formales de la función son variables que se crean recientemente para la invocación de la función y que se inicializan con los valores de sus argumentos.

Esto funciona exactamente de la misma manera que cualquier otro tipo de variables se inicializan con valores. Por ejemplo:

int arg = 1;
int another_variable = arg;

Aquí argy another_variableson variables completamente independientes: sus valores pueden cambiar independientemente uno del otro. Sin embargo, en el punto donde another_variablese declara, se inicializa para mantener el mismo valor que argtiene, que es 1.

Como son variables independientes, los cambios another_variableno afectan arg:

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

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

Esto es exactamente lo mismo que la relación entre argy paramen nuestro ejemplo anterior, que repetiré aquí por simetría:

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

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

Es exactamente como si hubiéramos escrito el código de esta manera:

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

Es decir, la característica definitoria de lo que significa llamar por valor es que la persona que llama ( fooen este caso) recibe valores como argumentos, pero tiene sus propias variables separadas para esos valores de las variables de la persona que llama ( baren este caso).

Volviendo a mi metáfora anterior, si soy bary eres foo, cuando te llamo, te entrego un papel con un valor escrito en él. Llamas a ese pedazo de papel param. Ese valor es una copia del valor que he escrito en mi cuaderno (mis variables locales), en una variable que llamo arg.

(Como comentario aparte: según el hardware y el sistema operativo, existen varias convenciones de llamadas sobre cómo llamar a una función desde otra. La convención de llamadas es como si decidiéramos si escribo el valor en un pedazo de mi papel y luego se lo entrego , o si tiene un papel en el que lo escribo, o si lo escribo en la pared frente a los dos. Este es un tema interesante también, pero mucho más allá del alcance de esta respuesta ya larga).

Llamar por referencia

En la llamada por referencia , los parámetros formales de la función son simplemente nuevos nombres para las mismas variables que el llamador proporciona como argumentos.

Volviendo a nuestro ejemplo anterior, es 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

Dado que parames solo otro nombre para arg, es decir, son la misma variable , los cambios paramse reflejan en arg. Esta es la forma fundamental en que la llamada por referencia difiere de la llamada por valor.

Muy pocos lenguajes admiten llamadas por referencia, pero C ++ puede hacerlo así:

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

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

En este caso, paramno solo tiene el mismo valor que arg, en realidad lo es arg (solo por un nombre diferente) y, por barlo tanto, puede observar que argse ha incrementado.

Tenga en cuenta que no es así como funciona Java, JavaScript, C, Objective-C, Python o casi cualquier otro lenguaje popular en la actualidad. Esto significa que esos idiomas no se llaman por referencia, se llaman por valor.

Anexo: llamada por objeto compartido

Si lo que tiene es llamar por valor , pero el valor real es un tipo de referencia o un tipo de puntero , entonces el "valor" en sí mismo no es muy interesante (por ejemplo, en C es solo un número entero de un tamaño específico de plataforma) interesante es a lo que apunta ese valor .

Si lo que señala ese tipo de referencia (es decir, el puntero) es mutable, entonces es posible un efecto interesante: puede modificar el valor señalado y la persona que llama puede observar los cambios en el valor señalado, aunque la persona que llama no puede observar cambia al puntero mismo.

Para tomar prestada la analogía de la URL nuevamente, el hecho de que le haya dado una copia de la URL a un sitio web no es particularmente interesante si lo que nos importa es el sitio web, no la URL. El hecho de que garabatee sobre su copia de la URL no afecta mi copia de la URL no es algo que nos importe (y de hecho, en lenguajes como Java y Python, la "URL", o valor de tipo de referencia, puede No se puede modificar en absoluto, solo lo que señala puede hacerlo).

Barbara Liskov, cuando inventó el lenguaje de programación CLU (que tenía estas semánticas), se dio cuenta de que los términos existentes "llamar por valor" y "llamar por referencia" no eran particularmente útiles para describir la semántica de este nuevo lenguaje. Entonces ella inventó un nuevo término: llamada por compartir objetos .

Cuando se discuten lenguajes que técnicamente se llaman por valor, pero donde los tipos comunes en uso son tipos de referencia o puntero (es decir, casi todos los lenguajes modernos de programación imperativa, orientada a objetos o multi-paradigma), encuentro que es mucho menos confuso simplemente evite hablar de llamada por valor o llamada por referencia . Siga llamando por compartir objetos (o simplemente llame por objeto ) y nadie se confundirá. :-)

Daniel Pryden
fuente
Explicado mejor: hay dos conjuntos de conceptos muy importantes para distinguir aquí. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat
2
Excelente respuesta Creo que agregaría que no es necesario crear un nuevo almacenamiento en el pase por referencia. El nombre del parámetro hace referencia al almacenamiento original (memoria). Gracias
drlolly
1
Mejor respuesta OMI
Rafael Eyng
59

Antes de comprender los 2 términos, DEBE comprender lo siguiente. Cada objeto tiene 2 cosas que pueden distinguirlo.

  • Es valioso.
  • Su direccion.

Entonces si dices employee.name = "John"

Sé que hay 2 cosas sobre name. Su valor, que es "John"y también su ubicación en la memoria, que es un número hexadecimal tal como esto: 0x7fd5d258dd00.

Dependiendo de la arquitectura del lenguaje o del tipo (clase, estructura, etc.) de su objeto, usted estaría transfiriendo "John"o0x7fd5d258dd00

Pasar "John"se conoce como pasar por valor. Pasar 0x7fd5d258dd00se conoce como pasar por referencia. Cualquiera que señale esta ubicación de memoria tendrá acceso al valor de "John".

Para más información sobre esto, le recomiendo que lea sobre la desreferenciación de un puntero y también por qué elegir struct (tipo de valor) sobre clase (tipo de referencia)

Miel
fuente
3
Eso es lo que estaba buscando, en realidad uno debería buscar el concepto no solo la explicación, pulgares arriba hermano.
Haisum Usman
Java siempre se pasa por valor. Pasar referencias a objetos en Java se considera pasar por valor. Esto contradice su afirmación "Pasar 0x7fd5d258dd00 se conoce como pasar por referencia".
chetan raina
53

Aquí hay un ejemplo:

#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
}
pyon
fuente
1
Creo que hay un problema, ya que la última línea debería imprimir 0 en lugar de 2. Por favor, dígame si me falta algo.
Taimoor Changaiz
@TaimoorChangaiz; ¿Qué "última línea"? Por cierto, si puede usar IRC, vaya a la programación ## en Freenode. Sería mucho más fácil explicar las cosas allí. Mi nick es "pyon".
pyon
1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // impresiones 2
Taimoor Changaiz
55
@TaimoorChangaiz: ¿Por qué no imprimiría 2? yya se ha establecido en 2 por la línea anterior. ¿Por qué volvería a 0?
pyon
@ EduardoLeón mi mal. Sí, tiene usted razón. Gracias por la corrección
Taimoor Changaiz
28

La forma más sencilla de obtener esto es en un archivo de Excel. Digamos, por ejemplo, que tiene dos números, 5 y 2 en las celdas A1 y B1 en consecuencia, y desea encontrar su suma en una tercera celda, digamos A2. Puede hacer esto de dos maneras.

  • O bien pasando sus valores a la celda A2 escribiendo = 5 + 2 en esta celda. En este caso, si los valores de las celdas A1 o B1 cambian, la suma en A2 sigue siendo la misma.

  • O pasando las "referencias" de las celdas A1 y B1 a la celda A2 escribiendo = A1 + B1 . En este caso, si los valores de las celdas A1 o B1 cambian, la suma en A2 también cambia.

Than Skourtan
fuente
Este es el ejemplo más simple y el mejor entre todas las demás respuestas.
Amit Ray
18

Al pasar por ref, básicamente está pasando un puntero a la variable. Pase por valor está pasando una copia de la variable. En el uso básico, esto normalmente significa que los cambios de referencia a la variable se verán como el método de llamada y pasarán por el valor que no.

Craig
fuente
12

Pase por valor envía una COPIA de los datos almacenados en la variable que especifique, pase por referencia envía un enlace directo a la variable en sí. Entonces, si pasa una variable por referencia y luego cambia la variable dentro del bloque al que la pasó, la variable original cambiará. Si simplemente pasa por valor, la variable original no podrá ser cambiada por el bloque al que la pasó, pero obtendrá una copia de lo que contenía en el momento de la llamada.

MetaGuru
fuente
7

Pasar por valor: la función copia la variable y funciona con una copia (por lo que no cambia nada en la variable original)

Pasar por referencia: la función usa la variable original; si cambia la variable en la otra función, también cambia en la variable original.

Ejemplo (copie y use / intente esto usted mismo y vea):

#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;
}

Mantenlo simple, píos. Los muros de texto pueden ser un mal hábito.

usuario326964
fuente
Esto es realmente útil para entender si el valor del parámetro fue cambiado o no, ¡gracias!
Kevin Zhao
5

Una diferencia importante entre ellas es que las variables de tipo de valor almacenan valores, por lo que especificar una variable de tipo de valor en una llamada al método pasa una copia del valor de esa variable al método. Las variables de tipo de referencia almacenan referencias a objetos, por lo que al especificar una variable de tipo de referencia como argumento se pasa al método una copia de la referencia real que se refiere al objeto. Aunque la referencia en sí misma se pasa por valor, el método aún puede usar la referencia que recibe para interactuar con, y posiblemente modificar, el objeto original. De manera similar, cuando se devuelve información de un método a través de una declaración de devolución, el método devuelve una copia del valor almacenado en una variable de tipo de valor o una copia de la referencia almacenada en una variable de tipo de referencia. Cuando se devuelve una referencia, el método de llamada puede usar esa referencia para interactuar con el objeto referenciado. Entonces,

En c #, para pasar una variable por referencia para que el método llamado pueda modificar la variable, C # proporciona palabras clave ref y out. La aplicación de la palabra clave ref a una declaración de parámetro le permite pasar una variable a un método por referencia: el método llamado podrá modificar la variable original en la persona que llama. La palabra clave ref se usa para variables que ya se han inicializado en el método de llamada. Normalmente, cuando una llamada al método contiene una variable no inicializada como argumento, el compilador genera un error. Preceder un parámetro con una palabra clave fuera crea un parámetro de salida. Esto indica al compilador que el argumento se pasará al método llamado por referencia y que el método llamado asignará un valor a la variable original en la persona que llama. Si el método no asigna un valor al parámetro de salida en cada ruta de ejecución posible, el compilador genera un error. Esto también evita que el compilador genere un mensaje de error para una variable no inicializada que se pasa como argumento a un método. Un método puede devolver solo un valor a su llamador a través de una declaración de retorno, pero puede devolver muchos valores al especificar múltiples parámetros de salida (ref y / o out).

vea la discusión de C # y ejemplos aquí enlace de texto

Tina Endresen
fuente
3

Ejemplos:

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

const &Generalmente es lo mejor. No incurres en la pena de construcción y destrucción. Si la referencia no es constante, su interfaz sugiere que cambiará los datos pasados.

Deduplicador
fuente
2

En resumen, Pasado por valor es QUÉ es y pasado por referencia es DONDE está.

Si su valor es VAR1 = "Happy Guy!", Solo verá "Happy Guy!". Si VAR1 cambia a "Happy Gal!", No lo sabrá. Si se pasa por referencia y VAR1 cambia, lo hará.

Monstruo
fuente
2

Si no desea cambiar el valor de la variable original después de pasarla a una función, la función debe construirse con un parámetro " pasar por valor ".

Entonces la función tendrá SOLO el valor pero no la dirección de la variable pasada en. Sin la dirección de la variable, el código dentro de la función no puede cambiar el valor de la variable como se ve desde el exterior de la función.

Pero si desea dar a la función la capacidad de cambiar el valor de la variable como se ve desde el exterior, debe usar pasar por referencia . Como tanto el valor como la dirección (referencia) se pasan y están disponibles dentro de la función.

Stanley
fuente
1

pasar por valor significa cómo pasar valor a una función haciendo uso de argumentos. en pasar por valor copiamos los datos almacenados en la variable que especificamos y es más lento que pasar por referencia, ya que los datos se copian. de los cambios realizados en los datos copiados, los datos originales no se ven afectados. En caso de pasar por referencia o pasar por dirección, enviamos un enlace directo a la variable misma. o pasar el puntero a una variable. es más rápido porque se consume menos tiempo

abhinisha thakur
fuente
0

Aquí hay un ejemplo que demuestra las diferencias entre pasar por valor - valor del puntero - referencia :

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
}

El método de "pasar por referencia" tiene una limitación importante . Si un parámetro se declara como aprobado por referencia (por lo que está precedido por el signo &), su parámetro real correspondiente debe ser una variable .

Un parámetro real que se refiere al parámetro formal "pasado por valor" puede ser una expresión en general, por lo que está permitido usar no solo una variable sino también un resultado de invocación literal o incluso de función.

La función no puede colocar un valor en algo que no sea una variable. No puede asignar un nuevo valor a un literal ni forzar a una expresión a cambiar su resultado.

PD: También puede verificar la respuesta de Dylan Beattie en el hilo actual que lo explica en palabras simples.

BugShotGG
fuente
Usted declara "si un parámetro se declara [como referencia] su parámetro real correspondiente debe ser una variable", pero eso no es cierto en general. Si una referencia está vinculada a una temporal (como el valor de retorno de una función), su duración se extiende para coincidir con la referencia. Ver aquí para más detalles.
Chris Hunt