C [ c ] ist eine universelle Programmiersprache, die in den 1970er Jahren von Dennis Ritchie entwickelt wurde . C ermöglicht dem Programmierer einen relativ direkten Zugriff auf die Funktionen der typischen CPU- Architektur, angepasst an den jeweiligen Befehlssatz . Sie wurde und wird weiterhin zur Implementierung von Betriebssystemen (insbesondere Kerneln [ 10 ] ), Gerätetreibern und Protokollstapeln verwendet , ihre Nutzung in Anwendungssoftware ist jedoch rückläufig. [ 11 ] C kommt auf Rechnern zum Einsatz, die von den größten Supercomputern bis hin zu den kleinsten Mikrocontrollern und eingebetteten Systemen reichen .

Als Nachfolger der Programmiersprache B wurde C ursprünglich zwischen 1972 und 1973 von Ritchie bei den Bell Labs entwickelt, um unter Unix lauffähige Dienstprogramme zu erstellen . Sie wurde zur Neuimplementierung des Unix-Kernels eingesetzt. [ 12 ] In den 1980er Jahren gewann C zunehmend an Popularität. Sie entwickelte sich zu einer der am weitesten verbreiteten Programmiersprachen [ 13 ] [ 14 ] , und C -Compiler sind für praktisch alle modernen Computerarchitekturen und Betriebssysteme verfügbar . Das Buch „The C Programming Language“ , das vom ursprünglichen Sprachentwickler mitverfasst wurde, diente viele Jahre als De-facto- Standard für die Sprache. [ 15 ] [ 1 ] C wird seit 1989 vom American National Standards Institute (ANSI) und später gemeinsam von der Internationalen Organisation für Normung (ISO) und der Internationalen Elektrotechnischen Kommission (IEC) standardisiert.

C ist eine imperative , prozedurale Programmiersprache, die strukturierte Programmierung , lexikalischen Variablenbereich und Rekursion mit einem statischen Typsystem unterstützt . Sie wurde so konzipiert, dass sie kompiliert werden kann, um einen direkten Zugriff auf Speicher und Sprachkonstrukte zu ermöglichen , die effizient auf Maschinenbefehle abgebildet werden können – und das alles mit minimalem Laufzeitaufwand . Trotz dieser Low-Level-Fähigkeiten wurde die Sprache so entwickelt, dass sie plattformübergreifende Programmierung fördert. Ein standardkonformes C-Programm, das mit Blick auf Portabilität geschrieben wurde , kann mit wenigen Änderungen am Quellcode für eine Vielzahl von Computerplattformen und Betriebssystemen kompiliert werden.

Obwohl weder C noch seine Standardbibliothek einige gängige Funktionen anderer Sprachen bereitstellen, ist es flexibel genug, um diese zu unterstützen. So werden beispielsweise Objektorientierung und automatische Speicherbereinigung durch externe Bibliotheken wie GLib Object System bzw. Boehm Garbage Collector realisiert .

Seit dem Jahr 2000 belegt C im TIOBE-Index in der Regel den ersten oder zweiten Platz der beliebtesten Programmiersprachen . [ 16 ]

Eigenschaften

Dennis Ritchie (rechts), der Erfinder der Programmiersprache C, mit Ken Thompson

Die Programmiersprache C weist folgende Merkmale auf:

  • Freier Quellcode
  • Semikolons beenden Aussagen
  • Geschweifte Klammern gruppieren Anweisungen in Blöcke.
  • Der ausführbare Code ist in Funktionen enthalten (keine skriptähnliche Syntax).
  • Parameter werden per Wert übergeben; die Übergabe per Referenz erfolgt durch Übergabe eines Zeigers auf einen Wert.
  • Relativ geringe Anzahl an Schlüsselwörtern
  • Kontrollflusskonstrukte , einschließlich if, for, do, while, undswitch
  • Arithmetische , bitweise und logische Operatoren, einschließlich +, +=, ++, &,||
  • Mehrere Zuweisungen können in einer einzigen Anweisung durchgeführt werden.
  • Benutzerdefinierte Bezeichner werden nicht von Schlüsselwörtern unterschieden (d. h. durch ein Symbol ).
  • Eine innerhalb eines Blocks deklarierte Variable ist nur innerhalb dieses Blocks und nur unterhalb der Deklaration zugänglich.
  • Der Rückgabewert einer Funktion kann ignoriert werden.
  • Eine Funktion kann nicht in eine andere Funktion verschachtelt werden, aber einige Übersetzer unterstützen dies.
  • Laufzeitpolymorphismus kann mithilfe von Funktionszeigern erreicht werden.
  • Unterstützt Rekursion
  • Die Datentypisierung ist statisch , wird aber nur schwach durchgesetzt ; alle Variablen haben einen Typ, doch die implizite Konvertierung zwischen primitiven Typen schwächt die Trennung der verschiedenen Typen.
  • Benutzerdefinierte Datentypen ermöglichen die Verwendung von Aliasen für einen Datentypspezifizierer.
  • Die Syntax für Array -Definition und -Zugriff erfolgt über eckige Klammern, z. B. `array.array.array` month[11]. Die Indizierung erfolgt über Zeigerarithmetik. Ganze Arrays können nicht ohne benutzerdefinierten Code oder Bibliothekscode kopiert oder verglichen werden.
  • Benutzerdefinierte Strukturtypen ermöglichen das Übergeben und Kopieren zusammengehöriger Datenelemente als Einheit, allerdings können zwei Strukturen nicht ohne benutzerdefinierten Code zum Vergleich jedes einzelnen Feldes verglichen werden.
  • Benutzerdefinierte Union- Typen unterstützen überlappende Elemente, sodass mehrere Datentypen denselben Speicherplatz nutzen können.
  • Benutzerdefinierte Aufzählungstypen unterstützen die Aliasierung von Ganzzahlwerten
  • Es fehlt zwar ein String-Datentyp , aber die Standardbibliothek bietet Syntax für nullterminierte Strings mit zugehöriger Behandlung .
  • Unterstützt den Zugriff auf den Computerspeicher auf niedriger Ebene über Zeiger.
  • Unterstützt prozedurähnliche Konstruktionen als Funktionen, die einen Rückgabewert haben.void
  • Unterstützt dynamischen Speicher über Standardbibliotheksfunktionen
  • Beinhaltet den C-Präprozessor zur Durchführung von Makrodefinitionen , Quellcode- Dateieinbindungen und bedingter Kompilierung .
  • Unterstützt Modularität , indem Dateien separat verarbeitet werden, wobei die Sichtbarkeit über Attribute gesteuert staticwird extern.
  • Minimierte Funktionalität in der Kernsprache, während relativ komplexe Funktionalitäten wie Ein-/Ausgabe , Stringmanipulation und mathematische Funktionen über Standardbibliotheksfunktionen unterstützt werden.
  • Der resultierende kompilierte Code stellt relativ einfache Anforderungen an die zugrundeliegende Plattform, was ihn für Betriebssysteme und eingebettete Systeme attraktiv macht.

Beispiel „Hallo Welt“

„Hello, World“-Programm von Brian Kernighan (1978)

Das Programmbeispiel „Hello, World!“ , das in der ersten Auflage von K&R erschien , ist zum Vorbild für einführende Programme in den meisten Programmierlehrbüchern geworden. Das Programm gibt „hello, world“ auf der Standardausgabe aus .

Die Originalfassung lautete: [ 17 ]

main () { printf ( "Hallo Welt \n " ); }

Eine modernere Version lautet: [ d ]

#include <stdio.h>int main ( void ) { printf ( "hello, world \n " ); }

Die erste Zeile ist eine Präprozessor- Direktive , gekennzeichnet durch #include, die den Präprozessor veranlasst, diese Codezeile durch den Text der stdio.hHeader-Datei zu ersetzen. Diese enthält Deklarationen für Ein- und Ausgabefunktionen, einschließlich printf. Die spitzen Klammern um stdio.hzeigen an, dass die Header-Datei mithilfe einer Suchstrategie gefunden werden kann, die die mit dem Compiler bereitgestellten Header-Dateien gegenüber gleichnamigen Dateien in projektspezifischen Verzeichnissen bevorzugt.

Die nächste Codezeile deklariert die Einstiegsfunktionmain . Die Laufzeitumgebung ruft diese Funktion auf , um die Programmausführung zu starten. Der Typspezifizierer intgibt an, dass die Funktion einen ganzzahligen Wert zurückgibt. Die voidParameterliste zeigt an, dass die Funktion keine Argumente benötigt. Die Laufzeitumgebung übergibt zwar zwei Argumente (void intund char *[]), diese Implementierung ignoriert sie jedoch. Der ISO-C-Standard (Abschnitt 5.1.2.2.1) fordert eine Syntax, die entweder void oder diese beiden Argumente enthält – eine Sonderbehandlung, die anderen Funktionen nicht zuteilwird.

Die öffnende geschweifte Klammer kennzeichnet den Beginn des Codes, der die Funktion definiert.

Die nächste Codezeile ruft die C-Standardbibliotheksfunktion printfmit der Adresse des ersten Zeichens einer nullterminierten Zeichenkette als Stringliteral auf . Der Text \nist eine Escape-Sequenz für das Zeilenumbruchzeichen , die bei Ausgabe im Terminal den Cursor an den Anfang der nächsten Zeile setzt. Obwohl die Funktion printfeinen intWert zurückgibt, wird dieser stillschweigend verworfen. Das Semikolon ;beendet den Funktionsaufruf.

Die schließende geschweifte Klammer signalisiert das Ende der mainFunktion. Vor C99 return 0;war am Ende einer mainFunktion eine explizite Anweisung erforderlich, seit C99 kehrt die mainFunktion (als erster Funktionsaufruf) jedoch implizit zurück, 0sobald die letzte schließende geschweifte Klammer erreicht ist. [ e ]

Geschichte

Frühe Entwicklungen

Zeitleiste der Programmiersprache C
Jahr Informeller Name Offizieller Standard
1972 erste Veröffentlichung N / A
1978 K&R CN / A
1989, 1990 ANSI C , C89, ISO C, C90 ANSI X3.159-1989 ISO/IEC 9899:1990
1999 C99 , C9X ISO/IEC 9899:1999
2011 C11 , C1X ISO/IEC 9899:2011
2018 C17 , C18 ISO/IEC 9899:2018
2024 C23 , C2X ISO/IEC 9899:2024
Wird noch bekanntgegebenC2Y

Die Entstehung von C ist eng mit der Entwicklung des Unix -Betriebssystems verbunden, das ursprünglich von Dennis Ritchie und Ken Thompson in Assemblersprache auf einem PDP-7 implementiert wurde und dabei mehrere Ideen von Kollegen aufgriff. Schließlich beschlossen sie, das Betriebssystem auf einen PDP-11 zu portieren . Die ursprüngliche PDP-11-Version von Unix wurde ebenfalls in Assemblersprache entwickelt. [ 12 ]

B

Thompson benötigte eine Programmiersprache zur Entwicklung von Hilfsprogrammen für die neue Plattform. Zunächst versuchte er, einen Fortran- Compiler zu schreiben, gab diesen Gedanken jedoch bald auf und entwickelte stattdessen eine abgespeckte Version der kurz zuvor entwickelten Systemprogrammiersprache BCPL . Da die offizielle Beschreibung von BCPL zu diesem Zeitpunkt noch nicht verfügbar war [ 19 ] , modifizierte Thompson die Syntax, um sie weniger wortreich und ähnlicher zu einem vereinfachten ALGOL namens SMALGOL zu gestalten [ 20 ] . Er nannte das Ergebnis B [ 12 ] und beschrieb es als „BCPL-Semantik mit viel SMALGOL-Syntax“ [ 20 ] . Wie BCPL verfügte auch B über einen Bootstrapping- Compiler, um die Portierung auf neue Rechner zu erleichtern [ 20 ] . Letztendlich wurden nur wenige Hilfsprogramme in B geschrieben, da die Sprache zu langsam war und PDP-11-Funktionen wie die Byte- Adressierung nicht nutzen konnte .

