C (Programmiersprache)

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche
C
Paradigmen: imperativ, strukturiert
Erscheinungsjahr: 1972
Entwickler: Dennis Ritchie & Bell Labs
Aktuelle Version C11
Wichtige Implementierungen: GCC, MSVC, Borland C, Portland Group, Intel, Clang
Beeinflusst von: B, BCPL, ALGOL 68[1]
Beeinflusste: awk, C++, C#, Objective-C, D, Go, Java, JavaScript, PHP, Perl, Python, Vala, Seed7

C ist eine imperative und prozedurale Programmiersprache, die der Informatiker Dennis Ritchie in den frühen 1970er Jahren an den Bell Laboratories entwickelte. Seitdem ist sie eine der am weitesten verbreiteten Programmiersprachen.

Die Anwendungsbereiche von C sind sehr verschieden. Sie wird zur System- und Anwendungsprogrammierung eingesetzt. Die grundlegenden Programme aller Unix-Systeme und die Systemkernel vieler Betriebssysteme sind in C programmiert. Zahlreiche Sprachen, wie C++, Objective-C, C#, D, Java, JavaScript, PHP, Vala oder Perl, orientieren sich an der Syntax und anderen Eigenschaften von C.

Geschichte[Bearbeiten | Quelltext bearbeiten]

Ken Thompson (links) und Dennis Ritchie (rechts)

Entstehung[Bearbeiten | Quelltext bearbeiten]

C wurde 1969–1973 von Dennis Ritchie[2] in den Bell Laboratories für die Programmierung des damals neuen UNIX-Betriebssystems entwickelt. Er stützte sich dabei auf die Programmiersprache B, die Ken Thompson und Dennis Ritchie in den Jahren 1969/70 geschrieben hatten – der Name C entstand als Weiterentwicklung von B. B wiederum geht auf die von Martin Richards Mitte der 1960er-Jahre entwickelte Programmiersprache BCPL zurück.[3] Ritchie schrieb auch den ersten Compiler für C. 1973 war die Sprache so weit ausgereift, dass man nun den Unix-Kernel für die PDP-11 neu in C schreiben konnte.

Weitere Entwicklung[Bearbeiten | Quelltext bearbeiten]

K&R C erweiterte die ursprüngliche Sprache um neue Schlüsselwörter wie long int oder unsigned int und führte erstmals die I/O-Standardbibliothek ein.

Standards[Bearbeiten | Quelltext bearbeiten]

C ist eine Programmiersprache, die auf fast allen Computersystemen zur Verfügung steht. Um den Wildwuchs zahlreicher Dialekte einzudämmen, wurde C mehrfach standardisiert (C89/C90, C99, C11). Abgesehen vom Mikrocontrollerbereich, wo eigene Dialekte existieren, sind die meisten aktuellen PC-/Server-Implementierungen eng an den Standard angelehnt; eine vollständige Implementierung aktueller Standards ist aber selten. In den meisten C-Systemen mit Laufzeitumgebung steht auch die genormte C-Standard-Bibliothek zur Verfügung. Dadurch können C-Programme, die keine sehr hardware-nahe Programmierung enthalten, in der Regel gut auf andere Zielsysteme portiert werden.

Die 1978 erschienene erste Auflage von The C Programming Language beinhaltet den ehemaligen inoffiziellen Standard K&R C

K&R C[Bearbeiten | Quelltext bearbeiten]

Bis ins Jahr 1989 gab es keinen offiziellen Standard der Sprache. Seit 1978 galt hingegen das Buch The C Programming Language als informeller De-facto-Standard, welches Kernighan und Ritchie im selben Jahr veröffentlicht hatten.[4] Bezeichnet wird diese Spezifikation als K&R C.

Da in den folgenden Jahren die Zahl an Erweiterungen der Sprache ständig wuchs, man sich nicht auf eine gemeinsame Standard-Bibliothek einigen konnte und nicht einmal die UNIX-Compiler K&R C vollständig implementierten, wurde beschlossen, einen offiziellen Standard festzulegen. Nachdem dieser schließlich im Jahr 1989 erschienen war, blieb K&R C zwar noch für einige Jahre De-facto-Standard vieler Programmierer, verlor dann aber rasch an Bedeutung.

ANSI C[Bearbeiten | Quelltext bearbeiten]

Im Jahr 1983 setzte das American National Standards Institute (ANSI) ein Komitee namens X3J11 ein, das 1989 seine Arbeit abschloss und die Norm ANSI X3.159-1989 Programming Language C verabschiedete. Diese Version der Sprache C wird auch kurz als ANSI C, Standard C oder C89 bezeichnet.

Ein Jahr später übernahm die International Organization for Standardization (ISO) den bis dahin rein amerikanischen Standard auch als internationale Norm, die ISO/IEC 9899:1990, kurz auch als C90 bezeichnet. Die Namen C89 und C90 beziehen sich also auf dieselbe Version von C.

Nach der ersten Entwicklung durch ANSI und ISO, wurde der Sprachstandard für einige Jahre kaum geändert. Erst 1995 erschien das Normative Amendment 1 zu C90. Es hieß ISO/IEC 9899/AMD1:1995 und wird auch kurz als C95 bezeichnet. Neben der Korrektur einiger Details wurden mit C95 internationale Schriftsätze besser unterstützt.

