Nach all diesen Jahren wird die Welt immer noch von der C-Programmierung angetrieben

Veröffentlicht: 2022-03-11

Viele der heute existierenden C-Projekte wurden vor Jahrzehnten gestartet.

Die Entwicklung des UNIX-Betriebssystems begann 1969, und sein Code wurde 1972 in C umgeschrieben. Die C-Sprache wurde eigentlich geschaffen, um den UNIX-Kernel-Code von Assembly in eine höhere Programmiersprache zu verschieben, die die gleichen Aufgaben mit weniger Codezeilen erledigen würde .

Die Entwicklung der Oracle-Datenbank begann 1977, und ihr Code wurde 1983 von Assembly auf C umgeschrieben. Sie wurde zu einer der beliebtesten Datenbanken der Welt.

1985 wurde Windows 1.0 veröffentlicht. Obwohl der Windows-Quellcode nicht öffentlich verfügbar ist, wurde festgestellt, dass sein Kernel hauptsächlich in C geschrieben ist, mit einigen Teilen in Assembler. Die Entwicklung des Linux-Kernels begann 1991 und ist ebenfalls in C geschrieben. Im nächsten Jahr wurde es unter der GNU-Lizenz veröffentlicht und als Teil des GNU-Betriebssystems verwendet. Das GNU-Betriebssystem selbst wurde mit den Programmiersprachen C und Lisp gestartet, daher sind viele seiner Komponenten in C geschrieben.

Aber C-Programmierung ist nicht auf Projekte beschränkt, die vor Jahrzehnten begonnen haben, als es noch nicht so viele Programmiersprachen gab wie heute. Viele C-Projekte werden noch heute gestartet; Dafür gibt es einige gute Gründe.

Wie wird die Welt von C angetrieben?

Trotz der Verbreitung von höheren Sprachen stärkt C weiterhin die Welt. Im Folgenden sind einige der Systeme aufgeführt, die millionenfach verwendet werden und in der Sprache C programmiert sind.

Microsoft Windows

Der Windows-Kernel von Microsoft wird hauptsächlich in C entwickelt, mit einigen Teilen in Assemblersprache. Seit Jahrzehnten wird das weltweit meistgenutzte Betriebssystem mit etwa 90 Prozent Marktanteil von einem in C geschriebenen Kernel angetrieben.

Linux

Linux ist auch größtenteils in C geschrieben, mit einigen Teilen in Assembler. Etwa 97 Prozent der 500 leistungsstärksten Supercomputer der Welt führen den Linux-Kernel aus. Es wird auch in vielen Personalcomputern verwendet.

Mac

Mac-Computer werden ebenfalls von C betrieben, da der OS X-Kernel hauptsächlich in C geschrieben ist. Jedes Programm und jeder Treiber auf einem Mac läuft, wie auf Windows- und Linux-Computern, auf einem C-basierten Kernel.

Handy, Mobiltelefon

iOS-, Android- und Windows Phone-Kernel sind ebenfalls in C geschrieben. Sie sind nur mobile Anpassungen bestehender Mac OS-, Linux- und Windows-Kernel. Smartphones, die Sie täglich verwenden, laufen also auf einem C-Kernel.

Betriebssystem-Kernel geschrieben in C

Datenbanken

Die weltweit beliebtesten Datenbanken, einschließlich Oracle Database, MySQL, MS SQL Server und PostgreSQL, sind in C codiert (die ersten drei davon tatsächlich sowohl in C als auch in C++).

Datenbanken werden in allen Arten von Systemen verwendet: Finanzen, Regierung, Medien, Unterhaltung, Telekommunikation, Gesundheit, Bildung, Einzelhandel, soziale Netzwerke, Web und dergleichen.

Datenbanken Powered by C

3D-Filme

