Home > Informatik > Stufe Q1 > Referenzen

15.1 Referenzen, Allgemeines

Allgemeines - dynamischer Stack - sortierte Liste - NRW-Liste - doppelt verkettete Liste

"Die Schülerinnen und Schüler erläutern Operationen dynamischer (linearer oder nichtlinearer) Datenstrukturen."

Zitat aus " Kompetenzerwartungen und inhaltliche Schwerpunkte in der Qualifikationsphase", Kernlehrplan Informatik NRW.

Zeiger sind nichts Neues!

Wussen Sie eigentlich, dass wir bisher schon immer mit Zeigern gearbeitet haben, wenn wir Java-Programme geschrieben haben? Immer dann, wenn Sie ein Objekt einer Klasse deklarieren, verwenden Sie einen Zeiger! Schauen wir uns mal die einfache Klasse Bruch an:

Klasse Bruch
public class Bruch
{
    int zaehler, nenner;
    
    public Bruch(int z, int n)
    {
        zaehler = z;        
        nenner  = n;
    }
    
    public void show()
    {
       System.out.println(zaehler + "/" + nenner);
    }
}

Diese Klasse hatten wir bereits in der letzten Folge bei der Implementation des ADT List verwendet.

Die beiden Attribute zaehler und nenner dieser Klasse sind ganz normale primitive Datentypen, keine Zeiger. Das heißt, wenn wir mit einem Supermikroskop in die Speicherzellen von zaehler und nenner hineinschauen könnten, würden wir dort tatsächlich int-Zahlen entdecken, die den Zähler und den Nenner eines Bruches darstellen.

Klasse ZeigeZeiger

Wir machen nun ein kleines wissenschaftliches Experiment. Betrachten Sie dazu den folgenden Quelltext:

public class ZeigeZeiger
{
    Bruch b1, b2;
    
    public ZeigeZeiger()
    {
       b1 = new Bruch(3,4);
       b2 = new Bruch(5,6);
       
       System.out.println(b1);
       System.out.println(b2);
    }
}

Im Konstruktor dieser Klasse werden zunächst zwei Bruch-Objekte b1 und b2 erzeugt, das eine Objekt repräsentiert den Bruch $\frac{3}{4}$, das andere Objekt den Bruch $\frac{5}{6}$. Anschließend sollen die Werte der Brüche in der Konsole angezeigt werden. Unser Software-Entwickler Karl Schlau hat sich jetzt aber etwas besonderes ausgedacht: Was passiert, wenn man nicht b1.show und b2.show aufruft, wie es sich eigentlich gehört, sondern die beiden Objekt direkt mit System.out.println anzeigen lässt?

Hier das Ergebnis eines Testdurchlaufs:

asdf

Konsolenausgabe für die Anzeige der Objekte b1 und b2

Es wird tatsächlich etwas angezeigt, allerdings nicht die Werte der Brüche. Wie sie diese beiden kryptischen Ausgaben zu interpretieren?

Und hier kommt etwas Neues, auf das sich dann die ganze Folge 15 aufbaut. Was wir hier in der Konsole sehen, sind nicht die Werte von irgendwelchen Variablen, sondern Speicheradressen. 43d10ad7 ist die hexadezimale Darstellung einer Adresse irgendwo im Arbeitsspeicher des Rechners. Das ist genau die Stelle im Speicher, an der das Bruch-Objekt "untergebracht" ist. Schauen wir uns dazu mal eine kleine Graphik an:

asdf

Eine modellhafte Darstellung des Arbeitsspeichers

Die Abbildung 2 soll ein Modell des Arbeitsspeichers eines Rechners zeigen. Die einzelnen Speicherzellen werden hier durch kleine quadratische Kästchen dargestellt. Wenn die Klasse ZeigeZeiger ein Objekt b1 der Klasse Bruch deklariert, dann wird irgendwo im Speicher eine Speicherzelle für die Variable b1 reserviert. Diese Speicherzelle befindet sich in unserem Modell in dem Kasten mit der Adresse 4.  Entsprechend ist die Variable b2 in dem Kasten mit der Adresse 7 untergebracht.

