Home | Computer | Pascal Kurs | Site Map |
In diesem Teil geht es um einen sauberen Programmierstill, um möglichst fehlerfreies Programmieren. Das alleine wäre schon ein eigener Kurs, aber ich will mich hier darauf beschränken die Sprachmittel von Turbo Pascal auszunutzen. Wenn sie das tun sind sie schon weiter als viele C Programmierer die ich kenne.
Bei Anfängern zu kurz kommt oft die Deklaration eigener Typen. Natürlich werden Typen deklariert die man dringend braucht wie Records oder Arrays. Aber wenn man eine Routine hat die Menüpunkte verarbeitet, wie deklarieren Sie eine Variable die einen Buchstaben entgegennimmt?
Es gibt hier zwei Möglichkeiten:
Var taste: char;
oder
Type menutyp = 'A'..'Z';
Var taste: Menutyp;
Beides mal wird der Compiler eine Char Variable einsetzen. Im zweiten Fall haben sie aber durch den Compiler Switch {$R+} die Möglichkeit das Range Checking einzuschalten: Der Compiler prüft ob bei solchen Typen eine Zuweisung in dem deklarierten Bereich ist. Eine Zuweisung wie
taste:=' ';
löst schon beim Compilieren einen Fehler aus, beim Testen zur Laufzeit gibt es ähnliche Fehlermeldungen. Diese dienen natürlich nicht um sie zu ärgern, denn wenn Sie in einem CASE den Wert abfragen und es taucht ein Wert auf den sie nie vorgesehen haben, so haben sie ein Problem. Ist ihr Programm getestet, so können Sie das Prüfen abschalten und der Code ist genauso effizient wie der erste Code mit einer Char Variablen.
Dies gilt auch für Aufzählungstypen, Arrays und Mengentypen. Es empfiehlt sich Beispielsweise den Index eines Arrays separat zu deklarieren:
Type
feldindex = 1..80;
arraytyp = Array [Feldindex] of String[40];
Noch stiefmütterlicher behandeln Anfänger die Konstantendeklaration - "Wozu Konstanten deklarieren, wenn ich den Wert direkt einsetzen kann?" Nun weil wenn man eine Konstante mehrmals im Quelltext hat, die Gefahr von Schreibfehlern gebannt ist, weil wenn man Änderungen an den Konstanten hat diese nur einmal machen muss und weil Namen oft besser merkbar sind Zahlen.
Der wichtigste Grund sind aber Programänderungen. Angenommen Sie haben ein Array kreiert und Sortieren dieses mit Bubble Sort so steht irgendwo vorne:
Feld: Array [1..100] of String;
und irgendwo im Programm:
procedure Bubblesort;
Var i,j: integer;
temp: String;
begin
for i:=1 to 99 do
for j:=i+1 to 100 do
if feld[i]>feld[j] then
begin
temp:=feld[i]; feld[i]:=feld[j]; feld[j]:=temp;
end;
end;
Nun ändert sich die Dimension auf 200. Nun als schlauer Programmierer ersetzen Sie nun überall im Quelltext über die Suchen/Ersetzenfunktion 100 durch 200. Doch was ist mit der Zeile mit der 99? Richtig die bleibt so und schon sortiert ihre Routine nur die Hälfte des Arrays durch. Richtig geschrieben sähe das Programmstück so aus:
const
feld_u 1;
feld_o 100;
type
feld_bound feld_u..feld_o;
feldtyp : Array [feld_bound] of String;
Var
Feld : feldtyp;
und irgendwo im Programm :
procedure Bubblesort;
var i,j : feld_bound;
temp : String;
begin
for i:=feld_u to feld_o-1 do
for j:=i+1 to feld_o do
if feld[i]>feld[j] then
begin
temp:=feld[i]; feld[i]:=feld[j]; feld[j]:=temp;
end;
end;
Das ist zugegeben etwas extrem, aber sehr wartungsfreundlich. Zudem nutzt es auch den range Check für Array und i,j. In der Praxis ist es so, das man meistens als Programmierer Arrays bei 1 oder 0 anfangen lässt. Man kann sich in diesem Fall die untere Grenze sparen. Doch eine Konstante für die Obere Grenze macht immer Sinn!
Zeile:=Leerzeichen_weg(Zeile);
Wenn Sie immer den Übergabeparameter als Funktionsergebnis benutzen ist es besser zu schrieben:
Leerzeichen_weg(Zeile)
wobei Leerzeichen_weg definiert ist als
procedure Leerzeichen_weg(VAR Zeile: String)
Der Vorteil: Bei der Funktion haben Sie zweimal den String kopiert: Einmal bei der Übergabe und einmal bei der Zuweisung. Dies fällt bei der Prozedur weg. Aus diesem Sachverhalt ist auch klar, das es etwas bringt komplexe Typen (alles was intern nicht als Ganzzahl oder Fliesskommazahl gespeichert wird, also Strings, Arrays, Records und Dateien) als VAR Parameter zu übergeben, wenn die Funktion es nicht verändert. Das ist z.B. bei Arrays of der Fall, wenn Funktionen diese nutzen, um Werte zu extrahieren oder zu berechnen.
Der zweite ist, das man so Strukturen wie Arrays, Strings oder Records als Variablenparameter übergeben kann, also den gleichen kompakten Code wie bei VAR erzeugt, jedoch die zusätzliche Absicherung durch die Prüfung hat.
Es ist daher beim Schreiben des Funktionskopfes genau zu überlegen, welche Art der Parameter ist und das richtige Schlüsselwort zu wählen.
if x=0 then erg:=0 else erg:=y/x;
Hier wird vor der Division geprüft ob der Teiler 0 ist und wenn ja als Ergebnis 0 gesetzt. Als zusätzliche Absicherung kann man eine Procedure deklarieren, die Turbo Pascal vor dem ende des Programms auf jeden Fall aufruft.
Es ist eine normale Prozedur ohne Parameter aber mit dem Schlüsselwort far deklariert, da sie von jedem Programmteil aus erreichbar sein muss:
procedure Error; far;
begin
if offene_datei
then close(datei);
end;
Diese Prozedur schließt z.B. offene Dateien vor dem Beenden.
Delphi hat neu das Exception Handling eingeführt, das man auch in den DOS Versionen Free
Pascal und Virtual Pascal findet Grundlage ist folgender Block:
try
anweisungen;
except
Anweisungen im Fehlerfall;
end;
Zwischen Try und exept werden Anweisungen eingeschlossen die Einen Laufzeitfehler verursachen können, z.B. einen Divisionsfehler oder einen I/O Fehler. Tritt dieser Fehler auf, so wird der except Teil angesprungen und die dort befindlichen Anweisungen ausgeführt. Der Fehler ist damit gegessen und verursacht keinen Programmabbruch.
Oftmals hat man keine Anweisungen für den Except Fall, muss aber in jedem Fall Anweisungen durchführen, egal ob ein Fehler auftritt oder nicht. Sehr häufig ist dies z.B. das schließen offener Dateien. Das muss immer passieren. In diesem Fall gibt es eine Variante des try Statements
try
anweisungen;
finally
Anweisungen in jedem Fall;
end;
Egal wann bei try die Bearbeitung durch einen Fehler abgebrochen wurde oder es keinen gab, die Anweisungen bei finally werden auf jeden Fall ausgeführt. So was braucht man wenn man dynamisch Speicher allokiert und diesen im Finally Teil freigeben will, oder wenn man eine Datei bearbeitet und diese in jedem Falle geschlossen werden soll. Wenn es mehrere Fehlermöglichkeiten gibt so kann man diese noch eingrenzen:
try
...
Except
on EZeroDivide do
HandleZeroDivide;
on EOverflow do
HandleOverflow;
on EMathError do
HandleMathError;
else donothing;
end;
Anstatt normaler Anweisungen kann man im Except Teil eine Liste von on Fehler do folgendes aufführen. Die einzelnen Fehlertypen sind definiert, wie zu überlegen ist ein Ezerodivide Fehler ein Laufzeitfehler der durch eine Division durch 0 entsteht. Man kann so auf den Fehler gezielt agieren. Try..except Blöcke können auch geschachtelt werden, für eine lokale Sonderbehandlung. Wichtig ist die Reihenfolge: EMathError ist ein allgemeiner Fehler und EOverflow ein spezieller EMathError. Der Compiler nimmt den ersten auf den der Fehler passt. Würde man die Reihenfolge umkehren, so würde nur der HandleMathError ausgeführt, weil EZeroDivide und EOverflow besondere EMathError sind (abgeleitete Klassen für die Leser, die schon Vererbung beherschen).
Man kann auch Fehlertypen selbst definieren und über raise einen Fehler auslösen. Dies kann wichtig sein wenn eine aufgerufene Funktion einen Fehler feststellt, und dies über raise an die aufrufende Funktion meldet, wodurch deren exception Handling aktiviert wird. Das wird man z.B. nutzen wenn man eigene Objekte mit einem Array hat und versucht wird auf einen ungültigen Index zuzugreifen.
Versuchen Sie ihr Problem in mehrere kleine Funktionen zu zerlegen. Eine Funktion sollte nicht zu lang sein (Faustregel: Auf dem Bildschirm sollte sie komplett Platz haben) und nicht zu viele Parameter entgegennehmen. Ist das der Fall so zerteilen sie weiter. Meistens kommt man so von selbst zu oftmals wiederverwendbaren Funktionen.
Vermeiden sie globale Daten: Diese sind gefährlich. Jeder kann sie ändern. Nutzen Sie stattdessen die Vorteile von Pascal. Pascal bietet ihnen anders als Java oder C/C++ zwei Vorteile an:
Da Sie mit Pascal eine sehr lesbare Programmiersprache haben, erleichtert Ihnen schon einiges. Andere Sprachen sind von Natur aus schwer zu lesen (COBOL, C, C++ als prominente Beispiel) und bei manchen machen sogar die Erfinder vor wie man schlechte Programme schreibt (C). Damit ist neben den etwas langschwafeligen Vorarbeiten, die ich weiter oben getan habe schon etwas getan um ihre Programme sicher und leichter wartbar zu machen. Doch es gehört mehr dazu und das hat nicht einmal etwas unbedingt mit Programmieren zu tun. Man sollte sich überlegen:
Dies bezeichnet man als defensives Programmieren: Sein Programm gegen alle Eventualitäten absichern. Oftmals merkt man beim Nachdenken über diese Fragen, dass man komplette Prüfungen vergessen hat. Der meiner Meinung nach wichtigste Punkt ist der letzte. Sehr oft hat man eine Funktion geschrieben die ganz nützlich ist. Diese war einmal in einen Kontext eingebettet der ein paar Vorarbeiten gemacht hat, nun ist sie in einem anderen Programm nützlich, man übernimmt sie, aber die Vorarbeiten fehlen!
Daher mein Rat: Wenn sie etwas mehrfach verwenden können: lagern sie es in eine Unit aus, packen Sie alles dazu was sie an zusätzlichen Funktionen brauchen. Nutzen Sie auch das in Pascal einmalige Konzept, dass eine Funktion oder Prozedur wiederum einzelne Funktionen als "Unterprogramme" haben kann. Diese können Nebenarbeiten erledigen mit eigenen Variablen und sehen nur das was innerhalb der Funktion bekannt ist - Keine Seiteneffekte wie in C oder C++.´
Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.
Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.
Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.
Mehr über das Buch auf dieser eigenen Seite.
Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.
Sitemap | Kontakt | Impressum / Datenschutz | Neues | Hier werben / advertisment here | Buchshop | Bücher vom Autor |