3D-Filme werden mit Anwendungen erstellt, die im Allgemeinen in C und C++ geschrieben sind. Diese Anwendungen müssen sehr effizient und schnell sein, da sie riesige Datenmengen verarbeiten und viele Berechnungen pro Sekunde durchführen. Je effizienter sie sind, desto weniger Zeit benötigen die Künstler und Animatoren, um die Filmaufnahmen zu erstellen, und desto mehr Geld spart das Unternehmen.

Eingebettete Systeme

Stellen Sie sich vor, Sie wachen eines Tages auf und gehen einkaufen. Der Wecker, der Sie aufweckt, ist wahrscheinlich in C programmiert. Dann verwenden Sie Ihre Mikrowelle oder Kaffeemaschine, um Ihr Frühstück zuzubereiten. Sie sind ebenfalls eingebettete Systeme und daher wahrscheinlich in C programmiert. Sie schalten Ihren Fernseher oder Ihr Radio ein, während Sie frühstücken. Das sind auch eingebettete Systeme, powered by C. Wenn Sie Ihr Garagentor mit der Fernbedienung öffnen, verwenden Sie auch ein eingebettetes System, das höchstwahrscheinlich in C programmiert ist.

Dann steigst du in dein Auto. Wenn es folgende Eigenschaften hat, auch in C programmiert:

  • automatische Übertragung
  • Reifendruckerkennungssysteme
  • Sensoren (Sauerstoff, Temperatur, Ölstand etc.)
  • Speicher für Sitze und Spiegeleinstellungen.
  • Dashboard-Anzeige
  • Antiblockiersystem
  • automatische Stabilitätskontrolle
  • Tempomat
  • Klimakontrolle
  • Kindersicherung
  • schlüsselloser Zugang
  • beheizte Sitze
  • Airbag-Steuerung

Sie kommen zum Laden, parken Ihr Auto und gehen zu einem Automaten, um eine Limonade zu holen. In welcher Sprache haben sie diesen Verkaufsautomaten programmiert? Wahrscheinlich C. Dann kaufst du etwas im Laden. Auch die Kasse ist in C programmiert. Und wenn Sie mit Ihrer Kreditkarte bezahlen? Sie haben es erraten: Der Kreditkartenleser ist wahrscheinlich wieder in C programmiert.

Alle diese Geräte sind eingebettete Systeme. Sie sind wie kleine Computer, die einen Mikrocontroller/Mikroprozessor enthalten, der ein Programm, auch Firmware genannt, auf eingebetteten Geräten ausführt. Dieses Programm muss Tastendrücke erkennen und entsprechend handeln und dem Benutzer auch Informationen anzeigen. Beispielsweise muss der Wecker mit dem Benutzer interagieren, erkennen, welche Taste der Benutzer drückt und manchmal wie lange er gedrückt wird, und das Gerät entsprechend programmieren, während er dem Benutzer die relevanten Informationen anzeigt. So muss beispielsweise das Antiblockiersystem des Autos ein plötzliches Blockieren der Reifen erkennen und die Bremsen kurzzeitig entlasten, entriegeln und so ein unkontrolliertes Schleudern verhindern. Alle diese Berechnungen werden von einem programmierten eingebetteten System durchgeführt.

Obwohl die auf eingebetteten Systemen verwendete Programmiersprache von Marke zu Marke variieren kann, werden sie aufgrund der Flexibilität, Effizienz, Leistung und Nähe zur Hardware am häufigsten in der Sprache C programmiert.

Eingebettete Systeme werden oft in C geschrieben

Warum wird die Programmiersprache C immer noch verwendet?

Heutzutage gibt es viele Programmiersprachen, die es Entwicklern ermöglichen, bei verschiedenen Arten von Projekten produktiver zu sein als mit C. Es gibt höhere Programmiersprachen, die viel größere integrierte Bibliotheken bereitstellen, die die Arbeit mit JSON, XML, UI, Webseiten, Client-Anfragen, Datenbankverbindungen, Medienmanipulation und so weiter vereinfachen.

Trotzdem gibt es viele Gründe zu der Annahme, dass die C-Programmierung noch lange aktiv bleiben wird.