Im Gegensatz zu BCPL , wo // commentKommentare bis zum Zeilenende markiert wurden, verwendete B /* comment */ein Kommentarzeichen, das eher PL/1 ähnelte und Kommentare mitten in Zeilen zuließ. (Der Kommentarstil von BCPL wurde in C++ wieder eingeführt.) [ 12 ]

Neue B- und erste C-Version

1971 begann Ritchie, B zu verbessern, um die Funktionen des leistungsfähigeren PDP-11 zu nutzen. Eine wichtige Neuerung war der Datentyp „Zeichen“. Er nannte diesen „New B“ (NB). [ 20 ] Thompson begann, NB für die Entwicklung des Unix- Kernels zu verwenden , und seine Anforderungen prägten die Richtung der Sprachentwicklung. [ 20 ] [ 21 ]

Bis 1972 wurden der NB-Sprache komplexere Datentypen hinzugefügt. NB kannte Arrays vom Typ `int` intund char`string`, und zu diesen Typen kamen Zeiger, die Möglichkeit, Zeiger auf andere Typen zu erzeugen, Arrays aller Typen sowie Rückgabetypen von Funktionen hinzu. Arrays innerhalb von Ausdrücken wurden effektiv wie Zeiger behandelt. Ein neuer Compiler wurde entwickelt, und die Sprache wurde in C umbenannt. [ 12 ]

Der C-Compiler und einige damit erstellte Hilfsprogramme wurden in Version 2 Unix , auch bekannt als Research Unix , aufgenommen . [ 22 ]

Strukturen und Neuentwicklung des Unix-Kernels

Bei Version 4 Unix , die im November 1973 veröffentlicht wurde, wurde der Unix- Kernel umfassend in C neu implementiert. [ 12 ] Zu diesem Zeitpunkt hatte die C-Sprache bereits einige leistungsstarke Funktionen wie structTypen erhalten.

Der Präprozessor wurde um 1973 auf Drängen von Alan Snyder und in Anerkennung der Nützlichkeit der in BCPL und PL/I verfügbaren Dateieinbindungsmechanismen eingeführt . Seine ursprüngliche Version bot lediglich eingebundene Dateien und einfache Zeichenkettenersetzungen #includesowie #defineparameterlose Makros. Bald darauf wurde er, hauptsächlich von Mike Lesk und später von John Reiser, um Makros mit Argumenten und bedingte Kompilierung erweitert . [ 12 ]

Unix war einer der ersten Betriebssystemkerne, der in einer anderen Sprache als Assembler implementiert wurde . Frühere Beispiele sind das Multics -System (geschrieben in PL/I ) und das Master Control Program (MCP) für den Burroughs B5000 (geschrieben in ALGOL ) aus dem Jahr 1961. Um 1977 nahmen Ritchie und Stephen C. Johnson weitere Änderungen an der Sprache vor, um die Portabilität des Unix-Betriebssystems zu verbessern . Johnsons Portable C Compiler diente als Grundlage für mehrere C-Implementierungen auf neuen Plattformen. [ 21 ]

K&R C

Das Cover des Buches „Die Programmiersprache C“ , erste Auflage, von Brian Kernighan und Dennis Ritchie

1978 veröffentlichten Brian Kernighan und Dennis Ritchie die erste Ausgabe von „The C Programming Language“ [ 23 ] . Das Buch, bekannt als K&R ( nach den Initialen der Autoren), diente viele Jahre als informelle Spezifikation der Sprache. Die darin beschriebene Version von C wird gemeinhin als „ K&R C “ bezeichnet. Da sie 1978 erschien, wird sie heute auch als C78 bezeichnet [ 24 ] . Die zweite Ausgabe des Buches [ 25 ] behandelt den späteren ANSI-C- Standard, der weiter unten beschrieben wird.

K&R führte mehrere sprachliche Neuerungen ein:

  • Standard-E/A-Bibliothek
  • long intDatentyp
  • unsigned intDatentyp
  • Zusammengesetzte Zuweisungsoperatoren der Form (wie z. B. ) wurden in die Form (d. h. ) geändert , um die semantische Mehrdeutigkeit zu beseitigen, die durch Konstrukte wie entstanden war, die als ( um 10 verringern) anstatt des möglicherweise beabsichtigten ( sei −10) interpretiert worden waren.=op=-op=-=i=-10i =- 10ii = -10i

Auch nach der Veröffentlichung des ANSI-Standards von 1989 galt K&R C noch viele Jahre als der „ kleinste gemeinsame Nenner “, auf den sich C-Programmierer beschränkten, wenn maximale Portabilität gewünscht war, da viele ältere Compiler noch im Einsatz waren und sorgfältig geschriebener K&R-C-Code auch gültiger Standard-C-Code sein kann.

Obwohl spätere Versionen von C eine explizite Typdeklaration für Funktionen vorschreiben, verlangt K&R C lediglich, dass Funktionen, die einen anderen Typ als zurückgeben, intvor ihrer Verwendung deklariert werden. Funktionen, die ohne vorherige Deklaration verwendet wurden, wurden als untypisch angenommen int.

Zum Beispiel:

long long_function ();Aufruf der Funktion (){lange Longvar ;intvar registrieren ;longvar = long_function ();if ( longvar > 1 )intvar = 0 ;andersintvar = int_function ();return intvar ;}

Die Deklaration long_function()(in Zeile 1) ist erforderlich, da sie `undefined` longund nicht `undefined` zurückgibt int. Die Funktion int_functionkann (Zeile 11) aufgerufen werden, obwohl sie nicht deklariert ist, da sie `undefined` zurückgibt int. Außerdem intvarmuss die Variable nicht als Typ deklariert werden, intda dies der Standardtyp für registerdas Schlüsselwort `undefined` ist.

Da Funktionsdeklarationen keine Informationen über Argumente enthielten, wurden keine Typüberprüfungen durchgeführt, obwohl einige Compiler eine Warnung ausgaben, wenn verschiedene Funktionsaufrufe unterschiedliche Anzahlen oder Typen von Argumenten verwendeten. Es wurden Werkzeuge wie das Unix- Dienstprogramm lint entwickelt, die (unter anderem) die Konsistenz der Funktionsverwendung in mehreren Quelldateien überprüften.

In den Jahren nach der Veröffentlichung von K&R C wurden der Sprache mehrere Funktionen hinzugefügt, die von Compilern von AT&T (insbesondere PCC [ 26 ] ) und anderen Anbietern unterstützt wurden. Dazu gehörten:

Die Popularität der Sprache, die fehlende Einigung über Standardbibliotheksschnittstellen und die mangelnde Einhaltung der K&R-Spezifikation führten zu Standardisierungsbemühungen. [ 27 ]

ANSI C und ISO C

In den späten 1970er und 1980er Jahren wurden Versionen von C für eine breite Palette von Großrechnern , Minicomputern und Mikrocomputern implementiert , darunter auch für den IBM PC , dessen Popularität deutlich zunahm.

1983 gründete das American National Standards Institute (ANSI) das Komitee X3J11, um eine Standardspezifikation für C zu erarbeiten. X3J11 basierte den C-Standard auf der Unix-Implementierung; der nicht portable Teil der Unix-C-Bibliothek wurde jedoch an die IEEE- Arbeitsgruppe 1003 übergeben und bildete die Grundlage für den POSIX- Standard von 1988. 1989 wurde der C-Standard als ANSI X3.159-1989 „Programming Language C“ ratifiziert. Diese Version der Sprache wird häufig als ANSI C , Standard C oder auch C89 bezeichnet .

1990 wurde der ANSI-C-Standard (mit Formatierungsänderungen) von der Internationalen Organisation für Normung (ISO) als ISO/IEC 9899:1990 übernommen, der manchmal auch als C90 bezeichnet wird . Daher beziehen sich die Begriffe „C89“ und „C90“ auf dieselbe Programmiersprache.

ANSI entwickelt, wie andere nationale Normungsorganisationen auch, den C-Standard nicht mehr selbstständig, sondern orientiert sich am internationalen C-Standard, der von der Arbeitsgruppe ISO/IEC JTC1/SC22 /WG14 gepflegt wird. Die nationale Übernahme einer Aktualisierung des internationalen Standards erfolgt in der Regel innerhalb eines Jahres nach dessen Veröffentlichung durch die ISO.

Eines der Ziele des C-Standardisierungsprozesses war die Entwicklung einer Obermenge von K&R C, die viele der später eingeführten inoffiziellen Funktionen integrieren sollte. Das Standardisierungskomitee fügte außerdem mehrere zusätzliche Funktionen hinzu, darunter Funktionsprototypen (aus C++ übernommen), voidZeiger, Unterstützung für internationale Zeichensätze und Gebietsschemas sowie Verbesserungen des Präprozessors. Obwohl die Syntax für Parameterdeklarationen um den in C++ verwendeten Stil erweitert wurde, blieb die K&R-Schnittstelle aus Gründen der Kompatibilität mit bestehendem Quellcode weiterhin zulässig.

C89 wird von aktuellen C-Compilern unterstützt, und der Großteil des modernen C-Codes basiert darauf. Jedes Programm, das ausschließlich in Standard-C und ohne hardwareabhängige Annahmen geschrieben ist, läuft innerhalb der jeweiligen Ressourcengrenzen auf jeder Plattform mit einer konformen C-Implementierung korrekt. Ohne solche Vorkehrungen lassen sich Programme möglicherweise nur auf einer bestimmten Plattform oder mit einem bestimmten Compiler kompilieren, beispielsweise aufgrund der Verwendung nicht standardkonformer Bibliotheken wie GUI- Bibliotheken oder aufgrund der Abhängigkeit von compiler- oder plattformspezifischen Attributen wie der exakten Größe von Datentypen und der Byte- Reihenfolge .

In Fällen, in denen der Code sowohl von standardkonformen als auch von K&R-basierten C-Compilern kompiliert werden muss, __STDC__kann das Makro verwendet werden, um den Code in Standard- und K&R-Abschnitte aufzuteilen und so die Verwendung von Funktionen, die nur in Standard-C verfügbar sind, durch einen K&R-basierten C-Compiler zu verhindern.

Nach dem ANSI/ISO-Standardisierungsprozess blieb die C-Sprachspezifikation mehrere Jahre lang relativ unverändert. 1995 wurde die Normative Änderung 1 zum C-Standard von 1990 (ISO/IEC 9899/AMD1:1995, informell als C95 bekannt) veröffentlicht, um einige Details zu korrigieren und die Unterstützung für internationale Zeichensätze zu erweitern. [ 28 ]

C99

Der C-Standard wurde Ende der 1990er Jahre weiter überarbeitet, was 1999 zur Veröffentlichung von ISO/IEC 9899:1999 führte, die allgemein als „ C99 “ bezeichnet wird. Sie wurde seither dreimal durch technische Berichtigungen geändert. [ 29 ]

