Home Computer Programmiersprachen Site Map counter

Warum "C" nicht meine Lieblingsprogrammiersprache ist

Einleitung

Es gibt von Kerningham, einem der Entwickler von C, den Aufsatz "Warum Pascal nicht meine Lieblingsprogrammiersprache ist". Dieser Aufsatz von Anfang der 80er Jahre ist lesenswert. Ich konnte danach erheblich besser verstehen, warum er C entwickelt hat, aber: Er hat die Vorteile von Pascal überhaupt nicht verstanden! Nun bin ich nicht Kerningham und ich habe auch nicht vor, 20 Jahren später eine Retourkutsche auf diesen Aufsatz zu verfassen. Aber ich denke, es ist an der Zeit, mal darzulegen, warum ich die Rolle, die C heute spielt, nicht verstehen kann.

Die Entstehungsgeschichte beider Sprachen.

Warum beide Sprachen die Anwenderschar entzweien, hat auch mit ihrer Geschichte und den daraus gebildeten Sprachmöglichkeiten zu tun, aber auch mit den persönlichen Vorstellungen der Entwickler, die sich in der Sprache niederschlugen.

Die Geschichte von Pascal

Pascal ist die ältere der beiden Sprachen. Sie wurde von 1968-1972 von Niklaus Wirth entwickelt. Wirth war schon damals mit der Entwicklung von Programmiersprachen vertraut. Er arbeitete vorher mit an der Sprache Algol-68. Algol ist der Urvater fast aller heutigen prozeduralen Programmiersprachen. Die Sprachen, die vorher entwickelt wurden, waren weitgehend unstrukturiert. Sie boten nur das GOTO, um bedingte Sprünge zu machen. Algol wird oft im Streben nach einer höheren Abstraktionsebene - von der Maschine zum Modell - als die erste Stufe angesehen: Die Strukturierung des Codes.

Algol hatte aber auch Schwächen. Die Sprache war wie FORTRAN vornehmlich auf Berechnungen ausgerichtet. Es fehlten sowohl leistungsfähige Stringfunktionen als auch Ein/Ausgabefunktionen. Die Syntax war noch nicht so eingängig. Dies wollte Wirth mit Pascal verbessern. Die Sprache sollte primär als Lehrsprache eingesetzt werden. Also nicht als Sprache, um damit kommerzielle Programme zu schreiben, sondern um Programmieren zu lernen. Damit hatte Pascal eigentlich gute Voraussetzungen einen neuen Standard zu setzen.

Dazu wurde die Syntax so entworfen, das Pascalprogramme sehr leicht lesbar und verständlich sind. Das zweite Ziel von Wirth war die Einführung des Typkonzepts. Alle Sprachen vorher kannten nur elementare Datentypen: Zeichen, Zahlen. In Pascal war es möglich, eigene Typen zu definieren. Entweder, indem die schon vorhandenen zusammengefasst wurden (Record), oder als eine Aufzählung oder ein Teilbereich eines schon existieren Typs. Als neuen Typ gab es den Mengentyp (SET OF) und den Zeigertyp (^).

Nicht nur das, der Compiler wachte auch auf die Typeinhaltung, auf Wunsch auch zur Laufzeit. Vorbei waren die Zeiten, als man einer Unterroutine, die eine Ganzzahl erwartete, eine Zeichenkette übergeben konnte. Pascal hatte verbesserte Zeichenkettenfunktionen, aber leider in der Urform auch so wenige Möglichkeiten, Dateien zu bearbeiten, wie Algol.

1970erschien die erste Implementierung für den CDC-6600 Supercomputer. Sehr bald breitete sich die Sprache aus, nicht nur im Unterricht sondern auch in der Programmierung. Die Rolle die heute C hat, hatte Pascal in den siebziger und achtziger Jahre inne. Für die Cray Supercomputer gab es z.B. zwar einen FORTRAN und PASCAL Compiler, doch erst recht spät einen C Compiler. Für eine schnelle Adaption wurde später eine virtuelle Maschine, entwickelt, die P-Code, ähnlich wie heute Java Bytecode interpretierte. Das dabei implementierte Prinzip des Stacks wurde später von den Hardware-Bauern übernommen.

Wirth, der auch Bücher über Algorithmen und Compiler-Bau schrieb, machte dann aber einen Fehler, der sicher daran schuld ist, dass Pascal heute nicht die Rolle spielt, die es verdient hat. Er ging die nächste Stufe der Kapselung von maschinenspezifischen Details an. Algol führte die Strukturierung des Codes ein, Pascal die Strukturierung der Daten. Das nächste ist das Prinzip des Information Hidings, das Verbergen von Details, die unwichtig sind. Doch dies geschah nicht in einem "Pascal-2" sondern einer neuen Sprache: Modula-2. Klar ist, dass eine Sprache, bei welcher der Entwickler nicht mehr hinter ihr steht, einen schlechten Stand hat. Anfang der 80er Jahre erschien Modula-2. Anfang der 90er kam als dritte Sprache von Wirth noch Oberon hinzu: Eine objektorientierte Sprache, deren Laufzeitsystem untypische 200 KByte klein ist.

Für Wirth zählte mehr akademischer Ruhm als die Möglichkeit, dass die von ihm entwickelte Sprache weite Verbreitung findet. Das ist ein bedeutsamer Unterschied zu Kerningham und Ritchie. Unabhängig davon hat sich aber auch Pascal weiter entwickelt. Es gibt heute einen ANSI-Standard, der im wesentlichen die Einschränkungen beseitigte, die Pascal hinderte, als kommerzielle Sprache benutzt zu werden. Viel erfolgreicher wurde aber ein kommerzieller Ableger, doch dazu später mehr.

Die Geschichte von "C"

Die Geschichte von C ist, wie viele wissen, an die von UNIX gekoppelt. Die ersten Versionen von UNIX entstanden noch in Assembler. Kerningham und Ritchie arbeiteten damals mit der Sprache BCPL, einer Sprache, um systemnah zu programmieren. C entstand aus BCPL. (Es ist der nächste Buchstabe im Alphabet). Das Ziel war, dass man eine Sprache hatte, die Assembler für die Programmierung von Betriebssystemen und systemnahen Programmen ersetzen sollte.

C unterscheidet sich daher schon von der Konzeption von Pascal. Viele sehen es als einen Zwischenschritt zwischen Assembler und einer höheren Programmiersprache an, sehr oft stolpert man über den Begriff "Superassembler". Ich finde diesen sehr passend, denn wie in Assembler gibt es in C nur wenige nicht elementare Datentypen. Wie in Assembler verzeiht die Sprache keine Fehler und hat nur rudimentäre Prüfungen implementiert. Es sind allerdings die von Algol eingeführten Möglichkeiten zur strukturierten Programmierung vorhanden.

1978 erschien von Kerningham und Ritchie das Buch "Programmieren in C", dies gilt auch als Geburtsstunde der Sprache. C verbreitete sich über UNIX. Dieses wurde lange Zeit von AT&T verschenkt. Dazu gehörten natürlich die C-Compiler. Vor allem Universitäten, die sparen mussten, setzten dieses Betriebssystem ein. So konnten die Softwarekosten gesenkt werden. Da UNIX in C implementiert war, konnten Studenten so auch das Betriebssystem verstehen lernen. Da sie dazu C lernen mussten, war wohl ein Grund, weshalb sich C durchsetzte. Denn: Worin programmiert der Uni Abgänger? Vornehmlich in dem, was er gelernt hat. So verbreitete sich C langsam, aber sicher.

