Wie unterscheidet sich eine Java-Referenz von einem C-Zeiger?


97

C hat Zeiger und Java hat sogenannte Referenzen. Sie haben einige Gemeinsamkeiten in dem Sinne, dass sie alle auf etwas hinweisen. Ich weiß, dass Zeiger in C die Adressen speichern, auf die sie zeigen. Speichert die Referenz auch die Adresse? Wie unterscheiden sie sich, außer dass der Zeiger flexibler und fehleranfälliger ist?


10
note C ++ hat auch Referenzen, die sich von Zeigern oder Java-Referenzen unterscheiden
jk.

@jk. Ich dachte, es wäre dasselbe wie in Java. Was ist der Unterschied?
Gnijuohz

17
C ++ - Referenzen können nicht erneut gebunden werden (dh Sie können das angegebene Objekt nicht ändern) und sind nicht nullfähig (dh Sie können nicht zulassen, dass sie überhaupt kein Objekt referenzieren).
AProgrammer

@Gnijuohz Umformulieren von @AProgrammer: Eine finalReferenz in Java entspricht fast einer C ++ - Referenz. Der Grund, warum sie nicht genau gleichwertig sind, ist, dass eine C ++ - Referenz nicht nullwertfähig ist, wohingegen eine finalReferenz in Java nullwertfähig ist.
Utku

Antworten:


142

Referenzen können durch Speichern der Adresse implementiert werden. In der Regel werden Java-Referenzen als Zeiger implementiert, dies ist jedoch in der Spezifikation nicht erforderlich. Möglicherweise verwenden sie eine zusätzliche Indirektionsebene, um die Garbage Collection zu vereinfachen. Am Ende kommt es jedoch (fast immer) darauf an, dass (C-artige) Zeiger an der Implementierung von (Java-artigen) Referenzen beteiligt sind.

Sie können mit Referenzen keine Zeigerarithmetik ausführen. Der wichtigste Unterschied zwischen einem Zeiger in C und einer Referenz in Java besteht darin, dass Sie den zugrunde liegenden Wert einer Referenz in Java tatsächlich nicht ermitteln (und bearbeiten) können. Mit anderen Worten: Sie können keine Zeigerarithmetik ausführen.

In C können Sie einem Zeiger (dh der Adresse) etwas hinzufügen oder etwas subtrahieren, um auf "nahe" liegende Objekte oder Orte zu verweisen, die sich an einem beliebigen Ort befinden.

In Java verweist ein Verweis auf eine Sache und nur auf diese Sache. Sie können festlegen, dass eine Variable einen anderen Verweis enthält, aber Sie können sie nicht einfach bitten, auf "das Objekt nach dem ursprünglichen Objekt" zu verweisen.

Referenzen sind stark typisiert. Ein weiterer Unterschied besteht darin, dass der Typ einer Referenz in Java viel strenger gesteuert wird als der Typ eines Zeigers in C. In C können Sie ein haben int*und es in ein char*umwandeln und den Speicher an dieser Stelle einfach neu interpretieren. Diese Neuinterpretation funktioniert in Java nicht: Sie können das Objekt am anderen Ende der Referenz nur als etwas interpretieren, das es bereits ist (dh Sie können eine Referenz nur dann auf eine ObjectReferenz Stringumwandeln , wenn das Objekt, auf das verwiesen wird, tatsächlich eine ist String).

Diese Unterschiede machen C-Zeiger leistungsfähiger, aber auch gefährlicher. Beide Möglichkeiten (Zeigerarithmetik und Neuinterpretation der Werte, auf die verwiesen wird) verleihen C Flexibilität und sind die Quelle für einen Teil der Macht der Sprache. Sie sind jedoch auch eine große Problemquelle, da sie bei falscher Verwendung leicht die Annahme aufheben können, dass Ihr Code darauf aufgebaut ist. Und es ist ziemlich einfach, sie falsch zu benutzen.


18
+1 für Macht . Verlassen Sie sich nicht auf Implementierungsdetails.
ein Lebenslauf vom

2
+1 Ist die Müllabfuhr nicht als ein bestimmter fetter Punkt zu erwähnen? Es ist eine andere Möglichkeit, dass C-Zeiger leistungsfähiger, aber auch gefährlicher sind (Risiko, dass Zeiger auf den freigegebenen Speicher baumeln und Speicherbeschädigung verursachen, Risiko von Speicherlecks)
MarkJ

Ein weiterer Unterschied zwischen Referenzen und Zeigern besteht darin, dass ein Zeiger in C in eine Folge von Zahlen umgewandelt werden kann (z. B. durch memcpyVerschieben eines Zeigers nach a char[]) und umgekehrt. Wenn ein Zeiger in eine Folge von Zahlen konvertiert wird, die irgendwo gespeichert ist (möglicherweise auf dem Bildschirm angezeigt und vom Bediener auf einen Zettel kopiert), werden alle Kopien des Zeigers im Computer zerstört und diese Folge von Zahlen konvertiert Zurück zu einem Zeiger (möglicherweise nachdem er vom Bediener eingegeben wurde), muss der Zeiger immer noch auf dasselbe zeigen, was er zuvor getan hat. Ein Programm, das ...
Supercat