C99[Bearbeiten | Quelltext bearbeiten]

Nach einigen kleineren Revisionen erschien im Jahr 1999 der neue Standard ISO/IEC 9899:1999, kurz C99. Er war größtenteils mit C90 kompatibel und führte einige neue, teilweise von C++ übernommene Features ein, von denen einige bereits zuvor von verschiedenen Compilern implementiert worden waren. C99 wurde im Lauf der Jahre durch drei Technical Corrigendas ergänzt.

C11[Bearbeiten | Quelltext bearbeiten]

Im Jahr 2007 begann die Entwicklung eines neuen Standards mit dem inoffiziellen Arbeitstitel C1X. Er wurde im Dezember 2011 veröffentlicht und ist in der Kurzform als C11 bekannt. Neben einer besseren Kompatibilität mit C++ wurden der Sprache wiederum neue Features hinzugefügt.[5][6]

Seit dem ersten internationalen Standard C90 wird C von der internationalen Arbeitsgruppe ISO/IEC JTC1/SC22/WG14 weiterentwickelt. Die nationalen Standardisierungsorganisationen übernehmen die Veröffentlichungen des internationalen Standards in an ihre Bedürfnisse angepasster Form.

Verwendung[Bearbeiten | Quelltext bearbeiten]

Trotz des eher hohen Alters ist die Sprache C auch heute weit verbreitet und wird sowohl im Hochschulbereich, wie auch in der Industrie und im Open Source-Bereich verwendet.[7]

System- und Anwendungsprogrammierung[Bearbeiten | Quelltext bearbeiten]

Das Haupteinsatzgebiet von C liegt in der Systemprogrammierung einschließlich der Erstellung von Betriebssystemen und der Programmierung von eingebetteten Systemen. Der Grund liegt in der Kombination von erwünschten Charakteristiken wie Portabilität und Effizienz mit der Möglichkeit, Hardware direkt anzusprechen und dabei niedrige Anforderungen an die Laufzeitumgebung zu haben.

Auch Anwendungssoftware wird oft in C erstellt. Viele Programmierschnittstellen für Anwendungsprogramme und Betriebssystem-APIs werden in Form von C-Schnittstellen implementiert, zum Beispiel Win32.[8] Gemäß C-Standard existieren jedoch keine Funktionen zur positionierten Ausgabe auf Displays, d. h. text- oder grafisch orientierte Benutzeroberflächen sind in reinem C nicht realisierbar. Es existieren jedoch zahlreiche Bibliotheken, die für das jeweilige Zielsystem eine solche Ausgabe ermöglichen.

Implementierung anderer Sprachen[Bearbeiten | Quelltext bearbeiten]

Wegen der hohen Ausführungsgeschwindigkeit und geringen Codegröße werden Compiler, Programmbibliotheken und Interpreter anderer höherer Programmiersprachen (wie z. B. die Java Virtual Machine) oft in C implementiert.

C wird als Zwischencode einiger Implementierungen höherer Programmiersprachen verwendet. Dabei wird diese zuerst in C-Code übersetzt, der dann kompiliert wird. Dieser Ansatz wird entweder verwendet, um die Portabilität zu erhöhen (C-Compiler existieren für nahezu jede Plattform), oder aus Bequemlichkeit, da kein maschinenspezifischer Codegenerator entwickelt werden muss. Einige Compiler, die C auf diese Art benutzen, sind Chicken, EiffelStudio, Esterel, PyPy, Sather, Squeak und Vala.

C wurde allerdings als Programmiersprache und nicht als Zielsprache für Compiler entworfen. Als Zwischensprache ist es daher eher schlecht geeignet. Das führte zu C-basierten Zwischensprachen wie C--.

C wird oft für die Erstellung von Anbindungen (engl. bindings) genutzt (zum Beispiel Java Native Interface). Diese Anbindungen erlauben es Programmen, die in einer anderen Hochsprache geschrieben sind, Funktionen aufzurufen, die in C implementiert wurden. Der umgekehrte Weg ist oft ebenfalls möglich und kann verwendet werden, um in C geschriebene Programme mit einer anderen Sprache zu erweitern (z. B. mod perl).

Syntax[Bearbeiten | Quelltext bearbeiten]

Einsprungspunkt[Bearbeiten | Quelltext bearbeiten]

Jedes C-Programm muss eine Funktion mit dem Namen main haben, anderenfalls wird das Programm nicht kompiliert. Die main-Funktion ist der Einsprungspunkt eines C-Programms, das heißt die Programmausführung beginnt immer mit dieser Funktion.

main(){}         /* das kürzeste mögliche C-Programm */

Außer der main-Funktion müssen in einem C-Programm keine weiteren Funktionen enthalten sein. Sollen andere Funktionen ausgeführt werden, müssen sie in der main-Funktionen aufgerufen werden. Die main-Funktion wird deshalb auch als Hauptprogramm bezeichnet, alle weiteren Funktionen als Unterprogramme.

main() {
    funktion_1();
    funktion_2();
}

Schlüsselwörter[Bearbeiten | Quelltext bearbeiten]

