C (Programmiersprache)
Die Programmiersprache C wurde von Brian W. Kernighan, Ken Thompson und Dennis Ritchie entworfen, um das Betriebssystem Unix portabel aber trotzdem systemnah zu programmieren. Die Programmiersprache 'B', der viele grundlegende Merkmale von 'C' entstammen, wurde zuvor von Ken Thompson entworfen. Die grundlegenden Programme aller Unix-Systeme und die Kernel vieler Betriebssysteme sind in 'C' programmiert.
'C' ist eine Programmiersprache, die auf fast allen Computersystemen zur Verfügung steht. Im Gegensatz zu z.B. BASIC gibt es seit der Definition von ANSI-C relativ einheitliche Implementationen auf den verschiedenen Plattformen. In jedem C-System steht auch die genormte Standard C Library zur Verfügung. Dies und die sehr gute Performance der resultierenden Programme erklärt die weiterhin relativ hohe Popularität der Sprache, sowohl im kommerziellen als auch im Open-Source-Bereich.
Es gibt Meinungen das C softwaretechnologisch gesehen nicht dem heutigen Stand der Technik entspäche, oft wird C auch als portabler Highlevel-Assembler bezeichnet. Dem lässt sich entgegen halten das die Kerne fast aller heute verbreiteten Betriebssysteme in C implementiert wurden, und dies obwohl C++ bereits seit über 20 Jahren zur Verfügung steht. C eignet hervorragend für die Systemprogrammierung, anders sieht die Sache jedoch bei der Anwendungsentwicklung aus. Dort wird C zunehmend durch die objektorientierten Sprachen C++, Java und C#, die C in Bezug auf Wartbarkeit, Produktivität, Entwurfsunterstützung, Abstraktionsniveau, geringerer Fehleranfälligkeit, betriebssystemunabhängiger Ablauffähigkeit und Leistungsfähigkeit der zugehörigen Bibliotheken teilweise eklatant ausstechen.
Die Sprachbeschreibung wurde 1972 erstmals publiziert.
Ein ausführbares C-Programm wird durch den so genannten Linker aus Objektcode erzeugt (gebunden). Die Objektcodedateien ihrerseits werden durch den Compiler aus Textdatei(en) erzeugt (übersetzt), die eine Anzahl Funktions- und Variablendefinitionen enthalten. Ein ausführbares Programm muss eine Funktion mit dem Namen main enthalten.
"Hello, World!"-Programm in C
Der folgende Code stellt ein minimales C-Programm dar, das eine Meldung auf dem Standardausgabemedium ausgibt.
#include <stdio.h>
int main (void) { puts ("Hello, World!"); return 0; }
If ... else
if (Bedingung) { Anweisungen; } [else { Anweisungen; }]
While-Schleife
while (Bedingung) { Anweisungen }
Do ... while
do { Anweisungen } while (Bedingung);
For-Schleife
for (Startausdruck; Bedingung; Inkrementierausdruck) { Anweisungen }
Switch-Ausdruck
switch (Ausdruck) { case marke1 : Anweisungen; break; case marke2 : Anweisungen; break; default : Anweisungen; }
Siehe auch: C++, Objective-C, GCC, Zeittafel der Programmiersprachen
Stärken von C
- minimalistischer Sprachumfang, der kleinste bekannte C Compiler besteht aus 3742 Bytes C Code und kann sich selbst compilieren.
- Hardware Nähe, direkter Umgang mit Bits, Bytes, direkter Speicherzugriff und Zeigerarithmetik
- niedriger Abstraktionsgrad
Schwächen von C
"Der Vorteil einer echten höheren Programmiersprache, und dazu zähle ich C nicht, ist der, dass die Sprachregeln vom Compiler überprüft werden können ..." (Niklaus Wirth).
Unvollständigkeit der Spezifikation
Eine der grössten Schwächen von C ist die Unvollständigkeit der Sprachspezifikation, die viele Dinge offen lässt. So ist z.B. für viele Operatoren nicht festgelegt, in welcher Reihenfolge eine Reihe von gleichrangig nebeneinander stehenden Operatoren ausgewertet werden, ebensowenig, in welcher Reihenfolge die Argumente eines Funktionsaufrufs ausgewertet werden.
Beispiele:
a=b*c*d;
Hier ist nicht festgelegt, ob nun zuerst b*c oder c*d ausgewertet wird. In Bezug auf Rundungsfehler und Überläufe macht das bereits einen Unterschied. Noch deutlicher wird der Unterschied bei Operatoren (und Funktionsaufrufen, die als Operanden auch erlaubt sind) mit Seiteneffekten.
Nimmt man nun stattdessen
a=b++ * ++b * b;
so ergeben sich mehrere mögliche Endergebnisse, u.a.
b*(b+2)*(b+2); (Auswertung von Links nach Rechts, Seiteneffekte sofort effektiv) (b+1) * (b+1) * b; (Auswertung von Rechts nach Links, Seiteneffekte sofort effektiv); b*b*b (Wenn die Seiteneffekte nicht sofort effektiv werden).
Anmerkung: Die Spezifikation von C liefert eindeutige Regeln für solche Ausdrücke. Siehe Diskussion.
Fehlende Unterstützung für Objektorientierung
Der Sprachumfang von C unterstützt die objektorientierte Programmierung nicht, dass heisst, es gibt z.B. keine Sprachkonstrukte, die bei der Erstellung von Vererbungshierarchien helfen, oder die den Programmierer bei der Erstellung und beim Aufruf virtueller Funktionen helfen.
Leistungsschwache Bibliotheken
Wer professionell mit C arbeiten will, kommt mit den dürftigen Bibliotheken in aller Regel nicht aus.
Mangelhafte Stringunterstützung
Den Typ String (Zeichenkette) gibt es nicht im Sprachstandard. Ersatzweise existiert eine Konvention, wie man mit Zeichenketten umgeht und eine begrenzte Zahl von Funktionen der Standard-C-Bibliothek, die diese Konvention umsetzt (strlen, strcpy, strcat, strchr, sprintf und andere). Die Konvention besteht darin, dass für einen String ein Character-Array verwendet werden und das Ende des Strings durch einen Nullcharcter gekennzeichnet wird. Man ging dabei von der Annahme aus, dass dieser in Zeichenketten nicht vorkommt.
Unbekannte Arraylängen
Arrays werden an Funktionen als Pointer (Zeiger) übergeben. Innerhalb der Funktion ist dadurch die Länge des Arrays nicht mehr bekannt und ein Überschreiben von Speicher ohne weiteres möglich. Die Folge sind schwer zu diagnostizierende achitekturabhängige Fehler.
"Wilde" Pointer
Man kann Pointer auf beliebige Stellen des Speichers richten. Insbesondere zeigen nicht explizit initialisierte Stack-Variablen oft auf beliebige Stellen des Speichers. Die Folge sind schwer zu diagnostizierende Fehler.
Aufwändige Speicherverwaltung
Der Programmierer muss den dynamischen Speicher selbst verwalten. Beim Zugriff auf dynamischen Speicher muss der Programmierer selbst wissen, welcher Datentyp wo abgelegt wurde. Es gibt keine Destruktoren und es werden Speicherbereiche nicht freigegeben, wenn die Lebensdauer ihrer Variablen abgelaufen ist.
Portabilitätsprobleme
Die Längen (sizeof) verschiedener Typen sind in der Sprachdefinition nicht vorgeschrieben. So ist z.B. nur zugesichert, dass ein short int wohl nicht länger sein wird als ein long int. Auch die wichtige Länge von Pointern und der Ergebnistyp von Pointerdifferenzen liegt nicht fest. Daher ist eine Portierung auf andere Systeme, insbesondere auf 64-Bit-Architekturen oft mit einigem Aufwand verbunden.
Ein weiteres schwerwiegendes Problem ist die Existenz von little Endian und big Endian Maschinen; Zahlentypen können nicht ohne weiteres auf andere Architekturen übertragen werden.
Schließlich ist auch das Alignment einer structure nicht in der Sprache definiert, so dass Structures auf verschiedenen Architekturen sogar unterschiedliche Größen im Speicher belegen.
Literatur
- The C Programming Language, Brian W. Kernighan and Dennis M. Ritchie. Prentice Hall, Inc., 1988., ISBN 0-13-110362-8 (paperback), ISBN 0-13-110370-9 (hardback).
- Deutscher Titel: Programmieren in C, Hanser, ISBN 3-446-15497-3