In che modo un riferimento Java è diverso da un puntatore C?


97

C ha puntatori e Java ha quelli che vengono chiamati riferimenti. Hanno alcune cose in comune nel senso che indicano tutte qualcosa. So che i puntatori in C memorizzano gli indirizzi a cui puntano. Il riferimento memorizza anche l'indirizzo? In che modo differiscono se non che il puntatore è più flessibile e soggetto a errori?


10
nota C ++ ha anche riferimenti che sono diversi da puntatori o riferimenti java
jk.

@jk. Ho pensato che sarebbe stato lo stesso di Java. Qual è la differenza?
Gnijuohz,

17
I riferimenti C ++ non sono ricambiabili (ovvero non è possibile modificare l'oggetto designato) e non sono nullable (ovvero non è possibile validamente farli fare riferimento a nessun oggetto).
AProgrammer

@Gnijuohz Sostituendo ciò che ha detto @AProgrammer: un finalriferimento in Java è quasi equivalente a un riferimento C ++. Il motivo per cui non sono esattamente equivalenti è che un riferimento C ++ non è inoltre nullable, mentre un finalriferimento in Java è nullable.
Utku,

Risposte:


142

I riferimenti potrebbero essere implementati memorizzando l'indirizzo. Di solito i riferimenti Java verranno implementati come puntatori, ma ciò non è richiesto dalla specifica. Potrebbero utilizzare un ulteriore livello di riferimento indiretto per consentire una più semplice raccolta dei rifiuti. Ma alla fine si ridurrà (quasi sempre) a puntatori (stile C) coinvolti nell'implementazione di riferimenti (stile Java).

Non è possibile eseguire l'aritmetica del puntatore con i riferimenti. La differenza più importante tra un puntatore in C e un riferimento in Java è che in realtà non è possibile ottenere (e manipolare) il valore sottostante di un riferimento in Java. In altre parole: non è possibile eseguire l'aritmetica del puntatore.

In C puoi aggiungere qualcosa a un puntatore (cioè l'indirizzo) o sottrarre qualcosa per indicare cose che sono "vicine" o puntare a luoghi che sono in qualsiasi luogo.

In Java, un riferimento indica solo una cosa. Puoi fare in modo che una variabile contenga un riferimento diverso , ma non puoi semplicemente chiederle di indicare "la cosa dopo la cosa originale".

I riferimenti sono fortemente tipizzati. Un'altra differenza è che il tipo di riferimento è molto più rigorosamente controllato in Java rispetto al tipo di puntatore in C. In C è possibile avere un int*cast e inserirlo in un char*e reinterpretare la memoria in quella posizione. Questa reinterpretazione non funziona in Java: puoi solo interpretare l'oggetto all'altro capo del riferimento come qualcosa che è già (cioè puoi lanciare un Objectriferimento a Stringriferimento solo se l'oggetto indicato è effettivamente a String).

Queste differenze rendono i puntatori C più potenti, ma anche più pericolosi. Entrambe queste possibilità (puntatore aritmetico e reinterpretazione dei valori indicati) aggiungono flessibilità a C e sono la fonte di parte del potere del linguaggio. Ma sono anche grandi fonti di problemi, perché se usati in modo errato possono facilmente infrangere i presupposti che il tuo codice è costruito intorno. Ed è abbastanza facile usarli in modo errato.


18
+1 per potenza . Non fare affidamento sui dettagli di implementazione.
un CVn il

2
+1 La raccolta dei rifiuti non merita di essere menzionata come punto in grassetto specifico? È un altro modo in cui i puntatori C sono più potenti ma anche più pericolosi (rischio di puntini penzolanti alla memoria liberata che causa corruzione della memoria, rischio di perdite di memoria)
MarkJ

Un'altra differenza tra riferimenti e puntatori è che un puntatore in C può essere convertito in una sequenza di numeri (ad es. Usando memcpyper spostarne uno in a char[]) e viceversa. Se un puntatore viene convertito in una sequenza di numeri che viene memorizzata da qualche parte (forse mostrato sullo schermo e copiato dall'operatore su una distinta di carta), tutte le copie del puntatore all'interno del computer vengono distrutte e quella sequenza di numeri viene convertita tornando a un puntatore (forse dopo essere stato digitato dall'operatore), il puntatore deve ancora puntare alla stessa cosa che aveva fatto prima. Un programma che ...
supercat