C besitzt eine sehr kleine Menge an Schlüsselwörtern. Die Anzahl der Schlüsselwörter ist so gering, weil fast alle Aufgaben, welche in anderen Sprachen über eigene Schlüsselwörter realisiert werden, über einzubindende Bibliotheksroutinen realisiert werden (zum Beispiel die Ein-/Ausgabe über Konsole oder auf Dateien, die dynamische Speicherverwaltung, …) oder über spezielle syntaktische Konstrukte (zum Beispiel Variablendeklarationen). („C kann ohne Bibliotheken fast gar nichts.“)

Anweisungen[Bearbeiten | Quelltext bearbeiten]

Ein C-Programm besteht aus der main-Funktion und optional aus weiteren Funktionen. Jede Funktion besteht wiederum aus Anweisungen. Wie in den meisten Programmiersprachen sind die wichtigsten Anweisungen: Deklarationen und Definitionen, Zuweisungen, bedingte Anweisungen, Anweisungen die Schleifen umsetzen sowie Funktionsaufrufe. Im Folgenden eher sinnlosen Programm finden sich Beispiele.

/* Unterprogramme */
void funktion_die_nichts_tut(void) {    /* Definition */
    return;                             /* Return-Anweisung */
}
int plus_eins_funktion(int argument) {  /* Definition */
    return argument + 1;                /* Return-Anweisung */
}

/* Hauptprogramm */
int main() {                            /* Definition */
    int zahl;                           /* Definition */
    funktion_die_nichts_tut();          /* Funktionsaufruf */
    zahl = 5;                           /* Zuweisung */
    zahl = plus_eins_funktion(zahl);    /* Funktionsaufruf und Zuweisung */
    if(zahl > 5) {                      /* bedingte Anweisung */
        zahl = zahl - 1;                /* Zuweisung: der Wert von „zahl“ ist wieder „5“ */             
    }
    return 0;                           /* Return-Anweisung */
}

Datentypen[Bearbeiten | Quelltext bearbeiten]

C verfügt über die vier arithmetischen Datentypen char, int (beide für ganze Zahlen), float und double (beide für Kommazahlen). Die Auswahl eines dieser Datentypen beeinflusst die Größe des reservierten Speichers und die Größe des darstellbaren Werte. Darüber hinaus sind für die verschiedenen Datentypen unterschiedliche Operatoren und Funktionen zugelassen.

Der Verzicht auf festgeschriebene Größen und Wertebereiche, um möglichst viele Architekturen zu unterstützen, wird durch definierte minimale Wertebereiche und die folgende feste Relation abgemildert:

signed charshort intintlong intlong long int.
(„≤“ bedeutet dabei, dass der rechts stehende Typ alle Werte des links stehenden Typs aufnehmen kann.)

Character[Bearbeiten | Quelltext bearbeiten]

Zum Speichern eines Zeichens verwendet man in C den Datentyp Character, geschrieben als char. Vom Computer tatsächlich gespeichert wird nicht das Zeichen sondern eine gleichbedeutende acht Bit lange Binärzahl (dadurch ergeben sich 256 verschiedene Werte, die einem Character zugewiesen werden können). Der Programmierer kann sich die Binärzahl leicht vom Computer in ein Zeichen oder eine dezimale Ganzzahl übersetzen lassen. Die Übersetzung einer Zahl in ein Zeichen und umgekehrt geschieht dabei anhand einer Tabelle (z. B.: ASCII-Tabelle oder EBCDIC).

char zeichen = 'A';    /* intern gespeichert wird nicht der Buchstabe „A“ sondern
                        * die anhand der ASCII-Tabelle errechnete Binärzahl „01000001“ */
printf("%d", zeichen); /* gibt „01000001“ als Dezimalzahl aus, also: „65“ */
printf("%c", zeichen); /* gibt „01000001“ als ASCII-Zeichen aus, also: „A“ */

Ein Character repräsentiert die kleinste adressierbare Einheit in C, in der Regel acht Bit. Deshalb wird die Größe von Objekten und Typen oft als ganzzahliges Vielfaches eines Characters angegeben. Je nach Compiler kann char entweder gleichbedeutend sein mit signed char (-128 bis 127, fast immer der Fall) oder mit unsigned char (0 bis 255). Um auch Zeichen aus Zeichensätzen aufnehmen zu können, die mehr Zeichen umfassen als der relativ kleine ASCII-Zeichensatz, wurde mit wchar_t bald ein zweiter für Zeichen konzipierter Datentyp eingeführt. Er umfasst in fast allen Implementierungen mehr als acht Bit.

Integer[Bearbeiten | Quelltext bearbeiten]

Zum Speichern einer ganzen Zahl verwendet man eine Variable vom Datentyp Integer, geschrieben als int. Um den Wertebereich eines Integers zu verkleinern oder zu vergrößern, stellt man ihm einen der Qualifizierer short, long oder long long voran. Das Schlüsselwort int kann dann auch weggelassen werden, so ist long gleichbedeutend mit long int. Um zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen zu wechseln, gibt es die beiden Qualifizierer signed und unsigned. Für einen vorzeichenbehafteten Integer kann der Qualifizierer aber auch weggelassen werden, so ist signed int gleichbedeutend mit int.

