Zuweisungskompatibilität

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Zuweisungskompatibilität liegt bei in Programmiersprachen formulierten Anweisungen vor, wenn Ausdrücke und Variablen aufgrund kompatibler Datentypen einander zugewiesen, miteinander verglichen oder miteinander verknüpft werden können.

Compiler oder Interpreter können bei typsicheren Programmiersprachen bereits im Quelltext prüfen, ob eine hinreichende Zuweisungskompatibilität vorliegt.[1] Um eine Typverletzung zu beheben, muss durch den Compiler beziehungsweise Interpreter eine implizite oder durch den Programmierer eine explizite Typumwandlung durchgeführt werden. In beiden Fällen kann es leicht zu Programmfehlern kommen und daher ist in vielen modernen Programmiersprachen die implizite Typumwandlung nur in den Ausnahmen zulässig, wo sie die Quelltexte vereinfacht, aber keine Gefahr darstellt.

Strenge Zuweisungskompatibilität[Bearbeiten | Quelltext bearbeiten]

Zuweisungen sind uneingeschränkt zulässig, wenn eine strenge Zuweisungskompatibilität gegeben ist. Dazu müssen die Datentypen des zuzuweisenden Ausdrucks und der Variable exakt übereinstimmen.

Bei Verbunden müssen die Anzahl, die Reihenfolge und die Datentypen aller Komponenten des Verbunds übereinstimmen, was dadurch gewährleistet werden kann, dass alle beteiligten Variablen mit demselben Datentyp deklariert worden sind.[2] Beispiel:

TYPE Mann = Verbund von INTEGER alter; und REAL groesse;
VARIABLE otto, emil: Mann;
otto.alter   := 50;
otto.groesse := 1.80;
emil := otto; (Alle Attribute von „otto“ werden „emil“ zugewiesen)

Bei Feldern ist die strenge Zuweisungskompatibilität nur gegeben, wenn der Basisdatentyp und die Länge identisch sind. Beispiel:

TYPE Vorname = Feld der Größe 4 von CHARACTER; (Zeichenkette der Länge 4)
VARIABLE personename: Vorname;
personename := "Hugo";      (Die Konstante „Hugo“ ist zuweisungskompatibel zur Variablen „personenname“)
personename := "Alexander"; (Die Konstante „Alexander“ ist nicht zuweisungskompatibel zur Variablen „personenname“)

Ein Sonderfall sind offene Felder mit nicht deklarierter Länge: Bei formalen Parametern beziehungsweise dynamisch erst zur Laufzeit vollständig definierten Feldern reicht zur Erlangung der Zuweisungskompatibilität die Übereinstimmung des jeweiligen Basisdatentyps der Felder.[3] Beispiel:

TYPE Vorname = Feld von CHARACTER; (Zeichenkette mit offener Länge)
VARIABLE personename: Vorname;
personename := "Hugo";      (Die Variable „personenname“ wird dynamisch mit der Länge 4 erzeugt)
personename := "Alexander"; (Die Variable „personenname“ wird erneut dynamisch erzeugt, diesmal mit der Länge 9)

Bei Prozedurvariablen müssen die Anzahl, die Art, die Reihenfolge und die Datentypen aller Parameter und Rückgabewerte übereinstimmen, also abgesehen vom Prozedurnamen die Signaturen der beiden Prozeduren übereinstimmen.

Zwei Instanzen sind in diesem Sinne zuweisungskompatibel, wenn sie exakt derselben Klasse angehören. Beispiel:

TYPE Rechteck = Klasse mit INTEGER breite, hoehe und mit Methode flaechenberechnung();
VARIABLE fenster1, fenster2: Rechteck;
fenster1.breite := 200;
fenster1.hoehe  := 100;
fenster1.flaechenberechnung(); (Flächenberechnung für „fenster1“ ausführen; Ergebnis = 20000)
fenster2 := fenster1; (Zuweisung ist möglich, da beide Instanzen derselben Klasse angehören)
fenster2.flaechenberechnung(); (Flächenberechnung auch für „fenster2“ ausführen; Ergebnis = 20000)

Logische Kompatibilität[Bearbeiten | Quelltext bearbeiten]