C99 führte mehrere neue Funktionen ein, darunter Inline-Funktionen , verschiedene neue Datentypen (einschließlich long long inteines complexTyps zur Darstellung komplexer Zahlen ), Arrays variabler Länge und flexible Array-Elemente , verbesserte Unterstützung für IEEE-754- Gleitkommazahlen, Unterstützung für variadische Makros (Makros mit variabler Stelligkeit ) und Unterstützung für einzeilige Kommentare, die mit einem Komma beginnen //, wie in BCPL oder C++. Viele dieser Funktionen waren bereits als Erweiterungen in verschiedenen C-Compilern implementiert.

C99 ist größtenteils abwärtskompatibel zu C90, jedoch in mancher Hinsicht strenger; insbesondere wird bei Deklarationen ohne Typspezifizierer kein Typ mehr intimplizit angenommen. Ein Standardmakro __STDC_VERSION__ist mit einem Wert definiert, 199901Lder die Verfügbarkeit von C99-Unterstützung anzeigt. GCC , Solaris Studio und andere C-Compiler unterstützen mittlerweile viele oder alle neuen Funktionen von C99. Der C-Compiler in Microsoft Visual C++ implementiert hingegen den C89-Standard und nur die für die Kompatibilität mit C++11 erforderlichen Teile von C99 . [ 30 ]

Darüber hinaus verlangt der C99-Standard die Unterstützung von Bezeichnern , die Unicode in Form von Escape-Zeichen verwenden (z. B. \u0040oder \U0001f431), und schlägt die Unterstützung von rohen Unicode-Namen vor.

C11

Im Jahr 2007 begannen die Arbeiten an einer weiteren Überarbeitung des C-Standards, der informell als „C1X“ bezeichnet wurde, bis er am 8. Dezember 2011 offiziell als ISO/IEC 9899:2011 veröffentlicht wurde. Das C-Normenkomitee verabschiedete Richtlinien, um die Übernahme neuer Funktionen einzuschränken, die nicht durch bestehende Implementierungen getestet worden waren.

Der C11-Standard erweitert C und die Bibliothek um zahlreiche neue Funktionen, darunter typgenerische Makros, anonyme Strukturen, verbesserte Unicode-Unterstützung, atomare Operationen, Multithreading und Funktionen mit Bereichsprüfung. Er macht außerdem Teile der bestehenden C99-Bibliothek optional und verbessert die Kompatibilität mit C++. Das Standardmakro __STDC_VERSION__ist so definiert, dass 201112Les anzeigt, dass C11 unterstützt wird.

C17

C17 ist die inoffizielle Bezeichnung für ISO/IEC 9899:2018, einen Standard für die Programmiersprache C, der im Juni 2018 veröffentlicht wurde. Er führt keine neuen Sprachmerkmale ein, sondern lediglich technische Korrekturen und Klarstellungen zu Fehlern in C11. Das Standardmakro __STDC_VERSION__ist so definiert, dass 201710Les anzeigt, dass C17 unterstützt wird.

C23

C23 ist die inoffizielle Bezeichnung für die aktuelle Hauptrevision des C-Sprachstandards. Während des größten Teils ihrer Entwicklung war sie als „C2X“ bekannt. Sie baut auf früheren Versionen auf und führt Funktionen wie neue Schlüsselwörter, zusätzliche Bedeutungen für autodie Typinferenz bei der Variablendeklaration, neue Typen wie `int` nullptr_tund _BitInt(N)`string` sowie Erweiterungen der Standardbibliothek ein. [ 31 ]

C23 wurde im Oktober 2024 als ISO/IEC 9899:2024 veröffentlicht. [ 32 ] Das Standardmakro __STDC_VERSION__ist so definiert, dass 202311Les anzeigt, dass C23-Unterstützung verfügbar ist.

C2Y

C2Y ist die inoffizielle Bezeichnung für die nächste größere Überarbeitung des C-Sprachstandards nach C23 (C2X), deren Veröffentlichung für die späten 2020er Jahre erwartet wird; daher die „2“ in „C2Y“. Ein erster Arbeitsentwurf von C2Y wurde im Februar 2024 als N3220 von der Arbeitsgruppe ISO/IEC JTC1/SC22 /WG14 veröffentlicht. [ 33 ]

Eingebettetes C

Historisch gesehen erfordert die Programmierung eingebetteter Systeme in C nicht standardmäßige Erweiterungen der C-Sprache, um exotische Funktionen wie Festkommaarithmetik , mehrere unterschiedliche Speicherbänke und grundlegende E/A-Operationen zu unterstützen.

Im Jahr 2008 veröffentlichte das C Standards Committee einen technischen Bericht, der die Programmiersprache C [ 34 ] erweiterte , um diese Probleme durch einen gemeinsamen Standard für alle Implementierungen zu beheben. Dieser umfasst eine Reihe von Funktionen, die im normalen C nicht verfügbar sind, wie z. B. Festkommaarithmetik, benannte Adressräume und grundlegende Hardware-Adressierung für Ein- und Ausgabegeräte.

Definition

C besitzt eine formale Grammatik, die im C-Standard spezifiziert ist. [ 35 ] Zeilenumbrüche sind in C im Allgemeinen nicht relevant; Zeilengrenzen hingegen sind während der Vorverarbeitungsphase von Bedeutung. Kommentare können entweder zwischen den Begrenzern `<` /*und `</` stehen */oder (seit C99) //bis zum Zeilenende nach dem `</`-Zeichen folgen. Kommentare, die durch ` </` /*und `</` begrenzt sind */, können nicht verschachtelt werden, und diese Zeichenfolgen werden nicht als Kommentarbegrenzer interpretiert, wenn sie innerhalb von Zeichenketten oder Zeichenliteralen stehen. [ 36 ]

C-Quelldateien enthalten Deklarationen und Funktionsdefinitionen. Funktionsdefinitionen wiederum enthalten Deklarationen und Anweisungen . Deklarationen definieren entweder neue Typen mithilfe von Schlüsselwörtern wie `int` struct, union`int` und enum`string` oder weisen Variablen Typen zu und reservieren gegebenenfalls Speicherplatz für neue Variablen, üblicherweise durch Angabe des Typs gefolgt vom Variablennamen. Schlüsselwörter wie `int` charund `string` intspezifizieren eingebaute Typen. Codeabschnitte werden in geschweifte Klammern (``` {und }```, manchmal auch „gerundete Klammern“ genannt) eingeschlossen, um den Gültigkeitsbereich von Deklarationen zu begrenzen und als eine einzige Anweisung für Kontrollstrukturen zu fungieren.

Als imperative Sprache verwendet C Anweisungen , um Aktionen festzulegen. Die gebräuchlichste Anweisung ist die Ausdrucksanweisung , bestehend aus einem auszuwertenden Ausdruck, gefolgt von einem Semikolon. Als Folge der Auswertung können Funktionen aufgerufen und Variablen neue Werte zugewiesen werden. Um die normale sequentielle Ausführung von Anweisungen zu modifizieren, bietet C verschiedene Kontrollflussanweisungen, die durch reservierte Schlüsselwörter gekennzeichnet sind. Strukturierte Programmierung wird durch bedingte Ausführung (... [ ]) und ifiterative Ausführung (Schleifen) unterstützt . Die `break`-Anweisung verfügt über separate Initialisierungs- , Test- und Reinitialisierungsausdrücke, die einzeln oder alle weggelassen werden können. `break` kann innerhalb der Schleife verwendet werden. `break` verlässt die innerste Schleifenanweisung, und `continue` springt zu ihrer Reinitialisierung. Es gibt auch eine nicht-strukturierte Anweisung, die direkt zur angegebenen Marke innerhalb der Funktion springt. `continue` wählt eine auszuführende Funktion basierend auf dem Wert eines ganzzahligen Ausdrucks aus. Anders als in vielen anderen Sprachen wird der Kontrollfluss bis zum nächsten Aufruf fortgesetzt , sofern er nicht durch ein `break` beendet wird . elsedowhilewhileforforbreakcontinuegotoswitchcasecasebreak

Ausdrücke können verschiedene integrierte Operatoren verwenden und Funktionsaufrufe enthalten. Die Reihenfolge der Auswertung von Funktionsargumenten und Operanden der meisten Operatoren ist nicht festgelegt. Die Auswertungen können sogar verschachtelt erfolgen. Alle Seiteneffekte (einschließlich der Speicherung in Variablen) treten jedoch vor dem nächsten Sequenzpunkt auf . Sequenzpunkte umfassen das Ende jeder Ausdrucksanweisung sowie den Eintritt in und die Rückkehr aus jedem Funktionsaufruf. Sequenzpunkte treten auch bei der Auswertung von Ausdrücken auf, die bestimmte Operatoren (z. B. &&` ||,` ?:und den Kommaoperator ) enthalten. Dies ermöglicht dem Compiler eine hohe Optimierung des Objektcodes, erfordert von C-Programmierern jedoch mehr Sorgfalt, um zuverlässige Ergebnisse zu erzielen, als bei anderen Programmiersprachen.

Kernighan und Ritchie schreiben in der Einleitung zu „The C Programming Language“ : „C hat, wie jede andere Sprache auch, seine Schwächen. Einige Operatoren haben die falsche Priorität; manche Teile der Syntax könnten verbessert werden.“ [ 37 ] Der C-Standard verzichtete darauf, viele dieser Schwächen zu beheben, da solche Änderungen Auswirkungen auf bereits bestehende Software gehabt hätten.

Zeichensatz

Der grundlegende C-Quellzeichensatz umfasst die folgenden Zeichen: [ 38 ]

Das Zeilenumbruchzeichen kennzeichnet das Ende einer Textzeile; es muss nicht einem einzelnen Zeichen entsprechen, obwohl C es der Einfachheit halber so behandelt.

Der POSIX-Standard schreibt einen portablen Zeichensatz vor , der den grundlegenden C-Quellcode um einige Zeichen (insbesondere „@“) erweitert. Beide Standards schreiben keine bestimmte Wertekodierung vor – ASCII und EBCDIC erfüllen diese Standards, da sie zumindest diese grundlegenden Zeichen enthalten, auch wenn sie unterschiedliche Kodierungswerte für diese Zeichen verwenden.

Zusätzliche, mehrbytekodierte Zeichen können in Zeichenkettenliteralen verwendet werden , sind aber nicht vollständig portabel . Seit C99 können multinationale Unicode-Zeichen portabel in C-Quelltext eingebettet werden, indem die Kodierung `<binary data, 4 bytes>` \uXXXXoder `< binary data, 4 bytes>` verwendet wird \UXXXXXXXX(wobei ` X<binary data, 4 bytes>` ein Hexadezimalzeichen bezeichnet).

Der grundlegende C-Ausführungszeichensatz enthält dieselben Zeichen sowie Darstellungen für das Nullzeichen , alert , backspace und cargoreturn . [ 38 ]

Die Laufzeitunterstützung für erweiterte Zeichensätze hat sich mit jeder Revision des C-Standards verbessert.

Reservierte Wörter

Alle C-Versionen verfügen über reservierte Wörter , die zwischen Groß- und Kleinschreibung unterscheiden . Als reservierte Wörter können sie nicht für Variablennamen verwendet werden.

C89 enthält 32 reservierte Wörter:

C99 fügte fünf weitere reservierte Wörter hinzu: (‡ kennzeichnet eine alternative Schreibweise eines C23-Schlüsselworts)

C11 fügte sieben weitere reservierte Wörter hinzu: [ 39 ] (‡ kennzeichnet eine alternative Schreibweise für ein C23-Schlüsselwort)

  • _Alignas‡
  • _Alignof‡
  • _Atomic
  • _Generic
  • _Noreturn
  • _Static_assert‡
  • _Thread_local‡

