C-Programmierung Cheatsheet:
Die wichtigsten C-Befehle auf einen Blick

Dies C-Cheatsheet zeigt die Syntax der Programmiersprache C mit Beispielen, gefolgt von jeweils einem Quiz / Selbsttest, um das Verständnis zu prüfen. Zum schnellen Nachschlagen können Sie die C-Kurzreferenz einsetzen, eine tabellarische und sortierbare Übersicht der wichtigsten C-Befehle, oder zum Ausdrucken die Kurzreferenz als PDF-Datei. Als Entwicklungsumgebung verwenden wir Visual Studio Community Edition, diese unterstützt C-Programmierung als Teilmenge der C++-Entwicklung und man kann relativ unkompliziert von C zu C++ übergehen. Für das Testen kleiner Codefragmente kann der Online-Compiler OnlineGDB verwendet werden, dort im Dropdown rechts oben als Programmiersprache C auswählen.

 Vorab: Infos rund um die C-Programmierung

C ist eine prozedurale Programmiersprache, d.h. Programme werden mit Hilfe von Funktionen / Prozeduren strukturiert, die Daten (Variablen, Arrays, Strukturen) verarbeiten. Mittels Sequenzen von Einzelanweisungen (Variablen und Arrays deklarieren, Eingabe, Ausgabe, ...), Verzweigungen, Schleifen und Funktionen kann man Lösungsalgorithmen für eine Vielzahl von Aufgabenstellungen in C implementieren.

Mit C werden vor allem Konsolenprogramme geschrieben, Betriebssysteme und Mikrocontroller programmiert. Die Stärke der Programmiersprache C liegt darin, dass sie hardware-nah ist, mit Hilfe von Zeigervariablen kann man z.B. direkt auf Adressbereiche zugreifen. Für die Entwicklung grafischer Benutzeroberflächen bieten objektorientierte Sprachen wie C++, Java oder C# eine bessere Unterstützung.

Vor der Programmierung ist es hilfreich, den Algorithmus bzw. Programmablauf mit Hilfe eines Flussdiagramms oder eines Struktogramms zu visualisieren. Flussdiagramme und Struktogramme sind Diagramme mit standardisierten Elementen, deren Erstellung durch verschiedene Tools unterstützt wird. Schöne Flussdiagramme können z.B. mit yEd Graph Editor erstellt werden.

1. Erste Schritte mit C

C-Quellcode Header-Datei main-Funktion include define Kommentar Compiler IDE
    Top
Neue Quellcode-Datei anlegen

Ein minimales C-Programm besteht aus einer einzelnen Quellcode-Datei mit der Endung *.c, zum Beispiel myprog.c, die die Anweisungen des Programms sowie Kommentare enthält. Größere C-Programme können noch weitere benutzerdefinierte Header-Dateien (Endung *.h) und Quellcode-Dateien (Endung *.c) enthalten.

Mit Hilfe der #include-Direktiven werden benötigte Programm­bibliotheken über ihre Headerdateien importiert, danach können die darin enthaltenen Funktionen verwendet werden. #define-Direktiven werden ebenfalls noch vor die main an den Anfang des Programms gestellt, mit ihrer Hilfe werden Konstante und sogenannte Makros definiert, z.B. #define PI 3.14159. Wichtig: Direktiven sind keine Anweisungen, sie werden nicht mit Semikolon beendet. Jedes C-Programm enthält genau eine main-Funktion, diese ist der Einstiegspunkt des Programms. Die Anweisungen des Programms werden in die main()-Funktion geschrieben, ggf. auch in weitere selbstdefinierte Funktionen. In C werden Anweisungen mit einem Semikolon beendet, Ausnahme sind if-Anweisungen und Schleifen. Mehrzeilige Kommentare werden mit /* und */ umrahmt, einzeilige Kommentare mit // eingeleitet.

/* myprog.c  */
// TODO: Hier include-Direktiven einfügen
#include <stdio.h>
#include <stdlib.h>
// TODO: Hier define-Direktiven einfügen
#define PI 3.14159
int main(void){
  // TODO: Fügen Sie Ihre Anweisungen hier ein!
  printf("Hallo!\n"); // Ausgabe des Textes Hallo!
}
Programm erstellen und ausführen

Um aus C-Quellcode ein ausführbares Programm zu erstellen, benötigt man einen C-Compiler. Integrierte Entwicklungsumgebungen (engl. Integrated Development Environment, IDE) für C-Programmierung sind z.B. Eclipse IDE for C/C++, NetBeans, Code::Blocks oder Microsofts Visual Studio, diese enthalten einen C-Compiler und unterstützen darüber hinaus die Programmierung mit Syntaxhighlighting, Fehlersuche etc.

Der Screenshot zeigt die Anordnung der Fenster in Visual Studio Community Edition: Toolbar, Projekte, Source Code Editor, Ausgabefenster und Fehlerliste. Alle Entwicklungsumgebungen (IDEs) haben ähnliche Default-Fensterkonfigurationen, die den Entwicklungsprozess unterstützen.

Visual Studio Community Edition

2. Variablen und Konstante

Variable Konstante Datentyp Typumwandlung Operatoren Modulo-Operator sizeof enum
    Top
2-1 Variablen deklarieren

Variable sind benannte Speicherplätze, in denen die Daten des Programms gespeichert werden, z.B. a, b, c, text. Den Wert einer Variablen kann man verändern und überschreiben.

Syntax

Bei der Deklaration einer Variablen wird mit einem Datentyp (int, long long, float, double, char, ...) festgelegt, welche Art von Werten in dieser Variablen gespeichert werden kann. Dem Datentyp kann auch das Schlüsselwort unsigned vorangestellt werden, dies bedeutet, dass vorzeichenlose Zahlenwerte verwendet werden sollen. Deklariert man mehrere Variablen desselben Datentyps, werden sie mit Komma getrennt.

datentyp1 varname1; datentyp2 varname2;
datentyp varname1, varname2;
Beispiel
Variablen deklarieren
int a = 0, b = 0; // ganze Zahlen
unsigned int c = 100; // vorzeichenlose ganze Zahl
float x = 0.0; double y = 0.0; // Fließkommazahlen
char z = 'A'; // Zeichen
char *text = "Hallo!"; // Zeichenkette
Der sizeof -Operator gibt die Größe des Speicherbedarfs einer Variablen / eines Objektes in Bytes an.

Beispiel
sizeof-Operator

In diesem Beispiel finden wir den Speicherverbrauch der Variablen a und x heraus und stellen fest: Variablen des Datentyps int werden mit 4 Byte, Variablen des Datentyps double werden mit 8 Byte gespeichert.

// Ganze Zahl
int a = -100;
// Fließkommazahl mit doppelter Genauigkeit
double y = 0.999;
// groesse_von_a = 4 Byte = 32 Bit 
int groesse_von_a = sizeof(a);
// groesse_von_y = 8 Byte = 64 Bit 
int groesse_von_y = sizeof(y);
printf("%d %d\n", groesse_von_a, groesse_von_y);

