Die Programmiersprache C

C ist eine Programmiersprache, also eine Sprache, mit der Computerprogramme geschrieben werden.

Programmieren beschreibt das Handwerk, einem Computer beizubringen, dass er eine gewünschte Tätigkeit, einen klar definierten Prozess ausführen soll. Wie in jedem Prozess gibt es beim Programmieren Rohmaterial, das mit geeigneten Werkzeugen zu einem gewünschten Ergebnis verarbeitet wird. Das Rohmaterial ist durch die Begriffe Daten, Werte und Informationen zu beschreiben. Die Programmiersprache ist das Werkzeug.

Programmierung kann als die Steuerung eines Daten-Fluss verstanden werden. In mehreren hintereinandergehängten Modulen werden jeweils Daten eingelesen, verarbeitet und danach ausgegeben, wobei die ausgegebenen Daten von darauffolgenden Modulen wieder eingelesen werden, und so weiter und so fort.

Dieser Fluss ist die grundlegende Motivation der Programmierung und kann wie bei allen Programmiersprachen auch in sämtlichen Entwicklungsphasen der Sprache C beobachtet werden.

Vom elektrischen Strom zu Bits und Bytes

Elektrischer Strom wurde lange Zeit als reinen Energielieferanten betrachtet: Er wird durch Generatoren erzeugt, durch Leitungen transportiert und in Widerständen wie Glühbirnen verbraucht. Dieses Konstrukt wird zusammengefasst unter dem Namen Elektrik. Im Gegensatz dazu behandelt die ElektrONik die Nutzung einer messbaren Grösse des Stroms, beispielsweise die Stromspannung.

Es ist noch nicht lange her, seit die ersten elektronischen Schaltungen erfunden wurden: Eine Methode, mit der elektrischer Strom kontrolliert auf verschiedene Leitungen verteilt werden kann. Das Zusammenfügen mehrerer Schaltungen und Leitungen führte zu den ersten elektronischen Geräten: Einfache Steuerelemente wie beispielsweise automatische Türöffner, Zeitschaltuhren oder die Blinklichter eines Autos. Diese Steuerelemente wandeln stets irgendwelche Eingangssignale wie beispielsweise einen Tastendruck durch eine mehr oder weniger komplizierte Verknüpfung verschiedener Schaltungen in Ausgangssignale um. Erfüllt eine Zusammenstellung von Schaltungen einen bestimmten Zweck, so wird sie als sogenannt integrierte Schaltung in einen Chip gepackt, welcher als Ganzes die gewünschte Funktion ausführt.

Elektronische Bauteile wie Chips werden aufgrund ihrer physikalischen Greifbarkeit Hardware genannt. Mit der Zeit wurden die Ansprüche an Hardware immer höher und die Verknüpfungen in den Chips wurden immer komplizierter. Um beliebig komplexe Funktionen ausführen zu können, wurde schliesslich der programmierbare Chip erfunden: Ein Chip, der nicht nur eine bestimmte Funktion ausführen kann, sondern ein ganzes Set von grundlegenden Befehlen, darunter beispielsweise auch Addieren und Subtrahieren. Zusammen mit einem elektronischen Modul, welches Informationen zwischenspeichern kann (und auch heute noch als der Speicher bekannt ist), können beliebig komplexe Funktionsabläufe mittels eines einzigen Chips erledigt werden. Dies wird noch heute als die CPU, Central Processing Unit bezeichnet, oder schlicht: Prozessor.

In einem Prozessor eines modernen Computers werden anhand der Stromspannung zwei Zustände spezifiziert, bei denen entweder mehr oder weniger Strom als eine bestimmte Schwelle zu fliessen vermag. Diese beiden Zustände Strom und Nicht-Strom definieren die Basiseinheit Bit eines modernen Computers, wobei die Zustände besser bekannt sind als 1 und 0. Durch eine Aufreihung mehrerer Bits entstehen kombinierte Zustände: Acht Bits zusammen ergeben ein Byte, womit 256 verschiedene Zustände möglich sind, mehr Bits ergeben dementsprechend mehr Zustände. Alles, was mittels eines Zustands dargestellt werden kann, wird als Daten (im Singular: ein Datum) bezeichnet.

Maschinensprache und Assembler

Ein Prozessor weiss nicht von sich aus, wie Daten zu handhaben sind. Ob das Datum 11001000 nun die positive Zahl 200 representiert oder die negative Zahl -56, hat für einen Prozessor keine Relevanz, er arbeitet stets mit reinen Daten. Zu früheren Zeiten beinhaltete Programmierung dementsprechend die Aufgabe, den Prozessor anzuweisen, welche Manipulationen er ausführen soll, damit die Daten korrekt verarbeitet werden (EDV = elektronische Datenverarbeitung).