Bei besonders strenger Betrachtungsweise sind sogar zwei identische Definitionen von Datentypen nicht zuweisungskompatibel, weil zwar die Daten eindeutig ineinander überführt werden können (technische Kompatibilität), aber zwei verschiedene Definitionen verwendet wurden (logische Kompatibilität).[4] Folgendes Beispiel wäre demzufolge technisch korrekt, aber nicht logisch:

TYPE Mann = Verbund von INTEGER alter; und REAL groesse;
TYPE Frau = Verbund von INTEGER alter; und REAL groesse;
VARIABLE otto: Mann;
VARIABLE anna: Frau;
otto.alter   := 50;
otto.groesse := 1.80;
anna := otto; (Zuweisung ist technisch möglich aber logisch nicht korrekt)

Um logische Programmfehler zu vermeiden, sind solche Zuweisungen mit impliziter Typumwandlung in einigen Programmiersprachen mit verhältnismäßig starker Typisierung, wie zum Beispiel Modula-2 oder Oberon, nicht zulässig.

Zuweisungskompatibilität ohne Informationsverlust[Bearbeiten | Quelltext bearbeiten]

In gewissen Fällen kann die Information, die in einem Datentyp abgelegt ist, eindeutig und ohne Informationsverlust in einen anderen Datentyp überführt werden.

Typische Beispiele sind ganzzahlige Datentypen mit unterschiedlicher Speichergröße. So kann ein Integer mit 16 Bit Speichergröße eindeutig in einer vorzeichenbehafteten Integer-Variablen mit 32 Bit Speichergröße abgelegt werden, ohne dass die ursprünglich nur mit 16 Bit definierte Zahl verändert wird. Umgekehrt ist dies jedoch nicht immer möglich, insbesondere unter der Beachtung von Vorzeichen und zu großen Zahlen.

Weitere Beispiele ergeben sich bei Zeichenketten oder anderen Feldern, deren feste Länge verschieden ist. Das kürzere Feld kann im längeren gespeichert werden, aber nicht umgekehrt.

Zwei Instanzen sind ohne Informationsverlust zuweisungskompatibel, wenn sie die zuzuweisende Klasse derselben Klasse angehört wie die zugewiesene Klasse.

Beispiel mit Zuweisungskompatibilität[Bearbeiten | Quelltext bearbeiten]

zahl1: BYTE; (mit 8 Bit für ganze Zahlen von −128 bis +127)
zahl2: SHORTINT; (mit 16 Bit für ganze Zahlen von −32768 bis +32767)
zahl1 := 55; (Zuweisung der ganzen Zahl 55 an die Variable „zahl1“)
zahl2 := zahl1; (Zuweisung der ganzen Zahl 55 aus der Variablen „zahl1“ an die Variable „zahl2“)

Beispiel ohne Zuweisungskompatibilität[Bearbeiten | Quelltext bearbeiten]

zahl1: BYTE; (mit 8 Bit für ganze Zahlen von −128 bis +127)
zahl2: SHORTINT; (mit 16 Bit für ganze Zahlen von −32768 bis +32767)
zahl2 := 555; (Zuweisung der ganzen Zahl 555 an die Variable „zahl2“)
zahl1 := zahl2; (Ungültiger Versuch der Zuweisung der ganzen Zahl 555 aus der Variablen „zahl2“ an die Variable „zahl1“)

Bei einer solchen Zuweisung kann bereits der Compiler verhindern, dass ausführbarer Maschinencode erzeugt wird, da nicht sichergestellt werden kann, dass große Zahlen, die in der Variable „zahl2“ gespeichert sind, immer auch ohne Informationsverlust in der Variable „zahl1“ abgelegt werden können.

Bei fehlender Überprüfung durch den Compiler werden zwangsläufig und unbemerkt Ziffern abgeschnitten, so dass bei nachfolgenden Berechnungen unter Umständen grobe Berechnungsfehler auftreten können, die teilweise schwierig zu analysieren sind.

Zuweisungskompatibilität mit geringem Informationsverlust[Bearbeiten | Quelltext bearbeiten]

