1. Index
  2. Shell
  3. C
  4. POSIX
  5. JavaScript

C

C ist eine prozedurale Sprache für die Systemprogrammierung. Ein Übersetzer erzeugt optimierten Maschinencode für eine bestimmte Hardware-Architektur.

Versionen

Datum Version Neuerungen
1972 NB Urversion aus den Bell Labs
1978 K&R C The C Programming Language, 1st edition
1989 ANSI C89 Aufzählungen, Zuweisung von Strukturen
1994 ANSI C95 NA1 Internationalisierung, Multibyte Zeichenketten
2000 ANSI C99 Reihungen mit variabler Länge, Designierte Initialisierer
2011 ANSI C11 Threads
2018 ANSI C17 Keine

Beispiel

hello.c
#include <stdio.h> int main (int argc, char * argv[]) { if (argc == 1) { printf ("Hello, World!\n"); } else for (int i = 1; i < argc; i++) { printf ("Hello, %s!\n", argv[i]); } return 0; }

Übersetzen und ausführen mit:

gcc -std=c11 -o hello hello.c && ./hello
Hello, World!

Syntax

Der Quellcode für ein C-Programm oder Modul (Übersetzungseinheit) besteht aus:

  1. Direktiven für den Präprozessor,
  2. Deklarationen von globalen Variablen und Funktionen
  3. Definition dieser Funktionen

Makros definieren

Der Präprozessor (cpp) expandiart Makros nach folgenden Regeln, bevor der Übersetzer (cc) den Code sieht:

Direktive Parameter Beschreibung
Header
#include <File> Datei aus Include-Pfad einbinden
#include "File" Datei aus Arbeitsverzeichnis einbinden
Makro
#define Name Value Konstante durch Wert ersetzen
#define Name(...) __VA_ARGS__ Variadisches Makro definieren
#define Name(Param, Args ...) ArgsMakro mit benannter Argumentliste
#define Name(Param) #Param Parameter stringifizieren
#define Name(Param) ## Param Parameter konkatenieren
#undef Name Definition entfernen
Block
#ifdef Name Block expandieren, wenn Konstante existiert
#ifndef Name Block expandieren, wenn Konstante fehlt
#if Expression Block expandieren, wenn Ausdruck wahr ist
#elif Expression Weitere Bedingung prüfen
#else Alternative
#endif Blockende
Implementierung
#line Number Mitgeführte Zeilennummer ändern
#line Number "File" Mitgeführten Dateiname ändern
#error MESSAGE Übersetzer mit Fehlermeldung stoppen
Konstanten
__FILE__ Aktueller Dateiname
__LINE__ Aktuelle Zeilennummer
__func__ Name der aktuelle Funktion
__DATE__ mmm dd yyyy Aktuelles Datum
__TIME__ hh:mm:ss Aktuelle Uhrzeit
__STDC__ 1 Standardkonformität des Übersetzers
Konstante Wert Beschreibung
__STDC__ 1 Übersetzer ist standardkonform
0 Übersetzer nicht standardkonform
__STDC_VERSION__ Version des C Standards
199409L C95
199901L C99
201112L C11
__STDC_HOSTED__ 1 Betriebsystem vorhanden
0 Kernelprogrammierung

Primitive Datentypen

Die Deklaration einer Variable legt Speicherklasse, Zugriff, Vorzeichen, Größe, Typ, Name und den initialen Wert fest.

Speicherklasse Zugriff Vorzeichen Größe Typ Bits Literal
auto
register
extern
static
thread_local
const
volatile
restrict
void
_Bool 1 true, false
signed
unsigned
char 8 'A'
short
long
int 16/32/64 0
_Complex
_Imaginary
double 64/80 0.1
float 32 0.1f
Variable auf dem Stack ablegen. Standard für lokale Variablen und Parameter von Funktionen.
Variable wenn möglich im Prozessorregister halten. Moderne Übersetzer ignorieren das Schlüsselwort, weil sie bei der Optimierung bessere Resultate erzielen.
Funktion oder globale Variable wird in einer anderen Übersetzungseinheit definiert.
Variable im statischen Programmspeicher ablegen. Globale Symbole sind nur in der Übersetzungseinheit sichtbar.
Variable im statischen Threadspeicher ablegen. Variable wird vor Beginn des Threads initialisiert.
Variable ist unveränderlich.
Variable kann sich spontan ändern, verzichte auf Optimierungen.
Zeiger unterliegt keinem Aliasing, aktiviere Optimierungen.
Vorzeichenbehaftete Ganzzahl
Vorzeichenlose Ganzzahl
Gleitkommazahl mit imaginärem Anteil
Imaginäre Gleitkommazahl
Ganzzahl (int) mit minimaler Größe
Ganzzahl (int) mit maximaler Größe oder Gleitkommazahl mit maximaler Genauigkeit
Anonymer Typ für Zeiger, Funktionen ohne Rückgabewert und leere Parameterlisten.
Wahrheitswert kann die Werte true und false annehmen.
Zeichencode abhängig vom eingestellten Zeichensatz
Ganzzahl
Gleitpunktzahl mit einfacher Genauigkeit
Gleitpunktzahl mit doppelter Genauigkeit

Variablen deklarieren