Damit ein Prozessor weiss, was genau er verarbeiten soll, braucht er Instruktionen, einen Code, welcher genau vorgibt, wann welche Eingangsdaten mit welcher Methode verknüpft werden sollen. Die Zusammenstellung solcher Codes wird Software genannt und hat die Aufgabe, die Hardware zu steuern, zu kontrollieren. Eine Abfolge von Instruktionen für einen Prozessor wird Maschinen-Programm genannt, welches in der einzigen für den Prozessor verständlichen Sprache verfasst ist: Der Maschinen-Sprache. Jeder Prozessortyp hat eine eigene Maschinensprache, die nur er versteht. Sie ist für einen Menschen grundsätzlich unverständlich, da sie nur aus binären Werten besteht, welche bestenfalls als Zahlen interpretiert werden können.

Zu alten Zeiten mussten somit einem Prozessor Zahlenwerte übermittelt werden, um eine bestimmte Funktionalität zu erreichen. Da dies für einen Menschen schwierig zu handhaben ist, wurde der sogenannte Assembler erfunden, ein Programm, welches halbwegs menschliche Wörter in die gewünschten Zahlenwerte umwandelt. Diese Wörter werden Mnemonics genannt, was auf Deutsch Gedächtnisstütze bedeutet. Die Sprache, die durch diese Wörter entsteht, wird genauso wie das Umwandlungsprogramm selbst auch Assembler genannt. Folgendes ist ein Beispiel eines Assembler-Programmes:

pushl  %ebp
movl  %esp, %ebp
subl  $24, %esp
movl  $1, -20(%ebp)
movl  $1, -16(%ebp)
movl  -16(%ebp), %eax
addl  -20(%ebp), %eax
movl  %eax, -12(%ebp)
movl  $0, %eax
leave
ret

Da jeder Prozessortyp seine eigene Maschinensprache hat, hat auch jeder Prozessortyp seine eigene Assemblersprache. Die Entwicklung von Prozessoren hält bis heute an, und die Organisation der Daten wurde immer wieder verändert. Dies würde bedeuten, dass für jeden Prozessortyp ein neuer Code geschrieben werden müsste. Um dies zu verhindern, wurden neue Programmiersprachen entwickelt, welche sich nie verändern, im Hintergrund jedoch anhand eines Compilers und/oder Interpreters den Code in jeden gewünschten Assembler übersetzen. Da solche Sprachen über allen Assemblern stehen, werden sie als Hochsprachen bezeichnet. Ein Beispiel für eine Hochsprache ist C.

Die Sprache C

Während bei Assembler der Prozessor explizit angewiesen werden musste, wie er Daten zu speichern und verarbeiten hat, übernimmt bei C diese Arbeit ein Compiler. Dem Compiler muss jedoch zusätzlich mitgeteilt werden, wie die Daten strukturell zu interpretieren sind. Dies ist die Definition für einen Wert: Daten mit einer strukturellen Interpretation. In C wird die strukturelle Interpretation durch einen Typ festgelegt.

Je nach Typ produziert ein Compiler unterschiedlichen, aber genau den zur strukturellen Interpretation passenden Maschinencode. Da in C sämtliche Werte einem Typ zugewiesen werden, ist deren Speicherung als Daten so eindeutig vorbestimmt. Die Aufgabe während des Programmierens besteht somit viel mehr darin, diese Werte korrekt miteinander in Verbindung zu setzen, ihnen eine Bedeutung zu geben. Die Zahl 200 könnte die Anzahl Elemente in einem Array darstellen oder die Zählvariable einer Schleife. Ein Wert mit einer Bedeutung ist eine Information. Der Umgang mit Informationen wird Informatik genannt, oder auf Englisch Information-Technology (IT).

Sobald die Struktur von Daten bekannt ist, kann damit gearbeitet werden. Mit Werten kann ein Computer rechnen. Hier erschliesst sich die wahre Bedeutung des Wortes Computer: To compute = rechnen. Ein Computer ist gemacht, um mathematische Berechnungen auszuführen. In C wird die Verarbeitung von Werten mittels Operatoren verwirklicht. Jeder Operator (beispielsweise eine Addition) nimmt eine bestimmte Anzahl an Eingabewerten, führt eine Verarbeitung aus und gibt einen Wert zurück. Die Ausgabe dieser Berechnung ist wiederum ein Wert, also ein Datum mit Typ. Dies bedeutet, dass jeder Operator selbst wieder als Eingabewert fungieren kann.

Durch Hintereinanderreihung oder Verschachtelung von Operatoren entstehen sogenannte Ausdrücke. Erfüllt ein Ausdruck einen abgeschlossenen Zweck, so wird er als sogenannte Anweisung bezeichnet. Mittels einer Anweisung werden somit mehr oder weniger komplexe Befehle an den Computer übergeben. Aus diesem Grund wird die Sprache C als Befehls-Sprache (oder auch imperative Sprache) bezeichnet.