Der sizeof -Operator wird z.B. bei der dynamischen Speicherallokation verwendet, in Kombination mit den Funktionen malloc und calloc.

2-2 Konstante deklarieren

Im Unterschied zu Variablen bleibt der Wert einer Konstanten immer gleich. Konstanten kann man mit der Präprozessor-Direktive #define als Makro oder mit dem Schlüsselwort const deklarieren. #define-Makros werden ohne Zuweisung (=) und ohne Semikolon am Ende definiert!

Syntax
#include <stdio.h>
#define name wert // (1) 
int main(void){
    const datentyp name; // (2)
}	
Beispiel
Konstante mit define oder const

Hier wird die Konstante PI einmal als Makro mit #define und einmal mit const deklariert. Ihr Wert kann durch Zuweisung nachher nicht mehr veränder werden.

#define PI 3.14159 // Konstante mit #define
int main(void){
  const double PI2  = 3.14159; // Konstante mit const
  PI  = 3.14; // FEHLER!
  PI2  = 3.141; // FEHLER!
}

In C können auch Aufzählungen verwendet werden, um Listen ganzzahliger Konstanten zu verwalten, dies mit Hilfe des Schlüsselworts enum.

Syntax

Eine Aufzählung wird durch das Schlüsselwort enum eingeleitet, gefolgt von einem selbstdefinierten Aufzählung-Namen und einer Liste von Elementen, die in geschweifte Klammern gesetzt werden. Den Elementen einer Aufzählung werden defaultmäßig ganzzahlige Werte zugewiesen, die bei 0 beginnen, es können jedoch auch eigene Werte vergeben werden.

enum ENUM_NAME {elem_1, ..., elem_n}; // (1)
int main(void){
    enum ENUM_NAME var = elem_i; // (2)
}	
Beispiel
Konstanten mit enum deklarieren

Die Konstante antw hat den Datentyp enum ANTWORT und kann nur einen der festgelegten Werte: ja, nein, vielleicht annehmen.

enum ANTWORT { ja=10, nein=20, vielleicht=15 };
int main(void) {
	enum ANTWORT antw = vielleicht;
	printf("Antwort = %d\n", antw); // Ausgabe: 15
}

2-3 Zuweisung und Typumwandlung

Einer zuvor deklarierten Variablen kann man mit Hilfe des = Zeichens Werte zuweisen. Dabei ist der Datentyp zu beachten, einer int-Variablen weist man ganzzahlige Werte zu, einer float-Variablen Kommazahlen. Hat der zugewiesene Wert einen anderen Datentyp, wird er implizit in den Datentyp der Variablen umgewandelt. Typumwandlung kann auch explizit erfolgen, indem man den Namen eines Datentyps vor den Namen des zugewiesenen Wertes schreibt. Die Zuweisung double x = (double)10; bewirkt, dass 10 als Fließkommazahl mit doppelter Genauigkeit gespeichert wird.

Syntax
variablen_name = variablen_wert;
variablen_name = (datentyp)variablen_wert;
Beispiel
Zuweisung
int a = 0; double b = 0.0; char z = ' ';
// Zuweisung des Wertes 10 an die Variable a
a = 10; 
// Zuweisung des Wertes 20.5 an die Variable b
b = 20.5; 
// Zuweisung des Zeichens a an die Variable z
z = 'a'; 
// Nachkommastellen werden abgeschnitten, a = 3
a = 3.99; 
Beispiel
Typumwandlung
int a = 10; float b = 1.2345; 
double c = 1.9;
// Implizite Typumwandlung a = 10
a = 10.2; 
// Explizite Typumwandlung, a = 1
a = (int)b; 
// Explizite Typumwandlung, a = 1
a = (int)c; 
// Explizite Typumwandlung, b = 20.00(...) 
b = (double)20; 
2-4 Berechnungen

Berechnungen werden durchgeführt, indem Variablen über Operatoren (+, -, *, /, ...) zu Ausdrücken verknüpft werden. C verfügt über Operatoren für abkürzende Zuweisung (+=, *= etc.), Inkrementierung und Dekrementierung (++, --), Vergleichsoperatoren, deren Ergebnis WAHR oder FALSCH ist (==, !=, >, <, >=, <= ) und logische Operatoren (&&, ||, !), deren Ergebnis WAHR oder FALSCH ist.
Ein wichtiger Operator ist der Modulo-Operator %: a % b gibt den Rest der Ganzzahl-Division a / b zurück. Die Auswertung eines Ausdrucks erfolgt entsprechend einer festgelegten Priorität der Operatoren, die durch Klammern beeinflusst werden kann: a * b + c ist wie (a * b) + c, jedoch anders als a * (b + c).

Beispiel
Operatoren in C
int a = 25, b = 3, c = 0;
// Inkrementierung - erhöhe Wert um 1
a += 1; // a = 26	
// Abkürzende Zuweisung
b *= 2; // b = 6
// Modulo-Operator: Rest der Teilung
c = 17 % 5; // c = 2
// Fließkommadivision: Typumwandlung erforderlich!
double res1 = (double) a / b; // res1 = 4.33 
// Ganzzahldivision
int res2 = a / b; // res2 = 4
// Ausgabe: 26, 6, 4.33, 4
printf("%d, %d, %.2f, %d\n", a, b, res1, res2);
// Vergleichs-Operatoren: ==, !=, <, >, <=, >= 
int cond1 = (a == (b + 20)); // cond1 = 1, d.h. wahr
int cond2 = (a != b); // cond2 = 1 : 26 ist ungleich 6
// Logische Operatoren: &&, ||, !
int cond = cond1 && cond2; // cond = 0
printf("%d, %d, %d\n", cond1, cond2, cond); // Ausgabe: 1, 0, 0
Beispiel
Berechne Flächeninhalt des Dreiecks mit Seiten a, b, c
#include<math.h>
int main(void){
   double a = 10, b = 20, c = 20;
   double s = (a + b + c ) / 2;
   double F = sqrt(s * (s-a) * (s-b) * (s-c));
}

3. Ausgabe und Eingabe

printf scanf Formatzeichen %d, %f, %lf Steuerzeichen
    Top
3-1 Die printf-Funktion

In C erfolgt die Ausgabe auf die Konsole mit Hilfe der printf -Funktion. Die Werte von Variablen können mit Hilfe von Formatzeichen in eine formatierende Zeichenkette eingefügt werden. Zu jedem Datentyp gehören passende Formatzeichen, z.B.: %d oder %i für int (ganze Zahlen), %f für float (Fließkommazahlen mit einfacher Genauigkeit), %lf für double (Fließkommazahlen mit doppelter Genauigkeit). Weitere Steuerzeichen für die Erzeugung einer formatierten Ausgabe sind: \n - erzeugt einen Zeilenumbruch, \t - erzeugt einen Tabulator. Um die Steuerzeichen selber auszugeben, verwendet man \\ - gibt den Rückschrägstrich aus, %% - gibt ein Prozentzeichen aus, \" - gibt Anführungsstriche aus. In der printf-Funktion können lange Zeichenkette mit Hilfe eines einzelnen Rückschrägstrichs auf mehrere Zeilen verteilt werden.