char ganzzahl = 1;      /* mindestens 8 Bit, also 256 mögliche Werte */
short ganzzahl = 2;     /* mindestens 16 Bit, also 65536 mögliche Werte */
int ganzzahl = 3;       /* mindestens 16 Bit, also 65536 mögliche Werte */
long ganzzahl = 4;      /* mindestens 32 Bit, also 4294967296 mögliche Werte */
long long ganzzahl = 5; /* mindestens 64 Bit, also 18446744073709551616 mögliche Werte */

Die Größe eines Integers ist vom jeweiligen Compiler abhängig, der C-Standard garantiert aber eine minimale Größe von 16 Bit. Die tatsächliche Größe beträgt heutzutage (je nach Prozessorarchitektur und Betriebssystem) meist 32 Bit, oft aber auch schon 64 und manchmal noch 16 Bit. In 16 Bit lassen sich 65536 verschiedene Werte speichern. Um die Verwendung von negativen Zahlen zu ermöglichen, reicht der Wertebereich bei 16 Bit gewöhnlich von -32768 bis 32767. Werden keine negativen Zahlen benötigt, kann der Programmierer mit unsigned int aber einen vorzeichenlosen Integer verwenden. Bei 16 Bit großen Integern ergibt das einen Wertebereich von 0 bis 65535.

Die tatsächliche Größe eines Integers ist in der Headerdatei <limits.h> abgelegt. INT_MAX ersetzt der C-Präprozessor beispielsweise durch den Wert, den der Typ int maximal annehmen kann.[9]

Die C-Standard-Bibliothek ergänzt diese Datentypen über die plattformunabhängige Header-Datei <stdint.h> in der ein Set von Ganzzahltypen mit fester Länge definiert ist.

Float, Double und Long Double[Bearbeiten | Quelltext bearbeiten]

Zahlen mit Nachkommastellen werden in einem der drei Datentypen float, double und long double gespeichert.

float kommazahl = 0.000001;           /* 6-stellige Genauigkeit */
double kommazahl = 0.000000000000002; /* 15-stellige Genauigkeit */
long double kommazahl = 0.3;          /* Genauigkeit ist implementierunsabhängig */

In den meisten C-Implementierungen entsprechen die Datentypen Float und Double dem international gültigen Standard für binäre Gleitpunktarithmetiken (IEC 559, im Jahr 1989 aus dem älteren amerikanischen Standard IEEE 754 hervorgegangen). Ein Float implementiert das „einfach lange Format“, ein Double das „doppelt lange Format“. Dabei umfasst ein Float 32 Bit, ein Double 64 Bit. Doubles sind also genauer. Floats werden aufgrund dieses Umstands nur noch in speziellen Fällen verwendet. Die Größe von Long Doubles ist je nach Implementierung unterschiedlich, ein Long Double darf aber auf keinen Fall kleiner sein als ein Double.

Die genauen Eigenschaften und Wertebereiche auf der benutzten Architektur können über die Headerdatei <float.h> ermittelt werden.[10]

Komplexe Zahlen[Bearbeiten | Quelltext bearbeiten]

Zusätzlich existieren seit C99 noch drei Gleitkomma-Datentypen für komplexe Zahlen, welche aus den drei Gleitkommatypen abgeleitet sind: float _Complex, double _Complex und long double _Complex. Ebenfalls in C99 eingeführt wurden Gleitkomma-Datentypen für rein imaginäre Zahlen: float _Imaginary, double _Imaginary und long double _Imaginary.

In einer hosted-Umgebung müssen die _Complex-Datentypen vorhanden sein; die _Imaginary-Typen sind optional. In einer freestanding-Umgebung sind diese sechs Datentypen optional.[11]

Bool[Bearbeiten | Quelltext bearbeiten]

Bis zum C99-Standard gab es keinen Datentyp zum Speichern eines Wahrheitswerts. Erst seit 1999 können Variablen als _Bool deklariert werden und einen der beiden Werte 0 (falsch) oder 1 (wahr) aufnehmen. Die Größe einer _Bool-Variablen ist plattformabhängig und kann 8 Bit übersteigen. Inkludiert man den Header stdbool.h kann auch der Alias bool statt _Bool verwendet werden, sowie false und true statt 0 und 1.

_Bool a = 23;   /* Alle zugewiesenen Werte ungleich 0, werden von einer _Bool-Variablen als 1 gespeichert */

#include <stdbool.h>
bool b = false;

Void[Bearbeiten | Quelltext bearbeiten]

Der Datentyp void wird im C-Standard als „unvollständiger Typ“ bezeichnet. Man kann keine Variablen von diesem Typ erzeugen. Verwendet wird void erstens, wenn eine Funktion keinen Wert zurückgeben soll und zweitens, wenn ein Zeiger auf „Objekte beliebigen Typs“ zeigen soll.

void funktionsname();      /* Deklaration einer Funktion, die keinen Wert zurückgibt */
void* zeigername;          /* Zeiger auf ein Objekt von beliebigem Typ */

Verbundtypen[Bearbeiten | Quelltext bearbeiten]

Felder[Bearbeiten | Quelltext bearbeiten]

Wie Felder (arrays) in einigen anderen Programmiersprachen, verwendet man Felder in C um mehrere Werte desselben Datentyps zu speichern. Die Werte eines Feldes haben aufeinanderfolgende Speicheradressen. Die Anzahl der verschiedenen Werte eines Feldes ist als Index des Feldes festgelegt.

