Home » Tutorials » Object Pascal/RTL » Delphi-Crashkurs

Delphi-Crashkurs

Nochmal Variablen

Die Benutzung von Variablen wurde ja schon besprochen und auch angewendet. Jedoch war das noch nicht alles. Bei einer so wichtigen Einrichtung wie Variablen muss noch ein wenig mehr beschrieben werden. So gibt es verschiedene Möglichkeiten, Variablen zu deklarieren. Ebenfalls wichtig und noch nicht besprochen ist die Speicherung von Zahlen. Diese zu kennen ist wichtiger, als man zuerst denken mag.

Globale und lokale Deklaration

Lokale Deklaration

Wir haben bereits Prozeduren und Funktionen kennen gelernt. Auch in diesen kann man Variablen deklarieren. Diese dort deklarierten Variablen nennt man „lokal deklariert„. Sie gelten nur in dieser Prozedur bzw. Funktion (ich werde demnächst nur noch von Prozeduren sprechen, wenn nichts anderes gesagt wird, ist das Gesagte auch auf Funktionen anzuwenden!). Sobald das Programm diese Prozedur verlassen hat, gibt es die Variablen nicht mehr und sie sind nicht mehr bekannt. Auch ihren Wert verlieren sie. Dieser Wert taucht auch nicht mehr auf, wenn die Methode erneut aufgerufen wird. Ihr Wert ist dann wieder undefiniert.
Hier ein Beispiel:

procedure myProz;
var i,a : Integer;
begin
  for i:=0 to 10 do
    a:=a+1;
end;

Hier sind sowohl die Variable „i“ und die Variable „a“ nur lokal definiert. Sie sind also außerhalb dieser Prozedur nicht bekannt und man kann auf sie nicht zugreifen. Noch dazu ist der Wert der Variable a nicht definiert, da sie deklariert wurde, ihr aber vor Schleifeneintritt kein Wert zugewiesen wurde. Der Delphi-Compiler wird Sie jedoch darauf hinweisen.
Wenn Sie diese Prozedur nun zweimal hintereinander aufrufen, so hat a beim zweiten Durchlauf keinesfalls den Wert, den es nach dem ersten Durchlauf hatte. Der Wert ist wieder undefiniert, so als hätte es den ersten Aufruf der Prozedur nie gegeben.

Globale Deklaration

Sie haben aber auch die Möglichkeit, Variablen so zu deklarieren, dass diese in jeder Prozedur der Datei sichtbar und verwendbar ist. Diese global deklarierten Variablen verlieren ihren Wert niemals. Um eine solche Variable zu deklarieren, tun Sie dies außerhalb jeder Prozedur entweder direkt vor oder direkt nach dem Wort implementation. Die Position der Deklaration hat ebenfalls eine Bedeutung, auf die ich jedoch später eingehen werde.

Die Deklaration globaler Variablen erfolgt genau wie die Deklaration lokaler Variablen: Einfach mit dem Schlüsselwort „var“ einleiten und dann die Deklaration wie gehabt. Sie sollten jedoch dreimal überlegen und dann noch einmal, bevor Sie eine Variable global deklarieren. Denn dies widerspricht dem Prinzip der objektorientierten Programmierung, auf welche ich im nächsten Kapitel noch eingehen werde.
Sollten Sie nach reiflicher Überlegung immer noch zu dem Schluss kommen, dass Sie eine Variable global deklarieren möchten, dann achten Sie darauf, dass Sie einen Namen wählen, der nicht mit einer lokalen Deklaration identsich und gut unterscheidbar ist. Für Delphi ist das zwar kein Problem, aber es beugt Missverständinssen auf Seiten des Programmierers vor. Sollten eine lokale und eine globale Variable aber doch einmal den gleichen Namen haben, so wird – falls man sich in der Prozedur mit der lokalen Variable befindet – die lokale Variable verwendet.

Die Deklaration einer globalen Variable unterscheidet sich in einer Möglichkeit von der Deklaration einer lokalen Variable. Und zwar kann man einer globalen Variable direkt bei der Deklaration einen Wert zuweisen, einen Initialwert. Dies tut man folgendermaßen (als Beispiel):

var myVar : Integer = 5;

Zahlendarstellung im Rechner

Darstellung ganzer Zahlen

Äußerst wichtig aber leider kaum beachtet ist die Zahlendarstellung im Rechner. Um zu verstehen, wie die Darstellung im Rechner funktioniert, muss man sich erst noch einmal klar machen, wie die „richtige“ Zahlendarstellung funktioniert. Nehmen wir als Beispiel die Zahl 1234.
Die Zahl ist

     4 mal 1    = 4*100