Mehrere aufeinanderfolgende Anweisungen werden sequentiell abgearbeitet. Diese Teile des Codes sind diejenigen, an welchen der Prozessor schlussendlich Daten verarbeitet, rechnet, produktiv ist. An gewissen Stellen jedoch wird dem Prozessor mitgeteilt, dass er an eine andere Stelle im Code springen soll, beispielsweise, da er je nach Eingabewert etwas anderes ausführen soll. Eine solche Änderung des Befehlsflusses wird als Kontrollstruktur bezeichnet.

Bei sehr vielen aufeinanderfolgenden Anweisungen und Kontrollstrukturen mit komplexen, komplizierten, verwirrenden Zusammenhängen wird dieser Teil der Software auch als Spaghetti-Code bezeichnet. Solche Software ist generell unerwünscht. Um dem entgegenzuwirken, können zusammengehörige Anweisungen und Kontrollstrukturen als sogenannte Funktion verpackt werden, was dann eine komplexe funktionale Einheit darstellt, welche wiederum aus Eingabewerten eine Ausgabe erzeugt. Solche Funktionen können sodann an beliebigen Stellen in der Software einfach aufgerufen werden.

Sämtliche Anweisungen, Kontrollstrukturen und Funktionen werden in Textdateien geschrieben, was als Code oder genauer Source-Code bezeichnet wird. Die deutschen Bezeichnungen Quellcode und Quelltext werden auf dieser Seite nicht verwendet.

Damit aus dem geschriebenen Code ein lauffähiges Programm entstehen kann, wird mithilfe eines Compilers der Source-Code zuerst in Assembler übersetzt und dann in Maschinensprache. Komplexere Programme bestehen zudem aus mehreren Source-Codes und verlinken sich üblicherweise mit existierenden Bibliotheken (auf Englisch Libraries). Um mehrere Source-Codes zusammenzufügen und um zu steuern, wie die einzelnen Teile aufeinander abzustimmen sind, besitzt C einen sogenannten Preprozessor, dem mittels einer Vielzahl an sogenannten Direktiven direkt im Source-Code Anweisungen gegeben werden kann. Am Ende einer Übersetzung verlinkt ein sogenannter Linker alle übersetzten Teile und erstellt ein direkt auf dem Prozessor lauffähiges Programm.

C erlaubt es zudem, ein Programm in einer übersichtlichen und intuitiven Form zu schreiben. Beispielsweise können alle verfügbaren Daten mit einem Namen versehen werden, was als Variable bezeichnet wird. Der Umgang mit Werten wird erleichtert, indem mathematische Operationen mittels Symbolen wie +, -, * und / geschrieben werden können. Kontrollstrukturen können in verständlicher und gar visuell unterstützender Form dargestellt werden. Dasselbe Programm wie im obigen Assembler-Beispiel sieht in C folgendermassen aus:

int main(){
  int a = 1;
  int b = 1;
  int c = a + b;
  return 0;
}

Mit der Sprache C wurde ein Hilfsmittel geschaffen, um Datenmanipulationen schnell und geordnet durchführen zu können. Die Sprache ist durch die Nähe zu Assembler nicht immer ganz einfach, jedoch sehr praktisch. Viele Dinge, die häufig geschrieben werden müssen, sind mit C vereinfacht worden. Viele bekannte kommerzielle Softwarepakete sowie die gängigen Computersysteme sind in ihrer Basis mit C programmiert worden. Die Sprache C kann auch heute noch mit anderen Sprachen konkurrenzieren, insbesondere durch die Erweiterung mit C++ auf objektorientierte Programmierung.

Mehr über C

Hier auf dieser Seite hört die Geschichte von C auf. Als Rahmen sei hier festgehalten, dass die Sprache C aus der Sprache B entstand, welche erstmals etwa 1969 auftrat. C wurde 1972 von Dennis Ritchie entwickelt.

Die Sprache C wurde standartisiert nach ANSI und ISO, wobei auf dieser Seite insbesondere die drei Standards C90, C99 und C11 angegeben werden. Die Zahl steht jeweils für das Publikations-Jahr des Standards (also 1990, 1999 und 2011). Der Standard C90 wird machmal auch mit ANSI C oder C89 bezeichnet, es handelt sich um ein und denselben Standard, nur ist C90 nach ISO zertifiziert. Es ist ein Standard für Code, welcher auch mit älteren Compilern laufen soll. Heutzutage (zum Zeitpunkt als dieser Eintrag geschrieben wurde), gilt C11 als der Standard, welcher von den meisten existierenden Compilern unterstützt wird. Ein neuer Standard C23 ist aktuell in Arbeit.

Nächstes Kapitel: Werte und Typsystem