Syntax

Ausgabe einer Zeichenkette

printf(zeichenkette);

Formatierte Ausgabe mit Platzhaltern für Variablen

printf(formatierende_zeichenkette, variablen_liste);
Beispiel
Formatierte Ausgaben
int a = 0; double b = 3.33; 
char z = 'a';
printf("====================\n");
printf("a = %d, b = %5.2f\n", a, b, z); 
printf("Ihr Zeichen:\n%c\n", z); 
char *text1 = "Eins", *text2 = "Zwei";
printf("Ihre Texte:\n%14s%14s\n", text1, text2);
3-2 Die scanf-Funktion

In C werden Werte von der Konsole mit Hilfe der Funktion scanf eingelesen. Die Funktion scanf erhält als ersten Parameter eine formatierende Zeichenkette, z.B. "%d %lf", gefolgt von einer Liste von Variablen, deren Anzahl und Datentyp zu den Formatbeschreibern passen muss. Jeder Variablen muss ein &-Zeichen vorangestellt werden, das die Adresse der Variablen bezeichnet. In Visual Studio wird scanf_s anstelle von scanf verwendet!

Das Einlesen von Zeichenketten ist mit scanf zwar möglich, jedoch sollte man dafür besser die Funktion fgets verwenden, wie im Beispiel unten.

Syntax
scanf("formatzeichen", &variablenname);
// In Visual Studio:
scanf_s("formatzeichen", &variablenname); 
Beispiel

Hier werden drei Variablen eingelesen. Vor jedem Einlesen wird eine Eingabe-Aufforderung für den Anwender ausgegeben. Wichtig: in scanf sollten die Formatzeichen %d, %f, %lf, %c ohne weitere Texte oder Steuerzeichen verwendet werden.

int a = 0; double b = 0.0; char z = ' ';
printf("Eingabe a: ");  scanf("%d", &a);
printf("Eingabe b: ");  scanf("%lf", &b);
printf("Eingabe z: ");  scanf("%c", &z);
// Eingabe eines Zeichens in Visual Studio
printf("Eingabe z: ");scanf_s("%c", &z, 1);
printf("Ihre Eingaben sind: %d %lf %c\n", a, b, z);
// Eingabe einer Zeichenkette mit fgets
char text[100];
printf("Text eingeben: ");
// Maximal 20 Zeichen von der Standardeingabe lesen
fgets(text, 20, stdin);

4. Verzweigungen und Fallunterscheidungen

if else else if Vergleichs-Operatoren (==, !=, ...) Logik-Operatoren (&&, ||, !) switch-case
    Top
4-1 if-else-Anweisung

Eine Verzweigung ist eine Anweisung für die Ablaufsteuerung, die festlegt, welcher von zwei (oder mehr) Anweisungsblöcken, abhängig von einer (oder mehreren) Bedingungen, ausgeführt wird. Sie wird in C, wie in fast allen Programmiersprachen, mit der if- bzw. if-else-Anweisung abgebildet. Die Bedingungen werden mit Hilfe von Vergleichs-Operatoren (==, !=, >, <, >=, <=) und logischen Operatoren (&&, ||, !) formuliert. Um z.B. zu überprüfen, ob ein Jahr ein Schaltjahr ist, lautet die Bedingung: ((jahr % 4 == 0) && !(jahr % 100 == 0)) || (jahr % 400 == 0). Damit wird überprüft, ob jahr teilbar durch 4 ist und nicht teilbar durch 100, oder teilbar durch 400.

Syntax
if-else-Anweisung

Die Wirkung der if-else-Anweisung ist wie folgt:
* Wenn ( if ) die Bedingung bedingung_1 wahr ist, werden die Anweisungen anweisungen_1 ausgeführt,
* sonst wenn ( else if ) die Bedingung bedingung_2 wahr ist, werden die Anweisungen anweisungen_2 ausgeführt,
* sonst ( else ) der Anweisungsblock anweisungen_default.

Es kann keinen, einen oder mehrere else-if-Teile geben. Der optionale else-Zweig greift dann, wenn es keine Übereinstimmung gibt. Die geschweiften Klammern werden dann benötigt, wenn es mehrere Anweisungen in einem Block gibt.

if (bedingung_1) {
    anweisungen_1 
}
else if (bedingung_2){
    anweisungen_2
}
else {
    anweisungen_default
}
Beispiel
Berechne Zinssatz abhängig von Betrag

Für betrag = 20000 wird der Zinssatz 2.0 berechnet: Die erste Bedingung 20000 > 50000 wird als FALSCH ausgewertet, also wird auch die nächste Bedingung 20000 > 10000 geprüft, diese nun als WAHR ausgewertet. Die dazu gehörende Anweisung in Zeile 5 wird ausgeführt und danach die if-Anweisung verlassen, d.h. der Code in Zeile 11 ausgeführt.

float betrag = 10000.0, zinssatz = 0.0;
if (betrag > 50000) {
  zinssatz = 3.0;
} else if (betrag > 10000) {
  zinssatz = 2.0;
} else if (betrag > 0) { 
  zinssatz = 1.0;
} else {
  zinssatz = -0.2;
}
printf("Fertig!\n");
4-2 switch-case-Anweisung

Die switch-case -Anweisung ist eine spezielle bedingte Verzweigung, die verwendet wird, wenn es viele Fallunterscheidungen gibt.

Syntax
switch-case-Anweisung

Ein Ausdruck bzw. der Wert einer Variablen wird mit verschiedenen Werten verglichen. Bei Übereinstimmung wird die entsprechende Anweisung ausgeführt. Die break-Anweisung bewirkt, dass der case-Zweig sofort verlassen wird. Wenn break in einem Zweig weggelassen wird, werden auch die folgenden case-Zweige ausgeführt, bis ein break gefunden wird, oder die switch-Anweisung zu Ende ist. Der optionale default-Zweig greift dann, wenn es keine Übereinstimmung gibt.

switch(ausdr){
    case ausdr_1: 
      anweisungen_1
      break;
    case ausdr_2: 
      anweisungen_2
      break;
    ... 
    default:
      anweisungen_default
}
Beispiel
Ausgabe abhängig von Wahl
int wahl;
printf("Eine der Zahlen 1, 2, 3 eingeben: ");
scanf("%d",&wahl);
switch(wahl){
  case 1: 
	printf("Erste Wahl\n");break; 
  case 2: 
	printf("Zweite Wahl\n");break; 
  case 3:	
    printf("Dritte Wahl\n");break;
  case 4: case 5:
    printf("Vierte oder fuenfte Wahl\n");
    break; 
  default:
	printf("Ungueltige Eingabe!\n");
}

Die switch-case-Anweisung wird dann eingesetzt, wenn anhand der Werte, die eine Variable annehmen kann, viele unterschiedliche Fallunterscheidungen abgebildet werden. Beispiel: Wenn "A": Hilfe anzeigen, wenn "B": Tabelle ausgeben, wen "C": [...], wenn "Z": Programm beenden.