plus 3 mal 10   = 3*101
plus 2 mal 100  = 2*102
plus 1 mal 1000 = 1*103

Dabei nennt man die 10 in diesem Beispiel die „Basis“ des Zahlensystems. Aus diesem Grund heißt unser Zahlensystem auch das „Dezimal„system. Die Zahl vor der 10 kann aus einem Bereich von 0 bis 9 stammen. Die Potenz an der Zehn kann eine beliebige ganze Zahl sein (für ganze Zahlen).
Eine Darstellung zur Basis 10 ist in einem Computer jedoch nicht praktisch. Als viel praktischer erweist sich die Darstellung zur Basis 2, auch das „Binär„system genannt. Der Computer kennt in seinem Innersten nur zwei Zustände, „an“ und „aus“. Oder auch „kein Strom“ oder „Strom“. In der Informatik: Null oder Eins. Und weil dies genau zwei Zustände sind, ist eine Zahlendarstellung zur Basis 2 bei einem Computer viel besser als zur Basis 10.
Die Darstellung funktioniert genauso wie die Darstellung zur Basis 10, nur dass man diese Darstellung nicht gewohnt ist und man daher etwas länger braucht, um sich zu überlegen, wie sich eine Zahl zusammensetzt. Als Beispiel (auf Grund der Schwierigkeiten dabei auch etwas kürzer ;-)):

12 =
 0 mal 1 = 0*2
 plus 0 mal 2 = 0*21
 plus 1 mal 4 = 1*22
 plus 1 mal 8 = 1*23
= (1100)2

Dabei bedeutet die Zahl in Klammern mit dem Index 2, dass man die Zahl zur Basis 2 darstellt. Man lässt diese Schreibweise weg, wenn die Zahl im Dezimalsystem dargestellt wird. Die Basis wird immer in Dezimaldarstellung angegeben.
Ebenfalls wichtig im Bereich des Computers ist das Hexadezimalsystem, also die Schreibweise zur Basis 16. Da wir aber nur zehn arabische Ziffern kennen, benutzt man für die Ziffern 10, 11, 12, 13, 14, 15 (und im Hexadezimalsystem sind die Ziffern) die Buchstaben von A bis F. Die Zahlen von 0 bis 15 lauten im Hexadezimalsysten dann:

0,1,...,A,B,C,D,E,F

Die Darstellung im Hexadezimalsystem wir sehr gerne im Bereich der Farbangaben verwendet. Um für jede Grundfarbe (Rot, Grün, Blau) eine Abstufung von 255 Schritten zu erreichen (und damit ca. 1,6 Millionen verschiedene Farben), bräuchte man normalerweise (also im Dezimalsystem) neun Stellen. Für jede Farbe drei.
Schreibt man eine solche Farbangabe jedoch im Hexadezimalsystem, so benötigt man nur noch sechs Stellen, da gilt:

255 = (FF)16 

Darstellung rationalen Zahlen

Bisher wurde nur beschrieben, wie man ganze Zahlen darstellen kann. Nun braucht man in genauso vielen Fällen aber rationale Zahlen, sprich „Kommazahlen„. Nur eine (wenn auch große) Teilmenge davon ist im Rechner darstellbar: die rationalen Zahlen, also Zahlen, die sich als Bruch darstellen lassen.
Prinzipiell wäre es möglich, rationale Zahlen analog zu den ganzen Zahlen darzustellen, indem man die Potenzen an der Basis einfach in den negativen Bereich erweitert. Als Beispiel:

 23 +  21 +  20 +  2-1 +  2-3 +  2-4 +  2-7 =
 8   +  2   +  1  +  1/2  +  1/8  +  1/16 +  1/128  =
 11,6953125

Dies nennt man eine „Festkommadarstellung„, da sich das Komma immer an einer beliebigen, aber festen Stelle befindet.
Es hat sich jedoch die so genannte „Fließkommadarstellung“ oder auch „Gleitkommadarstellung“ durchgesetzt. Dies ist eine Darstellung von der Form:

z = m*be

also z.B.:

300 = 3*102

Dabei heißt m die „Mantisse“ (dies ist in der Regel eine Festkommazahl), b ist die „Basis“ und e der „Exponent„. Die Basis muss dabei nicht mit der Basis der Zahlendarstellung übereinstimmen. Als Beispiel:

 1228,8 = 2,4*83 b=8, e=3, m=2,4