int personen[10];           /* Definition eines Feldes mit genau 10 ganzzahligen Werten */
int personen[] = {1, 2, 3}; /* Definition eines Feldes mit 3 initialisierten Werten */

Structures[Bearbeiten | Quelltext bearbeiten]

Um verschiedenartige Daten in einer Variablen zu speichern, verwendet man Structures, geschrieben als struct. Auf diese Weise können Variablen verschiedenen Datentyps zusammengefasst werden

struct person
{
    char* vorname;
    char nachname[20];
    int alter;
    double groesse;
};

Enums[Bearbeiten | Quelltext bearbeiten]

Wie in anderen Programmiersprachen dient ein Enum in C dazu, mehrere konstante Werte zu einem Typ zu kombinieren.

enum Temperatur { WARM, KALT, MITTEL };
enum Temperatur heutige_temperatur = WARM;
if (heutige_temperatur == KALT) {
    printf("Warm anziehen!");               /* wird nicht ausgegeben, da es heute „WARM“ ist */
}

Typedefs[Bearbeiten | Quelltext bearbeiten]

Das Schlüsselwort Typedef wird zur Erstellung eines Alias für einen Datentyp verwendet.

typedef int Ganzzahl;  /* legt den Alias „Ganzzahl“ für den Datentyp „int“ an */
Ganzzahl a, b;         /* ist jetzt gleichbedeutend zu „int a, b;“ */

Zeiger[Bearbeiten | Quelltext bearbeiten]

Wie Zeiger in anderen Programmiersprachen sind Zeiger in C Variablen, die statt eines direkt verwendbaren Wertes eine Speicheradresse speichern. Sie ermöglichen es, auf den Wert zuzugreifen, der an dieser Speicheradresse liegt. Dieser Wert kann wiederum eine Adresse sein, die auf eine weitere Speicheradresse zeigt. Bei der Deklaration eines Zeigers wird zuerst der Datentyp des Objekts angegeben, auf das gezeigt wird, danach ein Asterisk, danach der gewünschte Name des Zeigers.

char *zeiger;    /* Kann die Adresse eines Characters speichern */
double *zeiger;  /* Kann die Adresse eines Doubles speichern */

Funktionen[Bearbeiten | Quelltext bearbeiten]

Funktionen (function types): Zu jedem Typ T existieren Typen „Funktion, die ein T zurückgibt“. Dies wird durch Anhängen von () gekennzeichnet, wobei in die Klammern ggf. noch die Typen der Funktionsparameter geschrieben werden.

Deklarationen[Bearbeiten | Quelltext bearbeiten]

Bevor Variablen, Typen und Funktionen in C verwendet werden können, müssen sie deklariert werden (sog. Statische Typisierung). Eine so deklarierte Variable, Typ oder Funktion erhält einen Namen (Bezeichner, engl. identifier). Die Vergabe von Bezeichnern ist an bestimmte Regeln gebunden:

  • Das erste Zeichen eines Bezeichners muss ein Buchstabe oder Unterstrich sein.
  • Die folgenden Zeichen dürfen nur die Buchstaben A–Z und a–z, Ziffern und der Unterstrich sein.
  • Ein Bezeichner darf kein Schlüsselwort der Sprache – zum Beispiel if, void und auto – sein.

Seit C95 sind auch Zeichen aus dem Universal Character Set in Bezeichnern erlaubt, sofern die Implementierung es unterstützt. Die erlaubten Zeichen sind in Anhang D des ISO-C-Standards aufgelistet. Vereinfacht gesagt, sind es all jene Zeichen, die in irgendeiner Sprache als Buchstabe oder buchstabenähnliches Zeichen Verwendung finden. Im Quelltext lassen sich diese Zeichen plattformunabhängig über eine Escape-Sequenz wie folgt ersetzen:

  • \uXXXX (wobei X für eine Hexadezimalziffer steht) für Zeichen mit einem Code von 00A0hex bis FFFFhex.
  • \UXXXXXXXX für alle Zeichen mit einem Code ≥00A0hex.

Das folgende Beispiel deklariert und definiert die Namen variable_1 und variable_2 als Variablen vom Typ Integer:

void beispiel1()
{
  int variable_1, variable_2;
}

Bestimmte Bezeichner sind außerdem für die Implementierung reserviert:

  • Bezeichner, die mit zwei aufeinanderfolgenden Unterstrichen beginnen
  • Bezeichner, die mit Unterstrich gefolgt von einem Großbuchstaben anfangen.

Erweiterungen am Sprachkern, die neue Schlüsselwörter erfordern, verwenden dafür ebenfalls Namen aus diesem reservierten Bereich, um zu vermeiden, dass sie mit Bezeichnern in existierenden C-Programmen kollidieren, z. B. __attribute__, _Complex, _Generic, _Thread_local.

Sonstiges[Bearbeiten | Quelltext bearbeiten]

C ist case-sensitiv.

Datenmodell[Bearbeiten | Quelltext bearbeiten]