5. Schleifen

while do-while for break continue
    Top

C hat drei Arten von Schleifen, die sich darin unterscheiden, wo die Schleifenbedingung geprüft wird: die while-Schleife verwendet eine Ausführungsbedingung, die vor dem Ausführen des Schleifenrumpfs geprüft wird, die do while-Schleife funktioniert über eine Abbruchbedingung, die nach dem Ausführen des Schleifenrumpfs geprüft wird, und die for-Schleife verwendet eine Zählvariable, für die Startwert, Endwert und Schrittweite festgelegt werden. Eine Schleife kann mit break sofort verlassen werden, einzelne Schleifenschritte können mit continue übersprungen werden.

5-1 while- und do-while-Schleife

Eine while-Schleife ermöglicht es, Anweisungen wiederholt auszuführen, und zwar so lange, wie eine Ausführungsbedingung erfüllt ist. Dabei wird die Variable, die in der Bedingung abgefragt wird, nicht automatisch heraufgesetzt, sondern muss im Schleifenrumpf explizit inkrementiert werden. Die do-while-Schleife funktioniert ähnlich, allerdings wird die Bedingung ans Ende gestellt, d.h. die Anweisungen werden auf jeden Fall mindestens einmal durchgeführt.

Syntax
while (bedingung){
  anweisungen
}
do {
  anweisungen
} while (bedingung);
Beispiel
Berechne Summe 1 + 2 + ... + 5
double sum = 0.0; int i = 1;
while (i <= 5) {
  sum += i;
  i += 1;
}
printf("Summe: %.2f\n", sum);
sum = 0.0; i = 1;
do {
  sum += i;
  i += 1;
} while (i <= 5);
printf("Summe: %.2f\n", sum);
5-2 for-Schleife

Eine for-Schleife ist eine Zählschleife, die für eine Zählvariable eine Start- und Endbedingung sowie eine Schrittweite festlegt. Die Anweisungen werden für eine vorgegebene Anzahl an Schleifen-Durchläufen wiederholt. Die Angabe der Anzahl wird über die Zählvariable umgesetzt, die automatisch nach jedem Schleifendurchlauf um 1 (bzw. eine andere Schrittweite) erhöht wird.

Syntax
for (count = start;count <= end;count += schritt){
  anweisungen 
}
Beispiel
Berechne Summe 1 + 2 + ... + 5
double sum = 0.0;
for(int i=1;i<=5;i++){
  sum += i;
}
printf("Summe:\n");
printf("%.2f\n", sum);

Im folgenden Beispiel wird der Befehl break verwendet, um eine Schleife bei Erfüllung einer Bedingung zu verlassen.

Beispiel
Finde ungeraden Teiler von n
int n = 20;
for (int i = 3; i <= n / 2; i += 2) {
  if (n % i == 0) {
    printf("Ungerader Teiler gefunden: %d !", i);
    break;
  }
}

6 Funktionen

Parameter Referenzparameter Rückgabewert void return Geltungsbereich von Variablen
    Top

Eine Funktion ist ein benannter Codeblock, der nur ausgeführt wird, wenn er in einer anderen Funktion verwendet wird, dies nennt man den Funktionsaufruf. Funktionen werden einmal definiert und können dann beliebig oft aufgerufen werden.

Man kann Daten oder sogenannte Parameter an eine Funktion übergeben, entweder als Wert oder als Referenz. Referenzparameter werden als Zeigervariablen bzw. Adressen übergeben, sie haben die Besonderheit, dass ihr Wert in der aufrufenden Funktion weiterverwendet werden kann. Eine Funktion kann auch einen Rückgabewert haben, d.h. einen Wert zurückgeben, der in der aufrufenden Funktion weiterverwendet werden kann.

Sobald man ein C-Programm mit selbstdefinierten Funktionen strukturiert, muss man den Geltungsbereich von Variablen berücksichtigen: Lokale Variablen sind innerhalb einer Funktion definiert und können nur dort verwendet werden. Globale Variablen sind außerhalb einer Funktion definiert und können überall im Programm verwendet werden, also auch innerhalb jeder Funktion.

6-1 Funktionen ohne Rückgabewert

Eine Funktion ohne Rückgabewert bzw. mit Rückgabetyp "void" ist eine benannte Gruppierung von Anweisungen. Innerhalb der Funktion können neue Werte berechnet und direkt ausgegeben werden.

Syntax

Die Platzhalter typ_i stehen für Datentypen, param_i sind Parameter­namen, und arg_i die tatsächlichen Argumente. Anzahl, Reihenfolge und Datentyp muss bei den Parametern und den tatsächlichen Argumenten übereinstimmen.

/* (2) Funktionsprototyp */
void myfunc(typ_1, typ_2, ... typ_n);
int main(void){
/* (3) Funktionsaufruf */
  myfunc(arg_1, arg_2, ... arg_n);
}

/* (1) Funktionsdefinition */
void myfunc(typ_1 param_1, ... typ_n param_n){
  // Funktionsrumpf mit Anweisungen 
  // . . . 
}
Beispiel
Funktion trennzeile()
Diese Funktion gibt einfach eine Trennzeile aus, die aus x Sternchen besteht.
#include <stdio.h>
void trennzeile();
int main(void){
  // Erster Funktionsaufruf
  trennzeile(); 
  printf("Hello from K-Town!\n");
  // Zweiter Funktionsaufruf  
  trennzeile();  
}
/* Funktionsdefinition */
void trennzeile(void){
	printf("****************\n");
}
Beispiel
Formatierungsfunktion ausgabeInEuro()
#include <stdio.h>
void ausgabeInEuro(double);
int main(void){
  double x = 49.99;
  // Erster Funktionsaufruf
  ausgabeInEuro(x); 
  // Zweiter Funktionsaufruf  
  ausgabeInEuro(2.5);  
}
/* Funktionsdefinition */
void ausgabeInEuro(double betrag){
	printf("Betrag: %.2f Euro\n", betrag);
}
Beispiel
Funktion mit Referenzparametern

Der Referenzparameter m speichert den Mittelwert der Wert-Parameter a und b und soll in der main-Funktion weiter verwendet werden. Beachte: m wird als Zeigervariable übergeben, daher *m in Zeile 1 und 2 und &m in Zeile 6.

#include <stdio.h>
void mittelwert(double a, double b, double *m){
    *m = (a + b ) / 2;
}
int main(void){
    double x = 2.0,y = 4.0, m = 0;
    mittelwert(x, y, &m);
    printf("Mittelwert= %.2lf\n", m);
}
6-2 Funktionen mit Rückgabewert

Eine Funktion kann auch Daten/Parameter als Rückgabewert zurückgeben, diese können in der aufrufenden Funktion in Berechnungen oder Ausgaben weiter verwendet werden. Damit eine Funktion einen Wert zurückgeben kann, benutzt man das Schlüsselwort "return".

Syntax

Der Parameter r_typ bezeichnet den Datentyp des Rückgabewertes. Wichtig: Eine Funktion kann nur einen Rückgabewert haben!