In Programmiersprachen passt eine Größe nicht für alle. Hier sind einige Gründe, warum C für bestimmte Anwendungen unschlagbar und fast obligatorisch ist.

Portabilität und Effizienz

C ist fast eine portable Assemblersprache . Es ist so nah wie möglich an der Maschine, während es für bestehende Prozessorarchitekturen nahezu universell verfügbar ist. Für fast jede existierende Architektur gibt es mindestens einen C-Compiler. Und heutzutage ist es aufgrund hochoptimierter Binärdateien, die von modernen Compilern generiert werden, keine leichte Aufgabe, ihre Ausgabe mit handgeschriebener Assemblierung zu verbessern.

Seine Portabilität und Effizienz ist so groß, dass „Compiler, Bibliotheken und Interpreter anderer Programmiersprachen oft in C implementiert sind“. Interpretierte Sprachen wie Python, Ruby und PHP haben ihre primären Implementierungen in C geschrieben. Es wird sogar von Compilern für andere Sprachen verwendet, um mit der Maschine zu kommunizieren. Beispielsweise ist C die Zwischensprache, die Eiffel und Forth zugrunde liegt. Das bedeutet, dass, anstatt Maschinencode für jede zu unterstützende Architektur zu generieren, Compiler für diese Sprachen nur Zwischen-C-Code generieren und der C-Compiler die Maschinencode-Generierung übernimmt.

C ist auch zu einer Lingua Franca für die Kommunikation zwischen Entwicklern geworden. Wie Alex Allain, Dropbox Engineering Manager und Schöpfer von Cprogramming.com, es ausdrückt:

C ist eine großartige Sprache, um gemeinsame Ideen beim Programmieren auf eine Weise auszudrücken, mit der die meisten Menschen vertraut sind. Darüber hinaus werden viele der in C verwendeten Prinzipien – zum Beispiel argc und argv für Befehlszeilenparameter sowie Schleifenkonstrukte und Variablentypen – in vielen anderen Sprachen auftauchen, die Sie lernen, sodass Sie sprechen können an Leute, auch wenn sie C nicht auf eine Art und Weise kennen, die euch beiden gemeinsam ist.

Speichermanipulation

Der Zugriff auf beliebige Speicheradressen und Zeigerarithmetik ist ein wichtiges Merkmal, das C perfekt für die Systemprogrammierung (Betriebssysteme und eingebettete Systeme) geeignet macht.

An der Hardware/Software-Grenze bilden Computersysteme und Mikrocontroller ihre Peripheriegeräte und I/O-Pins in Speicheradressen ab. Systemanwendungen müssen diese benutzerdefinierten Speicherorte lesen und schreiben, um mit der Welt zu kommunizieren. Daher ist die Fähigkeit von C, beliebige Speicheradressen zu manipulieren, für die Systemprogrammierung unerlässlich.

Ein Mikrocontroller könnte beispielsweise so aufgebaut sein, dass das Byte in der Speicheradresse 0x40008000 jedes Mal vom universellen asynchronen Empfänger/Sender (oder UART, einer gemeinsamen Hardwarekomponente zur Kommunikation mit Peripheriegeräten) gesendet wird, wenn Bit Nummer 4 der Adresse 0x40008001 gesetzt wird auf 1, und dass, nachdem Sie dieses Bit gesetzt haben, es automatisch vom Peripheriegerät zurückgesetzt wird.

Dies wäre der Code für eine C-Funktion, die ein Byte durch diesen UART sendet:

 #define UART_BYTE *(char *)0x40008000 #define UART_SEND *(volatile char *)0x40008001 |= 0x08 void send_uart(char byte) { UART_BYTE = byte; // write byte to 0x40008000 address UART_SEND; // set bit number 4 of address 0x40008001 }

Die erste Zeile der Funktion wird erweitert zu:

 *(char *)0x40008000 = byte;