Die C-Sprachnorm legt die Größe (und damit den Wertebereich) der einzelnen Basisdatentypen nicht fest, sondern definiert lediglich Relationen zwischen den Größen der Basisdatentypen und fordert für jeden Basisdatentyp jeweils Mindestgrößen. Daraus ergeben sich in der Praxis mehrere Ausgestaltungsmöglichkeiten, welche man Datenmodell oder auch Programmiermodell nennt.

Der Datentyp int wird auf einer Plattform in der Regel so festgelegt, dass seine Größe der natürlichen Datenwortgröße der CPU entspricht. Die Größe der Zeigertypen richtet sich nach der Größe des Speicherbereichs, der vom Programm aus adressierbar sein soll. Dieser Speicherbereich kann kleiner, aber auch größer sein, als der von der CPU-Architektur adressierbare Speicherbereich.

Auf heutigen Architekturen ist ein char meist 8 Bit groß, die anderen Datentypen müssen somit ein ganzzahliges Vielfaches von 8 Bit groß sein. Damit ergeben sich folgende mögliche Datenmodelle:

Bits pro Datentyp
Datenmodell Datentyp Plattformen (Auswahl)
char short int long long long void*
IP16 8 16 16 32 64 16 MS DOS im SMALL memory model
LP32 8 16 16 32 64 32 MS DOS im LARGE memory model
ILP32 8 16 32 32 64 32 die meisten 32-Bit-Betriebssysteme
LLP64 8 16 32 32 64 64 Windows auf x86-64 und IA64
LP64 8 16 32 64 64 64 die meisten unixoiden Betriebssysteme auf 64-Bit-Plattformen
ILP64 8 16 64 64 64 64 SPARC64
SILP64 8 64 64 64 64 64 Unicos (Cray)

Beispielprogramm in C[Bearbeiten | Quelltext bearbeiten]

Der folgende Quelltext stellt ein einfaches C-Programm dar, das die Textzeile Hallo Welt!, gefolgt von einem Zeilenvorschub, ausgibt. Dieses Beispiel folgt den Vorgaben des ANSI-C-Standards; andere Versionen dieses Programms sind im Artikel Liste von Hallo-Welt-Programmen/Programmiersprachen beschrieben.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    puts("Hallo Welt!");
    return EXIT_SUCCESS;
} /* end main() */

In der ersten Zeile ermöglicht die Präprozessordirektive #include <stdio.h> die spätere Verwendung von Funktionen aus der Ein-/Ausgabe-Bibliothek stdio (auch „standard-input/output“ genannt). Diese include-Anweisung veranlasst den C-Präprozessor, vor der Übersetzung die Headerdatei <stdio.h> in den Quelltext zu kopieren, die unter anderem eine Deklaration der weiter unten verwendeten Ausgabefunktion puts enthält. In der zweiten Zeile wird die Headerdatei <stdlib.h> eingebunden, die die symbolische Konstante EXIT_SUCCESS definiert, damit die erfolgreiche Programmausführung dem Aufrufer plattformunabhängig signalisiert werden kann (siehe Zeile 7 des Programms). Include-Anweisungen können zwar nicht immer in beliebiger Reihenfolge, aber an jeder Stelle im Quelltext eingefügt werden – meist werden sie jedoch an den Anfang eines Programmtextes gestellt, um die Übersichtlichkeit zu erhöhen.

In der vierten Zeile beginnt das eigentliche Programm mit der Definition der Funktion main. Sie ist die Einstiegsfunktion eines C-Programms. main wird automatisch als erste Funktion aufgerufen. Anfang und Ende der Funktion main werden durch die beiden geschweiften Klammern markiert.

Die erste Anweisung innerhalb der Funktion main ruft die Funktion puts auf, die den Text „Hallo Welt!“ ausgibt. Die zweite Anweisung return EXIT_SUCCESS; beendet den Funktionsaufruf und legt den Rückgabewert fest. Damit wird der „Erfolgsstatus“ des ausgeführten Programms zum Ausdruck gebracht. Der Wert EXIT_SUCCESS bedeutet hier fehlerfreie Ausführung.

In der letzten Zeile folgt auf die schließende geschweifte Klammer ein Kommentar, eingeschlossen durch die Zeichenfolgen /* und */. Kommentare werden bei der Übersetzung ignoriert; sie sind für den menschlichen Leser gedacht, können aber auch von automatischen Software-Dokumentationswerkzeugen ausgewertet werden.

Beziehung zu Assembler, Portierbarkeit[Bearbeiten | Quelltext bearbeiten]

Die Programmiersprache C wurde mit dem Ziel entwickelt, eine echte Sprachabstraktion zur Assemblersprache zu implementieren. Es sollte eine direkte Zuordnung zu wenigen Maschineninstruktionen geben, um die Abhängigkeit von einer Laufzeitumgebung zu minimieren. Als Resultat dieses Designs ist es möglich, C-Code auf einer sehr hardwarenahen Ebene zu schreiben, analog zu Assemblerbefehlen. Die Portierung eines C-Compilers auf eine neue Prozessorplattform ist, verglichen mit anderen Sprachen, wenig aufwändig. Bspw. ist der freie GNU-C-Compiler (gcc) für eine Vielzahl unterschiedlicher Prozessoren und Betriebssysteme verfügbar. Für den Entwickler bedeutet das, dass unabhängig von der Zielplattform fast immer auch ein C-Compiler existiert. C unterstützt damit wesentlich die Portierbarkeit von Programmen, sofern der Programmierer auf Assemblerteile im Quelltext und/oder hardwarespezifische C-Konstrukte verzichten kann. In der Mikrocontroller-Programmierung ist C die mit Abstand am häufigsten verwendete Hochsprache.