/* (2) Funktionsprototyp */
r_typ myfunc(typ_1, typ_2, ... typ_n);
int main(void){
  /* (3) Funktionsaufrufe */
  r_wert_a = myfunc(arg_1a,... arg_na);
  r_wert_b = myfunc(arg_1b,... arg_nb);
}
/* (1) Funktionsdefinition */
r_typ myfunc(typ_1 param_1, ... typ_n param_n){
  r_typ r_wert;
  // Anweisungen, die den r_wert berechnen
  // Rückgabewert mit return zurückgeben
  return r_wert;
  }
Beispiel
Funktion brutto_aus_netto()

Die Funktion brutto_aus_netto() berechnet für einen gegebenen Netto-Betrag und eine gegebene Mehrwertsteuer den Bruttobetrag. Sie hat zwei Parameter, netto (Datentyp double) und mwst (Datentyp int) und einen Rückgabewert vom Datentyp double.

double brutto_aus_netto(double, int);
int main(void){
  // Erster Funktionsaufruf
  double betrag = 1000.95; int mwst = 19;
  printf("Brutto = %.2f\n",   
         brutto_aus_netto(betrag, mwst) );  
  // Zweiter Funktionsaufruf
  printf("Brutto = %.2f\n", 
         brutto_aus_netto(1234.55, 19) ); 
}
/* Funktionsdefinition */
double brutto_aus_netto(double netto, int mwst){
  double brutto = netto + netto * mwst / 100;
  return brutto;
}

Abgleich Argumente vs. Parameter
Die Argumente im Funktionsaufruf müssen in Anzahl und Datentyp mit den Parametern in der Definition übereinstimmen, jedoch nicht im Namen! D.h. der Name des übergebenen Argumentes (hier: betrag) in der aufrufenden Funktion muss nicht mit dem Namen des Parameters (hier: netto) übereinstimmen.

Werte zurückgeben: mit Rückgabewert oder mit Referenzparametern
Falls eine Funktion neue Werte berechnet, die in der aufrufenden Funktion weiterverwendet werden sollen, kann dies entweder durch Verwendung des Rückgabewertes geschehen, oder durch Verwendung von Referenzparametern. Wann sollte man bei Funktionen also den Rückgabewert verwenden, und wann Referenzparameter? Falls die Funktion nur einen neuen Wert berechnet, ist die Verwendung des Rückgabewertes besser. Falls die Funktion jedoch mehrere neue Werte berechnen soll, müssen Referenzparameter verwendet werden. Die Funktion void mittelwert(double a, double b, double *m) kann demnach als Funktion mit Rückgabewert umformulieren werden:

Beispiel
Funktion mit Rückgabewert
#include <stdio.h>
double mittelwert(double a, double b){
    return (a + b ) / 2;
}
int main(void){
    double x = 2.0,y = 4.0, m = 0;
    m = mittelwert(x, y);
    printf("Mittelwert= %.2lf\n", m);
}

7 Arrays

Element Dimension Index Zufallszahlen rand
    Top

Ein Array ist eine statische Datenstruktur, die Elemente desselben Datentyps in einem zusammenhängenden Speicherbereich ablegt. "Statische Datenstruktur" bedeutet, dass die Größe des Speicherbereichs vorab fest reserviert ist und nicht mehr verändert werden kann. Ein Element eines Arrays ist ein einzelnes Datenobjekt, dessen Position im Array über sogenannte Indizes festgelegt wird, die bei 0 anfangen. Die Dimension eines Arrays ist die Anzahl der Indizes, die benötigt werden, um ein Element eines Arrays auszuwählen. Ein eindimensionales Array entspricht einer Liste und benötigt nur einen Index, ein zweidimensionales Array entspricht einer Matrix oder Tabelle und benötigt zwei Indizes.

Eindimensionale Arrays

Eindimensionale Arrays werden verwendet, um Listen zu speichern. Die maximale Größe N des Arrays, d.h. die Anzahl an Elementen, die darin gespeichert werden können, muss zuvor als Konstante definiert werden, entweder als Zahl, oder mittels define, oder mittels const int. Die Zuweisung von Werten an die Elemente eines Arrays erfolgt entweder direkt durch Auflistung der Elemente, oder über Schleifen.

Syntax

dtyp steht für Datentyp, arr steht für den Namen des Arrays.

#define N 10
int main(void){
  // Alternativ: const int N = 10;
  dtyp arr[N];
  // Dekl. und Initialisierung aller Elemente mit 0
  dtyp arr[N] = {0};
  // Deklaration durch Aufzählen der Elemente
  dtyp arr[] = {el1, el2, ..., eln};
}
Beispiel
1D-Arrays deklarieren und verwenden
#define N 11
int main(void){
   // Explizite Zuweisung durch Auflisten
   float x1[] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5};
   // Deklaration und Initialisierung mit 0
   float x2[N] = {0};
   // Zuweisung mittels for-Schleife
   for(int i=0;i<5;i++) 
     x2[i] = 0.1*i;
}
Beispiel
1D-Array als Funktionsparameter

Sobald man mit Arrays arbeitet, bietet sich die Verwendung selbstdefinierter Funktionen an, z.B. für die formatierte Ein- und Ausgabe, für die Berechnung von Summen, statistischer Kennzahlen wie Mittelwert, Standardabweichung und viele mehr. Dabei muss die korrekte Verwendung des Arrays als Funktionsparameter beachtet werden. Bei der Funktionsdefinition schreibt man den Datentyp und Namen des Arrays, gefolgt von eckigen Klammern, beim Funktionsaufruf nur den Namen des Arrays, ohne eckige Klammern. Die Beispiel-Funktion print_array() gibt die ersten n Elemente des Arrays formatiert aus:
Funktionsdefinition: void print_array(float x[], int n){...}
Funktionsaufrufe: print_array(x1, n), print_array(x2, 4) etc.

#include <stdio.h>
void print_array(float[], int);
float mittelwert(float[], int);
int main(void) {
  float x1[] = {10, 20, 30, 40, 50};
  print_array(x1, 3); // Ausgabe: 10.0, 20.0, 30.0
  // Mittelwert der ersten 3 Elemente ist 15.0
  printf("Mittelwert = %.1f\n", mittelwert(x1, 3));
}
float mittelwert(float x[], int n){
  float mwert = 0.0;
  for(int i=0;i<n;i++)
    mwert += x[i];
  return mwert/n;
}
void print_array(float x[], int n) {
  for (int i = 0; i < n; i++)		
    printf("%.1f, ", x[i]);
}
Beispiel
1D-Array mit Zufallszahlen befüllen

In C können ganzzahlige Pseudo-Zufallszahlen im Wertebereich [0, RANDMAX] mit Hilfe der Funktion rand() erstellt werden, der Zufallsgenerator wird mittels der Funktion srand() initialisiert. Im folgenden Beispiel werden Zufallszahlen im Wertebereich [ug, og] auf zwei Arten erstellt: (1) durch Re-Skalierung des Wertebereichs und (2) durch Verwendung des Modulo-Operators.