Dadurch, dass C eine Programmiersprache zur hardwarenahen Programmierung ist, entstanden auch Betriebssysteme in C. So z.B. Windows und Mac OS. Nun ist aber eine Schnittstelle zum Betriebssystem eine Vereinbarungssache. Wie soll man Daten über den Stack übergeben? Wie werden Strings definiert? Indem C seine Schnittstelle hier vorgab, machte es die Anwendungsentwicklung in anderen Sprachen schwerer, denn diese mussten erst die Daten konvertieren.

C wurde zweimal vom ANSI-Gremium normiert. Einmal 1989 in einer Version, in der man die schlimmsten Probleme in der Implementierung von Kerningham und Ritchie beseitigte (z.B., dass man eine Funktion ohne Parameter deklarieren konnte und dann mit beliebig vielen Parametern aufrufen kann). 1999 kam dann eine zweite Normierung, die im wesentlichen darin bestand, die nicht objektorientierten Elemente von C++ zu übernehmen wie // Kommentare, const als Schlüsselwort für echte typisierte Konstanten etc. ...

C wurde zweimal vom ANSI Gremium normiert. Einmal 1989 in der man die schlimmsten Probleme in der Implementierung von Kerningham und Ritchie beseitigten (z.B., das man eine Funktion ohne Parameter deklarieren konnte und dann mit beliebig vielen Parametern definieren kann). 1999 kam dann eine zweite Normierung die im wesentlichen darin bestand, die nicht objektorientierten Elemente auf C++ zu übernehmen wie // Kommentare, const als Schlüsselwort für echte typisierte Konstanten etc....

C hat viele andere Sprachen beeinflusst, dies wird noch später diskutiert.

C und Pascal - ein Vergleich

Zwei Programmiersprachen zu vergleichen, ist nicht einfach. Jeder Entwickler hat in der Regel seine Implementierung vor den Augen. Was mir an dem oben erwähnten Aufsatz von Kerningham zum Beispiel missfiel, waren die Pascal Implementierungen, die er heranzog. Mein erstes Pascal war Turbo Pascal 3.0. Eine Implementierung, die mit Editor nur 32 KByte Speicher brauchte und auf einem 8 Bit CP/M Rechner lief. Obgleich es für diese Plattform keinen brauchbaren C-Compiler gab, (diesen war dieser Rechner zu leistungsschwach) war diese Implementierung denen, die Kerningham benutzte, überlegen, und viele Aussagen die er machte, trafen auf Turbo Pascal schon damals nicht zu.

Es ist daher sinnvoll sich auf die Kernbereiche der Sprachstandards zu begrenzen.

Die Lesbarkeit

Da Pascal eine Sprache ist, die in der Lehre eingesetzt werden sollte, sind Pascal Programme sehr gut lesbar. Dazu tragen viele Dinge bei, die man bei C weggelassen hat.

Eine Funktion zum Konvertieren eines Strings in Großbuchstaben sieht in Pascal z.B. so aus:

Function StringToUppercase(Zeile: String): String;

type Kleinbuchstaben = ['a'..'z'];

var temp: String;
    i    : Byte;

begin
  temp:=zeile;
  for i:=1 to Length(zeile) do
    if zeile[i] in Kleinbuchstaben  then temp[i]:=chr(ord(temp[i])-32);
  StringToUppercase:=temp;
end;

Eine entsprechende Routine in C sieht so aus:

char * StringToUpper(char * zeile)
{
  char * temp;
  int i;
  int p = strlen(zeile);
  temp = (char *) malloc(p);
  for (i=0; zeile[i]!=0 && i<256; i++)
    if (zeile[i]>='a' && zeile[i]<='z')
      temp[i]=(char) zeile[i]-32; else  temp[i]=zeile[i];
  temp[i]=0;
  return temp;
}

Wir erkennen hier schon einen sehr deutlichen Unterschied zwischen beiden Sprachen. C schneidet schon alleine durch die massive Verwendung von Sonderzeichen als Operatoren schlechter ab (Hier *,++,&&,!=). Pascal wurde aber auch erfunden, um eine Abstraktion des Problems zu ermöglichen, und das zeigt sich in der Syntax.

 

So hat jede Pascal-Funktion auch die Kennzeichnung als  "Function" oder Procedure". Das ist zwar etwas mehr Schreibarbeit, doch man kann im Quelltext diese besser finden. Daten und Code werden strikt getrennt: Der VAR Teil mit den Variablen und der Typteil stehen vor dem Code. Auch sonst sind selbst einfache Deklarationen dem Verstand des Nutzers angepasst. Was ist verständlicher "var temp: String" oder "char temp [256]"? Im ersten Fall kann man die Angabe fast wörtlich lesen. "Die Variable Temp ist ein String". Dagegen tauscht C die Definition um und deklariert den Variablentyp zuerst, und bei den Arrays wird es undurchsichtig, da die Dimension nicht am Variablentyp sondern am Name hängt.

Natürlich ist die schlechte Lesbarkeit von C eine direkte Folge der Verwendung von Sonderzeichen. Hier erkennt man, dass die Autoren eben nicht die Erfahrung von Wirth hatten, denn viele Operatoren sind schlecht gewählt. Das Symbol ^ zum Deferenzieren in Pascal gibt die Natur eines Zeigers viel besser wieder als der Stern, der zudem noch als Multiplikationszeichen benutzt wird. Besser gemacht hat man es beim "->", mit dem Verbundkomponenten angesprochen werden können. Warum man mit == und!= vergleicht anstatt mit = und <> habe ich auch nie verstanden. Bei der Syntax von C habe ich oft den Eindruck, dass sie nur anders sein sollte als bei Algol oder Pascal, aber nicht verständlicher.

