¿En qué se diferencia una referencia de Java de un puntero en C?

97

C tiene punteros y Java tiene lo que se llama referencias. Tienen algunas cosas en común en el sentido de que todas apuntan a algo. Sé que los punteros en C almacenan las direcciones a las que apuntan. ¿La referencia también almacena la dirección? ¿Cómo son diferentes, excepto que el puntero es más flexible y propenso a errores?

Gnijuohz
fuente
10
note C ++ también tiene referencias que son diferentes a punteros o referencias de Java
jk.
@jk. Pensé que sería lo mismo que en Java. ¿Cuál es la diferencia?
Gnijuohz
17
Las referencias de C ++ no son modificables (es decir, no puede cambiar el objeto designado) y no son anulables (es decir, no puede hacer que válidamente no hagan referencia a ningún objeto).
Programador
@Gnijuohz Reformulando lo que dijo @AProgrammer: Una finalreferencia en Java es casi equivalente a una referencia en C ++. La razón por la que no son exactamente equivalentes es que una referencia de C ++ tampoco es anulable, mientras que una finalreferencia en Java es anulable.
Utku

Respuestas:

142

Las referencias pueden implementarse almacenando la dirección. Por lo general, las referencias de Java se implementarán como punteros, pero eso no es requerido por la especificación. Pueden estar usando una capa adicional de indirección para permitir una recolección de basura más fácil. Pero al final (casi siempre) se reducirá a punteros (estilo C) involucrados en la implementación de referencias (estilo Java).

No puede hacer aritmética de puntero con referencias. La diferencia más importante entre un puntero en C y una referencia en Java es que en realidad no se puede obtener (y manipular) el valor subyacente de una referencia en Java. En otras palabras: no puedes hacer aritmética de punteros.

En C puede agregar algo a un puntero (es decir, la dirección) o restar algo para señalar cosas que están "cercanas" o señalar lugares que están en cualquier lugar.

En Java, una referencia apunta a una cosa y solo a esa cosa. Puede hacer que una variable contenga una referencia diferente , pero no puede simplemente pedirle que apunte a "la cosa después de la original".

Las referencias están fuertemente tipadas. Otra diferencia es que el tipo de referencia está mucho más estrictamente controlado en Java que el tipo de puntero en C. En C puede tener un int*y convertirlo en ay char*simplemente reinterpretar la memoria en esa ubicación. Esa reinterpretación no funciona en Java: solo puede interpretar el objeto en el otro extremo de la referencia como algo que ya es ( es decir, puede emitir una Objectreferencia a Stringreferencia solo si el objeto señalado es realmente a String).

Esas diferencias hacen que los punteros C sean más poderosos, pero también más peligrosos. Ambas posibilidades (aritmética de puntero y reinterpretación de los valores a los que se apunta) agregan flexibilidad a C y son la fuente de parte del poder del lenguaje. Pero son también grandes fuentes de problemas, ya que si se usan incorrectamente pueden romperse fácilmente suposiciones de que el código está construido alrededor. Y es bastante fácil usarlos incorrectamente.

Joachim Sauer
fuente
18
+1 por poder . No confíe en los detalles de implementación.
un CVn
2
+1 ¿La recolección de basura no merece mención como un punto en negrita específico? Es otra forma en que los punteros en C son más potentes pero también más peligrosos (riesgo de que los punteros cuelguen para liberar memoria causando corrupción de memoria, riesgo de pérdidas de memoria)
MarkJ
Otra diferencia entre referencias y punteros es que un puntero en C se puede convertir en una secuencia de números (por ejemplo, memcpypara mover uno a a char[]) y viceversa. Si un puntero se convierte en una secuencia de números que se almacena en algún lugar (tal vez se muestra en la pantalla y el operador lo copia en un trozo de papel), todas las copias del puntero dentro de la computadora se destruyen, y esa secuencia de números se convierte de vuelta a un puntero (tal vez después de haber sido ingresado por el operador), el puntero aún debe apuntar a lo mismo que antes. Un programa que ...
supercat
... mostraba un puntero como números, y luego convertía los números ingresados ​​manualmente en un puntero, podría ser "malvado", pero no invocaría ningún Comportamiento indefinido a menos que el operador ingresara números que no se habían mostrado como válidos puntero. La recolección de basura de uso general es, por lo tanto, imposible en C totalmente portátil, porque no hay forma de que la computadora pueda saber si una copia de un puntero podría existir en algún lugar del universo.
Supercat
Según el JLS, §4.3.1, las referencias en Java son punteros, por lo que "por lo general, las referencias de Java se implementarán como punteros, pero eso no es requerido por la especificación". Es falso.
Lew Bloch
8