Da man bei der Darstellung in der Fließkommadarstellung sehr viel Freiheiten hat, eine Zahl darzustellen, hat man sich auf einen Standard geeinigt. Dieser Standard ist in der Norm IEEE 754 festgeschrieben. Zum einen wird festgelegt, dass die Basis immer 2 ist, aus den gleichen Gründen, die schon bereits bei der Binärdarstellung erleutert wurden. Zum anderen wird festgelegt, wie die Bits bei einer 32bit-Darstellung (64bit-Darstellung) verteilt werden:

  1. 1 Bit wird für das Vorzeichen verwendet.
  2. 8 Bits (11 Bits) werden für den Exponenten verwendet. Dabei wird der Exponent als d ? 127 dargestellt und nur das d gespeichert.
  3. 23 Bits (52 Bits) werden für die Mantisse verwendet. Dabei wird festgelegt, dass die Mantisse in der normalen Darstellung die Form 1,f hat und nur die Darstellung des f wird gespeichert.
  4. In der subnormalen Darstellung (für sehr kleine Zahlen) gelten andere Regeln, um eine größere Genauigkeit zu erreichen:
    1. Der Exponent wird als -126 angenommen.
    2. Ein Bit wird auf das Vorzeichen verwendet.
    3. Die restlichen Bits werden für die Mantisse verwendet, wobei davon ausgegangen wird, dass die Mantisse die Form 0,f hat und nur das f gespeichert wird.
  5. Um pos. oder neg. Unendlich darzustellen, setzt man das Bit für das Vorzeichen entsprechend, setzt den Wert für die Mantisse auf Null und den Wert für den Exponenten auf 255.
  6. Um den Wert „Not a Number (NaN)“ darzustellen, setzt man das Bit für das Vorzeichen auf einen beliebigen Wert, den Wert für den Exponenten auf 255 und den Wert für die Mantisse auf einen Wert größer als Null.

Im Gegensatz zur Darstellung von ganzen Zahlen, ist es bei der Darstellung von rationalen Zahlen nicht möglich, einen Zahlenbereich komplett abzubilden. Es wird immer „Lücken“ geben, Zahlen können nur mit einer gewissen Genauigkeit dargestellt werden. Auch Rechenoperationen an diesen Zahlen führen zu Ungenauigkeiten.
So muss zum Beispiel für die Addition zweier Fließkommazahlen der Exponent der kleiner Zahl dem Exponenten der größeren Zahl angeglichen werden. Dies führt zu einem enormen Verlust von Genauigkeit.
Alle diese Probleme kann man nicht beheben, aber es ist sehr wichtig, sie zu kennen. So kann man seine Berechnungen so aufbauen, dass man möglichst wenig Genauigkeit verliert. Auch ist es wichtig, dass man weiß, dass bei der Darstellung als ganze Zahl diese Darstellung exakt ist, bei der Darstellung als Fließkommazahl jedoch nicht.
Welche Darstellung verwendet wird, hängt davon ab, welchen Datentyp Sie verwenden.

Umwandlung von Zahlen und Darstellungsbereiche