C23 reservierte fünfzehn weitere Wörter:

  • alignas
  • alignof
  • bool
  • constexpr
  • false
  • nullptr
  • static_assert
  • thread_local
  • true
  • typeof
  • typeof_unqual
  • _BitInt
  • _Decimal32
  • _Decimal64
  • _Decimal128

Die meisten der kürzlich reservierten Wörter beginnen mit einem Unterstrich gefolgt von einem Großbuchstaben, da Bezeichner dieser Form zuvor im C-Standard ausschließlich für Implementierungen reserviert waren. Da bestehender Quellcode diese Bezeichner nicht verwenden sollte, war er nicht betroffen, als C-Implementierungen begannen, diese Erweiterungen der Programmiersprache zu unterstützen. Einige Standard-Header definieren praktischere Synonyme für Bezeichner mit Unterstrich. Einige dieser Wörter wurden in C23 als Schlüsselwörter mit ihrer üblichen Schreibweise hinzugefügt, und die entsprechenden Makros wurden entfernt.

Vor C89 entrywar das Schlüsselwort reserviert. In der zweiten Auflage ihres Buches „The C Programming Language“ , das die später als C89 bekannte Sprache beschreibt, schrieben Kernighan und Ritchie: „Das … [Schlüsselwort] entry, das früher reserviert, aber nie verwendet wurde, ist nicht länger reserviert.“ und „Das nie verwendete entrySchlüsselwort wird zurückgezogen.“ [ 40 ]

Betreiber

C unterstützt eine Vielzahl von Operatoren . Diese Symbole werden innerhalb eines Ausdrucks verwendet , um die während der Auswertung dieses Ausdrucks durchzuführenden Operationen festzulegen. C verfügt über Operatoren für:

C verwendet den Operator ` ==` (aus der Mathematik für Gleichheit) zur Zuweisung, analog zu Fortran und PL/I , aber anders als ALGOL und dessen Derivate. Der Operator `=` dient ==zum Vergleichen auf Gleichheit. Die Ähnlichkeit der Operatoren für Zuweisung und Gleichheit kann zu einer versehentlichen Verwechslung führen, und in vielen Fällen wird keine Fehlermeldung ausgegeben (obwohl manche Compiler Warnungen ausgeben). Beispielsweise if (a == b + 1)könnte der bedingte Ausdruck fälschlicherweise als `x = 0` geschrieben werden if (a = b + 1), was als `x = 0` ausgewertet wird, truees sei denn, der Wert von `x` aist 0nach der Zuweisung `x = 0`. [ 41 ]

Die Operatorpriorität in C ist nicht immer intuitiv. Beispielsweise ==bindet der Operator stärker als die Operatoren &(bitweises UND) und |(bitweises ODER) in Ausdrücken wie x & 1 == 0, die so geschrieben werden müssen, als (x & 1) == 0ob dies die Absicht des Programmierers wäre. [ 42 ]

Datentypen

Das Typsystem in C ist statisch und schwach typisiert , wodurch es dem Typsystem von ALGOL- Nachfolgern wie Pascal ähnelt . [ 43 ] Es gibt eingebaute Datentypen für ganze Zahlen verschiedener Größen (mit und ohne Vorzeichen), Gleitkommazahlen und Aufzählungstypen enum. Der Integer-Datentyp charwird häufig für Einzelbyte-Zeichen verwendet. Mit C99 wurde der Datentyp Boolean eingeführt . Darüber hinaus gibt es abgeleitete Datentypen wie Arrays , Zeiger , Records und Unions .structunion

C wird häufig in der systemnahen Programmierung eingesetzt, wo das Umgehen des Typsystems erforderlich sein kann. Der Compiler versucht, die Typkorrektheit der meisten Ausdrücke sicherzustellen, aber der Programmierer kann diese Prüfungen auf verschiedene Weise überschreiben, entweder durch eine Typumwandlung, um einen Wert explizit von einem Typ in einen anderen zu konvertieren, oder durch die Verwendung von Zeigern oder Unions, um die zugrundeliegenden Bits eines Datenobjekts auf andere Weise neu zu interpretieren.

Manche empfinden die Deklarationssyntax von C als unintuitiv, insbesondere bei Funktionszeigern . (Ritchies Idee war es, Bezeichner in Kontexten zu deklarieren, die ihrer Verwendung ähneln: " Deklaration spiegelt Verwendung wider .") [ 44 ]

Die üblichen arithmetischen Konvertierungen in C ermöglichen zwar die Generierung effizienten Codes, können aber mitunter zu unerwarteten Ergebnissen führen. Beispielsweise erfordert der Vergleich von vorzeichenbehafteten und vorzeichenlosen Ganzzahlen gleicher Breite die Konvertierung des vorzeichenbehafteten Werts in einen vorzeichenlosen Wert. Dies kann zu unerwarteten Ergebnissen führen, wenn der vorzeichenbehaftete Wert negativ ist.

Hinweise

C unterstützt Zeiger , eine Art Referenz , die die Adresse oder den Speicherort eines Objekts oder einer Funktion speichert. Zeiger können dereferenziert werden , um auf Daten an der referenzierten Adresse zuzugreifen oder eine Funktion aufzurufen. Zeiger können mittels Zuweisung oder Zeigerarithmetik manipuliert werden . Die Laufzeitdarstellung eines Zeigerwerts ist typischerweise eine rohe Speicheradresse (gegebenenfalls ergänzt durch ein Offset-Feld innerhalb eines Wortes). Da der Typ eines Zeigers den Typ des referenzierten Objekts enthält, können Ausdrücke mit Zeigern zur Kompilierzeit typgeprüft werden. Die Zeigerarithmetik wird automatisch an die Größe des referenzierten Datentyps angepasst.

In C werden Zeiger für viele Zwecke verwendet. Textzeichenketten werden häufig mithilfe von Zeigern auf Zeichenarrays manipuliert. Die dynamische Speicherverwaltung erfolgt über Zeiger; das Ergebnis einer Operation mallocwird üblicherweise in den Datentyp der zu speichernden Daten umgewandelt . Viele Datentypen, wie beispielsweise Bäume , werden üblicherweise als dynamisch allokierte structObjekte implementiert, die über Zeiger miteinander verknüpft sind. Zeiger auf andere Zeiger werden häufig in mehrdimensionalen Arrays und Objektarrays verwendet struct. Funktionszeiger sind nützlich, um Funktionen als Argumente an Funktionen höherer Ordnung (wie z. B. `qsort` oder ` bsearch` ) zu übergeben , in Dispatch-Tabellen oder als Rückruffunktionen für Ereignisbehandler . [ 18 ]

Ein Nullzeiger verweist explizit auf keinen gültigen Speicherort. Das Dereferenzieren eines Nullzeigers ist undefiniert und führt häufig zu einem Segmentierungsfehler . Nullzeiger sind nützlich, um Sonderfälle zu kennzeichnen, beispielsweise wenn im letzten Knoten einer verketteten Liste kein „next“-Zeiger vorhanden ist, oder um Fehler von Funktionen anzuzeigen, die Zeiger zurückgeben. In geeigneten Kontexten im Quellcode, etwa bei der Zuweisung zu einer Zeigervariablen, kann eine Nullzeigerkonstante als `null` geschrieben werden 0, mit oder ohne expliziter Typumwandlung in einen Zeigertyp, als das NULLin mehreren Standard-Headern definierte Makro oder, seit C23, mit der Konstante `null` nullptr. In bedingten Kontexten werden Nullzeiger zu `null` ausgewertet false, während alle anderen Zeigerwerte zu `null` ausgewertet werden true.

Void-Pointer void *verweisen auf Objekte nicht spezifizierten Typs und können daher als „generische“ Datenzeiger verwendet werden. Da Größe und Typ des referenzierten Objekts unbekannt sind, können Void-Pointer nicht dereferenziert werden, und auch Zeigerarithmetik ist nicht zulässig. Sie lassen sich jedoch leicht (und in vielen Kontexten implizit) in und aus jedem anderen Objektzeigertyp konvertieren. [ 18 ]

Der unachtsame Umgang mit Zeigern birgt Gefahren. Da Zeiger in der Regel nicht überprüft werden, kann eine Zeigervariable auf beliebige Speicherorte verweisen, was unerwünschte Folgen haben kann. Obwohl Zeiger korrekt verwendet auf sichere Speicherbereiche zeigen, können sie durch ungültige Zeigerarithmetik auf unsichere Speicherbereiche verweisen . Die Objekte, auf die sie verweisen, können nach der Freigabe weiterhin verwendet werden ( hängende Zeiger ); sie können verwendet werden, ohne initialisiert worden zu sein ( wilde Zeiger ); oder ihnen kann direkt ein unsicherer Wert zugewiesen werden, entweder durch Typumwandlung, Union oder über einen anderen fehlerhaften Zeiger. Im Allgemeinen erlaubt C die Manipulation und Konvertierung von Zeigertypen, obwohl Compiler üblicherweise Optionen für verschiedene Überprüfungsstufen bieten. Einige andere Programmiersprachen begegnen diesen Problemen durch die Verwendung restriktiverer Referenztypen .

Arrays

Arrays in C haben traditionell eine feste, statische Größe, die zur Kompilierzeit festgelegt wird. Der neuere C99-Standard erlaubt auch Arrays variabler Länge. Es ist jedoch auch möglich, zur Laufzeit mithilfe der entsprechenden mallocFunktion der Standardbibliothek einen Speicherblock beliebiger Größe zu allokieren und ihn wie ein Array zu behandeln.

Da auf Arrays (faktisch) immer über Zeiger zugegriffen wird, werden Array-Zugriffe typischerweise nicht anhand der zugrunde liegenden Array-Größe geprüft, obwohl einige Compiler eine Bereichsprüfung als Option anbieten. [ 45 ] [ 46 ] Verletzungen der Array-Grenzen sind daher möglich und können verschiedene Folgen haben, darunter unzulässige Speicherzugriffe, Datenbeschädigung, Pufferüberläufe und Laufzeitfehler.

C bietet keine spezielle Möglichkeit zur Deklaration mehrdimensionaler Arrays , sondern verwendet Rekursion innerhalb des Typsystems, um Arrays von Arrays zu deklarieren, was im Prinzip dasselbe bewirkt. Die Indexwerte des resultierenden „mehrdimensionalen Arrays“ können als zeilenweise aufsteigend betrachtet werden . Mehrdimensionale Arrays werden häufig in numerischen Algorithmen (hauptsächlich in der angewandten linearen Algebra ) zur Speicherung von Matrizen verwendet. Die Struktur des C-Arrays ist für diese Aufgabe gut geeignet. In frühen C-Versionen mussten die Grenzen des Arrays jedoch als feste Werte bekannt sein oder explizit an jede Unterroutine übergeben werden, die sie benötigte. Dynamisch dimensionierte Arrays von Arrays konnten nicht über doppelte Indizierung angesprochen werden. (Ein Workaround bestand darin, das Array mit einem zusätzlichen „Zeilenvektor“ von Zeigern auf die Spalten zu allokieren.) C99 führte „Arrays variabler Länge“ ein, die dieses Problem beheben.

Das folgende Beispiel mit modernem C (C99 oder höher) zeigt die Allokation eines zweidimensionalen Arrays auf dem Heap und die Verwendung der mehrdimensionalen Array-Indizierung für Zugriffe (die bei vielen C-Compilern eine Bereichsprüfung verwenden kann):

