2009-03-01 12 views
15

He estado estudiando Java durante unos meses y ahora estoy empezando a aprender C.¿Cuál es la diferencia entre pasar por referencia en Java y pasar un puntero en C?

Estoy un poco confundido, tuve la impresión de que pasar un objeto por referencia y pasar un puntero a ese objeto era el mismo Una cosa: pensé que la diferencia era que en Java todo el paso de los objetos se hace con punteros automáticamente, mientras que en C uno tiene que rociar pequeños asteriscos y símbolos aquí y allá. Recientemente, en una conversación, ¡me aseguraron que había una diferencia!

¿Cuál es la diferencia entre pasar por referencia y pasar un puntero?

Respuesta

39

Ni Java ni C tienen referencias pasadas. Ambos son estrictamente pasados ​​por valor.

Semántica de pasadas de referencia significa que cuando cambia el valor del parámetro en el método, la persona que llama verá ese cambio en el argumento.

Ahora, usted puede estar pensando: "¡Pero ese es el caso en Java! Si cambio un objeto durante el método, la persona que llama ve ese cambio". El objeto no es el parámetro. El parámetro es solo variable, y si cambia el valor de esa variable, la persona que llama no lo verá. Por ejemplo:

public void foo(Object x) 
{ 
    x = null; 
} 

public void caller() 
{ 
    Object y = new Object(); 
    foo(y); 
    // y is still not null! 
} 

Si el parámetro eran realmente pasado por referencia, y sería nula después. En cambio, el valor de y es solo una referencia, y esa referencia se pasa por valor. Es confuso porque la palabra "referencia" es en ambos términos, pero son cosas diferentes.

Es posible que desee ver en my article about C# parameter passing para ver lo que sería posible si Java hizo que el paso por referencia semántica, como C# hace cuando (y sólo cuando) se utiliza la palabra clave ref.

Quizás también desee consultar los comentarios al my Stack Overflow answer relacionados con el tema.

+2

Muy, muy cierto. Este es uno de los aspectos más incomprendidos de la programación que he visto. – mmcdole

+0

Sí, la referencia que se pasa por valor es una de las primeras cosas que me colgaron en C# –

+0

¡Guau! Gracias Jon, excelente respuesta! – neo2862

3

Las diferencias son sutiles. En un nivel de conjunto, en ambos casos, la dirección del objeto se pasa a la función. Sin embargo, en el caso del puntero, el parámetro es un puntero independiente que puede usarse para modificar el objeto objetivo (* pch = 'x') o puede usarse para acceder a un objeto diferente (pch = "newstr"). En el caso de referencia, el parámetro se direcciona automáticamente al objeto objetivo.

1

Si está estudiando C (en lugar de C++), entonces no hay referencias.

El término "pasar por referencia" en C simplemente significa que está pasando un puntero como la dirección en la que se almacenará el valor, para que la función pueda cambiar su valor. De lo contrario, está pasando por valor, lo que significa que se genera una copia de la variable en la pila y las modificaciones no tienen ningún impacto.

En muchos aspectos esto es similar a lo que se ve en Java, excepto que en Java no tiene que convertir explícitamente las cosas en un puntero o eliminarlas. En otras palabras, cuando pasa un puntero, la dirección se copia en la pila, de modo que si su función cambia el puntero (en lugar de los datos a los que apunta), los cambios desaparecen cuando termina la función. De manera similar, cuando pasa una referencia de objeto en Java, puede cambiar los contenidos del objeto (por ejemplo, llamando a funciones), pero cambiar el varialbe para apuntar a otro objeto no tendrá efecto una vez que se vaya.

Si usaba C++ (que se parece a C), puede pasar por referencia, en cuyo caso no necesita tratar con punteros, y los cambios a la variable en la función realmente cambian el valor externo directamente (excepto que no puedes hacer que apunte a otra cosa).

+0

Oye, ¿podría decirse que: "puntero" es un tipo de datos, y "referencia" es solo una forma * de manejar los datos? Una es una cosa, la otra es una técnica? * Estoy tratando de evitar la ambigüedad de usar "método" aquí – Ziggy

+0

Un puntero es un tipo de datos seguro. Sin embargo, en C++ (que la mayoría de las personas C eventualmente usan) hay un tipo de datos llamado referencia. Esto es un problema porque en C++ hay dos maneras de "pasar por referencia", a través de referencias reales y mediante punteros. Entonces, "pasar por referencia" es una forma. – Uri

2

Algunas cosas

  • C no tiene referencias (a pesar de C++)
  • Java tiene ningún puntero
  • La diferencia entre los punteros y referencias es, que los punteros son prácticamente direcciones de memoria se puede calcular con. Las referencias no se pueden calcular con
  • Java y pase C por valor (Al pasar un objeto en Java, la referencia se copia a la función)
+0

Te sugiero que elimines la palabra "por defecto" de la última viñeta: C y Java pasan por valor, fin de la historia. –

2

creo un puntero es una variable que contiene una referencia. Con & puede obtener una dirección de memoria (referencia), y con * puede acceder al contenido de una dirección de memoria. Sugiero el libro The C programming Language.

Saludos

2

verdadera pass-by-reference significa que se puede asignar un nuevo valor a la referencia, y este nuevo valor será visible en el contexto de llamada al igual que en el interior se llama método.