In diesen beiden Kästen steht jetzt aber nicht der Wert des Bruches. Bei der Deklaration

Bruch b1, b2

ist ja auch noch gar kein Wert angegeben worden, das geschieht ja erst bei der Initialisierung

b1 = new Bruch(3,4);
b2 = new Bruch(5,6);

Bei der Initialisierung von b1 passiert folgendes: Es wird eine freie Stelle im Arbeitsspeicher gesucht, in die der Bruch "hineinpasst". Ist dies der Fall, so werden die beiden Attributwerte des Bruches in diese Speicherzellen hineingeschrieben. In die eine Speicherzelle wird der Wert 3 geschrieben, in die andere Speicherzelle der Wert 4. Das Gleiche passiert bei der Initialisierung von b2. In die eine Speicherzelle wird der Wert 5 geschrieben, in die andere der Wert 6.

In der Abbildung 2 sieht man auch, welche Werte in den Speicherzellen 4 und 7 für die Variablen b1 und b2 gespeichert sind: 229 und 334. Das sind die Adressen der beiden eben erwähnten Speicherbereiche, in denen sich die eigentlichen Bruch-Objekte befinden.

In Wirklichkeit ist ein Arbeitsspeicher natürlich viel, viel größer als in unserem Modellsystem, und für die Speicherung von Speicheradressen reichen drei Ziffern lange nicht aus. Das erklärt dann die kryptisch langen Speicheradressen, die wir in unserem Experiment gesehen haben.

Die Abbildung 2 zeigt uns auch, warum man ständig von "Zeigern" spricht, wenn man meint, dass eine Variable die Adresse eines Speicherbereichs speichert. Der Inhalt dieser Variable "zeigt" sozusagen auf den eigentlichen Speicherbereich. Oft wird auch der Begriff "Referenz" für einen Zeiger verwendet oder der englische Ausdruck "pointer". Referenzen und Zeiger sind nicht unbedingt das Selbe, aber um diese feinen Unterschiede müssen wir uns hier nicht kümmern.

Wenn Sie selbst einmal die Verhältnisse im Arbeitsspeicher graphisch darstellen müssen, können Sie das übrigens einfacher machen als in der Abbildung 2. Betrachten Sie dazu die folgende Abbildung 3:

asdf

Ein Kästchenschma

Was Sie hier sehen, bezeichnet man als Kästchenschema. Die Speicherbereiche werden als einfache oder doppelte Kästchen dargestellt. Die Speicheradressen werden nicht angegeben, weil sie erstens nicht reproduzierbar und zweitens auch völlig unwichtig sind. Sie lesen dieses Kästchenschema wie folgt: Die Variable b1 zeigt auf den Bruch 3/4, und die Variable b2 zeigt auf den Bruch 5/6.

Damit wären wir auch schon am Ende der allgemeinen Einführung zum Thema Referenzen bzw. Zeiger angekommen.

Aufgabe 15.1-1

Auf der Seite "7.4 Objekt-Arrays besser verstehen" ist genau erklärt, wie die Speicherbelegung eines Objektarrays aussieht. Auch hier ist ständig von Referenzen bzw. Zeigern die Rede; allerdings haben Sie in der Stufe EF vielleicht noch nicht so ganz richtig verstanden, um was es hier überhaupt geht. Jetzt sollten Sie dazu aber in der Lage sein.

Ihre Aufgabe ist es also, einen kleinen mündlichen Vortrag vorzubereiten, in dem Sie erklären, wie ein Objektarray aufgebaut ist und welche Rolle dabei Referenzen spielen.

Als Nächstes wollen wir den ADT Stack mit Hilfe von Zeigern implementieren. Ein sehr anspruchsvolles Unterfangen!

Seitenanfang -
Weiter mit der dynamischen Implementation eines Stacks...