... einen Zeiger als Zahlen anzeigt und dann manuell eingegebene Zahlen in einen Zeiger umwandelt, ist möglicherweise "böse", ruft jedoch kein undefiniertes Verhalten auf, es sei denn, der Operator hat Zahlen eingegeben, die nicht als gültig angezeigt wurden Zeiger. Eine universelle Speicherbereinigung ist daher in vollständig portierbarem C nicht möglich, da der Computer nicht erkennen kann, ob eine Kopie eines Zeigers irgendwo im Universum vorhanden ist.
Supercat

Laut JLS, §4.3.1, sind Referenzen in Java Zeiger. "Normalerweise werden Java-Referenzen als Zeiger implementiert, aber dies ist in der Spezifikation nicht erforderlich." ist falsch.
Lew Bloch

8

C ++ Referenzen sind wieder anders.

Sie müssen initialisiert werden und dürfen nicht null sein (zumindest nicht in einem wohlgeformten Programm) und können nicht neu gesetzt werden, um auf etwas anderes zu verweisen. Eine C ++ - Referenz ähnelt eher einem Alias ​​für ein Objekt.

Ein weiterer wichtiger Unterschied zwischen Zeigern und Java / C ++ - Referenzen besteht darin, dass Sie die Adresse eines Zeigers verwenden können, auf den Sie nicht zugreifen können (tatsächlich muss eine C ++ - Referenz überhaupt nicht als Objekt im Speicher vorhanden sein) Zeiger auf einen Zeiger, aber kein Verweis auf einen Verweis


4

Java-Referenzen und C-Zeiger unterscheiden sich in genau zwei Punkten:

  1. Für Ersteres gibt es keine Zeigerarithmetik.
  2. Und Sie können keinen Java-Verweis auf das erstellen, was Sie möchten. Sie können nur die Speicherorte kopieren, auf die zugegriffen werden kann (statische Felder, Objektfelder, lokale Variablen) oder die durch Funktionsaufrufe (wie Konstruktoraufrufe) zurückgegeben werden, die sich alle auf Java beziehen Objekte (nie auf Grundtypen wie Referenzen, char, intund so weiter).

Jemand hat geschrieben, dass Verweise stark typisiert sind, weil Sie den Compiler nicht zwingen können, ein int*als ein zu behandeln char*.
Ganz abgesehen von der Tatsache, dass diese bestimmte Umwandlung tatsächlich sicher ist , gibt es in C keinen Polymorphismus, so dass der Vergleich kein Starter ist.
Sicher ist Java stärker typisiert als C, nicht dass dies ein Merkmal von C-Zeigern gegenüber Java-Referenzen ist. Sie müssen die JNI verwenden, um die Typensicherheit aufzuheben (abgesehen von der Nichtbeachtung generischer Einschränkungen), aber selbst in C müssen Sie sie erzwingen der Compiler.

Jemand schrieb, dass Java-Referenzen als C-Zeiger implementiert werden könnten, auf denen ich sicher bin, dass sie auf 32-Bit-Computern, die in C implementiert sind, normalerweise weniger leistungsfähig sind . Auf 64-Bit- Rechnern handelt es sich normalerweise um komprimierte gewöhnliche Objektzeiger ("Compressed OOPs"), um Speicherplatz und Bandbreite zu sparen.
Auf jeden Fall müssen diese C-Zeiger auch nicht den Hardware-Adressen entsprechen, selbst wenn sie normalerweise (> 99% der Implementierungen) aus Leistungsgründen vorliegen.
Schließlich ist dies ein Implementierungsdetail, das dem Programmierer nicht bekannt ist.


-1

Sie sind etwas anders. In Java wird eine Kopie der Referenz in den Stapel einer aufgerufenen Funktion kopiert, wobei auf dasselbe Objekt wie die aufrufende Funktion verwiesen wird und Sie dieses Objekt bearbeiten können. Sie können jedoch nicht das Objekt ändern, auf das sich die aufrufende Funktion bezieht.

Betrachten Sie den folgenden Java-Code

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"
}

Betrachten Sie nun einen Zeiger 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;
}

Ich dachte, ich könnte hinzufügen, dass es so ähnlich wie ein const Dog * gesehen werden könnte
Eladian

2
Sie können das Objekt, auf das verwiesen wird, in Java genauso einfach ändern wie in C ++. Sie müssen nur Zugriff auf die richtigen Mitglieder haben. Java-Objekte haben keine Zuweisungsoperatoren, da Java keine benutzerdefinierte Operatorüberladung unterstützt. Sie können also keinen überladenen Operator verwenden, große Sache. Dieser Mangel an Java hat nichts damit zu tun, dass Java-Referenzen dieselben sind wie C ++ - Zeiger, denen Zeigerarithmetik fehlt.
Deduplizierer
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.