... visualizzare un puntatore come numeri, quindi convertire i numeri immessi manualmente in un puntatore, potrebbe essere "malvagio", ma non invocherebbe alcun comportamento indefinito a meno che l'operatore non abbia digitato numeri che non erano stati mostrati per formare un valido puntatore. La garbage collection per scopi generici è quindi impossibile in C completamente portatile, perché non c'è modo che il computer possa sapere se una copia di un puntatore potrebbe esistere da qualche parte nell'universo.
supercat

Secondo JLS, §4.3.1, i riferimenti in Java sono puntatori, quindi "Normalmente i riferimenti Java verranno implementati come puntatori, ma ciò non è richiesto dalle specifiche". è falso.
Lew Bloch,

8

I riferimenti C ++ sono di nuovo diversi.

Devono essere inizializzati e non possono essere nulli (almeno non in un programma ben formato) e non possono essere ripristinati per fare riferimento a qualcos'altro. un riferimento C ++ è molto più simile a un alias per un oggetto.

Un'altra importante differenza tra puntatori e riferimenti Java / C ++ è che puoi prendere l'indirizzo di un puntatore a cui non puoi accedere all'indirizzo di un riferimento (in effetti un riferimento C ++ non deve in realtà esistere come oggetto in memoria) di conseguenza puoi avere un puntatore a un puntatore ma non un riferimento a un riferimento


4

Riferimenti Java e puntatori C differiscono esattamente in due punti:

  1. Non c'è aritmetica puntatore per il primo.
  2. E non è possibile creare un riferimento Java a ciò che si desidera, è possibile copiare solo quelli salvati da qualche parte accessibili (campi statici, campi di oggetti, variabili locali) o restituiti da invocazioni di funzioni (come le chiamate del costruttore), che quindi fanno tutti riferimento a Java oggetti (mai a tipi di base come riferimenti, char, inte così via).

Qualcuno ha scritto che i riferimenti sono fortemente tipizzati, perché non è possibile forzare il compilatore a trattare un int*come char*.
A parte il fatto che quella particolare conversione è effettivamente sicura , non c'è polimorfismo in C, quindi il confronto non è un inizio.
Certamente Java è più fortemente tipizzato di C, non che sia una caratteristica dei puntatori C rispetto ai riferimenti Java, è necessario utilizzare JNI per interrompere la sicurezza dei tipi (oltre a ignorare le restrizioni generiche), ma anche in C è necessario forzare il compilatore.

Qualcuno ha scritto che i riferimenti Java possono essere implementati come puntatori C, alla quale dico sicuro, in quanto sono strettamente meno potente, su macchine 32Bit che in genere sono, se la JVM è implementato in C . Sebbene su macchine a 64 bit, sono normalmente puntatori di oggetti ordinari compressi ("OOP compressi") per risparmiare spazio e larghezza di banda.
In ogni caso, questi puntatori C non devono necessariamente essere equivalenti agli indirizzi hardware, anche se in genere (> 99% delle implementazioni) sono per motivi di prestazioni.
Infine, si tratta di un dettaglio di implementazione che non è esposto al programmatore.


-1

Sono leggermente diversi. In Java una copia del riferimento viene copiata nello stack di una funzione chiamata, indicando lo stesso oggetto della funzione chiamante e consentendo di manipolarlo. Tuttavia, non è possibile modificare l'oggetto a cui fa riferimento la funzione chiamante.

Considera il seguente codice 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"
}

Ora considera un puntatore in 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;
}

Ho pensato di aggiungere che potrebbe essere visto come simile a un cane da
costante

2
È possibile modificare l'oggetto puntato in Java con la stessa facilità di C ++. Devi solo avere accesso ai membri giusti. Gli oggetti Java non hanno operatori di assegnazione perché Java non supporta il sovraccarico dell'operatore definito dall'utente, quindi non è possibile utilizzare un operatore sovraccarico, un grosso problema. Questa mancanza di Java non ha nulla a che fare con il fatto che i riferimenti Java sono gli stessi dei puntatori C ++ privi di aritmetica dei puntatori.
Deduplicatore
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.