En Java, esto no es posible, porque object references are passed by value.

1

Hay un artículo reciente de Eric Lippert sobre esto. Él es un programador en el compilador de C#, pero lo que él dice que tiene suficiente generalidad se extienda a otros idiomas GC como Java:

http://blogs.msdn.com/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx

cita del artículo:

punteros son estrictamente "más poderoso" que las referencias; todo lo que puede hacer con referencias que puede hacer con punteros. [...]

Los punteros se implementan típicamente como direcciones. Una dirección es un número que se compensa con la "matriz de bytes" que es el espacio completo de direcciones virtuales del proceso. [...]

Por todas estas razones, no describimos las referencias como direcciones en la especificación. La especificación solo dice que una variable del tipo de referencia "almacena una referencia" a un objeto, y lo deja completamente vago en cuanto a cómo se podría implementar. De manera similar, una variable de puntero almacena "la dirección" de un objeto, que de nuevo queda bastante vaga. En ninguna parte decimos que las referencias son las mismas que las direcciones.

Por lo tanto, en C# una referencia es algo impreciso que le permite hacer referencia a un objeto. No puede hacer nada con una referencia, excepto desreferenciarla, y compararla con otra referencia para igualdad. Y en C#, un puntero se identifica como una dirección.

En contraste con una referencia, puede hacer mucho más con un puntero que contiene una dirección. Las direcciones pueden ser manipuladas matemáticamente; puede restar uno de otro, puede agregar enteros a ellos, y así sucesivamente. Sus operaciones legales indican que son "números extravagantes" que se indexan en la "matriz" que es el espacio de direcciones virtuales del proceso.

+0

Eso está hablando de referencias en lugar de "pasar por referencia": están relacionados pero con conceptos diferentes. –

7

Pensé que la diferencia era que en Java todos los objetos que pasan se hacen con punteros automáticamente, mientras que en C uno tiene que rociar pequeños asteriscos y símbolos aquí y allá.

Conceptional, eso es correcto. Si somos pedantes (y eso es algo bueno), podemos incluso decir que los objetos no se pasan en absoluto en Java. Lo que se pasa es solo el "puntero", que en Java se llama referencia. Todo el direccionamiento indirecto se hace automáticamente. Así que cuando haces "objref-> foo" en lugar de "objref.foo" en C y no puedes usar el punto porque trabajas con un puntero, en Java aún puedes usar el punto porque no sabe nada más de acceder a los miembros de todos modos. En C, puede pasar el puntero (y aquí, es realmente llamado puntero) y puede pasar el objeto en sí, en cuyo caso se pasa una copia. En C, puede acceder al objeto al que se refiere un puntero mediante direccionamiento indirecto utilizando la estrella o "->". En Java, la única forma en que se accede a los objetos de todas formas es mediante el uso del punto (objref.member).

Si volvemos a ser pedantes (ahora más importante otra vez), ni en Java ni en C hay "referencias pasadas". Lo que pasamos en Java es la referencia/puntero al objeto y en C pasamos una copia del objeto (caso obvio) o volvemos a pasar solo una copia de un puntero al objeto. Entonces, en ambos casos, Java y C, lo que pasamos son las direcciones de los objetos. No se trata de tipos de Java primitivos, que se copian tanto en Java como en C: aunque también puede pasar su dirección en C, no hay diferencia entre un tipo de agregado (es decir, una estructura) y un "tipo primitivo" en C en esa consideración.

0

no estoy seguro acerca de Java pero en C# o VB.net se puede pasar por valor o por refrence

ejemplo pasando por refrence

static void Main(string[] args) 
    { 
     int x = 1; 
     Foo(ref x); 
     Console.WriteLine(x.ToString());//this will print 2 
    } 

    private static void Foo(ref int x) 
    { 
     x++; 
    } 

aviso de que x es igual a 1, pero cuando se llama el método Foo y pasar x por refrence cuando x aumenta en uno en Foo el valor original aumenta también

1

Los lectores familiarizados con C/C++ probablemente han notado que las referencias a objetos aparecen para ser similares a los punteros. Esta sospecha es, esencialmente, correcta. Una referencia de objeto es similar a un puntero de memoria. La diferencia principal -y la clave para la seguridad de Java- es que no puede manipular referencias como puede hacerlo con punteros reales. Por lo tanto, no puede hacer que una referencia de objeto apunte a una ubicación de memoria arbitraria o la manipule como un entero.

  • java no compatible & operador entonces cómo puede obtener la dirección del objeto en java.
  • En referencia java es realizado por objeto por ejemplo

MyClass object1 = new MyClass();

MyClass object2 = object1;

es decir, podría pensar que object1 y object2 se refieren a objetos separados y distintos. Sin embargo, esto sería incorrecto. En cambio, después de que este fragmento se ejecute, object1 y object2 se referirán al mismo objeto. si cambias en cualquier otro efecto.

* no se puede cambiar la dirección del objeto después de la inicialización es decir MyClass obj = new MyClass(); como referencia en C++, con el objeto que se puede cambiar sólo oponerse valor de instancia

Nota: Java proporcionar una característica especial objeto1 se ha establecido en nulo, pero objeto2 aún apunta al objeto original.

Cuestiones relacionadas