int func ( int n , int m ) {float ( * p )[ n ][ m ] = malloc ( sizeof * p );if ( p == NULL ) {Rückgabewert -1 ;}for ( int i = 0 ; i < n ; i ++ ) {for ( int j = 0 ; j < m ; j ++ ) {( * p )[ i ][ j ] = i + j ;}}print_array ( n , m , p );frei ( p );return 1 ;}

Und hier ist eine ähnliche Implementierung unter Verwendung der Auto- VLA- Funktion von C99 : [ f ]

int func ( int n , int m ) {// Achtung: Es sollten Prüfungen durchgeführt werden, um sicherzustellen, dass n * m * sizeof(float) die Beschränkungen für automatische VLAs NICHT überschreitet und innerhalb der verfügbaren Größe des Stacks liegt.float p [ n ][ m ]; // auto VLA wird auf dem Stack gehalten und seine Größe wird beim Funktionsaufruf festgelegtfor ( int i = 0 ; i < n ; i ++ ) {for ( int j = 0 ; j < m ; j ++ ) {p [ i ][ j ] = i + j ;}}print_array ( n , m , p );// Es ist nicht nötig, free(p) aufzurufen, da p beim Verlassen der Funktion zusammen mit dem restlichen Stackframe verschwindet.return 1 ;}

Austauschbarkeit von Array und Zeiger

Die Indexnotation x[i](wobei xein Zeiger bezeichnet) ist eine syntaktische Vereinfachung für *(x+i). [ 47 ] Da der Compiler den Zeigertyp kennt, x + iist die Adresse, auf die zeigt, nicht die Basisadresse (auf die durch zeigt x) um Bytes erhöht i, sondern definiert als die Basisadresse, erhöht um , imultipliziert mit der Größe des Elements, xauf das zeigt. Somit x[i]bezeichnet das i+1i-te Element des Arrays.

In den meisten Ausdruckskontexten (eine bemerkenswerte Ausnahme bildet die Verwendung als Operand einer Funktion sizeof) wird ein Ausdruck vom Array-Typ automatisch in einen Zeiger auf das erste Element des Arrays umgewandelt. Dies bedeutet, dass ein Array beim Aufruf als Funktionsargument niemals als Ganzes kopiert wird, sondern lediglich die Adresse seines ersten Elements übergeben wird. Obwohl Funktionsaufrufe in C die Wertübergabe verwenden , werden Arrays faktisch per Referenz übergeben .

Die Gesamtgröße eines Arrays xlässt sich durch Anwenden des Operators sizeofauf einen Ausdruck vom Array-Typ bestimmen. Die Größe eines Elements kann durch Anwenden des Operators sizeofauf ein beliebiges dereferenziertes Element eines Arrays ermittelt werden A, wie in n = sizeof A[0]. Somit Alässt sich die Anzahl der Elemente in einem deklarierten Array als bestimmen sizeof A / sizeof A[0]. Beachten Sie, dass, wenn nur ein Zeiger auf das erste Element verfügbar ist, was in C-Code aufgrund der oben beschriebenen automatischen Konvertierung häufig der Fall ist, die Information über den vollständigen Typ des Arrays und seine Länge verloren geht.

Speicherverwaltung

Eine der wichtigsten Funktionen einer Programmiersprache ist die Bereitstellung von Möglichkeiten zur Speicherverwaltung und zur Verwaltung der darin gespeicherten Objekte. C bietet drei Hauptmethoden zur Speicherzuweisung für Objekte: [ 18 ]

  • Statische Speicherverwaltung : Der Speicherplatz für das Objekt wird zur Kompilierzeit in der Binärdatei bereitgestellt; diese Objekte haben eine Ausdehnung (oder Lebensdauer), solange die Binärdatei, die sie enthält, in den Speicher geladen ist.
  • Automatische Speicherverwaltung : Temporäre Objekte können auf dem Stack gespeichert werden , und dieser Speicherplatz wird automatisch freigegeben und kann wiederverwendet werden, nachdem der Block, in dem sie deklariert wurden, verlassen wurde.
  • Dynamische Speicherverwaltung : Speicherblöcke beliebiger Größe können zur Laufzeit mithilfe von Bibliotheksfunktionen wie mallocaus einem Speicherbereich namens Heap angefordert werden ; diese Blöcke bleiben bestehen, bis sie anschließend durch Aufruf der Bibliotheksfunktion reallocoder zur Wiederverwendung freigegeben werden free.

Diese drei Ansätze eignen sich für unterschiedliche Situationen und bringen verschiedene Vor- und Nachteile mit sich. Beispielsweise verursacht die statische Speicherverwaltung nur geringen Speicheraufwand, die automatische Speicherverwaltung kann etwas mehr Aufwand verursachen, und die dynamische Speicherverwaltung kann sowohl beim Zuweisen als auch beim Freigeben von Speicher einen erheblichen Aufwand mit sich bringen. Die Persistenz statischer Objekte ist nützlich, um Zustandsinformationen über Funktionsaufrufe hinweg zu erhalten. Die automatische Speicherverwaltung ist einfach zu handhaben, der Stack-Speicher ist jedoch typischerweise deutlich begrenzter und flüchtiger als der statische Speicher oder der Heap-Speicher. Die dynamische Speicherverwaltung ermöglicht die komfortable Verwaltung von Objekten, deren Größe erst zur Laufzeit bekannt ist. Die meisten C-Programme nutzen alle drei Ansätze intensiv.

Wo immer möglich, ist die automatische oder statische Speicherverwaltung meist am einfachsten, da der Speicher vom Compiler verwaltet wird. Dadurch entfällt für den Programmierer die potenziell fehleranfällige Aufgabe, Speicher manuell zuzuweisen und freizugeben. Viele Datenstrukturen können jedoch zur Laufzeit ihre Größe ändern, und da statische Speicherzuweisungen (und automatische Zuweisungen vor C99) zur Kompilierzeit eine feste Größe haben müssen, gibt es viele Situationen, in denen eine dynamische Speicherzuweisung erforderlich ist. [ 18 ] Vor dem C99-Standard waren Arrays variabler Größe ein gängiges Beispiel dafür. (Ein Beispiel für dynamisch zugewiesene Arrays finden Sie im Artikel zur dynamischen Speicherzuweisung in C. ) Im Gegensatz zur automatischen Speicherzuweisung, die zur Laufzeit mit unkontrollierbaren Folgen fehlschlagen kann, geben die Funktionen für die dynamische Speicherzuweisung einen Hinweis (in Form eines Nullzeigers) zurück, wenn der benötigte Speicher nicht zugewiesen werden kann. (Eine zu große statische Speicherzuweisung wird üblicherweise vom Linker oder Loader erkannt , bevor das Programm überhaupt ausgeführt werden kann.)

Sofern nicht anders angegeben, enthalten statische Objekte beim Programmstart Nullzeigerwerte. Automatisch und dynamisch allokierte Objekte werden nur dann initialisiert, wenn ein Anfangswert explizit angegeben wird; andernfalls haben sie anfänglich undefinierte Werte (typischerweise das Bitmuster , das gerade im Speicher vorhanden ist und möglicherweise keinen gültigen Wert für diesen Typ darstellt). Versucht das Programm auf einen nicht initialisierten Wert zuzugreifen, ist das Ergebnis undefiniert. Viele moderne Compiler versuchen, dieses Problem zu erkennen und davor zu warnen, jedoch können sowohl Fehlalarme als auch Fehlalarme auftreten.

Die Heap-Speicherverwaltung muss mit der tatsächlichen Nutzung in jedem Programm synchronisiert werden, um eine möglichst vollständige Wiederverwendung zu gewährleisten. Verliert beispielsweise der einzige Zeiger auf eine Heap-Speicherzuweisung seinen Gültigkeitsbereich oder wird sein Wert überschrieben, bevor der Speicher explizit freigegeben wird, kann dieser Speicher nicht für die spätere Wiederverwendung freigegeben werden und geht dem Programm verloren – ein Phänomen, das als Speicherleck bekannt ist. Umgekehrt kann Speicher zwar freigegeben, aber anschließend noch referenziert werden, was zu unvorhersehbaren Ergebnissen führt. Typischerweise treten die Fehlersymptome in einem Programmteil auf, der nicht mit dem fehlerverursachenden Code zusammenhängt, was die Fehlerdiagnose erschwert. Solche Probleme werden in Sprachen mit automatischer Speicherbereinigung (Garbage Collection) gemildert .

Bibliotheken

Die Programmiersprache C verwendet Bibliotheken als primäre Erweiterungsmethode. In C ist eine Bibliothek eine Sammlung von Funktionen, die in einer einzigen Archivdatei enthalten ist. Jede Bibliothek besitzt typischerweise eine Header-Datei , die die Prototypen der in der Bibliothek enthaltenen Funktionen sowie Deklarationen spezieller Datentypen und Makrosymbole enthält, die mit diesen Funktionen verwendet werden. Damit ein Programm eine Bibliothek nutzen kann, muss es die Header-Datei der Bibliothek einbinden und die Bibliothek mit dem Programm verknüpfen. Dies erfordert in vielen Fällen Compiler-Flags (z. B. `--link` -lm, Kurzform für „Link the math library“). [ 18 ]

Die gebräuchlichste C-Bibliothek ist die C-Standardbibliothek , die in den ISO- und ANSI-C- Standards spezifiziert ist und in jeder C-Implementierung enthalten ist (Implementierungen für eingeschränkte Umgebungen wie eingebettete Systeme bieten möglicherweise nur eine Teilmenge der Standardbibliothek). Diese Bibliothek unterstützt Stream-Ein- und -Ausgabe, Speicherverwaltung, mathematische Operationen, Zeichenketten und Zeitwerte. Mehrere separate Standard-Headerdateien (z. B. `.css` stdio.h) definieren die Schnittstellen für diese und weitere Funktionen der Standardbibliothek.

Eine weitere häufig verwendete Gruppe von C-Bibliotheksfunktionen sind jene, die von Anwendungen speziell für Unix- und Unix-ähnliche Systeme eingesetzt werden, insbesondere Funktionen, die eine Schnittstelle zum Kernel bereitstellen . Diese Funktionen sind in verschiedenen Standards wie POSIX und der Single UNIX Specification detailliert beschrieben .

Da viele Programme in C geschrieben wurden, existiert eine Vielzahl weiterer Bibliotheken. Bibliotheken werden häufig in C geschrieben, da C-Compiler effizienten Objektcode generieren ; Programmierer erstellen dann Schnittstellen zur Bibliothek, sodass die Routinen von höheren Programmiersprachen wie Java , Perl und Python aus verwendet werden können . [ 18 ]

Dateiverarbeitung und Datenströme

Datei-Ein- und -Ausgabe (E/A) ist nicht Teil der Programmiersprache C selbst, sondern wird von Bibliotheken (wie der C-Standardbibliothek) und deren zugehörigen Headerdateien (z. B. `.bashrc` stdio.h) realisiert. Die Dateiverarbeitung erfolgt in der Regel über High-Level-E/A, die mit Datenströmen arbeitet . Ein Datenstrom ist dabei ein geräteunabhängiger Datenfluss, während eine Datei ein konkretes Gerät darstellt. Die High-Level-E/A wird durch die Zuordnung eines Datenstroms zu einer Datei realisiert. In der C-Standardbibliothek wird ein Puffer (ein Speicherbereich oder eine Warteschlange) verwendet, um Daten temporär zu speichern, bevor sie an ihr endgültiges Ziel gesendet werden. Dies reduziert die Wartezeiten auf langsamere Geräte, beispielsweise Festplatten oder SSDs . Low-Level-E/A-Funktionen sind nicht Teil der C-Standardbibliothek, sondern werden üblicherweise in der Bare-Metal-Programmierung (Programmierung unabhängig von Betriebssystemen , wie z. B. die meisten Embedded-Systeme ) eingesetzt. Mit wenigen Ausnahmen beinhalten Implementierungen Low-Level-E/A.

Sprachwerkzeuge

Es wurden zahlreiche Tools entwickelt, um C-Programmierern zu helfen, Anweisungen mit undefiniertem Verhalten oder möglicherweise fehlerhaften Ausdrücken zu finden und zu korrigieren, und zwar mit größerer Genauigkeit als vom Compiler bereitgestellt.

Es gibt automatisierte Tools zur Quellcodeprüfung und -analyse, wie beispielsweise Lint . Üblicherweise wird Lint verwendet, um fragwürdigen Code bereits beim Schreiben eines Programms zu erkennen. Sobald ein Programm die Lint-Prüfung bestanden hat, wird es mit dem C-Compiler kompiliert. Viele Compiler können optional auch vor syntaktisch korrekten Konstrukten warnen, die wahrscheinlich Fehler darstellen. MISRA C ist ein proprietärer Leitfaden zur Vermeidung solchen fragwürdigen Codes, der für eingebettete Systeme entwickelt wurde. [ 48 ]

Darüber hinaus gibt es Compiler, Bibliotheken und Mechanismen auf Betriebssystemebene, um Aktionen durchzuführen, die nicht zum Standardbestandteil von C gehören, wie z. B. Bereichsprüfung für Arrays, Erkennung von Pufferüberläufen , Serialisierung , dynamische Speicherverwaltung und automatische Speicherbereinigung .

Tools zur Speichermanagementprüfung wie Purify oder Valgrind und die Verknüpfung mit Bibliotheken, die spezielle Versionen der Speicherverwaltungsfunktionen enthalten, können helfen, Laufzeitfehler bei der Speichernutzung aufzudecken. [ 49 ] [ 50 ]

Verwendung

C wird häufig zur Implementierung von Endbenutzer- und Systemanwendungen eingesetzt. [ 51 ]

Begründung für die Verwendung in der Systemprogrammierung

Einige in C geschriebene Software

C wird häufig für die Systemprogrammierung zur Implementierung von Betriebssystemen und eingebetteten Systemanwendungen eingesetzt . [ 52 ] Dies hat mehrere Gründe:

  • Die Programmiersprache C ermöglicht den Zugriff auf Plattformhardware und Speicher mittels Zeigern und Typumwandlung , sodass systemspezifische Funktionen (z. B. Kontroll-/Statusregister , E/A-Register ) mit in C geschriebenem Code konfiguriert und verwendet werden können – sie ermöglicht die umfassendste Kontrolle über die Plattform, auf der sie ausgeführt wird.
  • Der durch die Kompilierung erzeugte Code benötigt nicht viele Systemfunktionen und kann auf unkomplizierte Weise aus dem Bootcode aufgerufen werden – er ist einfach auszuführen.
  • Die Anweisungen und Ausdrücke der Programmiersprache C lassen sich in der Regel gut auf Befehlsfolgen für den Zielprozessor abbilden, und folglich ist der Bedarf an Systemressourcen zur Laufzeit gering – die Ausführung erfolgt schnell.
  • Dank seines umfangreichen Operatorensatzes kann die Programmiersprache C viele Funktionen der Zielprozessoren nutzen. Verfügt ein bestimmter Prozessor über speziellere Befehle, lässt sich eine Sprachvariante mit intrinsischen Funktionen entwickeln , um diese Befehle auszunutzen – sie kann praktisch alle Funktionen des Zielprozessors verwenden.
  • Die Sprache ermöglicht es, Strukturen einfach über Blöcke binärer Daten zu legen, sodass die Daten verstanden, durchsucht und modifiziert werden können – sie kann Datenstrukturen und sogar Dateisysteme schreiben.
  • Die Sprache unterstützt eine Vielzahl von Operatoren, einschließlich Bitmanipulation, für die Arithmetik und Logik von ganzen Zahlen sowie möglicherweise verschiedene Größen von Gleitkommazahlen – sie kann entsprechend strukturierte Daten effektiv verarbeiten.
  • C ist eine relativ kleine Sprache mit nur wenigen Anweisungen und ohne allzu viele Merkmale, die umfangreichen Zielcode erzeugen – sie ist verständlich.
  • C hat die direkte Kontrolle über die Speicherzuweisung und -freigabe, was für eine angemessene Effizienz und ein vorhersehbares Timing bei Speicherverwaltungsoperationen sorgt, ohne dass man sich Gedanken über sporadische Stop-the-World -Garbage-Collection-Ereignisse machen muss – es bietet eine vorhersehbare Leistung.
  • C erlaubt die Verwendung und Implementierung verschiedener Speicherverwaltungsschemata , darunter ein typisches mallocund free; ein ausgefeilterer Mechanismus mit Arenen ; oder eine Version für einen OS-Kernel , die für DMA geeignet sein kann , innerhalb von Interrupt-Handlern verwendet werden kann oder in das virtuelle Speichersystem integriert ist .
  • Je nach Linker und Umgebung kann C-Code auch in Assemblersprache geschriebene Bibliotheken aufrufen und umgekehrt – er ist gut mit anderem Low-Level-Code kompatibel.
  • C und seine Aufrufkonventionen und Linkerstrukturen werden häufig in Verbindung mit anderen höheren Programmiersprachen verwendet, wobei sowohl Aufrufe an C als auch von C unterstützt werden – es ist gut mit anderem Code höherer Programmiersprachen interoperabel.
  • C verfügt über ein ausgereiftes und umfangreiches Ökosystem mit Bibliotheken, Frameworks, Open-Source-Compilern, Debuggern und Hilfsprogrammen und ist der De-facto-Standard. Die benötigten Treiber existieren wahrscheinlich bereits in C, oder es gibt eine ähnliche CPU-Architektur wie im Backend eines C-Compilers, sodass der Anreiz zur Wahl einer anderen Sprache geringer ist.

Spiele

Computerspiele werden häufig aus einer Kombination mehrerer Programmiersprachen entwickelt. C spielte dabei eine bedeutende Rolle, insbesondere bei Spielen, die die bestmögliche Leistung aus den jeweiligen Computerplattformen herausholen wollten. Ein Beispiel hierfür ist Doom aus dem Jahr 1993. [ 53 ]

Weltweites Netz

Historisch gesehen wurde C gelegentlich für die Webentwicklung verwendet, wobei das Common Gateway Interface (CGI) als Schnittstelle für den Informationsaustausch zwischen Webanwendung, Server und Browser diente. [ 54 ] C wurde möglicherweise aufgrund seiner Geschwindigkeit, Stabilität und nahezu universellen Verfügbarkeit gegenüber interpretierten Sprachen bevorzugt. [ 55 ] Die Webentwicklung in C ist heutzutage nicht mehr üblich, [ 56 ] und viele andere Webentwicklungssprachen sind populär. Anwendungsbereiche, in denen C-basierte Webentwicklung weiterhin eingesetzt wird, sind beispielsweise die HTTP- Konfigurationsseiten von Routern , IoT- Geräten und ähnlichen Geräten, obwohl auch hier einige Projekte Teile in höheren Programmiersprachen verwenden, z. B. Lua innerhalb von OpenWRT .

Zwei populäre Webserver , Apache HTTP Server und Nginx , sind in C geschrieben. [ 57 ] [ 58 ] Der hardwarenahe Ansatz von C ermöglicht die Entwicklung dieser leistungsstarken Softwaresysteme. [ 59 ]

C als Zwischensprache

C wird mitunter als Zwischensprache in Implementierungen anderer Sprachen verwendet. Dieser Ansatz kann aus Gründen der Portabilität oder Bequemlichkeit gewählt werden; durch die Verwendung von C als Zwischensprache sind keine zusätzlichen maschinenspezifischen Codegeneratoren erforderlich. C verfügt über einige Merkmale, wie z. B. Zeilennummern-Präprozessordirektiven und optionale überflüssige Kommas am Ende von Initialisierungslisten, die die Kompilierung des generierten Codes unterstützen. Einige Schwächen von C haben jedoch die Entwicklung anderer C-basierter Sprachen , wie z. B. C--, angeregt , die speziell für die Verwendung als Zwischensprachen konzipiert sind . Auch die modernen großen Compiler GCC und LLVM bieten eine Zwischenrepräsentation , die nicht C ist, und unterstützen Frontends für viele Sprachen, einschließlich C.

Rechenintensive Bibliotheken

C ermöglicht Programmierern die effiziente Implementierung von Algorithmen und Datenstrukturen, da die Abstraktionsschicht zur Hardware dünn und der Overhead gering ist – ein wichtiges Kriterium für rechenintensive Programme. Beispielsweise sind die GNU Multiple Precision Arithmetic Library , die GNU Scientific Library , Mathematica und MATLAB vollständig oder teilweise in C geschrieben. Viele Programmiersprachen unterstützen den Aufruf von Bibliotheksfunktionen in C; so nutzt beispielsweise das Python- basierte Framework NumPy C für seine hohe Performance und die Interaktion mit der Hardware.

Andere Sprachen werden in C geschrieben.

Eine Folge der weiten Verbreitung und Effizienz von C ist, dass Compiler , Bibliotheken und Interpreter anderer Programmiersprachen häufig in C implementiert werden. [ 60 ] Beispielsweise sind die Referenzimplementierungen von Python , [ 61 ] Perl , [ 62 ] Ruby , [ 63 ] und PHP [ 64 ] in C geschrieben.

Einschränkungen

Ritchie selbst scherzte über die Grenzen der von ihm geschaffenen Sprache: [ 65 ]

die Leistungsfähigkeit der Assemblersprache und der Komfort der ... Assemblersprache

— Dennis Ritchie

Obwohl C populär, einflussreich und enorm erfolgreich ist, hat es auch Nachteile, darunter:

  • Die standardmäßige dynamische Speicherverwaltung mit malloc`and` freeist fehleranfällig. Unsachgemäße Verwendung kann zu Speicherlecks und hängenden Zeigern führen . [ 66 ]
  • Die Verwendung von Zeigern und die direkte Manipulation des Speichers bergen das Risiko einer Speicherbeschädigung.
  • Es gibt zwar eine Typüberprüfung , diese greift jedoch nicht in allen Bereichen, beispielsweise bei Funktionen mit mehreren Parametern , und kann leicht oder unbeabsichtigt umgangen werden. Die Funktionalität ist schwach typisiert , obwohl sie statisch typisiert ist.
  • Da der vom Compiler generierte Code nur wenige Laufzeitprüfungen enthält, obliegt es dem Programmierer, alle möglichen Ergebnisse zu berücksichtigen, um sich vor Pufferüberläufen, Arraygrenzenprüfungen, Stack-Überläufen und Speichererschöpfung zu schützen und Race Conditions , Thread-Isolation usw. zu berücksichtigen.
  • Die Verwendung von Zeigern und deren Manipulation zur Laufzeit ermöglicht den Zugriff auf dieselben Daten über zwei Wege (Aliasing), was zur Kompilierzeit nicht immer feststellbar ist. Daher sind einige Optimierungen, die in anderen Sprachen wie Fortran möglich sind, in C nicht realisierbar. Aus diesem Grund gilt Fortran mitunter als schneller.
  • Einige der Standardbibliotheksfunktionen, z. B. scanfoder , können zu Pufferüberläufenstrncat führen .
  • Die Unterstützung für Varianten auf niedriger Ebene im generierten Code ist nur unzureichend standardisiert. Dies betrifft beispielsweise unterschiedliche Funktionsaufrufkonventionen und ABIs , unterschiedliche Strukturpackungskonventionen sowie unterschiedliche Byte-Reihenfolge innerhalb größerer Ganzzahlen (einschließlich Endianness). In vielen Sprachimplementierungen lassen sich einige dieser Optionen mit der Präprozessor-Direktive #pragma` use` [ 67 ] [ 68 ] und andere mit zusätzlichen Schlüsselwörtern wie z. B. `use __cdeclcalling convention` handhaben. Die Direktive und die Optionen werden jedoch nicht einheitlich unterstützt. [ 69 ]
  • Die Verarbeitung von Strings mit der Standardbibliothek ist codeintensiv und erfordert eine explizite Speicherverwaltung.
  • Die Sprache unterstützt keine Objektorientierung, Introspektion , Laufzeitauswertung von Ausdrücken (wie evalin JavaScript), automatische Speicherbereinigung usw.
  • Es gibt nur wenige Schutzmechanismen gegen den Missbrauch von Sprachmerkmalen, der zu schwer wartbarem Code führen kann. Insbesondere der C-Präprozessor kann problematische Effekte wie doppelte Auswertung und Schlimmeres verbergen. [ 70 ] Diese Fähigkeit zur Codeverschleierung wurde mit Wettbewerben wie dem International Obfuscated C Code Contest und dem Underhanded C Contest gefeiert .
  • C bietet keine standardmäßige Unterstützung für die Ausnahmebehandlung und stellt lediglich Rückgabewerte für die Fehlerprüfung bereit. Die setjmpStandardbibliotheksfunktionenlongjmp `try` und `catch` wurden [ 71 ] verwendet , um einen try-catch-Mechanismus mittels Makros zu implementieren. Auch ` gototry`-Anweisungen werden häufig zur Fehlerbehandlung eingesetzt.

Maßnahmen zur Milderung der Probleme von C

Für bestimmte Zwecke wurden eingeschränkte C-Stile eingeführt, z. B. MISRA C oder CERT C , um unerwünschtes Verhalten zu minimieren. [ 72 ] Datenbanken wie CWE erfassen potenzielle Schwachstellen von Systemen im Allgemeinen, insbesondere von in C programmierten Systemen, und geben Empfehlungen zu deren Behebung.

Es gibt Werkzeuge , die einige der Nachteile abmildern können. Moderne C-Compiler enthalten Prüfungen, die Warnungen generieren können, um viele potenzielle Fehler zu identifizieren.

Die Verwendung von Zeigern in C kann durch Erweiterungen der Befehlssatzarchitektur wie CHERI oder Permission Overlay Extensions sicherer gestaltet werden . Diese Techniken verändern die grundlegende Funktionsweise von Zeigern auf Hardwareebene, indem sie Bereichsprüfungen und Zweckbestimmungen einführen. Dadurch können Pufferüberläufe und unzulässige Heap-Zugriffe verhindert werden.

Seit Anfang der 2020er Jahre enthält der Linux-Kernel Abschnitte, die in Rust geschrieben sind , einer Sprache, die spezifische Sicherheitsmaßnahmen beinhaltet. [ 73 ]

TIOBE-Index

Viele nach C entwickelte Sprachen wurden von C beeinflusst und übernahmen Aspekte von C, darunter C++ , C# , C Shell , D , Go , Java , JavaScript , Julia , Limbo , LPC , Objective-C , Perl , PHP , Python , Ruby , Rust , Swift , Verilog und SystemVerilog . [ 8 ] [ 74 ] Manche behaupten, der prägendste Einfluss sei syntaktischer Natur – dass diese Sprachen die Anweisungs- und Ausdruckssyntax von C mit Typsystemen, Datenmodellen und groß angelegten Programmstrukturen kombinieren, die sich von denen von C unterscheiden, manchmal sogar radikal.

Es gibt mehrere C- oder C-ähnliche Interpreter, darunter Ch und CINT , die auch für Skripte verwendet werden können.

Als objektorientierte Programmiersprachen populär wurden, waren C++ und Objective-C zwei verschiedene Erweiterungen von C, die objektorientierte Funktionen boten. Beide Sprachen wurden ursprünglich als Quellcode-zu-Quellcode-Compiler implementiert ; der Quellcode wurde in C übersetzt und anschließend mit einem C-Compiler kompiliert. [ 75 ]

Die Programmiersprache C++ (ursprünglich „C mit Klassen “ genannt) wurde von Bjarne Stroustrup entwickelt , um objektorientierte Funktionalität mit einer C-ähnlichen Syntax bereitzustellen. [ 76 ] C++ bietet erweiterte Typisierung, Gültigkeitsbereiche und weitere nützliche Werkzeuge der objektorientierten Programmierung und ermöglicht generische Programmierung mittels Templates. Als nahezu vollständige Erweiterung von C unterstützt C++ heute fast den gesamten Funktionsumfang von C, mit wenigen Ausnahmen .

Objective-C war ursprünglich eine dünne Schicht über C und ist nach wie vor eine strikte Obermenge von C, die objektorientierte Programmierung mit einem hybriden dynamisch-statischen Typisierungsparadigma ermöglicht. Die Syntax von Objective-C stammt sowohl von C als auch von Smalltalk : Syntax für Präprozessoren, Ausdrücke, Funktionsdeklarationen und Funktionsaufrufe wurde von C übernommen, während die Syntax für objektorientierte Funktionen ursprünglich von Smalltalk stammt.

Neben C++ und Objective-C sind Ch, Cilk und Unified Parallel C nahezu Obermengen von C.

Siehe auch

Anmerkungen

  1. ^ „Thompson hatte 1972 einen kurzen Versuch unternommen, ein System in einer frühen Version von C – vor der Einführung von Strukturen – zu entwickeln, gab das Vorhaben jedoch auf.“ [ 2 ] [ 3 ] [ 4 ]
  2. ^ „Das von C gewählte Schema der Schriftsatzgestaltung verdankt Algol 68 viel, auch wenn es vielleicht nicht in einer Form entstanden ist, die Algols Anhänger gutgeheißen hätten.“ [ 6 ] [ 7 ] [ 4 ]
  3. ^ Ausgesprochen / ˈsiː / , wie der Buchstabe ' c ' . [ 9 ]
  4. Der ursprüngliche Beispielcode lässt sich auf den meisten modernen Compilern kompilieren, die nicht im strikten Standardkonformitätsmodus arbeiten, entspricht aber weder vollständig den Anforderungen von C89 noch von C99. Tatsächlich verlangt C99 die Ausgabe einer Diagnosemeldung .
  5. ^ Der Rückgabewert0wird in diesem Kontext typischerweise verwendet, um Erfolg anzuzeigen. [ 18 ]
  6. ^ Der Code vonprint_array(nicht gezeigt) unterscheidet sich ebenfalls geringfügig aufgrund des Typs von p , der in der malloc'd-Version ein Zeiger auf das 2D-Array ist und in der Auto-VNA-Version einfach ein 2D-Array.

Referenzen

  1. ^ a b Prinz, Peter; Crawford, Tony (16. Dezember 2005). C in a Nutshell . O'Reilly Media, Inc. S. 3. ISBN 978-0-596-55071-4Die
  2. ^ Ritchie (1993a) , S. 9.
  3. ^ Ritchie (1993b) , S. 9.
  4. ^ a b Ritchie (2003) .
  5. ^ „N3221 – Bericht des Herausgebers nach dem Treffen in Straßburg, Frankreich, Januar 2024“ . ISO/IEC JTC1/SC22/WG14 . Open Standards. 21. Februar 2024. Abgerufen am 24. Mai 2024 .
  6. ^ Ritchie (1993a) , S. 8.
  7. ^ Ritchie (1993b) , S. 8.
  8. ^ a b „Verilog HDL (und C)“ (PDF) . Die Forschungsschule für Informatik der Australian National University. 3. Juni 2010. Archiviert vom Original (PDF) am 6. November 2013. Abgerufen am 19. August 2013. 1980er Jahre : Verilog wurde erstmals eingeführt; Verilog ist von der Programmiersprache C inspiriert.
  9. ^ „Der Name basiert auf dem Buchstaben C im englischen Alphabet und wird wie dieser ausgesprochen . “ (Der Klang der Programmiersprache C. Englisch-Chinesisches Wörterbuch. Archiviert vom Original am 17. November 2022. Abgerufen am 17. November 2022. )
  10. ^ Munoz, Daniel. „Nach all den Jahren wird die Welt immer noch von C-Programmierung angetrieben | Toptal“ . Toptal Engineering Blog . Abgerufen am 15. Juni 2024 .
  11. ^ „C-Sprache sinkt auf den niedrigsten Beliebtheitswert“ . Developer.com . 9. August 2016. Archiviert vom Original am 22. August 2022. Abgerufen am 1. August 2022 .
  12. ^ a b c d e f g Ritchie (1993a) .
  13. ^ „Popularität von Programmiersprachen“ . 2009. Archiviert vom Original am 16. Januar 2009. Abgerufen am 16. Januar 2009 .
  14. ^ „TIOBE Programming Community Index“ . 2009. Archiviert vom Original am 4. Mai 2009. Abgerufen am 6. Mai 2009 .
  15. ^ Ward, Terry A. (August 1983). „Annotated C / A Bibliography of the C Language“ . Byte . S. 268. Abgerufen am 31. Januar 2015 .
  16. ^ „TIOBE-Index für September 2024“ . Archiviert vom Original am 18. September 2024. Abgerufen am 16. Dezember 2025 .
  17. ^ Kernighan & Ritchie (1978) , S. 6.
  18. ^ a b c d e f g Klemens, Ben (2013). 21. Jahrhundert C. O'Reilly Media . ISBN 978-1-4493-2714-9Die
  19. ^ Ritchie, Dennis. „BCPL to B to C“ . lysator.liu.se . Archiviert vom Original am 12. Dezember 2019. Abgerufen am 10. September 2019 .
  20. ^ a b c d e Jensen, Richard (9. Dezember 2020). "„Eine verdammt dumme Sache“ – die Ursprünge von C. Ars Technica . Archiviert vom Original am 28. März 2022. Abgerufen am 28. März 2022 .
  21. ^ a b Johnson, SC ; Ritchie, DM (1978). „Portabilität von C-Programmen und dem UNIX-System“. Bell System Tech. J . 57 (6): 2021– 2048. CiteSeerX 10.1.1.138.35 . doi : 10.1002/j.1538-7305.1978.tb02141.x . ISSN 0005-8580 . S2CID 17510065 .   (Hinweis: Die PDF-Datei ist ein OCR-Scan des Originals und enthält eine Darstellung von „IBM 370“ als „IBM 310“.)
  22. ^ McIlroy, MD (1987). A Research Unix reader: annotated excerpts from the Programmer's Manual, 1971–1986 (PDF) (Technical report). CSTR. Bell Labs. S. 10, 139. Archiviert vom Original (PDF) am 11. November 2017. Abgerufen am 1. Februar 2015 .
  23. ^ Kernighan & Ritchie (1978) .
  24. ^ „C-Handbuchseiten“. FreeBSD Miscellaneous Information Manual (FreeBSD 13.0 ed.). 30. Mai 2011. Archiviert vom Original am 21. Januar 2021. Abgerufen am 15. Januar 2021 .[1] Archiviert am 21. Januar 2021 in der Wayback Machine
  25. ^ Kernighan & Ritchie (1988) .
  26. ^ Stroustrup, Bjarne (2002). Geschwisterrivalität: C und C++ (PDF) (Bericht). AT&T Labs. Archiviert vom Original (PDF) am 24. August 2014. Abgerufen am 14. April 2014 .
  27. ^ „Begründung für den amerikanischen nationalen Standard für Informationssysteme – Programmiersprache – C“ . Archiviert vom Original am 17. Juli 2024. Abgerufen am 17. Juli 2024 .
  28. ^ C Integrität . Internationale Organisation für Normung. 30. März 1995. Archiviert vom Original am 25. Juli 2018. Abgerufen am 24. Juli 2018 .
  29. ^ „JTC1/SC22/WG14 – C“ . Homepage . ISO/IEC. Archiviert vom Original am 12. Februar 2018. Abgerufen am 2. Juni 2011 .
  30. ^ Andrew Binstock (12. Oktober 2011). „Interview mit Herb Sutter“ . Dr. Dobbs . Archiviert vom Original am 2. August 2013. Abgerufen am 7. September 2013 .
  31. ^ „ISO/IEC 9899:2024 (en) — N3220 Arbeitsentwurf“ (PDF) . Abgerufen am 11. Juli 2025 .
  32. ^ „WG14-N3132 : Überarbeiteter C23-Zeitplan“ (PDF) . open-std.org . 4. Juni 2023. Archiviert vom Original (PDF) am 9. Juni 2023.
  33. ^ „WG14-N3220 : Arbeitsentwurf, C2y“ (PDF) . open-std.org . 21. Februar 2024. Archiviert vom Original (PDF) am 26. Februar 2024.
  34. ^ „TR 18037: Eingebettetes C“ (PDF) . open-std.org . 4. April 2006. ISO/IEC JTC1 SC22 WG14 N1169. Archiviert vom Original (PDF) am 25. Februar 2021. Abgerufen am 26. Juli 2011 .
  35. ^ Harbison, Samuel P.; Steele, Guy L. (2002). C: Ein Nachschlagewerk (5. Aufl.). Englewood Cliffs, NJ : Prentice Hall . ISBN 978-0-13-089592-9DieEnthält eine BNF- Grammatik für C.
  36. ^ Kernighan & Ritchie (1988) , S. 192.
  37. ^ Kernighan & Ritchie (1978) , S. 3.
  38. ^ a b "Committee Draft ISO/IEC 9899:TC3: 5.2.1 Character sets" . 2007.
  39. ^ „ISO/IEC 9899:201x (ISO C11) Committee Draft“ (PDF) . open-std.org . 2. Dezember 2010. Archiviert vom Original (PDF) am 22. Dezember 2017. Abgerufen am 16. September 2011 .
  40. ^ Kernighan & Ritchie (1988) , S. 192, 259.
  41. ^ „10 häufige Programmierfehler in C++“ . Cs.ucr.edu . Archiviert vom Original am 21. Oktober 2008. Abgerufen am 26. Juni 2009 .
  42. ^ Schultz, Thomas (2004). C und die 8051 (3. Aufl.). Otsego, MI: PageFree Publishing Inc. S. 20. ISBN 978-1-58961-237-2Abgerufen am 10. Februar 2012 .
  43. ^ Feuer, Alan R.; Gehani, Narain H. (März 1982). „Vergleich der Programmiersprachen C und Pascal“. ACM Computing Surveys . 14 (1): 73–92 . doi : 10.1145/356869.356872 . S2CID 3136859 . 
  44. ^ Kernighan & Ritchie (1988) , S. 122.
  45. Beispielsweise stellt gcc _FORTIFY_SOURCE bereit. „Sicherheitsfunktionen: Pufferprüfungen zur Kompilierzeit (FORTIFY_SOURCE)“ . fedoraproject.org. Archiviert vom Original am 7. Januar 2007. Abgerufen am 5. August 2012 .
  46. ^เอี่ยมสิริวงศ์, โอภาศ (2016). Programmieren mit C . Bangkok, Thailand: SE-EDUCATION PUBLIC COMPANY LIMITED. S.  225–230 . ISBN 978-616-08-2740-4Die
  47. ^ Raymond, Eric S. (11. Oktober 1996). The New Hacker's Dictionary (3. Aufl.). MIT Press. S. 432. ISBN 978-0-262-68092-9Abgerufen am 5. August 2012 .
  48. ^ „Man Page for lint (freebsd Section 1)“ . unix.com . 24. Mai 2001. Abgerufen am 15. Juli 2014 .
  49. ^ Hardison, Nate. „CS107 Valgrind Memcheck“ . web.stanford.edu . Abgerufen am 23. Juni 2023 .
  50. ^ Hastings, Reed; Joyce, Bob. „Purify: Schnelle Erkennung von Speicherlecks und Zugriffsfehlern“ (PDF) . Pure Software Inc .: 9.
  51. ^ Munoz, Daniel. „Nach all den Jahren wird die Welt immer noch von der C-Programmierung angetrieben“ . Toptal Engineering Blog . Abgerufen am 17. November 2023 .
  52. ^ Dale, Nell B.; Weems, Chip (2014). Programmierung und Problemlösung mit C++ (6. Aufl.). Burlington, Massachusetts: Jones & Bartlett Learning. ISBN 978-1-4496-9428-9OCLC  894992484 .​
  53. ^ „Entwicklung von Doom“ . DoomWiki.org . 2. März 2025. Abgerufen am 2. März 2025 .
  54. ^ Dr. Dobbs Quellenbuch . USA: Miller Freeman, Inc. November–Dezember 1995.
  55. ^ „Using C for CGI Programming“ . linuxjournal.com. 1. März 2005. Archiviert vom Original am 13. Februar 2010. Abgerufen am 4. Januar 2010 .
  56. ^ Perkins, Luc (17. September 2013). „Webentwicklung in C: Verrückt? Oder verrückt wie ein Fuchs?“ Medium . Archiviert vom Original am 4. Oktober 2014. Abgerufen am 8. April 2022 .
  57. ^ "Welche Programmiersprache verwendet NGINX?" .
  58. ^ „Was ist Apache und was leistet es für die Webseitenentwicklung?“ . 15. Februar 2022.
  59. ^ „UNIX Time-Sharing System: The C Programming Language“ . Nokia Bell Labs . Abgerufen am 15. Juni 2026 .
  60. ^ „C – die Mutter aller Sprachen“ . ICT Academy am IITK . 13. November 2018. Archiviert vom Original am 31. Mai 2021. Abgerufen am 11. Oktober 2022 .
  61. ^ „1. Erweiterung von Python mit C oder C++“ . Python 3.10.7-Dokumentation . Archiviert vom Original am 5. November 2012. Abgerufen am 11. Oktober 2022 .
  62. ^ Conrad, Michael (22. Januar 2018). „Ein Überblick über die Perl-5-Engine“ . Opensource.com . Archiviert vom Original am 26. Mai 2022. Abgerufen am 11. Oktober 2022 .
  63. ^ „Von C und C++ zu Ruby“ . Ruby-Programmiersprache . Archiviert vom Original am 12. August 2013. Abgerufen am 11. Oktober 2022 .
  64. ^ Para, Michael (3. August 2022). „Was ist PHP? Wie man sein erstes PHP-Programm schreibt“ . freeCodeCamp . Archiviert vom Original am 4. August 2022. Abgerufen am 11. Oktober 2022 .
  65. ^ Metz, Cade (13. Oktober 2011). „Dennis Ritchie: Die Schultern, auf denen Steve Jobs stand“ . Wired . Archiviert vom Original am 12. April 2022. Abgerufen am 19. April 2022 .
  66. ^ Internet Security Research Group. „Was ist Speichersicherheit und warum ist sie wichtig?“ . Prossimo . Abgerufen am 3. März 2025 .
  67. ^ corob-msft (31. März 2022). „Pragma-Direktiven und die Schlüsselwörter __pragma und _Pragma“ . Microsoft Learn . Archiviert vom Original am 24. September 2022. Abgerufen am 24. September 2022 .
  68. ^ „Pragmas (Der C-Präprozessor)“ . GCC, die GNU Compiler Collection . Archiviert vom Original am 17. Juni 2002. Abgerufen am 24. September 2022 .
  69. ^ „Pragmas“ . Intel C++ Compiler Classic Developer Guide and Reference . Intel. Archiviert vom Original am 10. April 2022. Abgerufen am 10. April 2022 .
  70. ^ „Ein Loblied auf den C-Präprozessor“ . apenwarr . 13. August 2007. Abgerufen am 9. Juli 2023 .
  71. ^ Roberts, Eric S. (21. März 1989). „Implementing Exceptions in C“ (PDF) . DEC Systems Research Center . SRC-RR-40. Archiviert vom Original (PDF) am 15. Januar 2017. Abgerufen am 4. Januar 2022 .
  72. ^ „Überblick über sicheres Programmieren“ (PDF) . Software Engineering Institute, Carnegie Mellon University . Abgerufen am 15. Dezember 2025 .
  73. ^ „Neuer Linux-Patch bestätigt: Das Rust-Experiment ist beendet, Rust bleibt bestehen . “ www.phoronix.com . Abgerufen am 15. Dezember 2025 .
  74. ^ O'Regan, Gerard (24. September 2015). Säulen des Computing: Eine Sammlung ausgewählter, wegweisender Technologieunternehmen . Springer. ISBN 978-3-319-21464-1OCLC  922324121 .​
  75. ^ Rauchwerger, Lawrence (2004). Sprachen und Compiler für paralleles Rechnen: 16. Internationaler Workshop, LCPC 2003, College Station, TX, USA, 2.–4. Oktober 2003: Überarbeitete Beiträge . Springer. ISBN 978-3-540-24644-2OCLC  57965544 .​
  76. ^ Stroustrup, Bjarne (1993). „A History of C++: 1979–1991“ (PDF) . Archiviert vom Original (PDF) am 2. Februar 2019. Abgerufen am 9. Juni 2011 .

Quellen

Weiterführende Literatur

  • Plauger, PJ (1992). Die Standard-C-Bibliothek (1. Aufl.). Prentice Hall. ISBN 978-0-13-131509-9Die(Quelle)
  • Banahan, M.; Brady, D.; Doran, M. (1991). Das C-Buch: Mit dem ANSI C-Standard (2. Aufl.). Addison-Wesley. ISBN 978-0-201-54433-6Die(frei)
  • Feuer, Alan R. (1985). Das C-Rätselbuch (1. Aufl.). Prentice Hall. ISBN 0-13-109934-5Die
  • Harbison, Samuel; Steele, Guy Jr. (2002). C: Ein Nachschlagewerk (5. Aufl.). Pearson. ISBN 978-0-13-089592-9Die(Archiv)
  • King, KN (2008). C Programming: A Modern Approach (2. Aufl.). WW Norton. ISBN 978-0-393-97950-3Die(Archiv)
  • Griffiths, David; Griffiths, Dawn (2012). Head First C (1. Aufl.). O'Reilly. ISBN 978-1-4493-9991-7Die
  • Perry, Greg; Miller, Dean (2013). C Programming: Absolute Beginner's Guide (3. Aufl.). Que. ISBN 978-0-7897-5198-0Die
  • Deitel, Paul; Deitel, Harvey (2015). C: So programmieren Sie (8. Aufl.). Pearson. ISBN 978-0-13-397689-2Die
  • Gustedt, Jens (2025). Modernes C (3. Aufl.). Manning. ISBN 9781633437777Die(frei)