Diese Zeile weist den Compiler an, den Wert 0x40008000 als Zeiger auf ein char zu interpretieren, dann diesen Zeiger (mit dem Operator * ganz links) zu dereferenzieren (den Wert anzugeben, auf den gezeigt wird) und schließlich diesem dereferenzierten Zeiger einen byte zuzuweisen. Mit anderen Worten: Schreiben 0x40008000 byte

Die nächste Zeile wird erweitert zu:

 *(volatile char *)0x40008001 |= 0x08;

In dieser Zeile führen wir eine bitweise ODER-Operation mit dem Wert an Adresse 0x40008001 und dem Wert 0x08 ( 00001000 in Binärform, dh eine 1 in Bit Nummer 4) aus und speichern das Ergebnis wieder an Adresse 0x40008001 . Mit anderen Worten: Wir setzen Bit 4 des Bytes, das an Adresse 0x40008001 liegt. Wir erklären auch, dass der Wert bei Adresse 0x40008001 flüchtig ist. Dies teilt dem Compiler mit, dass dieser Wert von Prozessen außerhalb unseres Codes geändert werden kann, sodass der Compiler keine Annahmen über den Wert in dieser Adresse trifft, nachdem er darauf geschrieben hat. (In diesem Fall wird dieses Bit von der UART-Hardware zurückgesetzt, unmittelbar nachdem wir es von der Software gesetzt haben.) Diese Information ist wichtig für den Optimierer des Compilers. Wenn wir dies beispielsweise innerhalb einer for -Schleife tun, ohne anzugeben, dass der Wert flüchtig ist, könnte der Compiler davon ausgehen, dass sich dieser Wert nach dem Festlegen nie ändert, und die Ausführung des Befehls nach der ersten Schleife überspringen.

Deterministische Ressourcennutzung

Eine gemeinsame Sprachfunktion, auf die sich die Systemprogrammierung nicht verlassen kann, ist die Garbage Collection oder sogar nur die dynamische Zuordnung für einige eingebettete Systeme. Eingebettete Anwendungen sind hinsichtlich Zeit und Speicherressourcen sehr begrenzt. Sie werden häufig für Echtzeitsysteme verwendet, bei denen ein nicht deterministischer Aufruf des Garbage Collectors nicht möglich ist. Und wenn die dynamische Zuordnung aufgrund von Speichermangel nicht verwendet werden kann, ist es sehr wichtig, andere Mechanismen der Speicherverwaltung zu haben, wie das Platzieren von Daten in benutzerdefinierten Adressen, wie es C-Zeiger erlauben. Sprachen, die stark von dynamischer Zuweisung und Garbage Collection abhängen, wären für Systeme mit begrenzten Ressourcen nicht geeignet.

Codegröße

C hat eine sehr kleine Laufzeit. Und der Speicherbedarf für seinen Code ist kleiner als für die meisten anderen Sprachen.

Im Vergleich zu C++ beispielsweise ist eine von C generierte Binärdatei, die an ein eingebettetes Gerät gesendet wird, etwa halb so groß wie eine Binärdatei, die von ähnlichem C++-Code generiert wird. Einer der Hauptgründe dafür ist die Unterstützung von Ausnahmen.

Ausnahmen sind ein großartiges Werkzeug, das von C++ über C hinzugefügt wird, und wenn sie nicht ausgelöst und intelligent implementiert werden, haben sie praktisch keinen Overhead für die Ausführungszeit (allerdings auf Kosten einer Erhöhung der Codegröße).

Sehen wir uns ein Beispiel in C++ an:

 // Class A declaration. Methods defined somewhere else; class A { public: A(); // Constructor ~A(); // Destructor (called when the object goes out of scope or is deleted) void myMethod(); // Just a method }; // Class B declaration. Methods defined somewhere else; class B { public: B(); // Constructor ~B(); // Destructor void myMethod(); // Just a method }; // Class C declaration. Methods defined somewhere else; class C { public: C(); // Constructor ~C(); // Destructor void myMethod(); // Just a method }; void myFunction() { A a; // Constructor aA() called. (Checkpoint 1) { B b; // Constructor bB() called. (Checkpoint 2) b.myMethod(); // (Checkpoint 3) } // b.~B() destructor called. (Checkpoint 4) { C c; // Constructor cC() called. (Checkpoint 5) c.myMethod(); // (Checkpoint 6) } // c.~C() destructor called. (Checkpoint 7) a.myMethod(); // (Checkpoint 8) } // a.~A() destructor called. (Checkpoint 9)