Standardbibliothek[Bearbeiten | Quelltext bearbeiten]

Hauptartikel: C-Standard-Bibliothek

Die C-Standard-Bibliothek ist integraler Bestandteil einer gehosteten (engl. hosted) C-Implementierung. Sie enthält unter anderem Makros und Funktionen, die mittels der Standard-Header-Datei verfügbar gemacht werden. Auf freistehenden (engl. freestanding) Implementationen dagegen kann der Umfang der Standardbibliothek eingeschränkt sein.

Die Standardbibliothek ist aufgeteilt in mehrere Standard-Header-Dateien, die hinzugelinkte Bibliothek ist jedoch oft eine einzige große Datei.

  • „Gehostet“: C-Compiler und Programm befinden sich in einer Betriebssystem-Umgebung, welche übliche Dienste bietet (z. B. ein Dateisystem, textuelle Ein- und Ausgabekanäle, Speichermanagement).
  • „Freistehend“: Das C-Programm läuft nicht unter einem Betriebssystem, sondern muss alle Gerätefunktionen selbst implementieren. Häufig stehen dennoch zumindest einige Bibliotheken vorab zur Verfügung. Hier finden häufig Cross-Compiler (auch „Target-Compiler“) Verwendung.

Module[Bearbeiten | Quelltext bearbeiten]

Eine Modularisierung in C erfolgt auf Dateiebene. Eine Datei bildet eine Übersetzungseinheit; intern benötigte Funktionen und Variablen können so vor anderen Dateien verborgen werden. Die Bekanntgabe der öffentlichen Funktionsschnittstellen erfolgt mit so genannten Header-Dateien. Damit verfügt C über ein schwach ausgeprägtes Modulkonzept.[12][13]

Das globale Sprachdesign sieht vor, dass ein Programm aus mehreren Modulen bestehen kann. Für jedes Modul existiert eine Quellcode-Datei (mit der Endung .c) und eine Header-Datei (mit der Endung .h). Die Quellcode-Datei enthält im Wesentlichen die Implementierung, die Header-Datei das Interface nach außen. Beide Dateien konsistent zu halten, ist bei C (wie auch bei C++) Aufgabe des Programmierers.

Module, die Funktionen aus anderen Modulen benutzen, inkludieren deren Header-Dateien und geben dem Compiler damit die notwendigen Informationen über die vorhandenen Funktionen, Aufrufkonventionen, Typen und Konstanten.

Jedes Modul kann für sich übersetzt werden und erzeugt eine Object-Datei. Mehrere Object-Dateien können zu einer Bibliothek zusammengefasst oder einzeln verwendet werden.

Mehrere Object-Dateien sowie Bibliotheken (die auch nur eine Sammlung von Objekt-Dateien sind) können mittels Linker (deutsch: Binder) zu einem ausführbaren Programm gebunden werden.

Compiler[Bearbeiten | Quelltext bearbeiten]

Am weitesten verbreitet ist der seit 1987 bestehende freie C-Compiler der GNU Compiler Collection. Unter Windows ist auch der seit 1993 entwickelte Compiler Visual C++ weit verbreitet. Neben diesen beiden stehen zahlreiche weitere Compiler zur Verfügung.

Da es in C vergleichsweise wenige Schlüsselwörter gibt, ergibt sich der Vorteil eines sehr einfachen, kleinen Compilers. Auf neuen Computersystemen ist C deshalb oft die erste verfügbare Programmiersprache (nach Maschinencode und Assembler).

Sicherheit[Bearbeiten | Quelltext bearbeiten]

Konzeptionell ist C auf eine einfache Kompilierbarkeit der Quelltexte und für den schnellen Ablauf des Programmcodes ausgelegt. Die Compiler erzeugen in der Regel aber nur wenig Code zur Gewährleistung der Datensicherheit und Betriebssicherheit während der Laufzeit der Programme. Daher wird zunehmend versucht, diese Mängel durch formale Verifikation aufzudecken und zu korrigieren beziehungsweise durch zusätzliche vom Programmierer zu erstellende Quelltexte zu beheben.[14][15][16]

C schränkt direkte Speicherzugriffe kaum ein. Dadurch kann der Compiler (anders als zum Beispiel in Pascal) nur sehr eingeschränkt bei der Fehlersuche helfen. Aus diesem Grund ist C für sicherheitskritische Anwendungen (Medizintechnik, Verkehrsleittechnik, Raumfahrt) weniger geeignet. Wenn in diesen Bereichen dennoch C eingesetzt wird, so wird in der Regel versucht, die Qualität der erstellten Programme durch zusätzliche Prüfungen wie Code-Coverage zu erhöhen.