Vorweg ein paar Fachbegriffe: Die Umwandlung eines Datentyps in einen anderen (egal ob Zahentyp oder nicht) heißt „cast„. Dabei gibt es explizite und implizite casts. Ein impliziter cast wird vom Compiler durchgeführt, ohne dass der Programmierer etwas merkt. Er funktioniert wie eine Zuweisung zwischen zwei Variablen des selben Typs, nur dass sie bei einem impliziten cast nicht den selben Typ haben. Bei einem expliziten cast muss der Programmierer explizit angeben, dass er den einen Typ in den anderen umwandeln will.
Seien Sie beim casten immer sehr vorsichtig. Nicht immer weist Sie der Compiler darauf hin, wenn durch eine Umwandlung Daten verloren gehen können. Es liegt dann in der Verantwortung des Programmierers ? Ihrer Verantwortung ? sich darüber Gedanken zu machen. Wenn man jedoch die nötige Vorsicht walten lässt, kann ein cast ein sehr praktisches und mächtiges Werkzeug sein.
In diesem Abschnitt sei kurz auf das casten zwischen Zahlentypen eingegangen und auch auf den Darstellungsbereich dieser Typen. Dabei unterscheiden wir strikt zwischen den Integertypen und den Floattypen. Wie bereits gesagt, bilden Integertypen einen Bereich der natürlichen Zahlen lückenlos ab.
Dabei bildet der Datentyp „Integer“ je nach Compiler immer unterschiedliche Zahlenbereiche ab. Und zwar ist dieser Zahlenbereich immer dadurch definiert, dass er auf einem 32bit-System immer 32 Bit an Speicher belegt, auf einem 16bit-System 16 Bit an Speicher, usw. Natürlich nur, wenn der Compiler für das jeweilige System geschrieben wurde. Dies führt dazu, dass der Integertyp immer eine optimale Geschwindigkeit bereitstellt.
In aktuellen Compiler-Versionen von Delphi ist der Integer ein 32bit-Datentyp und identisch mit dem LongInt. Für größere Zahlen stellt Delphi auch noch einen 64bit-Integertypen, den Int64 zur Verfügung. Auch kleinere Integertypen, wie den SmallInt oder ShortInt gibt es. Am schnellsten ist jedoch immer der Integer.
Dies waren bisher alles Integertypen, die vorzeichenbehaftete Zahlen darstellen. Rechnet man immer nur mit nicht-negativen oder nicht-positiven Zahlen, so muss das Vorzeichen nicht mitgespeichert werden und statt den negativen Zahlenbereich abzubilden, kann man einen doppelt so großen positiven Zahlenbereich darstellen.
Auch hierfür bietet Delphi Datentypen und sogar einen „generischen“ Datentypen analog zum Integer. Dies ist der Datentyp „Cardinal„, dessen Bereich auch wieder vom Compiler abhängt. Momentan nimmt auch er 32 Bit in Anspruch und stellt damit einen Zahlenbereich von 0 bis 4294967295 dar und ist damit identisch mit dem Datentyp „Longword„. Hier kann Delphi jedoch nichts Größeres anbieten, nur die kleineren Typen Word und Byte stellt es zur Verfügung.
Damit sei auch genug zur Darstellung von ganzen Zahlen gesagt. Delphi bietet logischer Weise auch Datentypen an, um reelle Zahlen darzustellen. Im Unterschied zu der Darstellung von Ganzzahlen liegt darin, dass bei der Darstellung Floats nicht nur auf den Darstellungsbereich, sondern auch auf die Genauigkeit acht gegeben werden muss.
Der Datentyp mit dem größten Darstellungsbereich ist der Extended. Er bietet außerdem eine sehr hohe Genauigkeit. Dafür genehmigt er sich aber auch satte 10 Byte Speicherplatz. Auf 32bit-Systemen am schnellsten ist der Datentyp „Single„, da er 4 Byte also 32 Bit in Anspruch nimmt und somit optimal verarbeitet werden kann. Dafür ist der Darstellungsbereich wesentlich kleiner und auch die Genauigkeit ist nicht mit der des Extended zu vergleichen.
Ein guter Mittelweg, welcher auch für die meisten wissenschaftlichen Anwendungen ausreicht, ist der „Double„. Er bietet eine ausreichende Genauigkeit und Größe für so ziemlich jede Anwendung. Der generische Datentyp Real ist in seiner gegenwärtigen Implementation identisch mit dem Double.
Ein Spezialfall stellt der Typ „Currency“ dar. Er ist kein Fließkomma-Datentyp, also kein Float! Beim Datentyp Currency handelt es sich um einen Festkomma-Datentypen, welcher für finanz-mathematische Anwendungen entworfen wurde, da die Festkomma-Darstellung Rundungsfehler minimiert. Weitere Informationen dazu sollten in der Delphi-Hilfe unter „Reelle Typen“ nachgeschlagen werden.
Nun möchte ich wie versprochen jedoch auch noch kurz auf die Umwandlung von Datentypen ineinader eingehen. Nehmen wir folgendes Beispiel:

var a : Integer;
    b : ShortInt;
begin
  a := 204;
  b := a;
  ShowMessage(IntToStr(b));
end;

Hier weisen wir einem Integer zuerst den Wert 204 zu, anschließend weisen wir diesen Wert einem ShortInt zu. Die 204 sprengt jedoch den Darstellungsbereich eines ShortInts. Und was macht der Rechner: er fängt wieder von vorne an zu zählen, was in diesem Fall heißt, dass er im negativen Bereich weitermacht. Daher wird auch -52 ausgegeben und nicht 204.
Hierbei gibt Delphi keine Warnung aus! Solche Aktionen liegen in der Verantwortung des Programmierers!

2 Gedanken zu „Delphi-Crashkurs“

  1. Guten Morgen,

    erst einmal vielen Dank für die ausführlichen Erklärungen!

    Im ersten Beispiel zur Darstellung von Zahlen im Rechner müsste es heißen

    4 mal 1 = 4*10^0

    statt

    4 mal 1    = 4*10

     

    1. Vielen Dank für den Hinweis.
      Ich habe es entsprechend korrigiert.
      Auch Deinen weiteren Hinweis habe ich eingearbeiotet.

Kommentare sind geschlossen.