Methoden der Klassen A , B und C sind woanders definiert (z. B. in anderen Dateien). Daher kann der Compiler sie nicht analysieren und nicht wissen, ob sie Ausnahmen auslösen. Daher muss es sich darauf vorbereiten, Ausnahmen zu behandeln, die von einem ihrer Konstruktoren, Destruktoren oder anderen Methodenaufrufen ausgelöst werden. Destruktoren sollten nicht auslösen (sehr schlechte Praxis), aber der Benutzer könnte trotzdem auslösen, oder sie könnten indirekt auslösen, indem sie (explizit oder implizit) eine Funktion oder Methode aufrufen, die eine Ausnahme auslöst.

Wenn einer der Aufrufe in myFunction eine Ausnahme auslöst, muss der Stack-Unwinding- Mechanismus in der Lage sein, alle Destruktoren für die bereits erstellten Objekte aufzurufen. Eine Implementierung für den Mechanismus zum Entladen des Stapels verwendet die Rücksendeadresse des letzten Aufrufs dieser Funktion, um die „Prüfpunktnummer“ des Aufrufs zu überprüfen, der die Ausnahme ausgelöst hat (dies ist die einfache Erklärung). Dazu wird eine automatisch generierte Hilfsfunktion (eine Art Nachschlagetabelle) verwendet, die zum Entladen des Stapels verwendet wird, falls eine Ausnahme vom Hauptteil dieser Funktion ausgelöst wird, die der folgenden ähnelt:

 // Possible autogenerated function void autogeneratedStackUnwindingFor_myFunction(int checkpoint) { switch (checkpoint) { // case 1 and 9: do nothing; case 3: b.~B(); goto destroyA; // jumps to location of destroyA label case 6: c.~C(); // also goes to destroyA as that is the next line destroyA: // label case 2: case 4: case 5: case 7: case 8: a.~A(); } }

Wenn die Ausnahme von den Prüfpunkten 1 und 9 ausgelöst wird, muss kein Objekt zerstört werden. Für Checkpoint 3 müssen b und a zerstört werden. Für Checkpoint 6 müssen c und a zerstört werden. In allen Fällen ist die Vernichtungsreihenfolge einzuhalten. Für die Checkpoints 2, 4, 5, 7 und 8 muss nur Objekt a zerstört werden.

Diese Hilfsfunktion fügt dem Code Größe hinzu. Dies ist Teil des Speicherplatz-Overheads, den C++ C hinzufügt. Viele eingebettete Anwendungen können sich diesen zusätzlichen Speicherplatz nicht leisten. Daher verfügen C++-Compiler für eingebettete Systeme häufig über ein Flag zum Deaktivieren von Ausnahmen. Das Deaktivieren von Ausnahmen in C++ ist nicht kostenlos, da die Standard-Vorlagenbibliothek stark auf Ausnahmen angewiesen ist, um Fehler zu melden. Die Verwendung dieses modifizierten Schemas erfordert ausnahmslos mehr Training für C++-Entwickler, um mögliche Probleme zu erkennen oder Fehler zu finden.

Und wir sprechen von C++, einer Sprache, deren Prinzip lautet: „Sie zahlen nicht für das, was Sie nicht verwenden.“ Diese Zunahme der Binärgröße wird für andere Sprachen schlimmer, die zusätzlichen Overhead mit anderen Funktionen hinzufügen, die sehr nützlich sind, aber von eingebetteten Systemen nicht geleistet werden können. Während C Ihnen die Verwendung dieser zusätzlichen Funktionen nicht ermöglicht, ermöglicht es einen viel kompakteren Code-Fußabdruck als die anderen Sprachen.