Ein Sonderfall ist die Zuweisung von ganzen Zahlen an Variablen, die Gleitkommazahlen repräsentieren. Meist kann ohne die Gefahr von Programmfehlern toleriert werden, große ganze Zahlen implizit in Gleitkommazahlen umzuwandeln, da der Rechenfehler (wenn überhaupt vorhanden) hierbei sehr klein ist. Auch dies kann an einem Beispiel verdeutlicht werden:

zahl1: LONGINT; (mit 64 Bit für ganze Zahlen von  bis )
zahl2: REAL; (mit 64 Bit für Gleitkommazahlen mit einer Mantisse mit maximal 14 Nachkommastellen nach IEEE 754)
zahl1 := 9223372036854775807; (Zuweisung der ganzen Zahl  an die Variable „zahl1“)
zahl2 := zahl1; (Gültiger Versuch der Zuweisung der ganzen Zahl  aus der Variablen „zahl1“ an die Variable „zahl2“,
                 die allerdings anschließend den gerundeten Zahlenwert  enthält)

Der Fehler durch das Abschneiden der letzten Nachkommastellen liegt hier also nur in einer Größenordnung von und kann daher für praktisch alle Anwendungen vernachlässigt werden.

Zuweisungskompatibilität mit definiertem Informationsverlust[Bearbeiten | Quelltext bearbeiten]

Zwei Instanzen sind mit definiertem Informationsverlust zuweisungskompatibel, wenn die zuzuweisende Klasse einer von der zugewiesenen Klasse abgeleiteten Klasse angehört. Alle Daten die in der zugewiesenen Klasse deklariert und somit erforderlich sind, können zugewiesen werden, jedoch werden die in der zuzuweisenden, abgeleiteten Klasse hinzugefügten Attribute und Methoden ignoriert. Beispiel:

TYPE Lebewesen = Verbund von INTEGER alter, gewicht;
TYPE Mensch = Lebewesen mit INTEGER intelligenzquotient; (Der Datentyp "Mensch" erbt alle Eigenschaften von Lebewesen)
VARIABLE otto: Mensch;
VARIABLE eukaryot: Lebewesen;
otto.alter               := 50;
otto.gewicht             := 75;
otto.intelligenzquotient := 100;
eukaryot := otto; (Zuweisung ist korrekt, das Attribut "intelligenzquotient" wird jedoch nicht zugewiesen)

Zuweisungen ohne Typsicherheit[Bearbeiten | Quelltext bearbeiten]

Programmiersprachen, die für die maschinennahe Programmierung konzipiert wurden, wie zum Beispiel C, haben oft gar keine oder nur eine sehr schwache Typprüfung. Diese Toleranz führt leicht zu Programmfehlern.

Zeiger[Bearbeiten | Quelltext bearbeiten]

In manchen Programmiersprachen, wie zum Beispiel C, ist es erlaubt, beliebige Zeiger einer Zeigervariablen zuzuweisen, ohne dass geprüft wird oder überhaupt geprüft werden kann, ob die Datentypen der referenzierten Daten identisch oder kompatibel sind.

Wahrheitswerte[Bearbeiten | Quelltext bearbeiten]

In einigen älteren Programmiersprachen, wie zum Beispiel C, gibt es keinen eigenen Datentyp für zweiwertige boolesche Variablen. Zur Behandlung und Verarbeitung entsprechender Information wird dann häufig der ganzzahlige Datentyp mit dem kleinsten Speicherbedarf verwendet, wobei der Zahlenwert null für den Wahrheitswert „Falsch“ und alle anderen Zahlenwerte für den Wahrheitswert „Wahr“ Verwendung finden. Auch hier ergeben sich logische Inkompatibilitäten und somit Komplikationen, da mit binären Werten keine Arithmetik und mit Zahlen keine logischen Verknüpfungen oder logischen Operationen durchgeführt werden können. Im folgenden Beispiel wird dieser Missbrauch verdeutlicht:

VARIABLE falscheWahrheit1, falscheWahrheit2, ergebnisWahrheit: INTEGER (ganze Zahlen)
falscheWahrheit1 := 0; (0 soll den Wahrheitswert Falsch repräsentieren)
falscheWahrheit2 := 1; (1 soll den Wahrheitswert  Wahr  repräsentieren)
ergebnisWahrheit := falscheWahrheit1 + falscheWahrheit2;   (Ergebnis dieser unsinnigen arithmetischen Addition ist 1,
                                                            was für den Wahrheitswert  Wahr  steht)