Las referencias de C ++ son diferentes nuevamente.

Deben inicializarse y no pueden ser nulos (al menos no en un programa bien formado) y no pueden volver a asignarse para referirse a otra cosa. una referencia de C ++ es mucho más como un alias para un objeto.

Otra diferencia importante entre los punteros y las referencias de Java / C ++ es que puede tomar la dirección de un puntero, no puede acceder a la dirección de una referencia (de hecho, una referencia de C ++ no necesita existir realmente como un objeto en la memoria) en consecuencia, puede tener un puntero a un puntero pero no una referencia a una referencia

jk.
fuente
4

Las referencias de Java y los punteros en C difieren exactamente en dos puntos:

  1. No hay aritmética de puntero para el primero.
  2. Y no puede crear una referencia de Java a lo que quiera, solo puede copiar los guardados en algún lugar accesible (campos estáticos, campos de objetos, variables locales) o devueltos por invocaciones de funciones (como llamadas de constructor), que por lo tanto se refieren a Java objetos (nunca a los tipos básicos como referencias, char, inty así sucesivamente).

Alguien escribió que las referencias están fuertemente tipadas, porque no puede forzar al compilador a tratar un int*como a char*.
Completamente aparte del hecho de que esa conversión particular es realmente segura , no hay polimorfismo en C, por lo que la comparación no es un iniciador.
Ciertamente, Java está más fuertemente tipado que C, no es que sea una característica de los punteros de C frente a las referencias de Java, debe usar el JNI para romper la seguridad de tipos (aparte de ignorar las restricciones genéricas), pero incluso en C debe forzar El compilador.

Alguien escribió que las referencias de Java pueden ser implementados como los punteros de C, a la que me dicen seguro, ya que son estrictamente menor alcance, en las máquinas de 32Bit por lo general son, si la JVM está implementado en C . Aunque en máquinas de 64 bits, normalmente son punteros comprimidos de objetos ordinarios ("OOP comprimidos") para ahorrar espacio y ancho de banda.
De todos modos, esos punteros en C tampoco necesitan ser equivalentes a las direcciones de hardware, incluso si normalmente (> 99% de las implementaciones) son por razones de rendimiento.
Finalmente, ese es un detalle de implementación que no está expuesto al programador.

Deduplicador
fuente
-1

Son un poco diferentes. En Java, se copia una copia de la referencia en la pila de una función llamada, apuntando al mismo objeto que la función de llamada y permitiéndole manipular ese objeto. Sin embargo, no puede cambiar el objeto al que se refiere la función de llamada.

Considere el siguiente código de Java

public static void changeRValue(StringBuffer sb){
    sb = new StringBuffer("helllllo"); /*attempt to assign the reference
                                        to a new object*/
}
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("hi");     //Create a new string buffer
    changeRValue(sb);                             //Call changeRValue
    System.out.println(sb.toString());            //Prints "hi" not "hello"
}

Ahora considere un puntero en c ++:

void func(Dog* dog){
    *dog = Dog("hello world"); //Change the value of dog to a new object
}

int main(int argc, const char * argv[]) {
    Dog dog1("hi");                            //Create a dog object
    func(&dog1);                               //pass the address of dog
    cout << dog1.name;                         //Prints "hello world" not hi.
    return 0;
}
Eladian
fuente
Pensé que podría agregar que podría verse como algo similar a un
Const
2
Puede modificar el objeto señalado en Java con la misma facilidad que en C ++. Solo necesita tener acceso a los miembros correctos. Los objetos Java no tienen operadores de asignación porque Java no admite la sobrecarga de operadores definidos por el usuario, por lo que no puede utilizar un operador sobrecargado, gran cosa. Esa falta de Java no tiene nada que ver con el hecho de que las referencias de Java son las mismas que los punteros C ++ sin aritmética de punteros.
Deduplicador