in

agnóstico del lenguaje: ¿cuál es la diferencia entre pasar por referencia y pasar por valor?

apple touch icon@2

Muchas respuestas aquí (y en particular la respuesta más votada a favor) son fácticamente incorrectas, ya que malinterpretan 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 pasas valores como argumentos de función
  • llamar por referencia significa que pasas variables como argumentos de función

En términos metafóricos:

  • Llamar por valor es donde Escribo algo en un papel y te lo doy. 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 le he dado, por lo que ahora está efectivamente tu pedazo de papel. Ahora eres libre de garabatear en esa hoja de papel, o usar esa hoja de papel para encontrar algo en otro lugar y jugar con él, lo que sea.
  • Llamar por referencia es cuando Te doy mi cuaderno que tiene algo escrito en él.. Puedes garabatear en mi cuaderno (tal vez yo quiera que lo hagas, tal vez no), y luego me quedo con mi cuaderno, con los garabatos que hayas puesto allí. Además, si lo que escribimos tú o yo es información sobre cómo encontrar algo en otro lugar, tú o yo podemos ir allí y jugar con esa información.

Qué «llamar por valor» y «llamar por referencia» no significar

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 Objecty en C # todos class 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 en sí misma una pieza de información y es una referencia (a puntero, si lo desea) a otra información. Puede tener muchas copias de una URL en diferentes lugares, y no cambian el sitio web al que se vinculan; si el sitio web se actualiza, todas las copias de URL seguirán 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. Ej. int&) es decir no como los «tipos de referencia» de Java y C #, pero es como «llamar por referencia». «Tipos de referencia» de Java y C #, y todos tipos en Python, son como lo que C y C ++ llaman «tipos de puntero» (p. ej. int*).


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

Terminología

Para empezar, quiero resaltar algunos fragmentos importantes de terminología, para ayudar a aclarar mi respuesta y asegurarme de que todos nos referimos 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 se debe al uso de palabras que no comunican completamente el significado que se pretendía).

Para empezar, aquí hay un ejemplo en un lenguaje similar a C de una declaración de función:

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

Y aquí hay un ejemplo de cómo 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:

  • foo es un función declarado en la línea 1 (Java insiste en hacer que todas las funciones sean métodos, pero el concepto es el mismo sin pérdida de generalidad; C y C ++ hacen una distinción entre declaración y definición que no entraré aquí)
  • param es un parámetro formal para foo, también declarado en la línea 1
  • arg es un variable, específicamente un variable local de la función bar, declarado e inicializado en la línea 2
  • arg es también un argumento a un específico invocación de foo en la línea 3

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

  • A valor es el resultado de evaluar una expresión en el idioma. Por ejemplo, en el bar función arriba, después de la línea int arg = 1;, la expresion arg tiene el valor 1.
  • A variable es un contenedor de valores. Una variable puede ser mutable (este es el valor predeterminado en la mayoría de los lenguajes similares a C), de solo lectura (por ejemplo, declarada usando Java final o C # readonly) o profundamente inmutable (por ejemplo, usando C ++ const).

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

  • A parámetro (también llamado parámetro formal) es un variable que debe ser proporcionado por la persona que llama al llamar a una función.
  • Un argumento es un valor que es proporcionado por el llamador de una función para satisfacer un parámetro formal específico de esa función

Llamar por valor

En llamar 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 el valores de sus argumentos.

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

int arg = 1;
int another_variable = arg;

Aquí arg y another_variable son variables completamente independientes; sus valores pueden cambiar independientemente unos de otros. Sin embargo, en el punto donde another_variable se declara, se inicializa para contener el mismo valor que arg sostiene – que es 1.

Dado que son variables independientes, los cambios a another_variable no afecta 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 arg y param en nuestro ejemplo anterior, que repetiré aquí para la 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 llamar por valor significa es que el destinatariofoo en este caso) recibe valores como argumentos, pero tiene su propia variables para esos valores de las variables de la persona que llama (bar en este caso).

Volviendo a mi metáfora anterior, si estoy bar y tú eres foo, cuando te llamo, te entrego un trozo de papel con un valor escrito en él. Tu llamas ese pedazo de papel param. Ese valor es un Copiar del valor que he escrito en mi cuaderno (mis variables locales), en una variable que llamo arg.

(Como comentario al margen: dependiendo del hardware y el sistema operativo, hay varios convenciones de llamadas sobre cómo se llama a una función desde otra. La convención de convocatoria es como si decidiéramos si escribo el valor en una hoja de mi papel y luego se lo doy, o si tienes una hoja de papel en la que lo escribo, o si lo escribo en la pared frente a nosotros dos. Este también es un tema interesante, pero mucho más allá del alcance de esta ya larga respuesta).

Llamar por referencia

En llamar por referencia, los parámetros formales de la función son simplemente nuevos nombres para las mismas variables que la persona que llama 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

Ya que param es solo otro nombre para arg – es decir, son la misma variable, cambios a param se reflejan en arg. Esta es la forma fundamental en la 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, param no solo tiene lo mismo valor como arg, En realidad es arg (solo por un nombre diferente) y así bar puedo observar eso arg se ha incrementado.

Tenga en cuenta que esto es no cómo funciona Java, JavaScript, C, Objective-C, Python o casi cualquier otro lenguaje popular en la actualidad. Esto significa que esos idiomas son no llamar por referencia, se llaman por valor.

Anexo: llamada por intercambio de objetos

Si lo que tienes es llamar por valor, pero el valor real es un tipo de referencia o 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 la plataforma). Lo interesante es cuál es ese valor puntos a.

Si a lo que apunta ese tipo de referencia (es decir, puntero) es mudable entonces es posible un efecto interesante: puede modificar el valor apuntado, y la persona que llama puede observar cambios en el valor apuntado, aunque la persona que llama no puede observar cambios en el puntero en sí.

Para tomar prestada de nuevo la analogía de la URL, el hecho de que te di una Copiar de la URL de un sitio web no es particularmente interesante si lo que nos importa a ambos es el sitio web, no la URL. El hecho de que garabatees sobre tu 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 el valor del tipo de referencia, puede no se puede modificar en absoluto, solo lo que apunta).

Barbara Liskov, cuando inventó el lenguaje de programación CLU (que tenía esta semántica), 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: llamar por intercambio de objetos.

Cuando se habla de lenguajes que técnicamente son llamados por valor, pero donde los tipos comunes en uso son tipos de referencia o puntero (es decir: casi todos los lenguajes de programación imperativos modernos, orientados a objetos o de múltiples paradigmas), encuentro que es mucho menos confuso simplemente evita hablar de llamar por valor o llamar por referencia. Atenerse a llamar por intercambio de objetos (o simplemente llamar por objeto) y nadie se confundirá. 🙂

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

HTTP: solicitudes

gfg 200x200 min

Excepción de puntero nulo en Java