Mag sein, dass man ursprünglich Speicherplatz sparen wollte, doch hat dies zur Folge, das man C Programme erheblich mehr kommentieren muss, wodurch man nichts gewonnen hat. Die teilweise kryptische Art von C zieht auch einige Programmierer an, die dies lieben; es gibt in C sogar Programmierwettbewerbe wie "Obfusticated C". Bei diesen gewinnen Programme Preise, die kein Mensch lesen kann. In diesem Beispiel ist durch die Notwendigkeit, einen Buffer-Overflow zu vermeiden und die binäre Null anzuhängen, das C-Programm sogar länger als das Pascal-Programm. (hinweis: NBatürlich haben "Obfusticated C Wettbewerbe nichts direkt mit C zu tun, doch sie stehen für eine Kultur die eine Kunst darin sieht nicht gut lesbare und leicht verständliche Programme, sondern kryptische zu schreiben. Dass Personen die dafür empfänglich sind dafür C und nicht Pascal nutzen hat allerdings doch etwas mit der Programmiersprache zu tun).

Unübersichtbarkeit und Unlogisches

Ein Merkmal von Pascal ist, dass es versucht, sich der Syntax einer echten Sprache zu nähern. Das ist bei C bei weitem nicht so. Ich halte das auch für eines der großen Probleme von C.

Nehmen wir die Struct-Anweisung. Diese dient dazu, einen zusammengesetzten Typ zu deklarieren. Man kann ihn aber auch namentlich bekannt machen, ohne ihn zu deklarieren oder gleich Variablen davon anlegen; ja, man kann sogar Variablen eines Typs anlegen, ohne einen Typ anzulegen:

struct mystruct; // Nur definition des Namens
struct mystruct { int a; float b;}; // Typdefinition
struct {int a; float b} astruct; // Nur Variablendefinition ohne Typdefinition
struct mystruct {int a; float b;} astruct *, bstruct // Typ und Variablendefinition. astruct ist Zeiger auf mystruct, bstruct aber ein mystruct typ!

Das ganze ist so kompliziert und unübersichtlich, dass ich mich frage, ob nicht Absicht dahinter steckt. Dasselbe gilt für andere Sprachbestandteile. Warum müssen Prozeduren einen Datentyp void (nichts) zurückgeben? Was wird zurückgegeben, wenn ich bei einer mit void deklarierten Funktion doch einen Wert zurückgebe und warum geht dies überhaupt? (Leider ist auch in C sehr vieles nicht geregelt und implementierungsabhängig - jeder Compiler-Bauer kann sein eigenes Süppchen kochen).

Die unsaubere Syntax zieht sich quer durch die Sprache: Während bei Pascal mit

Type neuertyp = altertyp

klar ist, welches der alte Typ ist, heißt dasselbe in C  
typedef altertyp neuertyp

Ohne Gleichheitszeichen fehlt eine leichte Einprägsamkeit, noch dazu kann man an die Definition dieses Typs noch Variablen Vereinbarungen anhängen. Das macht das ganze unübersichtlich.

Damit es Umsteiger von anderen Sprachen nicht zu einfach haben, hat man extra mit anderen Konventionen gebrochen: Der Doppelpunkt als Teil des Zuweisungsoperators oder Deklarationszeichen von Algol und Pascal wird z.B. im Fragezeichenoperator als Trennzeichen  eingesetzt, und Zuweisungen werden nun mit "=" und Vergleiche mit "==" gemacht. Die Logik bei der Repeat...Until Schleife ist bei C umgedreht:

Anstatt

Repeat....UNTIL Bedingung erfüllt heißt es:

do... while Bedingung nicht erfüllt.

Bei Pascal wird also wiederholt, solange die Bedingung falsch ist, bei C, solange sie wahr ist. Einer ähnlichen Merkwürdigkeit begegnet man bei der Mehrfachauswahl, in Pascal CASE und in C Switch genannt. Während man in Pascal formuliert:

case charval of
  'a'..'z','A'..'Z' : art:=alpha;
  '0'..'9'          : art:=numeric;
  else              art:=undef;
end;

Muss man in C hinter jeden case ein break; setzen, damit nicht die folgenden Fälle auch abgearbeitet werden. Es gibt eigentlich keinen Grund, wozu dass nützlich sein sollte. In jedem Fall ist ein break-Befehl notwendig, also warum erzeugt ihn nicht wie in Pascal der Compiler? Wenn ich mehrere Punkt gemeinsam nutzen will, warum führe ich nicht wie in Pascal einen leistungsfähigen Typ wie den teilbereichstyp ein, anstatt diese fehlerträchtige Konstruktion?

Bei den Operatoren muss man sehr oft berücksichtigen, dass diese "Nebenbedingungen " haben können. Man muss eine Liste der Operatoren-Reihenfolge am Monitor kleben haben (wie ein Professor unserer FH, der soviel von C hält), damit man das Ergebnis des folgenden Ausdrucks kennt:

int x;
x=5;
--x=(++x)--;

Nein, das Ergebnis ist nicht 4 sondern 6. Die Operatorreihenfolge ist sehr schlecht gewählt, oftmals muss man Klammern setzen, vor allem bei Vergleichen und dem sehr häufig benutzten Dereferenzierungsoperator *. Insgesamt fehlt es C stark an Übersichtlichkeit. Dazu trägt auch der Präprozessor bei, eine vornehme Bezeichnung für eine einfache Such/Ersetzfunktion, die im Quelltext Zeichen durch andere ersetzt und über Makros noch viel lustigere Dionge mit dem Quelltext anstellen kann.

Die Unübersichtlichkeit entsteht auch durch das Mehrfachverwenden von Schlüsselwörtern. static ist einmal eine lokale Variable auf dem Heap [wirklich?] und einmal eine extern deklarierte Funktion. Bei C++ wird die Unübersichtlichkeit noch gesteigert, da Schlüsselwörter hier noch mehr Bedeutungen haben.

Pascal hat natürlich auch einige logische Schwächen. Schlecht angedacht ist die Typisierung. Sie ist sehr Streng und basiert auf Typennamen, nicht Typen. So sind

So sind
a = [A'..'Z'];
und
b = ['A'..'Z'];

verschiedene Typen, dagegen aber nicht, wenn die Zuweisung in der Form erfolgt:


type
atyp = ['A'..'Z'];
var
a : atyp;
    b : atyp;

Ein Ziel mag gewesen sein, den Benutzer konsequent zur Einführung von Typen mit den Vorteilen der Typprüfung anzuhalten Bei Strings, die aber verschiedene Länge haben können, ist dieses Konzept falsch. (und da ist es auch aufgeweicht - Zuweisungen zwischen strings verschiedener Länge sind z.B. möglich). Weiterhin haben die Vergleichsoperatoren AND, OR und NOT eine niedrigere Priorität als Vergleichsoperationen, so dass man sehr oft in Vergleichen Klammern muss. Der variable Record ist in Pascal genauso unübersichtlich definiert wie das Pendant in C. In Pascal gibt es aber noch eine die Sicherheit unterstützende Form.

Datentypen

C und Pascal kennen elementare Datentypen wie Ganzzahlen und Gleitkommazahlen. C hat auch den zusammengesetzten Typ von Pascal übernommen, der in C Struct heißt und in Pascal Record. Damit hören aber schon die Gemeinsamkeiten auf, und Umsteiger müssen lernen.

Aufzählungstypen: Ein Aufzählungstyp nimmt nur Werte an, die man angibt. In Pascal ist dies z.B. so etwas:

Farbe = (gelb,rot,blau,gruen);

Es ist eine Form von benannten Konstanten, die das Leben erleichtert, weil man nun im Quelltext z.B. schreiben kann

if wahl=rot then Alarm(err);

Auch C bietet eine Simulation des Typs als enum. Doch damit hören die Gemeinsamkeiten auf. In C ist es nichts anderes als, dass man etliche #define Zeilen einspart. Da diese Typen intern als Zahlen gespeichert werden, kann man ihnen jede Ganzzahl zuweisen, auch z.B. wahl = 10, obgleich wir nur 4 Farben definiert haben. In Pascal geht die Zuweisung einer Zahl über eine Typkonvertierung und löst einen Fehler aus. Hier wird auch deutlich, dass die fehlende Typisierung von C kein Vorteil ist. Denn: Welchen Sinn soll es ergeben, 10 an eine Variable eines Typs zuzuweisen, für den nur 4 Werte definiert sind? Dann müsste man entweder manuell eine Fehlerprüfung einführen oder den Typ erweitern, oder man arbeitet gleich nur mit #defines und einem unbeschränkten Ganzzahltyp wie int.

Teilbereichstypen: Sie sind eine Untermenge eines existierenden Typs, und es gibt sie nur in Pascal. Ein Teilbereichstyp ist z.B.

type Kleinbuchstaben = ['a'..'z'];

Der Compiler kann prüfen, ob eine Variable des Typs Kleinbuchstaben andere Werte wie z.B. "9","A" oder "ü" aufnimmt. Kerningham hat vorgeschlagen, diese automatische Compilerprüfung durch eine Funktion wie islower(..) zu ersetzen. Doch was bedeutet dies?

  1. Mehr Schreibarbeit für den Programmierer anstatt einmal einen Compilerschalter zu setzen
  2. Die Möglichkeit dass man diese Funktion einmal vergisst - der Compilerschalter gilt global.
  3. Der Code wird unübersichtlicher - man muss jedesmal in die Funktion schauen, anstatt den Code direkt vor den Augen zu haben.
  4. Wenn alle Fehler beseitigt sind, legt der Pascal Programmierer den Bereichsüberprüfungsschalter auf "Aus" um und die Prüfungen werden beim Compilieren weggelassen, während in C immer noch die ganzen Funktionen drin sind und Speicher und Rechenzeit brauchen.

Strings

Strings unterscheiden sich in beiden Sprachen grundlegend. In Pascal haben Strings eine feste Länge und im Byte 0 die Länge gespeichert (somit maximal 255 Zeichen lang). In C sind Strings Zeiger auf eine Zeichentabelle. Alles, worauf sie zeigen, wird als Zeichentabelle interpretiert (auch wenn es keine ist). Die Länge ist so dynamisch und das Ende wird durch das Zeichen 0 festgelegt.

Beide Konzepte haben Nachteile. Das Pascal Konzept verschwendet Speicherplatz und limitiert Strings auf max. 255 Zeichen Länge. Es erlaubt aber auch einfachere Stringoperationen. Dazu ist durch die bekannte Längenangabe ausgeschlossen, dass man über die Länge hinaus schreibt. Pascal ist auch sehr gutmütig, wenn man bei Copy, Delete und Insert mehr Zeichen angibt als in den String hineinpassen. Hier wird einfach soviel kopiert, wie die Länge hergibt und der Rest abgeschnitten. Manche Autoren vertreten die Meinung, dass man Strings in C nicht als eigenen Datentype ansehen sollte, sondern einfach als ein besonderes Array of Char.

Das C Konzept erlaubt beliebig lange Strings, da man über malloc und calloc jederzeit neue Speicherbereiche anfordern kann. Das Konzept ist aber sehr unsicher und erfordert vom Programmierer, dass er immer manuell die binäre 0 einfügt. Wird diese überschrieben, so hat man ein Problem. Das Konzept ist noch dazu langsam, da man schon bei einer Längenangabe über den String iterieren muss. Skriptsprachen wie Perl verweisen gerne darauf, dass ihre Stringroutinen schneller als die von C sind - weil viele Leuten meinen C wäre eine sehr schnelle Sprache. Gerade bei den Strings ist dies aber nicht der Fall. Für fast jede Operation (Kopieren, Zuweisen, Länge feststellen) muss man über den String iterieren.

Die zugehörigen Stringroutinen sind ihren Namen nicht wert. [falsch] Hier bemerkt man das C eben eine Sprache für die Entwicklung von Betriebssystemen ist, und da kommen Zeichenketten meistens nur als Meldungen vor. Es gibt keine Operatoren für Strings wie Verkettung, Vergleich oder Zuweisung sondern nur Routinen. Durch das Konzept der binären Null gibt es so genannte Buffer-Overflow Attacken auf Webservern nur bei C Programmen, da hier ein zu langer String die Endkennung und Code überschreibt und man so eigenen Code in fremde System einschleusen kann. [falsch: wenn der Empfänger korrekt programmiert ist, passiert das Selbe wie in Pascal: der über den Platz hinausragende Teil wird abgeschnitten.]

Die zugehörigen Stringroutinen sind ihren Namen nicht wert. Hier bemerkt man das C eben eine Sprache für die Entwicklung von Betriebssystemen ist und da kommen Zeichenketten meistens nur als Meldungen vor. Es gibt keine Operatoren für Strings wie Verkettung, Vergleich oder Zuweisung sondern nur Routinen. Durch das Konzept der binären Null gibt es so genannte Buffer-Overflow Attacken auf Webservern nur bei C Programmen, da hier ein zu langer String die Endkennung und Code überschreibt und man so eigenen Code in fremde System einschleusen kann.

Die String Routinen sind auch ineffektiv, denn man kann nicht einfach die Länge eines Strings bestimmen sondern muss dazu über das Array iterieren. Das kostet Zeit und macht Code ineffektiv. Will man zum Beispiel einen String kopieren so kann man nicht einen Assemblerbefehl eines Prozessors nutzen der einen ganzen Speicherblock kopiert. Will man zwei Strings vergleichen so könnte man bei unterschiedlicher Länge sofort abbrechen. will man zwei Strings verketten so muss man zweimal die Länge ermitteln.

Das Pascal Konzept ist hier einfacher, aber für Anwendungen nicht zu gebrachen, dazu verbraucht es zuviel Speicher und erlaubt keine langen Strings. Es gab daher Bibliotheken die das ganze dynamisch über Speicherallokierung erledigten oder bei Turbo Pascal wurde von C der Zeiger auf eine Zeichenkette (PChar) eingeführt. Damit war das ganze jedoch genauso fehlerhaft und umständlich wie unter C.

Sowohl C++, wie auch Java und auch der Pascal Nachfolger Delphi verwenden ein abgewandeltes Pascal String Design. Hier verwendet man vor dem String zwei Felder zur Speicherung von max. Länge und akt. Länge. Strings können dann größer sein (bis 2 GByte) und wenn man die Verwaltung an die Speicherverwaltung koppelt auch dynamisch in der Größe verändert werden, ohne dass man alten Pascal Code modifizieren muss.

Arrays

Arrays sind in Pascal zuerst einmal statisch, während man in C Arrays als Zeiger auf Speicherbereiche aufpasst, die man als Arrays interpretiert. Auch hier wirkt das C Konzept auf den ersten Blick besser, denn man kann über calloc neuen Speicher anfordern. Doch eigentlich betreibt man hier nur Speicherarithmetik, die auch in Pascal über Getmem und Freemem möglich ist, man hat aber hier zusätzlich zu den dynamischen Arrays ohne jede Prüfung auch noch die statischen mit Bereichsprüfung. Arrays fangen in C immer bei 0 an und werden über Integervariablen indiziert. In Pascal dagegen mit jeder beliebigen Untergrenze und jedem Ordinaltyp als Index. Auch damit sind Programme erheblich besser lesbar schreibbar.

Das Array of char nimmt in C eine Sonderrolle ein. Eigentlich ist char ein Ganzzahltyp mit 1 Byte Größe. Diesem kann man aber auch Buchstaben also Zeichen zuweisen. Dies ist eine Folge der schwachen Typisierung von C. Man simuliert also nur einen String, genauso gut könnte man den Stringroutinen auch einen Speicherblock geben den man von der Festplatte eingelesen hat, ebenfalls eine Folge der fehlenden Typisierung.

Sets

Sets gibt es nur in Pascal. Es sind Mengentypen, die aus den schon beschriebenen Typen bestehen. Das besondere: Während sonst nur ein Wert angenommen werden kann, sind es bei Mengen mehrere Werte. Es gibt die klassischen Mengenoperationen wie Schnittmenge (-) und Vereinigungsmenge (+). Sehr beliebt ist die Operation "in":

if a in ['A'..'Z' then

dies müsste man in C umschreiben in folgendes Konstrukt:

if (a>='A' && a<='Z')

Mengentypen erlauben es sehr viele Vergleiche und Abfragen prägnanter zu formulieren und minimieren Fehler, da man sie als eigene Typen deklarieren kann und nicht manuell viele Prüfungen hinschreiben muss.

Unions

gibt es nur in C. Es sind Bitfelder mit denen man die einzelnen Bits eines Bytes ansprechen kann. Solche Datenstrukturen benötigt natürlich eine hardwarenahe Sprache um diese zu setzen oder abzufragen. In die gleiche Kategorie, die auf die hardwarenähe von C hinweist gehört das Schlüsselwort Volatile, das anweist eine Variable nicht in einem Register zu halten sondern immer an der Hardwareadresse (z.B. wenn dies eine Karte ist bei der ein Schreibzugriff auf den Speicher eine Aktion auslöst).

Zeiger

Zeiger gibt es in beiden Sprachen. In Pascal sind Zeiger grundsätzlich typisiert. In C grundsätzlich nicht. In beiden Sprachen können Zeiger ineinander umgewandelt werden. Für diesen Zweck gibt es in beiden Sprachen aber einen untypisierten Speicher, der in Pascal Pointer heißt und in C void *. Zeiger benötigt man in Pascal um bei verketteten Strukturen (Bäume / Listen) auf das nächste Element zeigen zu lassen und um dynamisch Speicher anzufordern.

In C sind Zeiger der Rettungsanker für fehlende Teile der Sprachspezifikation:

Das ist der Grund warum man in C dauernd über Zeiger stolpert und es so fehleranfällig gilt. Denn wegen dieser Eigenschaften gibt es nur wenige Prüfungen für Zeiger und viele Fehlermöglichkeiten. Man sieht manchmal die Fehler vor lauter Derefentierungssternen nicht mehr.

Natürlich kann man in C mehr mit Zeigern machen. Ist A ein Zeiger auf einen Int und B einer auf eine Float so kann man B an A zuweisen und den Inhalt der Fliesskommazahl interpretieren. Solche Beispiele werden oft als besonderes Sprachmerkmal angegeben. Wozu das allerdings gut sein soll ist mir verborgen geblieben.

Funktionstypen

gibt es ebenfalls nur in C. Diese sind jedoch sehr nützlich und wurden inzwischen auch im Extended Standard von Pascal übernommen. Damit ist es möglich selbst modifizieren Code zu schreiben oder einen interpretierenden Parser durch einen Compiler zu ersetzen, wenn Konstruktionen häufig auftreten. Windows nutzt dieses Sprachmittel als "Callback" sehr weidlich aus.

Die Sprachmittel

C enthält als die jüngere Sprache natürlich etwas mehr Sprachmittel, doch es hält sich in Grenzen. Eigentlich ist es nur das Unterbrechen einer Schleife mit Break und Continue. Es gibt natürlich Unterschiede. So ist die FOR Schleife in Pascal sehr einfach, die in C sehr mächtig und die annehmende Schleife, in Pascal als REPEAT..UNTIL und in C als do..while implementiert hat in C eine umgekehrte Bedingungsprüfung.

Es gibt allerdings Unterschiede was die zur Verfügung stehenden Prozeduren und ihre Implementierung betrifft. C arbeitet mit einem System von Headerdateien welche die Funktionsköpfe bekannt machen. In Pascal kennt das System dagegen bestimmte Funktionen als Bestandteil der Sprache. Das C System ist eindeutig flexibler als das von Pascal. Dies wird erkauft durch eine längere Compilierzeit durch Einbeziehung der vielen Headerdateien bei der Compilierung und bei größeren Projekten durch die Notwendigkeit von Make Dateien, welche die Compilierung steuern.

Viel wichtiger halte ich die systemnahen Sprachmittel von C um Bits zu manipulieren und Unions sowie Compilerangaben wie register. Damit ist C eine Verbesserung gegenüber Assembler bei der Systemprogrammierung. C ist eine gute Sprache um Betriebssysteme zu erstellen oder Treiber zu programmieren. Hier kommen auch die Nachteile von C kaum zum Tragen und hier kann man es sinnvoll einsetzen.

Sicherheit

Eine Sprache gilt in der Informatik sicher, wenn sie möglichst viele Fehler im Programm erkennt und wenn sie auftreten sie die Stabilität des Programms möglichst wenig beeinflussen. Hier sind die deutlichsten Unterschiede zwischen Pascal und C zu erkennen.

Pascal ist eine streng typisierte Sprache. Daten können nur ineinander umgewandelt werden, wenn sie zueinander kompatibel sind. Dies ist z.B. der Fall wenn ein Byte Wert einer Integer Variable zugewiesen wird, da diese den Wertebereich von Byte mit abdeckt. Bei anderen Umwandlungen benutzt man eine Konvertierfunktion wir Trunc() die z.B. einen Fliesskommawert in eine Integerzahl umwandelt oder schreibt selbst eine.

C dagegen ist schwach typisiert. Über den Cast Operator () kann man jede Variable in eine andere Umwandlung, auch wenn dies aufgrund der internen Repräsentation der Daten Unsinn ist. In den wenigen Fällen in denen dies nicht geht gibt es den Typ void * als universeller Zeiger zur Konvertierung. Die meisten Routinen müssen aber diesen Zeiger benutzen um eine Call by Reference Schnittstelle zu implementieren. Damit kann ein C Compiler von sich aus schon weniger Fehler erkennen. Oftmals sieht es so aus, als gäbe es in C sehr viele Typen wie z.B. time_t. Schaut man genauer hin so sind es letztlich nur einfache Datentypen ohne Bereichsprüfung. Ob eine Jahreszahl von -1 Mill. also ein sinnvoller Wert ist kann ein C Programm nicht erkennen.

C kennt keinerlei Prüfungen zur Laufzeit. Überläufe, Bereichsüberschreitungen werden nicht erkannt. Andere Variablen und Codebereiche können so überschrieben werden. Besonders gerne passiert dies bei Strings. Auch sonst scheint der Compiler selbst offen für offensichtlichen Unsinn zu sein. Von Pascal gewohnt, bei Funktionen ohne Parameter keine Klammern zu schreiben, schrieb ich am Anfang:

void func(void)
{
}

void main(void)
{
func;
}

Dies ist jedoch kein Aufruf der Funktion func sondern ein Funktionspointer ohne Variablen Deklarierung. Es ist nichts weiter als Unsinn. Trotzdem ist dies einwandfrei kompiliert worden. Da C sehr effizienten Code produzieren musste gibt es dort sehr viele Operatoren und Konstruktionen die andere Sprachen nicht aufweisen. So ist eine Zuweisung (=) in einer IF Konstruktion möglich die gerne mit einem Vergleich (==) verwechselt wird. Dies geschah ursprünglich um einen Rückgabewert einer Funktion der 0 ist, bei einem Fehler als Möglichkeit zur Prüfung ob die Funktion korrekt funktionierte nutzen zu können.

In Pascal werden alle Variablen auf dem Stack angelegt, der Rückgabewert einer Funktion existiert aber immer. In C gibt es auch Variablen auf dem Heap (Die Werte überleben also einen Funktionsaufruf), aber passt man nicht auf und übergibt nicht als static deklarierte Variablen als Ergebnis, so kann das Ergebnis durch folgende Funktionen überschrieben werden.

Während Pascal ein Laufzeitsystem hat, welches Fehler erkennen kann, die erst beim Programmlauf entstehen, führt C eines ein bei printf die Formatangaben umzusetzen. Wer jemals bei printf den falschen Datentyp angegeben hat weiß, wie fehlerhaft dieses ist.

Eine Fehlerquelle bei C sind auch die Funktionsaufrufe. In C räumt die aufrufende Funktion den Stack auf, in Pascal die aufgerufene Funktion. Dieses unwichtige Detail hat weit reichende Folgen. Wenn man in einer Funktion eine lokale Variable deklariert und diese als Ergebnis zurückliefert, so ist das Ergebnis noch vorhanden, wenn auch die funktion verlassen wurde (und der Speicherbereich auf dem Heap überschrieben wurde). Bei C dagegen schütten weitere Funktionsaufrufe den Stack zu und überschreiben ds Ergebnis. Hier muss man eine lokale Variable explizit als "static" deklarieren um dies zu verhindern. Wer nicht daran denkt hat funktionierende Programme, die nur falsch rechnen.

Das Problem der "Buffer Overflow Errors", wurde ja schon bei den Arrays angesprochen. Es ist eine echte Spezialität von C, die gerne bei Attacken auf Webserver genutzt wird, wenn der Programmierer nicht an die Tests der Größe von übergebenen Strings denkt.

Der Präprozessor

Der Präprozessor ist nichts weiter als eine globale Such/Ersetzfunktion vor dem Übersetzen. Da C keine Konstanten kennt, werden über #define Texte definiert die dann ersetzt werden. Gleiches gilt für Makros die Funktionen ersetzen können. Neben der fehlenden Typprüfung durch solche Ersetzfunktionen können diese auch in rätselhaftem Programmverhalten resultieren, wenn klammern nicht richtig gesetzt werden und so Parameter falsch übergeben werden.

Alte Sprachen wie FORTRAN oder COBOL erkannten nur Grossbuchstaben für Variablennamen an. Pascal ist dagegen unabhängig von der Gross- und Kleinschreibung. "Size" und "size" sind also ein und dieselbe Variable. Bei C wird dagegen auf die genaue Gross- und Kleinschreibung geachtet. Dies macht beim Programmieren nur das Leben schwer. Denn niemand der sich nicht das Leben unnötig schwer machen will, wird zwei Variablen definieren die sich nur durch die Gross- und Kleinschreibung unterscheiden. In der Tat scheint man dies nur eingeführt zu haben, damit man den Präprozessor im Quelltext erkennen kann. Denn in C werden alle mit #define definierten Konstanten nach Konvention immer GROSS geschrieben. Da man in C einer Variablen im Quelltext bei Operationen nur bedingt ansieht, was sie für einen Datentyp hat, führten zahlreiche Firmen eigene Notationssysteme ein damit größere Programme wenigstens einigermaßen lesbar sind. Das bekannteste ist die Ungarische Notation, die auch von Microsoft verwendet wird. Hier kann man die Gross/Kleinschreibung nutzen um die Prefixe lesbarer zu gestalten. In Pascal gibt es ein solches System nicht, wenn man an dem Variablennamen nicht den Typ erkennt so ist dies im Quelltext bei Zuweisungen und Operationen immer eindeutig.

Zusammenfassung

C und Pascal haben zwei Konzepte die diametral entgegengesetzt sind. C verzichtet auf Sprachmöglichkeiten, die es unter Pascal welches ja deutlich älter ist schon gab. Viele Dinge werden dem Programmierer aufgebürdet. Als Gegenleistung erhält man mehr Freiheiten, die man auch nutzen kann um effiziente Programme zu schreiben. Als C entstand waren Compiler noch dümmer als heute, so konnte man mit vielen Befehlen explizit dem Compiler sagen was er zu tun hatte, z.B. bei dem ++ Operator einen Inc anstatt einem Add Befehl zu benutzen. Eventuell wollten Kerningham und Ritchie auch nur schneller einen Compiler erstellen können und haben daher auf vieles verzichtet was Pascal schon bot. Vielleicht waren sie auch keine so gute Compilerbauer, dass sie abspecken mussten. Wirth hatte nicht umsonst schon bei Algol mitgearbeitet und seine Werke über Compilerbau und Algorithmen gehören zu den Standardwerken in der Informatik.

Viele Dinge waren in dem Einsatzzweck von C zur Programmierung von Betriebssystemen oder nahe an der Hardware von Vorteil. Viele Nachteile von C treten in solchen Umgebungen nicht auf, da man es selten mit komplexen Datentypen oder größeren komplexen Funktionen zu tun hat. Auch Stingverarbeitung ist dort nicht gefordert, dagegen sehr oft das Bewegen untypisierter Daten wie Speicherblöcke. Derartige Low-Level Funktionen gab es im ursprünglichen Pascal nicht, aber in nahezu jeder praktisch benutzten Implementierung wurden sie nachgerüstet.

Pascal wurde als Lernsprache konzipiert. Mit dem Ziel Studenten zu lernen saubere Programme zu schreiben. Daher ist die Sprache strenger in der Prüfung und insgesamt sicherer in der Anwendung. Als Lehrsprache wurde leider versäumt eine für die Praxis notwendige Bibliothek an Funktionen, insbesondere für hardwarenahe Operationen und Ein/Ausgabe mitzuliefern.

Pascal hat echte Defizite wenn es darum geht im kommerziellen Umfeld sehr große Programme zu erstellen, da der ursprüngliche Standard nur eine schlechte I/O Bibliothek enthielt und es an Sprachmitteln fehlt Code zu modularisieren, wie es in C (wenn auch umständlich über die Trennung von Header und Programmdateien) möglich ist.

C merkt man an, dass seine Schöpfer noch keine Erfahrungen mit dem Entwurf von Programmiersprachen hatten. Es ist sehr unübersichtlich und unlogisch. Teile sind wohl extra anders als in Pascal gemacht worden, nur um sich zu unterscheiden. Welchen Sinn das haben sollte verstehe ich nicht. (Waren K&R neidisch auf Pascal?) Damit man möglichst schnell einen Compiler fertig stellen konnte hat man bewusst vieles weggelassen, was Pascal schon eingeführt hatte, wie Typüberprüfungen, Arrays mit unterschiedlicher Untergrenze oder eine Laufzeitprüfung. Es überrascht daher nicht, dass die erste Standardisierung 1983 vieles was bei Kerningham & Ritchie erlaubt war abschaffte und zumindest einige Typprüfungen einführte. Selten sind Schöpfer von Sprachen so abgewatscht worden. In der Regel werden Sprachen erweitert nicht verkleinert.

Es ist daher kein Wunder, wenn Verfechter beider Programmiersprachen sich in die Haare kommen, denn meistens haben sie verschiedene Anwendungen im Hinterkopf, für die sich jeweils eine der beiden Programmiersprachen besser eignet.

Die Entwicklung beider Sprachen in den letzten 20 Jahren.

Der Vergleich der beiden Sprachen von Kerningham ist nun 20 Jahre alt - wie haben sich beide Sprachen seitdem entwickelt? Nun C hat sich relativ wenig geändert. Es gab zwei ANSI Standards, im ersten wurden die schlimmsten Sprachschnitzer von Kerningham und Ritchie ausgeräumt, im zweiten einige Sprachfeatures von C++ übernommen, sofern sie nicht die Objektorientierung betreffen.

Auch Pascal wurde zweimal normiert und es gibt einen ANSI Entwurf für objektorientiertes Pascal. Dieser liegt seit 1993 vor, ist aber nicht verabschiedet worden. Die Änderungen betrafen vor allem existierende Schranken einzureißen die Kerningham in seinem Aufsatz auch moniert hat. So gibt es in Pascal heute dynamische Arrays (ohne calloc und malloc zu benutzen wie in C) und offene String Parameter.

"Firmen-Standards" wie Borland Pascal und Delphi gehen hier viel weiter und verwenden Konzepte aus anderen Sprachen wo sie hineinpassen wie Units aus Modula, Objektorientierung oder Interfaces aus Java. Was der Anwender erhält ist ein ungemein mächtiges Werkzeug, aber inkompatibel zu anderen Standards.

Anstatt C weiter zu entwickeln gibt es C++. C++ beinhaltet als Untermenge C. Darüber hinaus ist die Sprache sehr komplex, weil ihr Schöpfer Bjarne Stroustrup meint, dass eine Sprache leistungsfähig genug sein muss alle Anwendungsgebiete abzudecken. Die Tatsache das C als Untermenge enthalten ist macht die Bewertung schwierig. Zwar hat C++ für viele C Probleme Alternativen geboten - Templates als Ersatz für Makros, Referenzen als Ersatz für untypisierte Pointer bei der Übergabe und Objekte anstatt Zeiger auf Structs. Doch man muss sie nicht benutzen, weil C eben noch als Untermenge enthalten ist. Dementsprechend gibt es "C ähnliche" C++ Entwicklungssysteme und mehr "Java ähnliche" Entwicklungssysteme. Zum ersten gehört sicher Visual Studio, wo ohne unzählige Makros, vagabundierende Pointer auf GDI Objekte und Ressourcen man meint dass man C anstatt in C++ programmiert. Auf der anderen Seite gibt es Komponenten basierende Entwicklungsumgebungen wie den CBuilder, der mit Objekten, Eigenschaften und Methoden arbeitet.

Einflüsse auf andere Sprachen

Jede der beiden Sprachen hat Einflüsse auf andere Sprachen gehabt. C war in dieser Hinsicht erfolgreicher. Pascal ähnliche Programmiersprachen gibt es vergleichsweise wenige. Es ist mir neben den Nachfolgern Modula und Oberon noch nur Ada, Delphi und COMAL bekannt. Das liegt vielleicht auch daran. Dass viele Pascal Compiler einfach fehlende Sprachbestandteile nachgerüstet haben. So ist Delphi zwar ein "Objekt-Pascal", jedoch ungleich leistungsfähiger als das urplötzliche Pascal. Es ähnelt in den Sprachmöglichkeiten mehr einem Java mit Pascal Syntax als dem ursprünglichen Pascal.

C hat die wesentlichsten Elemente der Syntax an viele Sprachen weitergegeben. Das erscheint unverständlich, denn selbst die größten C Verfechter halten es nicht für sehr übersichtlich und leicht lesbar. Aber C ist nun mal mit UNIX verknüpft. Die C/Unix Erfinder schufen auch Shell Skriptsprachen die sich an C orientieren. Viele lernten C wegen UNIX und als sie dann an eine neue Sprache gingen, so ähnelte die in der Syntax C. Die grundsätzlichen Elemente findet man in Java, C#, PHP, Javascript, Perl wieder.

Allerdings muss man sehen, das in der Reihe C++ -> PHP -> Java man immer mehr Konzepte von Pascal übernommen hat. C++ führte das typsichere Zeigerkonzept und die Call by Reference Schnittstelle ein. In Java gibt es gar keine Zeiger mehr, sondern wie in Pascal Referenzen auf Speicherbereiche, die wie in Pascal mit New allokiert werden.

Delphi zeigt wo Pascal heute sein könnte, wenn man es konsequent weiterentwickelt hätte. Delphi ist nicht nur modular wie Modula (Durch das Unit konzept), es ist objektorientiert wie C++ oder Java. Es ist auch ein System, das es erlaubt GUI Anwendungen sehr einfach zu bauen indem die Elemente als Objekte repräsentiert werden. Der in C++ nicht implementierte Namensraum "published" erlaubt es zudem einer visuellen IDE Eigenschaften zur Verfügung zu stellen , die der Anwender ohne Code zu schreiben in einem Objektinspektor ändern kann.

Wer einmal mit Delphi programmiert hat und dann mit C konfrontiert wird bekommt einen Kulturschock. Aufgefallen ist mir in meinem Stoftwaretechnik Studium, dass die Professoren es weitgehend nicht kennen. So bekommt man beim Neueinführen von C++, Java oder nun C# im Unterricht jedes mal gesagt "Unter C++ brauchen wir keine Pointer mehr wie in C++", "Mit Java können wir nun auch GUI programmieren", "Mit C# kann man visuell programmieren". Als wäre dies das neueste und tollste: Hier steht Delphi schon seit 1994. Dabei ist der Einfluss bei Java (wo Borland bei der Bean Entwicklung beteiligt ist) und vor allem C# bei dem der ehemalige Schöpfer von Turbo Pascal Anders Hejlsberg Chef der Entwicklungsabteilung ist sehr deutlich zu sehen. Pascal Programmierer sind ihren C Kollegen eben um fast ein Jahrzehnt voraus. Besonders amüsant finde ich dass alles was C angeblich so toll machen soll unter C# nun "unsafe Code" ist. Endlich wird es mal zugegeben!

Einen vergleichbaren Entwicklungsstand hat der Free Pascal Compiler, der jedoch nicht ein GUI Werkzeug ist. Wer mit solchen Sprachmitteln aufgewachsen ist und dann an ein "Visual Studio" eines berühmten Redmonder Herstellers geht fühlt sich in die Computersteinzeit zurückversetzt. Das ist Windows Programmierung auf die Harte Tour und API Funktionen. Leider ist Delphi oder Pendant Kylix aber eben nur auf Windows oder Linux verfügbar. Das ist eben das Problem, das man Pascal nicht wirklich weiter entwickelt hat. Auch die Nachfolger Modula und Oberon sind eben weitaus weniger erfolgreich als C++ oder Java geblieben.

Es ist allerdings beruhigend zu sehen, dass viele moderne Sprachen die nun aufkommen wie Python oder Ruby sich an einer sauberen Syntax wie in Pascal und Laufzeitprüfungen orientieren. Während die Vorteile von C durch den rasanten Fortschritt in der Computerleistung, die viel intelligentere Compiler ermöglicht heute ohne Belang sind, fehlen der Sprache noch immer die Möglichkeiten die Pascal bietet. C ist wie Windows wohl eines der erfolgreichsten Provisorien in der Computerwelt.

Zum Schluss noch ein Satz von Wikipedia über Pascal:

"Modern implementations of Pascal eliminate nearly all major flaws (and add means for object-oriented programming), making Pascal as useful as C or C++, whilst retaining the characteristic simplicity and safety of the language. However most modern Pascal compilers focus on application development rather than system development, which has different requirements from a programming language."

Warum ist C so erfolgreich?

Es ist unbestreitbar das C / C++ heute die Sprachen sind für die am meisten Programmierer gesucht werden. Viele halten diesen Erfolg als einen Beweis, das diese Sprachen besser seien als Pascal. Hier mal eine Liste der großen C Mythen:

C erzeugt sehr schnellen Code

C hat sicherlich Eigenschaften, die es einem Compiler erlauben schnellen Code zu produzieren. Er ist jedoch nicht de fakto alleine deswegen schneller. Natürlich sind Pointer mit denen man über Arrays iteriert eine Vorgabe für den Compiler so auf die Elemente zuzugreifen. Doch die Zeiten in denen Compiler so dumm waren, dass sie nicht erkennen konnten das i=i+1 dasselbe wie i++ ist, ist lange vorbei. Schon Turbo Pascal zeigte 1983, dass ein Compiler auch Pascal effektiv in Code umsetzen konnte. Es gab damals auf der PC Basis keinen C Compiler der schnelleren Code erzeugte. Schon vorher zeigten Pascal-Compiler auf den Supercomputern von Cyber und Cray, das Pascal sich in der Geschwindigkeit nicht hinter C oder FORTRAN verstecken muss.

C hat sich durchgesetzt weil es besser als Pascal ist

C hat sich durchgesetzt weil UNIX sich verbreitet hat. C hat natürlich einen Einfluss als hardwarenahe Sprache auf die Erstellung von Betriebssystemen. Die gesamte API eines in C geschriebenen Betriebssystems besteht dann in Aufrufen für C. Die gesamte Dokumentation und Beispiele wie man programmiert sind dann in C. Das gilt nicht nur für UNIX, wer die hilfe für die Windows API installiert findet alle Routinen mit C Parametern und auch alle Programmbeispiel sind in C. Neben dem Problem der Strings legen Pascal und C ihre Parameter auch anders herum auf den Stack. Andere Sprachen (nicht nur Pascal) sind daher benachteiligt. Das ganze hat aber auch einen psychologischen Effekt. Diesen kann man an Microsoft und Windows erkennen. Viele Firmen und Programmierer programmieren unter Windows mit Visual Studio. Warum: Nun es gibt von Microsoft einen Dienst den MSDN, der den Entwickler mit Informationen, Bugfixes und Hilfen versorgt, alles natürlich in C und auf das hauseigene Produkt abgestimmt. Viele Firmen kennen Konkurrenzprodukte und wissen dass diese teilweise erheblich mehr können, aber mit Visual Studio ist man auf der sicheren Seite. Gerade Microsoft zeigt auch dass nicht immer das beste Produkt auch das erfolgreichste ist...

Im Ernst: Es gibt auch bei Programmiersprachen zahlreiche Beispiele, dass nicht immer die erfolgreichsten die besten sind. Wer Smalltalk kennt schüttelt den Kopf über die Geschwindigkeit wie sich Java durchgesetzt hat. Bei Skriptsprachen sind die Gegensätze sicher Perl und Python oder Ruby.

C ist portabler als Pascal

Man hat bei C sicher mehr auf das Problem der Portabilität geachtet als bei Pascal. Es ging ja bei Pascal nie darum eine kommerzielle Sprache zu schaffen. Doch wird dies bei jeder Sprache, die direkten Code für eine Maschine erzeugt, nie 100 % gelingen. Datentypen sind auf unterschiedlichen Maschinen unterschiedlich breit, Dateisysteme anders. Bei den meisten C Compilern artetet dies in eine ellenlange typedef oder #define Orgie aus. Das verbessert die Lesbarkeit des Quelltextes auch nicht gerade. Dazu kommt noch das Problem, das in C anders als in Pascal es viele Details gibt die nicht festgelegt sind, also jeder Compilerhersteller anders implementieren kann.

Um zwei Beispiele anzuführen: Delphi ist die wohl bekannteste objektorientierte Erweiterung von Pascal. Delphi kann man mit relativ kleinen Anpassungen einsetzen auf Linux, Windows und .NET. Das sind drei völlig unterschiedliche Plattformen, wobei wir hier nicht von Konsolenprogrammen reden sondern von grafischen Anwendungen, d.h. es kommt auch mit sehr unterschiedlichen API's in den Systemen zurecht. Dagegen kann man mit dem Intel C++ Compiler für Linux nicht die Sources von KDE kompilieren, die reines C++ sind. Mehr noch: Verschiedene Linux Programme benötigen verschiedene Versionen des GNU-C++ Compilers.

Solange man nicht eine virtuelle Maschine wie in Java einsetzt wird es immer so sein, dass man viel anpassen muss wenn man die Hardwareplattform ändert. Zudem spielen heute herstellerspezifische Bibliotheken eine sehr große Rolle. Fast niemand nutzt mehr in seinem C oder Pascal Programm nur die Standardroutinen.

Die Zukunft

Nun es ist sicher das die C Nachfolger C++ und Java auch in Zukunft wichtig sein werden. Ich denke aber C wird von C++ langsam abgelöst werden, weil es auch bei prozeduraler Programmierung erheblich bessere Konzepte bietet. (Neben den schon erwähnten Punkten auch das Exception Konzept). Pascal mag als Sprache sicher nicht mehr den großen Run erleben, auch wenn ich mir wünschte, das Delphi und ähnliche Systeme sich noch mehr durchsetzen werden. Es ist aber befriedigend zu sehen, das die Entwicklung weg von der kryptischen C Syntax zu einer sauberen Syntax geht, wie sie Pascal bietet. Das zeigt sowohl Java wie auch andere neue Sprachen wie Ruby oder Python. Auch C++ hat durch Java an Verbreitung verloren und in bestimmten Gebieten wie dem Webumfeld werden Skriptsprachen in denen man erheblich mehr machen kann als in den compilierten Sprachen Pascal und C noch mehr Verbreitung finden.

Zum Schluss noch die Beantwortung der Frage: C ist deswegen nicht meine Lieblingssprache, weil sie in fast allem schlechter ist als selbst das inzwischen veraltete Turbo Pascal. Ich kann keine wirklichen Vorteile erkennen, nur mehr Mühe und mehr Fehlermöglichkeiten. Letzteres scheint nicht nur meine Meinung zu sein. Ein Grund warum heute interpretierende Sprachen wie Java oder Python soviel mehr eingesetzt werden ist auch der, dass sie viel mehr Prüfungen vor allem auch zur Laufzeit bieten. Dabei hat man mehr Möglichkeiten als in Pascal mit seiner statischen Typprüfung.

Alles nur Quatsch?

Es gibt den satirischen Artikel "UNIX Erfinder geben zu - Alles nur Quatsch". In diesem wird geschrieben, das die Erfinder von C dieses nur geschrieben haben um die Sowjets 20 Jahre zurückzuwerfen, indem sie eine Programmiersprache erfanden, die es dem Anwender so schwierig wie möglich macht sauber zu Programmieren. Nun ist dies eine Satire, aber ist nicht doch ein Körnchen Wahrheit dran? Wer beim Bell Laboratorium die Lebensläufe von Kerningham und Ritchie durchschaut merkt, dass sie außer C und UNIX nichts entwickelt haben. Die letzten 25 Jahre - Sense! Warum haben Sie C nicht weiterentwickelt, warum ist UNIX auf dem Benutzerkomfort von CP/M und DOS beschränkt geblieben? Konnten Sie es nicht oder wollten Sie es nicht? Ich denke wenn man diese Fragen beantwortet bekommt dürfte mehr Klarheit herrschen warum C so ist, wie es ist.



© des Textes: Bernd Leitenberger. Jede Veröffentlichung dieses Textes im Ganzen oder in Auszügen darf nur mit Zustimmung des Urhebers erfolgen.

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 Lebensä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.

Sitemap Kontakt Neues Bücher vom Autor Buchempfehlungen Top 99