ergebnisWahrheit := falscheWahrheit1 UND falscheWahrheit2; (Ergebnis dieser logischen Konjunktion ist 0,
                                                            was für den Wahrheitswert Falsch steht)
ergebnisWahrheit := falscheWahrheit2 + falscheWahrheit2;   (Ergebnis ist 2 und zuweisungskompatibel,
                                                            aber ohne logischen Sinn)

Eine eindeutige und korrekte Implementierung könnte wie folgt aussehen:

VARIABLE richtigeWahrheit1, richtigeWahrheit2, ergebnisWahrheit: BOOLEAN (Wahrheitswerte)
richtigeWahrheit1 := FALSCH; (nur Wahrheitswerte sind zuweisungskompatibel)
richtigeWahrheit2 := WAHR;   (nur Wahrheitswerte sind zuweisungskompatibel)
ergebnisWahrheit := richtigeWahrheit1 UND richtigeWahrheit2; (nur logische Operatoren sind zulässig, und nur Ergebnisse
                                                              mit Wahrheitswerten (hier FALSCH) sind zuweisungskompatibel)

Mengen[Bearbeiten | Quelltext bearbeiten]

Sinngemäß gilt das Gleiche für die Verknüpfungen von Mengen. Wenn hier bei den Datentypen und zulässigen Operatoren nicht zwischen Mengen (englisch = Bitsets) und Zahlen unterschieden wird, kommt es wie zum Beispiel bei der Bestimmung von Differenzmengen zu Interpretationsproblemen:

VARIABLE falscheMenge1, falscheMenge2, differenzMenge: INTEGER (ganze Zahlen)
falscheMenge1 := 0; (0 soll eine leere Menge repräsentieren)
falscheMenge2 := 1; (1 soll eine Menge mit dem Element 1 repräsentieren)
differenzMenge := falscheMenge1 - falscheMenge2; (Ergebnis dieser unsinnigen arithmetischen Subtraktion ist −1)

Eine eindeutige und korrekte Implementierung könnte wie folgt aussehen:

VARIABLE richtigeMenge1, richtigeMenge2, differenzMenge: BITSET (Mengen)
richtigeMenge1 := { }; (soll eine leere Menge repräsentieren)
richtigeMenge2 := {1}; (soll eine Menge mit dem Element 1 repräsentieren)
differenzMenge := richtigeMenge1 - richtigeMenge2; (Nur Mengen-Operatoren sind zulässig,
                                                    und nur Ergebnisse mit Mengen (hier ist das Ergebnis die leere Menge)
                                                    sind zuweisungskompatibel)

Literatur[Bearbeiten | Quelltext bearbeiten]

Weblinks[Bearbeiten | Quelltext bearbeiten]

  • Stefan Middendorf, Reiner Singer, Jörn Heid: Zuweisungskompatibilität in: Objektorientierte Programmierung mit Java, Die Sprache Java, Programmierhandbuch und Referenz für die JavaTM-2-Plattform, 3. Auflage 2002
  • Uwe Schmidt: Zuweisungskompatibilität in: Objektorientierte Programmierung mit Java, Fachhochschule Wedel

Einzelnachweise[Bearbeiten | Quelltext bearbeiten]

  1. Kathleen Jensen, Niklaus Wirth, Andrew B. Mickel: Assignment Statements, in: Pascal user manual and report: ISO Pascal standard, Kapitel 9.1.1, Seite 170, Verlag Springer 1991, ISBN 978-0-3879-7649-5
  2. László Böszörményi, Jürg Gutknecht, Gustav Pomberger: Symbol Table Handling, in: The school of Niklaus Wirth: the art of simplicity, Seiten 61 und 62, Verlag Morgan Kaufmann 2000, ISBN 9781558607231
  3. Joel Pitt: Volition Systems Modula-2 Programming Language, in: InfoWorld vom 19. September 1983, Band 5, Nummer 38, InfoWorld Media Group, ISSN 0199-6649
  4. Kent Lee: Example 8.2, in: Programming Languages: An Active Learning Approach, Kapitel 8, Seiten 228 und 229, 2008, ISBN 9780387794211