Cuál es la diferencia entre
- un parámetro pasado por referencia
- un parámetro pasado por valor?
¿Me podría dar algunos ejemplos, por favor?
language-agnostic
pass-by-reference
pass-by-value
Deduplicador
fuente
fuente
Respuestas:
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 .
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:
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:
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.
fuente
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:
ref
utilizada en la persona que llama y llamada función). Jon Skeet también tiene una buena explicación de esto aquí .Códigos
Como mi lenguaje es C ++, lo usaré aquí
Y un ejemplo en Java no hará daño:
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
fuente
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:
En términos metafóricos:
¿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 losclass
tipos), 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 ejemploint*
).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:
Y aquí hay un ejemplo de llamar a esta función:
Usando este ejemplo, quiero definir algunos bits importantes de terminología:
foo
es 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í)param
es un parámetro formal parafoo
, también declarado en la línea 1arg
es una variable , específicamente una variable local de la funciónbar
, declarada e inicializada en la línea 2arg
también es un argumento para una invocación específica defoo
en la línea 3Hay dos conjuntos de conceptos muy importantes para distinguir aquí. El primero es el valor versus la variable :
bar
función anterior, después de la líneaint arg = 1;
, la expresiónarg
tiene el valor1
.final
o C # 'sreadonly
) o profundamente inmutable (por ejemplo, utilizando C ++' sconst
).El otro par importante de conceptos para distinguir es parámetro versus argumento :
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:
Aquí
arg
yanother_variable
son variables completamente independientes: sus valores pueden cambiar independientemente uno del otro. Sin embargo, en el punto dondeanother_variable
se declara, se inicializa para mantener el mismo valor quearg
tiene, que es1
.Como son variables independientes, los cambios
another_variable
no afectanarg
:Esto es exactamente lo mismo que la relación entre
arg
yparam
en nuestro ejemplo anterior, que repetiré aquí por simetría:Es exactamente como si hubiéramos escrito el código de esta manera:
Es decir, la característica definitoria de lo que significa llamar por valor es que la persona que llama (
foo
en este caso) recibe valores como argumentos, pero tiene sus propias variables separadas para esos valores de las variables de la persona que llama (bar
en este caso).Volviendo a mi metáfora anterior, si soy
bar
y eresfoo
, cuando te llamo, te entrego un papel con un valor escrito en él. Llamas a ese pedazo de papelparam
. Ese valor es una copia del valor que he escrito en mi cuaderno (mis variables locales), en una variable que llamoarg
.(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:
Dado que
param
es solo otro nombre paraarg
, es decir, son la misma variable , los cambiosparam
se reflejan enarg
. 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í:
En este caso,
param
no solo tiene el mismo valor quearg
, en realidad lo esarg
(solo por un nombre diferente) y, porbar
lo tanto, puede observar quearg
se 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á. :-)
fuente
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Antes de comprender los 2 términos, DEBE comprender lo siguiente. Cada objeto tiene 2 cosas que pueden distinguirlo.
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. Pasar0x7fd5d258dd00
se 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)
fuente
Aquí hay un ejemplo:
fuente
y
ya se ha establecido en 2 por la línea anterior. ¿Por qué volvería a 0?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.
fuente
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.
fuente
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.
fuente
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):
Mantenlo simple, píos. Los muros de texto pueden ser un mal hábito.
fuente
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
fuente
Ejemplos:
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.fuente
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á.
fuente
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.
fuente
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
fuente
Aquí hay un ejemplo que demuestra las diferencias entre pasar por valor - valor del puntero - referencia :
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.
fuente