C enthält einige sicherheitskritische Funktionen; so überschreibt zum Beispiel gets(), eine Funktion der Standardbibliothek, fremde Speicherbereiche (Pufferüberlauf), wenn es auf eine unpassende (zu lange) Eingabe stößt. Der Fehler ist innerhalb von C weder bemerk- noch abfangbar. Um den großen Vorteil von C – die Existenz zahlreicher älterer Quellcodes – nicht zu verlieren, unterstützen auch aktuelle Implementierungen weiterhin diese und ähnliche Funktionen, warnen jedoch in der Regel, wenn sie beim Übersetzen im Quelltext benutzt werden.

C ist nicht typsicher[17], da verschiedene Datentypen zuweisungskompatibel gehandhabt werden können.[18]

Literatur[Bearbeiten | Quelltext bearbeiten]

Einführungen

Handbücher

  • Rolf Isernhagen, Hartmut Helmke: Softwaretechnik in C und C++. Das Kompendium. Modulare, objektorientierte und generische Programmierung. ISO-C90, ISO-C99, ISO-C++98, MS-C++.NET. 4., vollständig überarbeitete Auflage, Hanser, München/Wien 2004, ISBN 3-446-22715-6.
  • Jürgen Wolf: C von A bis Z. Das umfassende Handbuch. 3. aktualisierte und erweiterte Auflage 2009, 4., korrigierter Nachdruck 2015, Rheinwerk, Bonn 2015, ISBN 978-3-8362-1411-7.

K&R C

  • Brian Kernighan, Dennis Ritchie: The C Programming Language. Prentice Hall, Englewood Cliffs (NJ) 1978, ISBN 0-13-110163-3. (Deutsche Übersetzung: Brian Kernighan, Dennis Ritchie: Programmieren in C. Mit dem reference manual in deutscher Sprache. Hanser, München/Wien 1983)
  • Brian Kernighan, Dennis Ritchie: The C Programming Language. 2. Auflage, Prentice Hall, Englewood Cliffs (NJ) 1988, ISBN 0-13-110362-8. (Deutsche Übersetzung: Brian Kernighan, Dennis Ritchie: Programmieren in C. Mit dem C-Reference Manual in deutscher Sprache. 2. Auflage, Hanser, München/Wien 1990, ISBN 978-3-446-15497-1)

Weblinks[Bearbeiten | Quelltext bearbeiten]

 Wikibooks: C-Programmierung – Lern- und Lehrmaterialien

Einzelnachweise[Bearbeiten | Quelltext bearbeiten]

  1. Dennis M. Ritchie: The Development of the C Language. Lucent Technologies. January 1993. Abgerufen am 10. September 2015: „The scheme of type composition adopted by C owes considerable debt to Algol 68, although it did not, perhaps, emerge in a form that Algol's adherents would approve of.“
  2. Dennis M. Ritchie: The Development of the C Language. Lucent Technologies. January 1993. Abgerufen am 10. September 2015.
  3. Ken Thompson: Users’ Reference to B. Abgerufen am 30. Mai 2015.
  4. Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language, Prentice Hall, Englewood Cliffs (NJ) 1978, ISBN 0-13-110163-3.
  5. Sprachdefinition von C11 als ISO-Standard ISO/IEC 9899:2011, veröffentlicht am 8. Dezember 2011.
  6. ISO aktualisiert C-Standard, Artikel auf heise online, vom 22. Dezember 2011.
  7. Rolf Isernhagen, Hartmut Helmke: Softwaretechnik in C und C++. Das Kompendium. Modulare, objektorientierte und generische Programmierung. ISO-C90, ISO-C99, ISO-C++98, MS-C++.NET. 4., vollständig überarbeitete Auflage, Hanser, München/Wien 2004, ISBN 3-446-22715-6, Seite 4.
  8. Agner Fog: Calling conventions for different C++ compilers and operating systems: Chapter 3, Data Representation (PDF; 416 kB) 16. Februar 2010. Abgerufen am 30. August 2010.
  9. Klaus Schmaranz: Softwareentwicklung in C, Springer, 2001, Seite 29 ff.
  10. Helmut Herold und Wolfgang Unger: „C“-Gesamtwerk, te-wi Verlag, 2. Auflage, München, 1992, 2–5 bis 2–7
  11. ISO/IEC 9899:1999 Kapitel 7.6.2 Absatz 3
  12. Scheler, Stilkerich, Schröder-Preikschat: Komponenten/Module (PDF; 1,1 MB)
  13. Bertrand Meyer: Objektorientierte Softwareentwicklung. Hanser, Wien, München; Prentice Hall Internat. 1990, S. 406 ISBN 3-446-15773-5.
  14. Junan Qian, Baowen Xu: Formal Verification for C Program, Informatica, Volume 18, Number 2 (2007), pages 289-304, abgerufen am 5. Juli 2016
  15. Harvey Tuch: Formal verification of C systems code, Sydney Research Lab., National ICT Australia (2009), abgerufen am 5. Juli 2016
  16. Jay Abraham: Improving Software Quality with Static Code Analysis, MathWorks (2012), abgerufen am 5. Juli 2016
  17. Markus Bautsch: Cycles of Software Crises – How to avoid insecure and uneconomic software, ENISA Quartely, Vol. 3, No. 4, Oct–Dec 2007, p. 3–5
  18. Lambert Kenneth Louden: Programming Languages: Principles and Practices, Ch. 8.6.1 Type Compatibility / 8.7 Type Conversion, Cengage Learning (2011), ISBN 9781133387497