Primitive, Aufzählungen, Alternativen, Strukturen und Funktionen bevölkern jeweils einen eigenen Namensraum. Daher kann man gleiche Namen für verschiedene Klassen verwenden. Sprich: Eine Struktur kann genauso heißen wie eine Aufzählung.

Type Name = Value;
Mit der Deklaration sollte auch gleich die Zuweisung eines Wertes erfolgen.
enum Name { Name = Value, … };
Eine Aufzählung deklariert eine Gruppe zusammengehörender Konstanten.
union Name { Type Name, … };
Eine Alternative interpoliert verschiedene Datentypen über dem gleichen Speicherbereich.
struct Name { Type Name, … } = { .Name = Value, … };
Eine Struktur fasst eine Liste von Variablen zusammen.
Aufzählung
Alternative mit überlappenden Mitgliedern
Datenstruktur
Type Name[Size] = { Value, … };
Eine Reihung speichert eine Liste von gleichartigen Objekten mit fester Länge.
Type Name[Size][Size]… = { {… Value, …}, …};
Mehrdimensionale Reihungen decken die Anwendungsfälle Matrix und Tensor ab.

Einen zusammengehörenden Bereich im Speicher bezeichnet man als Objekt. Der Übersetzer führt Buch über den Typ dieser Objekte, etwa ob es sich um eine Ganzzahl, eine Datenstruktur oder um eine Funktion handelt.

Funktionen definieren

Eine Funktion ist ein Objekt, dessen Adresse man als Zeiger speichern, als Element in Strukturen verwenden oder als Parameter und Rückgabewert anderen Funktionen übergeben kann.

Type Name (Argument, …) { Statement; … }
Eine Funktion besteht aus Rückgabewert, Funktionsname, Argumentliste und einem Block mit Anweisungen

Schlüsselwörter für Funktionen

inline
Rumpf expandieren. Lieber auf Optimierung verlassen.
static
Funktion ist nur in der Übersetzungeinheit sichtbar, das Symbol wird nicht exportiert.
_Noreturn
Funktion kehrt niemals zurück. Aktiviere Optimierung. Nur sinnvoll für void-Funktionen.

Operationen ausführen

Die Präzedenz der Operatoren ist auf intuitive Verwendung ausgelegt, so dass man häufig auf Klammern verzichten kann. Geklammerte Ausdrücke genießen immer Vorrang, gefolgt von Index-Angaben in Reihungen, Zeiger-Dereferenzierung und Feldzugriffe. In arithmetischen Ausdrücken werden zuerst unäre Operatoren, dann Multiplikation, Addition und schließlich Bitoperationen ausgewertet. Danach folgen logische Ausdrücke mit Vergleich, Bitverknpüfung, Wahrheitslogik und Entscheidungslogik mit dem ternären Operator. Am geringsten binden Zuweisungen.

Operator
Klammerausdrücke () [] -> .
Unäre Operatoren ! ~ ++ -- + - * & (Type) sizeof
Multiplikation * / %
Addition + -
Bitverschiebung << >>
Vergleich < <= > >= == !=
Bitverknüpfung & ^ |
Logisches UND/ODER && ||
Ternärer Operator ? … :
Zuweisung = += -= *= /= %= &= ^= |= <<= >>=
Separator ,

Ablauf steuern

if (Condition) { … } else { … }
Verzweigung
switch (Name) { case Value: … break; default: … break; }
Mehrfachverzweigung
for (Expression; Condition; Expression) …
Schleife für Iteration mit Initialisierung, Abbruchbedingung und Inkrement
while (Condition) …
Schleife mit Abbruchbedingung
do … while (Condition);
Schleife mit Abbruchbedingung am Ende
continue;
Aktuellen Schleifendurchlauf beenden
break;
Schleife verlassen
goto Label;
Unbedingter Sprung
return Expression;
Rücksprung aus Funktion
asm { Instruction … }
Anweisungen in Maschinencode einbetten

Zeiger dereferenzieren

Ein Zeiger ist eine Variable, welche die Speicheradresse eines Objekts enthält.

Type * Name = NULL;
Zeiger nach nirgendwo
Type (* Name) (Argument, …);
Zeiger auf eine Funktion
Type (* Name[Size]) (Argument, …);
Reihung von Funktionszeigern

Stilvoll programmieren

Quelltext dient in erster Linie der Kommunikation mit anderen Entwicklern. Er sollte daher leicht zu verstehen und leicht zu erweitern sein. Es ist die Aufgabe des Übersetzers, daraus für den Rechner optimierte Anweisungen zu erzeugen.

Typographische Konventionen

Programme sollte wie Bücher aufgebaut sein:

Struktur von Übersetzungseinheiten

Module fassen Datenstrukturen und alle darauf arbeitenden Funktionen zusammen.

Die Unix-Philosophie

Literatur

  1. Kernighan, Ritchie: The C Programming Language, Second Edition, Prentice Hall, 1988
  2. Kernighan, Pike: The Practice of Programming, Addison-Wesley, 1999
  3. Boswell, Foucher: The Art of Readable Code, O'Reilly, 2012
  4. Rob Pike: Notes on Programming in C
  5. Linus Torvalds: Linux kernel coding style
  6. Eric S. Raymond, Basics of the Unix Philosophy, The Art of Unix Programming, 2003
  7. Gustavo Duarte: Anatomy of a Program in Memory
  8. Pete Jinks: C Syntax in BNF