#include <stdio.h>
#include <stdlib.h>
#define N 10
void print_array(float[], int);
int main(void) {
  float a[N] = {0}, b[N] = {0}; int n = 0;
  int ug = 10, og = 25; // Untere und obere Grenze
  printf("Anz. Elemente: "); scanf("%d", &n);
  srand(1); // Initialisiere Zufallsgenerator 
  for (int i = 0; i < n; i++){ 
    a[i] = ug + (og-ug)*rand()/RAND_MAX; // (1)
    b[i] = ug + (rand()%(og-ug+1); // (2) 
  }
  print_array(a, n); print_array(b, n);
}
// TODO: Hier die Funktionsdefinition einfügen
Zweidimensionale Arrays

Zweidimensionale Arrays werden verwendet, um Tabellen und Matrizen zu speichern. Sie werden deklariert, indem nach dem Arraynamen in eckigen Klammern zuerst die maximale Zeilenanzahl M und danach die maximale Spaltenanzahl N als Konstante angegeben werden. Die Zuweisung der Elemente eines 2D-Arrays kann entweder explizit über Aufzählung erfolgen, oder mittels Schleifen.

Syntax

dtyp steht für Datentyp, arr steht für den Namen des Arrays.

#define M 20 // max. Anzahl Zeilen
#define N 10 // max. Anzahl Spalten
int main(void){
  dtyp arr[M][N];
  dtyp arr[M][N] = {0};
  dtyp arr[][N] = {{el11, el12, el13, ...}, 
                   {el21, el22, el23, ...}, 
                    ..., // weitere Zeilen 
                   };
}
Beispiel
2D-Arrays deklarieren und verwenden

In diesem Beispiel wird die 3x3 Einheitsmatrix zuerst durch explizite Aufzählung und danach mittels for-Schleifen deklariert. Ein zweidimensionales Array wird zeilenweise befüllt bzw. ausgelesen, dafür benötigt man zwei for-Schleifen: Die äußere Schleife mit Zählvariable zIdx iteriert über die Zeilen, die innere Schleife mit Zählvariable sIdx über die Spalten.

#include <stdio.h>	 
#define M 10 // max. Anzahl Zeilen
#define N 10 // max. Anzahl Spalten
int main(){
  float mat[][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
  float tab[M][N] = {0.0};
  for(int zIdx=0;zIdx<3;zIdx++){
    for(int sIdx=0;sIdx<3;sIdx++){
      if (zIdx == sIdx)
        tab[sIdx][sIdx] = 1;
  }}
}
Beispiel
2D-Array als Funktionsparameter

Wie bei 1D-Arrays muss die korrekte Verwendung des 2D-Arrays als Funktionsparameter beachtet werden: Bei der Funktionsdefinition schreibt man den Namen des Arrays, gefolgt von zwei eckigen Klammern, die erste, für die Zeilendimension, bleibt leer, in die zweite muss die maximale Anzahl der Spalten (hier: N) angegeben werden. Beim Funktionsaufruf wird der Namen des Arrays ohne eckige Klammern angegeben. Die Beispiel-Funktion print_array2d() wird für die formatierte Ausgabe von 2D-Arrays des Datentyps float verwendet, und zwar können jeweils die ersten anzZ Zeilen und anzS Spalten ausgegeben werden.

#include <stdio.h>
#define N 10 // Maximale Anzahl Zeilen
void print_array2d(float arr[][N],int anzZ,int anzS){
  for (int i = 0; i < anzZ; i++) {
    for (int j = 0; j < anzS; j++) {
      printf("%5.1f ", arr[i][j]);
    }
    printf("\n"); // Zeilenumbruch nach Zeile i
  }
  printf("\n"); 
}
int main(void) {
  float arr[][N] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; 
  // Ausgabe: 2 Zeilen und 2 Spalten
  print_array2d(arr, 2, 2);
  // Ausgabe: 2 Zeilen und 3 Spalten
  print_array2d(arr, 3, 3);
}
Beispiel
2D-Array mit Zufallszahlen befüllen

Um selbstdefinierte Funktionen für mathematisch-statistische Kennzahlen zu testen, ist es auch bei 2D-Arrays nützlich, Test-Arrays mit Zufallszahlen zu befüllen. Dies geschieht wieder mit Hilfe der Funktionen srand() und rand() der C-Standardbibliothek stdlib.h.

#include <stdio.h>	 
#include <stdlib.h>
#define N 10 // Max. Anzahl Zeilen / Spalten
void print_array2d(float arr[][N],int anzZ,int anzS){
int main(void) {
 float arr[N][N]; int anzZ = 3, anzS = 4;
 for(int i=0;i<anzZ;i++){
	 for(int j=0;j<anzS;j++){
	   arr[i][j] = 100*rand()/RAND_MAX; 
	 }
  }
  print_array2d(arr, anzZ, anzS);
}
// TODO: Hier die Funktionsdefinition einfügen

8 Adressen und Zeiger

Adresse Zeiger Referenzparameter Zeichenketten Dynamische Speicherallokation malloc calloc
    Top

Bei der Deklaration einer Variablen n legt der Compiler einen Speicherplatz der Größe x (1, 4, oder 8) Byte an und vergibt dabei eine Adresse (z.B. 0x46fc80), unter der die Variable wiedergefunden werden kann. Ein Zeiger / Pointer ist eine Referenzvariable, die die Adresse einer anderen Variablen speichern kann und z.B.für dynamische Speicherallokation, Textbearbeitung oder Referenzparameter in Funktionen verwendet wird.
Zeigerarithmetik: Die Operationen, die für "normale" Variablen definiert sind, sind für Zeiger nicht definiert, mit Ausnahme einer speziellen Zeigerarithmetik. Man kann Zeigervariablen inkrementieren / dekrementieren und allgemeiner ganzzahlige Werte addieren / subtrahieren und damit einen Speicherbereich durchlaufen. Weiterhin kann man Zeigervariablen auch vergleichen, d.h. (p1==p2) oder (p1<p2) sind gültige Ausdrücke.

Zeiger verwenden

Um einen Zeiger / Pointer zu definieren, wird dem Namen der Variablen ein Stern * vorangestellt, z.B. int *z; für einen Zeiger, der auf int-Variablen zeigen soll. Alternativ wird auch die Schreibweise int* z; verwendet, diese sagt aus, dass z den Datentyp (int*) hat, also Zeiger auf int. Zeiger müssen denselben Datentyp haben wie die Variablen, auf die sie zeigen.
Um einem Pointer die Adresse einer anderen Variablen zuzuweisen, wird der Adress-Operator & verwendet. Um über den Zeiger auf den Inhalt der referenzierten Variablen zuzugreifen, wird der Inhalts-Operator * verwendet, wie in Zeile 3: *z = 20; bedeutet, dass indirekt über den Zeiger der Wert von a geändert wird.

Syntax
// Variable deklarieren
datentyp var;
// Pointer deklarieren
datentyp *pointer;
// Pointer = Adresse
pointer = &var;
// Zeiger zeigt auf den nächsten Speicherplatz
pointer++; 
Beispiel

Wert der Variablen a wird mittels Pointer z geändert.

int a = 10, *z = NULL;   
z = &a; // z zeigt auf a
*z = 20; // Wert von a wird auf 20 gesetzt
printf("Wert von a = %d\n",*z);

Array arr wird mittels Pointer p durchlaufen.

double *p = NULL; // p mit NULL initialisieren
double arr[] = {1.2, 2.3, 3.4};
p = &arr[0]; // p zeigt auf den Anfang von arr
p++; // Zeiger p inkrementieren
p zeigt jetzt auf arr[1]
printf("%.2lf\n",*p); // Ausgabe: 2.3

Der NULL-Zeiger ist ein spezieller Zeiger, der für die Zeigerinitialisierung verwendet und im Fehlerfall zurückgegeben wird, wenn z.B. kein Speicher reserviert werden kann.

Anwendungsgebiete von Zeigern

Zeiger haben drei wichtige Anwendungsbereiche:
 (1) Arrays und Zeiger können in vielen Ausdrücken austauschbar verwendet werden, dies wird vor allem bei Zeichenketten verwendet.
 (2) Zeiger als Funktionsparameter ermöglichen Parameterübergabe als Referenz.
 (3) Zeiger ermöglichen dynamische Speicherverwaltung. Mit Hilfe der Funktionen malloc, calloc und free kann Speicher im Heap-Bereich allokiert und freigegeben werden.

Weiterhin können mit Hilfe von Zeigern dynamische Datenstrukturen wie z.B. verkettete Listen, Stacks und Queues umgesetzt werden.

Dynamische Speicherallokation bedeutet, dass die Größe des reservierten Speicherbereichs verändert und freigegeben werden kann. Die C-Standardbibliothek <stdlib.h> stellt für die dynamische Speicherallokation die Funktionen malloc, calloc(), realloc() und free() zur Verfügung, mit denen man einen Speicherbereich für eine Anzahl von Daten desselben Datentyps reservieren bzw. freigeben kann.

Beispiel
Berechne Länge einer Zeichenkette

Verwendete Funktionen: sizeof, malloc, calloc, fgets
Bei der Verwendung von calloc() wird der Speicher mit 0 initialisiert.

// Dynamische Speicherallokation
char *s = (char *)malloc(20*sizeof(char));
// Alternativ mit calloc(): 
// char *s = (char *)calloc(20, sizeof(char));

// falls kein Speicher reserviert werden kann
if (s == NULL)
	return; // beende das Programm
fgets(s, 20, stdin); // Zeichenkette einlesen
int length = 0;
while(*s != '\0'){ // Solange das Endzeichen nicht erreicht wurde
  length++; // wird die Länge hochgezählt
  s++; // und die Zeigervariable inkrementiert
}

Für die Bestimmung der Länge einer Zeichenkette kann die Funktion strlen() der Standardbibliothek string.h verwendet werden.

9 Strukturen

struct typedef
    Top

Eine Struktur ist eine Zusammenfassung mehrerer zusammengehöriger Variablen verschiedenen Datentyps unter einem gemeinsamen Namen. Strukturen werden verwendet, um komplexe Objekte der Realität abzubilden, die durch zusammen­gesetzte Informationen beschrieben werden. Durch die Deklaration einer Struktur wird zunächst ein neuer Datentyp mit benutzerdefiniertem Namen festgelegt, anschließend kann man neue Variablen deklarieren, die genau diesen Datentyp haben.

Strukturen verwenden

Eine Struktur wird definiert, indem man nach dem Schlüsselwort struct den Namen der Struktur angibt, gefolgt von geschweiften Klammern, in die man die zugehörigen Variablen-Deklarationen setzt. Eine Struktur wird verwendet, indem man neue Variablen deklariert, die den Datentyp struct struct_name haben.

Syntax
struct struct_name {
    datentyp1 var_1;
    ...
    datentyp_n var_n;
}
struct struct_name s1, s2;
Beispiel
Struktur für Studenten

Die Struktur struct Student fasst die Variablen zusammen, die die Attribute eines Studenten speichern können.

#include <stdio.h>
int main(void){
  struct Student {
    char* name;
    char* vorname;
    int matnr;
  };
  struct Student st = { "Muster", "Max", 12345 };
  printf("%s %s, %d\n", st.name, st.vorname, st.matnr);
  st.name = "Test"; st.vorname = "Anna"; st.matnr = 12346; 
  printf("%s %s, %d\n", st.name, st.vorname, st.matnr);
}
Strukturen mit typedef

In C besteht die Möglichkeit, mit Hilfe des Schlüsselwortes typedef symbolische Namen für Datentypen zu definieren und dadurch das Programm verständlicher zu gestalten. Die Verwendung von typedef ist in Kombination mit Strukturen besonders nützlich, da sonst das Schlüsselwort struct stets mit angegeben werden muss.

Beispiel

In diesem Beispiel wird mit typedef struct Student STUDENT; festgelegt, dass man bei späteren Deklarationen anstelle von struct Student einfach nur STUDENT schreiben kann. D.h. um Variablen vom Datentyp STUDENT zu deklarieren, schreibt man STUDENT st1, st2;

#include <stdio.h>
struct Student {
	char* name;
	char* vorname;
	int matnr;
};
typedef struct Student STUDENT;

int main(void) {
  STUDENT st1, st2;
  st1.name = "Muster"; st1.vorname = "Max"; 
  st1.matnr = 12345;
  printf("%s %s, %d\n", 
    st1.name, st1.vorname, st1.matnr);
  st2.name = "Test"; st2.vorname = "Anna"; 
  st2.matnr = 12346;
  printf("%s %s, %d\n", 
    st2.name, st2.vorname, st2.matnr);
}

10 Standardbibliotheken

    Top

C verfügt über eine überschaubare Anzahl von Standardbibliotheken mit vordefinierten Funktionen, die über ihre Header-Dateien eingebunden werden: stdio.h, stdlib.h, math.h, string.h, time.h, limits.h, float.h [...]. Eine Auswahl häufig benötigter Standardbibliotheken mit beispielhafter Verwendung finden Sie hier zusammengestellt.

Eingabe- und Ausgabe: stdio.h

printf scanf fgets fopen fclose fprintf fscanf
Die Funktionen für Ein- und Ausgabe werden über die stdio.h inkludiert. Ein- und Ausgabe erfolgt über Streams, ein Stream kann die Konsole, eine Datei oder eine Zeichenkette sein. Hier findet man Funktionen für die Eingabe von der Konsole (scanf, fgets, getchar) und in Dateien (fscanf, fread), für die Ausgabe auf die Konsole (printf, putchar), in Dateien (fprintf, fwrite) und Strings (sprintf), für Fehlermeldungen [...].
char text[100];
printf("Text eingeben: ");
fgets(text, 20, stdin);
printf("Ihr Text: %s", text);
printf("Laenge des Textes = %zu\n", strlen(text));

Mathematische Funktionen: math.h

sin pow sqrt fabs trunc ceil
Die Headerdatei math.h deklariert mathematische Funktionen: trigonometrische Funktionen (sin, cos, sinh, cosh, tan ...), Exponentialfunktion (exp), Logarithmen (log, log10, log2), Potenzfunktion (pow), Wurzelfunktion (sqrt), Absolutwert (fabs), Rundungsfunktionen (floor, ceil, trunc), Minimum- und Maximum-Funktionen(fmin, fmax). Hier werden auch diverse mathematische Konstanten deklariert, abhängig von der Compiler-Version, auch die Konstante M_PI. Um in Visual Studio die Konstante M_PI verwenden zu können, muss vor dem Inkludieren der math.h der Befehl #define _USE_MATH_DEFINES verwendet werden.
#include <stdio.h>
#include <math.h>
int main(void) {
  float x = 4.0;
  printf("Absolutwert von %.2f = %.2f\n", x, fabs(x));
  printf("Potenz %.2f^3 = %.2f\n", x, pow(x, 3));
  printf("Wurzel aus %.2f = %.2f\n", x, sqrt(x));
  printf("Sinus sin(%.2f) = %.2f\n", x, sin(x));
}

String-Manipulation: string.h

strlen memcpy strcpy strcat strcmp strchr
Die Headerdatei string.h deklariert Funktionen für die Bearbeitung von Zeichenketten: Länge einer Zeichenkette bestimmen (strlen), Zeichenketten kopieren (strcpy), vergleichen (strcmp), aneinanderfügen (strcat). Weiterhin: Zeichen in einer Zeichenkette suchen (strchr) [...]. Das folgende Beispiel zeigt, wie mit Hilfe dieser Funktionen zwei Zeichenketten aneinandergefügt werden.

char *text1 = "Lorem ipsum  ", *text2 = "dolor sit amet,";
char *text = NULL; 
// Berechne den benötigten Speicherplatz
int n = strlen(text1) + strlen(text2) + 1;
// Dynamische Speicherallokation
text = (char *)malloc(n*sizeof(char));
// Kopiere text1 nach text
strcpy(text, text1);
// Füge text2 an
strcat(text, text2); 
printf("Angefuegter Text:\n%s\n", text);
printf("hat Laenge %zu\n", strlen(text));

Diverse Hilfsfunktionen: stdlib.h

atoi rand srand malloc calloc free
In der Headerdatei stdlib.h wurden Makros und Hilfsfunktionen für unterschiedliche Anwendungsbereiche zusammengepackt:
  • Systemfunktionen (system, getenv).
    Mit system kann man Befehle aufrufen, die dann vom Betriebssystem ausgeführt werden. Die Parameter der system-Funktion hängen vom jeweiligen Betriebssystem ab, daher sollte diese Funktion aus Kompatibilitätsgründen nur sparsam verwendet werden. Der Funktionsaufruf system("pause"); führt einfach den Windows-Befehl PAUSE in der Kommandozeile (cmd.exe) aus, man kann anstattdessen auch getchar(); verwenden, um die Ausführung des Programms anzuhalten bis eine Eingabe von der Tastatur erfolgt. Mit getenv kann der Wert einer Systemumgebungsvariablen herausgefunden werden.

  • // Systemfunktionen (betriebssystemabhängig)
    system("title=Test"); // Titel der Konsole, nur Windows
    system("PAUSE"); // Programm anhalten, nur Windows 
    
  • Funktionen für Typkonvertierung
    (atoi, atof,...)

    Mit atoi wird eine Zeichenkette in eine Zahl konvertiert, z.B. wird aus "100" die Zahl 100.

  • // Konvertierungsfunktionen
    char *z = "100"; // Zeichenkette 100
    printf("%d, %.2f\n", atoi(z), atof(z)); // Ausgabe: 100, 100.00
    
  • Funktionen zur Erzeugung von Pseudo-Zufallszahlen
    (srand, rand)

    Mit srand initialisiert man den Zufallsgenerator, mit rand erzeugt man eine ganzzahlige Pseudo-Zufallszahl im Bereich 0 bis RANDMAX = 32767.

  • #include <stdio.h>
    #include <stdlib.h>
    int main(void){ 
      srand(1); // Initialisiere Zufallsgenerator mit Seed 1
      while(1){ // Endlosschleife
         // Erzeuge eine Zufallszahl im Wertebereich [0, RAND_MAX]
         printf("%d\n", rand());
         // Erzeuge eine Zufallszahl im Wertebereich [100, 150]
         printf("%d\n", 100 + (150-100)*rand()/RAND_MAX );
         getchar();
      }
    }
    
  • Dynamische Speicherallokation (malloc, calloc, free). Mit malloc und calloc reserviert man Speicherplatz und erhält einen Zeiger auf den reservierten Speicherbereich zurück. Mit free wird der Speicherbereich wieder freigegeben.

Wertebereiche: limits.h

INT_MIN INT_MAX SHRT:MIN SHRT_MAX
In der Headerdatei limits.h werden Konstanten / Makros definiert, die die Wertebereiche ganzzahliger Datentypen festlegen. Dort finden Sie Makros wie die folgenden:

#define INT_MIN     (-2147483647 - 1)
#define INT_MAX       2147483647
#define LONG_MIN    (-2147483647L - 1)
#define LONG_MAX      2147483647L
Der folgende Code gibt die Wertebereiche der Datentypen short und int aus.
printf("%14s| %14hd| %14hd\n",
       "Wertebereich short", SHRT_MIN, SHRT_MAX);
printf("%14s| %14d| %14d\n", 
       "Wertebereich int", INT_MIN, INT_MAX);


Tools & Quellen

  1. Visual Studio Community Edition: Für die Entwicklung von C-Programmen. Visual Studio unterstützt C-Programmierung indirekt über C++ und die Vollversion wird in vielen Unternehmen als Entwicklungsumgebung eingesetzt.
  2. yED Graph Editor: Für die Entwicklung von Fluss­diagrammen bzw. Programm­ablauf­plänen.
  3. Jürgen Wolf, C von A bis Z: openbook.rheinwerk-verlag.de/c_von_a_bis_z/, 2020.
  4. Thomas Theis: Einstieg in C. Für Programmiereinsteiger geeignet, Galileo Press, 2014.
  5. Manfred Daussman, C als erste Programmiersprache: Vom Einsteiger zum Fortgeschrittenen. Vieweg, 2010.
  6. Axel Böttcher, Franz Kneißl. Informatik für Ingenieure: Grundlagen und Programmierung in C. Oldenbourg Verlag, 1999.
  7. Brian Kernighan, Dennis Ritchie, The C programming language. Prentice-Hall, 2010.
  8. Visual Studio C Language Reference, docs.microsoft.com/en-us/cpp/c-language/
  9. C Cheatsheet (PDF): C-Programmierung_Cheatsheet_Die_wichtigsten_C-Befehle_Prof._E._Kiss_HS_KL.pdf: dies Cheatsheet als PDF-Datei.