C gehört zu den beliebtesten Programmiersprachen der Welt. Ursprünglich wurde sie zwischen 1969 und 1973 von Dennis Ritchie bei den AT&T Bell Labs entwickelt. Seitdem wurden viele Tausend Projekte in C umgesetzt. Die Sprache kommt in den unterschiedlichsten Bereichen zum Einsatz: bei Betriebssystemen, eingebetteten Anwendungen, in der Spieleentwicklung, Bildverarbeitung, Textverarbeitung und bei Datenbank-Engines.
Fast alle anderen Programmiersprachen haben sich im Laufe der Jahre erheblich weiterentwickelt. Bei C ist das deutlich weniger ausgeprägt; so brachte beispielsweise der C11-Standard nur wenige neue Funktionen für C – es wurden lediglich einige Änderungen eingeführt.
Was macht die Sprache C selbst 50 Jahre nach ihrer Entstehung noch so beliebt?
I – Stabilität der Sprache
Zurück ins Jahr 1990: die frühen Tage von Microsoft Word
Entwickler aus dieser Zeit erinnern sich an Computer mit vielleicht 8-MHz-Prozessor, 1 Megabyte Arbeitsspeicher, einer 20-Megabyte-Festplatte und einem Diskettenlaufwerk. Unter solchen Bedingungen war die Entwicklung einer Textverarbeitung eine große Herausforderung.
Microsoft hat den Quellcode von „Microsoft Word 1.1“ im Computer History Museum. Es lohnt sich sehr, seinen Quellcode zu untersuchen. Obwohl er vor 25 Jahren geschrieben wurde, können wir daraus noch immer einige sehr interessante Techniken lernen.
Hier ist ein Codeausschnitt daraus:

Wie zu sehen ist, wurde die Funktion im K&R-Stil deklariert. Sämtliche Funktionen verwenden denselben Stil.
Bei der Untersuchung der Quelldateien von „Microsoft Word 1.1“ fällt auf, dass sich die in heutigen Projekten verwendeten Techniken nicht grundlegend von denen unterscheiden, die bereits bei der Implementierung von „Microsoft Word 1.1“ zum Einsatz kamen.
Betrachten wir als Beispiel den folgenden Quellcode:

Man könnte meinen, dieser Code stamme aus einem erst kürzlich auf GitHub erstellten C-Projekt. Nichts deutet darauf hin, dass er 25 Jahre alt ist.
1994 erschien die erste Linux-Version
Am 14. März 1994 wurde Linux 1.0.0 veröffentlicht. Die Entwicklung hatte jedoch bereits 1991 begonnen – dank Linus Torvalds und aller weiteren Mitwirkenden. Der Quellcode dieser ersten Version ist hier.
Hier ist ein Codeausschnitt aus der Quelldatei sched.c:

Vergleicht man die Quelldateien des Linux-Kernels Version 1 mit den neuesten Versionen, überrascht ihre große Ähnlichkeit: Weder beim Stil noch bei den verwendeten Techniken gab es grundlegende Veränderungen. Der Code war von Anfang an einfach, sauber und geradlinig.
II – Grundlegende Mechanismen
Modularität
Modularität ist eine Technik des Softwaredesigns, bei der eine Anwendung möglichst stark in voneinander getrennte Teile gegliedert wird. Modularer Code lässt sich im Allgemeinen leichter verwalten und warten.
In einer prozeduralen Sprache wie C, in der logische Konstrukte wie Namespaces, Komponenten oder Klassen nicht existieren, lässt sich Modularität mithilfe von Verzeichnissen und Dateien erreichen.
Dafür gibt es beispielsweise folgende Möglichkeiten:
- Alle Quelldateien in einem einzigen Verzeichnis ablegen.
- Dateien eines Moduls oder Untermoduls in einem eigenen Verzeichnis zusammenfassen.
Beim Linux-Kernel werden beispielsweise Verzeichnisse und Unterverzeichnisse verwendet, um den Kernel-Quellcode modular zu strukturieren.

Kapselung
Bei der Kapselung werden Funktionen und Daten verborgen, die nur intern für eine Implementierung benötigt werden. In C wird dies mit dem Schlüsselwort static erreicht. Solche Elemente werden als Funktionen und Variablen mit Dateigültigkeitsbereich bezeichnet.
Suchen wir nach allen statischen Funktionen, indem wir die folgende CQLinq Abfrage ausführen.

Mit der Metric View lässt sich gut erkennen, wie viele Funktionen betroffen sind. Dort wird die Codebasis als Treemap dargestellt. Treemapping visualisiert hierarchisch strukturierte Daten mithilfe ineinander verschachtelter Rechtecke. Die in einer CppDepend Treemap verwendete Baumstruktur entspricht der üblichen Codehierarchie:
- Projekte enthalten Verzeichnisse.
- Verzeichnisse enthalten Dateien.
- Files contains structs, functions and variables.
Die Treemap-Ansicht eignet sich sehr gut, um das Ergebnis einer CQLinq-Abfrage darzustellen und die von der Abfrage betroffenen Elemente unmittelbar zu erkennen.

Wie zu sehen ist, sind viele Funktionen als static deklariert.
Suchen wir nun nach statischen Variablen:

Hier zeigt sich dasselbe Bild wie bei den Funktionen: Viele Variablen sind als static deklariert.
Im Quellcode des Linux-Kernels wird Kapselung immer dann eingesetzt, wenn Funktionen und Variablen auf den Gültigkeitsbereich einer Datei beschränkt bleiben sollen.
Fazit
Dieser kurze Rückblick auf einige ältere C-Projekte zeigt, dass sich im Vergleich zu modernen C-Projekten nur sehr wenige Implementierungstechniken verändert haben.
Eine der großen Stärken von C ist seine Beständigkeit über die Jahrzehnte hinweg: Die Sprache ist vergleichsweise einfach geblieben, nur wenige komplexe Mechanismen wurden hinzugefügt, und C-Code lässt sich nach wie vor gut verstehen und warten.