Gründe, C zu lernen

C ist keine schwer zu lernende Sprache, daher sind alle Vorteile des Erlernens recht günstig. Sehen wir uns einige dieser Vorteile an.

Lingua Franca

Wie bereits erwähnt, ist C eine Lingua Franca für Entwickler. Viele Implementierungen neuer Algorithmen in Büchern oder im Internet werden zuerst (oder nur) von ihren Autoren in C zur Verfügung gestellt. Dies ergibt die größtmögliche Portabilität für die Implementierung. Ich habe Programmierer gesehen, die sich im Internet abmühten, einen C-Algorithmus in andere Programmiersprachen umzuschreiben, weil er oder sie die grundlegenden Konzepte von C nicht kannte.

Beachten Sie, dass C eine alte und weit verbreitete Sprache ist, sodass Sie im Internet alle Arten von Algorithmen finden können, die in C geschrieben sind. Daher werden Sie sehr wahrscheinlich davon profitieren, diese Sprache zu kennen.

Die Maschine verstehen (In C denken)

Wenn wir mit Kollegen das Verhalten bestimmter Teile des Codes oder bestimmter Funktionen anderer Sprachen diskutieren, „reden“ wir schließlich in C: Übergibt dieser Teil einen „Zeiger“ an das Objekt oder kopiert er das gesamte Objekt? Könnte hier irgendein „Cast“ passieren? Und so weiter.

Wir würden selten über die Assembleranweisungen diskutieren (oder nachdenken), die ein Teil des Codes ausführt, wenn wir das Verhalten eines Teils des Codes einer Hochsprache analysieren. Stattdessen sprechen (oder denken) wir ziemlich deutlich in C, wenn wir diskutieren, was die Maschine tut.

Wenn Sie darüber hinaus nicht aufhören und auf diese Weise darüber nachdenken können, was Sie tun, programmieren Sie möglicherweise mit einer Art Aberglauben darüber, wie (magisch) Dinge getan werden.

Denken Sie wie die Maschine mit C

Arbeite an vielen interessanten C-Projekten

Viele interessante Projekte, von großen Datenbankservern oder Betriebssystemkernen bis hin zu kleinen eingebetteten Anwendungen, die Sie zu Ihrer persönlichen Zufriedenheit und zum Spaß sogar zu Hause ausführen können, werden in C ausgeführt. Es gibt keinen Grund, aus dem einzigen Grund damit aufzuhören, Dinge zu tun, die Sie vielleicht lieben dass Sie keine alte und kleine, aber starke und bewährte Programmiersprache wie C kennen.

Arbeiten Sie an coolen Projekten mit C

Fazit

Die Illuminaten regieren nicht die Welt. C-Programmierer tun das.
Twittern

Die Programmiersprache C scheint kein Ablaufdatum zu haben. Die Nähe zur Hardware, die hervorragende Portabilität und die deterministische Nutzung der Ressourcen machen es ideal für die Entwicklung auf niedriger Ebene für Dinge wie Betriebssystemkerne und eingebettete Software. Seine Vielseitigkeit, Effizienz und gute Leistung machen es zu einer ausgezeichneten Wahl für hochkomplexe Datenbearbeitungssoftware wie Datenbanken oder 3D-Animationen. Die Tatsache, dass viele Programmiersprachen heute für ihren Verwendungszweck besser sind als C, bedeutet nicht, dass sie C in allen Bereichen schlagen. C ist immer noch unübertroffen, wenn Leistung im Vordergrund steht.

Die Welt läuft auf C-betriebenen Geräten. Wir verwenden diese Geräte jeden Tag, ob wir uns dessen bewusst sind oder nicht. C ist die Vergangenheit, die Gegenwart und, soweit wir sehen können, immer noch die Zukunft für viele Bereiche der Software.

Siehe auch: So lernen Sie die Sprachen C und C++: Die ultimative Liste