2020-01-30 21:12:07 +03:00
---
2022-08-01 10:46:03 +03:00
language: C
2020-02-01 03:05:29 +03:00
filename: learnc-de.c
2020-01-30 21:12:07 +03:00
contributors:
- ["caminsha", "https://github.com/caminsha"]
lang: de-de
---
Ach, C. Immer noch **die** Sprache für modernes High-Performance Computing.
2024-09-06 22:52:26 +03:00
C ist wahrscheinlich die Programmiersprache mit dem niedrigsten Abstraktionsniveau,
2020-02-02 22:27:41 +03:00
welche die meisten Programmierer je brauchen werden.
Die Geschwindigkeit von C ist enorm, allerdings muss man sich stets der
manuellen Speicherverwaltung bewusst sein.
2020-01-30 21:12:07 +03:00
2020-02-01 03:00:55 +03:00
> **Über Compiler Optionen**
2020-01-30 21:12:07 +03:00
>
2020-02-02 23:44:15 +03:00
> Standardmäßig sind `gcc` und `clang` ziemlich ruhig bezüglich Warnungen und
2020-01-30 21:12:07 +03:00
> Fehlern, obwohl dies sehr nützliche Informationen sein können. Es wird
2020-02-01 03:00:55 +03:00
> empfohlen, strengere Compiler Optionen zu verwenden. Hier sind einige empfohlene
2020-01-30 21:12:07 +03:00
> Standards:
> `-Wall -Wextra -Werror -O2 -std=c99 -pedantic`
>
2024-08-30 20:56:14 +03:00
> Da gewisse Optionen (insbesondere der C-Standard) sehr stark vom Projekt
2020-02-03 01:08:38 +03:00
> abhängen, lohnt es sich, wenn die unterschiedlichen Optionen genauer
> angeschaut werden. Eine Übersicht über die Compiler-Optionen findet man unter
> [diesem](https://stackoverflow.com/questions/3375697/useful-gcc-flags-for-c) Stackoverflow-Beitrag.
>
2020-01-30 21:12:07 +03:00
> Für weitere Informationen, was diese und weitere Optionen genau machen,
> sollte die Man-Page des C-Compilers aufgerufen werden (z.B. `man 1 gcc`).
> Alternativ kann auch online nach den unterschiedlichen Optionen gesucht werden.
```c
// einzeilige Kommentare starten mit // - nur in C99 und später vorhanden.
/*
mehrzeilige Kommentare sehen so aus. Diese funktionieren auch in C89
*/
/*
2020-02-01 03:00:55 +03:00
mehrzeilige Kommentare können nicht verschachtelt werden /* Sei Vorsichtig! */ // Kommentar endet auf dieser Linie ...
2020-01-30 21:12:07 +03:00
*/ // ... nicht bei dieser!
// Konstanten: #define < keyword >
// Konstanten werden laut der Konvention immer in GROSSBUCHSTABEN geschrieben
2020-02-02 23:02:01 +03:00
#define DAYS_IN_YEAR 365
2020-01-30 21:12:07 +03:00
// Konstanten können auch als Aufzählungskonstanten (Enums) definiert werden.
// Alle Anweisungen müssen mit einem Semikolon beendet werden.
2020-02-02 23:02:01 +03:00
enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
// MON wird automatisch zu 2, TUE zu 3 etc.
2020-01-30 21:12:07 +03:00
// Importiere Header-Dateien mit #include
#include <stdlib.h>
2020-02-01 04:18:13 +03:00
#include <stdio.h>
2020-01-30 21:12:07 +03:00
#include <string.h>
// Dateien, welche zwischen < spitzen Klammern > stehen, sind Header-Dateien aus
// der C-Standard-Bibliothek.
// Für deine eigenen Header müssen Anführungszeichen verwendet werden, z.B.:
// #include "mein_header.h"
// Funktionssignaturen werden entweder vorher in einer .h-Datei deklariert oder
// am Anfang der .c-Datei.
2020-02-02 23:02:01 +03:00
void function_1();
2024-05-02 20:30:37 +03:00
int function_2(void);
2020-01-30 21:12:07 +03:00
// Es muss ein Funktionsprototyp deklariert werden vor der `main()` Funktion,
// wenn die Funktion nach der `main()` Funktion gebraucht wird.
2020-02-02 23:02:01 +03:00
int add_two_ints(int x1, int x2); // Funktionsprototyp
2024-08-30 20:56:14 +03:00
// Auch wenn der Ausdruck `int add_two_ints(int, int)` auch valid wäre,
2020-01-30 21:12:07 +03:00
// ist es empfohlen, dass man die Namen der Argumente hinschreibt für eine
// einfachere Analyse.
// Der Einstiegspunkt deines Programms ist eine Funktion mit dem Namen main und
// einem Integer als Rückgabewert.
2020-02-17 00:27:19 +03:00
int main(void) {
2020-01-30 21:12:07 +03:00
// dein Programm
}
2020-02-01 04:26:58 +03:00
// Die Kommandozeilenargumente, welche gebraucht werden, damit dein Programm
// läuft, werden als Argumente der `main` -Funktion mitgegeben.
2020-02-02 23:04:40 +03:00
// argc (argument counter) steht für die Anzahl von Argumenten.
// Der Programmname ist das erste Argument.
// argv (argument vector) ist ein Array von Zeichenarrays, welche die
// Argumente beinhaltet.
2020-01-30 21:12:07 +03:00
// argv[0] = Name des Programms
// argv[1] = erstes Argument usw.
2020-02-17 00:27:19 +03:00
int main (int argc, char** argv) {
2020-01-31 02:00:39 +03:00
// Ausgabe mit Hilfe von printf (print formatted)
// %d ist ein Integer.
// \n steht für eine neue Zeile
printf("%d\n",0); // => Gibt 0 aus.
2020-02-02 23:25:23 +03:00
////////////////////////////////////////////////
// Operatoren
////////////////////////////////////////////////
// Kurzschreibweise für mehrere Deklarationen
int i1 = 1, i2 = 2;
flaot f1 = 1.0, f2 = 2.0;
int b,c;
b = c = 0;
// Arithmetik ist unkompliziert
1 + 2; // => 3
2 - 1; // => 1
2 * 1; // => 2
1 / 2; // 0 (0.5, aber abgeschnitten, da es int sind.)
// Man muss mindestens ein Integer zu einen float konvertieren, damit man als
// Resultat eine Gleitkommazahl erhält.
(float)1 / 2; // => 0.5f
1 / (double)2; // => 0.5 // das gleiche mit dem Typ `double`
1.0 / 2.0; // => 0.5, plus oder minus Epsilon
// Gleitkommazahlen und deren Berechnungen sind nicht exakt.
// Es gibt auch die Möglichkeit, Modulo zu rechnen
11 % 3; // => 2
// Vergleichsoperatoren sind vielleicht schon bekannt, aber in C gibt es
// keinen Boolean-Typ. In C verwenden wir `int` . (Oder _Bool oder bool in C99)
// 0 ist falsch, alles andere ist wahr (Die Vergleichsoperatoren ergeben
// immer 1 oder 0.
3 == 2; // => 0 (falsch)
3 != 2; // => 1 (wahr)
3 > 2; // => 1
3 < 2 ; / / = > 0
2 < = 2; // => 1
2 >= 2; // => 1
// C ist nicht Python - Vergleiche können nicht einfach verkettet werden.
// Warnung: die folgende Zeile wird kompilieren, aber es bedeutet `(0 < a) < 2` .
// Dieser Ausdruck ist immer wahr, weil (0 < a ) kann entweder 1 oder 0 sein .
// In diesem Falle ist es 1, weil (0 < 1 ) .
int zwischen_0_und_2 = 0 < a < 2 ;
// Benutze stattdessen folgende Schreibweise:
int zwischen_0_und_2 = 0 < a & & a < 2 ;
// Logik funktioniert auch mit ints
!3; // => 0 (logisches Nicht)
!0; // => 1
1 & & 1; // => 1 (logisches Und)
0 & & 1; // => 0
0 || 1; // => 1 (logisches Oder)
0 || 0; // => 0
// Bedingter ternärer Ausdruck ( ? : )
int e = 5;
int f = 10;
int z;
z = ( e > f ) ? e : f; // => // => 10 "wenn e > f ist, gib e zurück, sonst f."
// Inkrementierungs- und Dekrementierungsoperatoren
int j = 0;
int s = j++; // gib j zurück und erhöhe danach j. (s = 0, j = 1)
s = ++j; // erhöhe zuerst j und gib dann j zurück (s = 2, j = 2)
// das gleiche gilt für j-- und --j
// Bitweise Operatoren
~0x0F; // => 0xFFFFFFF0 (Bitweise Negation, "Einer-Komplement",
// Beispielresultat für 32-Bit int)
0x0F & 0xF0; // => 0x00 (Bitweises UND)
0x0F | 0xF0; // => 0xFF (Bitweises ODER)
0x04 ^ 0x0F; // => 0x0B (Bitweises XOR)
0x01 < < 1 ; / / = > 0x02 (Bitweises Linksverschiebung (left shift) (um 1))
0x02 >> 1; // => 0x01 (Bitweises Rechtsverschiebung (right shift) (um 1))
// Sei vorsichtig beim Shift mit vorzeichenbehafteten Integern
// folgende Ausdrücke sind nicht definiert:
// - Verschiebung in das Vorzeichenbit (int a = 1 < < 31 )
// - Linksshift einer negativen Zahl (int a = -1 < < 2 )
// - Shift um einen Offset, welcher >= die Breite des linken Ausdrucks ist.
// int a = 1 < < 32 ; / / undefiniertes Verhalten , wenn int 32-Bit ist .
2020-01-31 02:00:39 +03:00
////////////////////////////////////////////////
// Typen
////////////////////////////////////////////////
2020-02-25 19:13:36 +03:00
// Compiler, welche nicht C99-kompatibel sind, verlangen, dass sämtliche
// Variablen zu Beginn des Blocks deklariert werden.
2020-01-31 02:00:39 +03:00
// C99-Konforme Compiler erlauben die Variablendeklaration an dem Punkt, an
// welchem die Variable verwendet wird.
2020-02-25 19:13:36 +03:00
// Wir deklarieren die Variablen dynamisch im Code um die Lesbarkeit im
// Tutorial zu verbessern.
2020-01-31 02:00:39 +03:00
2020-02-02 23:08:18 +03:00
// integer sind normalerweise 4 Bytes groß
2020-01-31 02:00:39 +03:00
int x_int = 0;
2020-02-02 23:08:18 +03:00
// shorts sind normalerweise 2 Bytes groß
2020-01-31 02:00:39 +03:00
short x_short = 0;
2020-02-02 23:08:18 +03:00
// chars sind garantiert 1 Byte groß
2020-01-31 02:00:39 +03:00
char x_char = 0;
char y_char = 'y'; // Charakterliterale werden mit '' gekennzeichnet.
2020-02-02 23:08:18 +03:00
// longs sind oft 4 bis 8 Bytes groß. long long sind garantiert mindestens
// 8 Bytes groß.
2020-01-31 02:00:39 +03:00
long x_long = 0;
long long x_long_long = 0;
// floats sind normalerweise 32-Bit Gleitkommazahlen
float x_float = 0.0f; // 'f'-Suffix beschreibt eine Gleitkommazahl.
// doubles sind normalerweise 64-Bit Gleitkommazahlen
double x_double = 0.0; // echte Zahlen ohne Suffix sind vom Typ double
2020-02-01 04:26:58 +03:00
// integer-Typen können vorzeichenlos (unsigned) sein
2020-02-02 23:44:15 +03:00
// (größer oder kleiner als 0)
2020-01-31 02:00:39 +03:00
unsigned short ux_short;
unsigned int ux_int;
unsigned long long ux_long_long;
2020-02-01 03:00:55 +03:00
// Zeichen innerhalb von einfachen Anführungszeichen sind Integers im
2020-01-31 02:00:39 +03:00
// Maschinenzeichensatz
'0'; // => 48 im ASCII-Zeichensatz
'A'; // => 65 im ASCII-Zeichensatz
2020-02-02 23:44:15 +03:00
// sizeof(T) gibt die Größe einer Variablen des Typen T in Bytes zurück.
// sizeof(obj) ergibt die Größe des Ausdrucks (Variable, Literal usw.)
2020-01-31 02:00:39 +03:00
2020-02-01 04:26:58 +03:00
printf("%zu\n", sizeof(int)); // => 4 (auf den Rechnern mit einem 4-Byte-Wort)
2020-01-31 02:00:39 +03:00
// Wenn das Argument des `sizeof` -Operator ein Ausdruck ist, dann wird das
2020-02-02 23:44:15 +03:00
// Argument nicht ausgewertet (außer Arrays mit variabler Länge)
2020-01-31 02:00:39 +03:00
// Der Wert, der in diesem Fall zurückgegeben wird, ist eine Konstante zur
2024-09-06 22:52:26 +03:00
// Kompilierzeit.
2020-01-31 02:00:39 +03:00
int a = 1;
//size_t ist ein vorzeichenloser Integer Typ mit mindestens 2 Byte um die
2020-02-02 23:44:15 +03:00
// Größe eines Objekts zu repräsentieren.
2020-01-31 02:00:39 +03:00
size_t size = sizeof(a++); // a++ wird nicht ausgewertet
printf("sizeof(a++) = %zu, wobei a=%d ist\n", size, a);
// Gibt "sizeof(a++) = 4, wobei a=1 ist" aus (mit einer 32-Bit-Architektur)
2020-02-02 23:44:15 +03:00
// Arrays müssen mit einer Größe initialisiert werden.
2020-02-02 23:02:01 +03:00
char my_char_array[20]; // Dieses Array beinhaltet 1 * 20 = 20 Bytes
int my_int_array[20]; // Dieses Array beinhaltet 4 * 20 = 80 Bytes.
2020-01-31 02:00:39 +03:00
// unter der Voraussetzung eines 4-Byte-Worts.
// Ein Array kann auf diese Weise mit 0 initialisiert werden.
2020-02-02 23:02:01 +03:00
char my_array[20] = {0};
2020-01-31 02:00:39 +03:00
// Hierbei ist der Teil "{0}" der "Array Initialisierer".
2020-01-31 03:11:48 +03:00
// Beachte, dass die Länge des Arrays nicht explizit definiert werden muss,
// wenn er auf derselben Linie initialisiert wird.
// Folgende Deklaration ist gleichwertig:
2020-02-02 23:02:01 +03:00
char my_array[] = {0};
2020-01-31 03:11:48 +03:00
// Allerdings muss die Länge des Arrays dann zur Laufzeit ausgewertet werden:
2020-02-02 23:02:01 +03:00
size_t my_array_size = sizeof(my_array) / sizeof(my_array[0]);
2020-01-31 03:11:48 +03:00
// WARNUNG: Wenn dieser Ansatz gewählt wird, muss man sicherstellen, dass die
2020-02-02 23:44:15 +03:00
// Größe des Arrays ermittelt werden *bevor* dieser einer Funktion als
2020-01-31 03:11:48 +03:00
// Argument weitergegeben wird (siehe Diskussion weiter unten), weil Arrays
// einer Funktion nur als Zeiger übergeben werden. => Das obere Statement
// würde innerhalb einer Funktion ein falsches Resultat liefern.
// Das Indexieren eines Arrays funktioniert wie in anderen Sprache - resp.
// in anderen Sprachen funktioniert es gleich wie in C.
2020-02-02 23:02:01 +03:00
my_array[0]; // => 0
2020-01-31 03:11:48 +03:00
// Arrays sind veränderbar; es ist nur Arbeitsspeicher!
2020-02-02 23:02:01 +03:00
my_array[1] = 2;
printf("%d\n", my_array[1]); // => 2
2020-01-31 03:11:48 +03:00
// In C99 (und als optionales Feature in C11) können Arrays mit variabler
2020-02-02 23:44:15 +03:00
// Länge deklariert werden. Die Größe eines solchen Array muss eine Konstante
2020-01-31 03:11:48 +03:00
// zur Kompilierzeit sein.
2020-02-02 23:44:15 +03:00
printf("Geben Sie die Arraygröße an: "); //Frag den Benutzer nach
// der Arraygröße
2020-01-31 03:11:48 +03:00
int array_size;
fcsanf(stdin, "%d", &array_size);
int var_length_array[array_size]; // deklariere Array mit variabler Länge
printf("sizeof array =%zu\n", sizeof var_length_array);
// Zum Beispiel:
2020-02-02 23:44:15 +03:00
// > Geben Sie die Arraygröße an: 10
2020-01-31 03:11:48 +03:00
// > sizeof array = 40
// Strings sind lediglich Arrays von `chars` , welche mit einem Null-Byte
// (0x00) beendet werden. In Strings wird das Nullbyte durch das Zeichen \0
// repräsentiert. Wir müssen das Null-Byte nicht angeben in String-Literalen;
2020-02-01 03:00:55 +03:00
// der Compiler fügt es am Ende des Array automatisch hinzu.
2020-02-02 23:02:01 +03:00
char a_string[20] = "Das ist ein String";
2024-09-06 22:52:26 +03:00
printf("%s\n", a_string); // %s formatiert einen String
2020-01-31 03:11:48 +03:00
2020-02-02 23:02:01 +03:00
printf("%d\n", a_string[18]); // => 0
2020-01-31 03:11:48 +03:00
// Hier ist das Byte #19 0 (wie auch Byte #20 )
// Wenn wir Zeichen zwischen einfachen Anführungszeichen haben, ist es ein
// Zeichenliteral vom Typ int und *nicht* char. (aus historischen Gründen)
int cha = 'a'; // Ok
char chb = 'a'; // auch ok (implizite Umwandlung von int zu char)
// Mehrdimensionale Arrays:
int multi_array[2][5] = {
{1,2,3,4,5},
{6,7,8,9,0}
};
// Auf Elemente zugreifen:
int array_int = multi_array[0][2]; // => 3
2020-01-31 02:00:39 +03:00
2020-01-31 03:42:28 +03:00
////////////////////////////////////////////////
// Kontrollstrukturen
////////////////////////////////////////////////
2020-02-17 00:27:19 +03:00
if (0) {
2020-01-31 03:42:28 +03:00
printf("Ich werde nie ausgeführt.");
}
2020-02-17 00:27:19 +03:00
else if (0) {
2020-01-31 03:42:28 +03:00
printf("Ich werde auch nie ausgeführt.");
}
2020-02-17 00:27:19 +03:00
else {
2020-01-31 03:42:28 +03:00
printf("Ich gebe etwas aus.");
}
// While-Schleifen existieren auch
int ii = 0;
2020-02-17 00:27:19 +03:00
while (ii < 10 ) { / / JEDER Wert unter zehn ist wahr
2020-02-01 04:26:58 +03:00
printf("%d, " ii++); //i++ inkrementiert ii NACHDEM der Wert gebraucht
// wurde.
2020-01-31 03:42:28 +03:00
} // => gibt folgendes aus: "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
int kk = 0;
2020-02-17 00:27:19 +03:00
do {
2020-01-31 03:42:28 +03:00
printf("%d, ", kk);
2020-02-17 00:27:19 +03:00
} while(++kk < 10 ) ; / / + + kk inkrementiert kk BEVOR der Wert gebraucht wurde .
2020-01-31 03:42:28 +03:00
// => gibt folgendes aus: "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
// In C gibt es auch for-Schleifen
int jj;
2020-02-17 00:27:19 +03:00
for (jj = 0; jj < 10 ; jj + + ) {
2020-01-31 03:42:28 +03:00
printf("%d, ", jj);
} // => gibt folgendes aus: "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n");
// **Merke**
2020-02-01 03:00:55 +03:00
// Schleifen und Funktionen müssen einen Rumpf haben. Wenn kein Rumpf gebraucht
2020-01-31 03:42:28 +03:00
// wird, kann folgendes gemacht werden:
int i;
2020-02-17 00:27:19 +03:00
for (i = 0; i < = 5; i++) {
2020-02-01 03:00:55 +03:00
; // Semikolon wird als Rumpf behandelt (Null-Anweisung)
2020-01-31 03:42:28 +03:00
}
// Alternativ kann auch folgendes geschrieben werden:
for (i = 0; i < = 5; i++);
// Verzweigungen mit mehreren Möglichkeiten: `switch()`
2020-02-17 00:27:19 +03:00
switch (a) {
2020-02-01 03:00:55 +03:00
case 0: // Labels müssen integrale *konstante* Ausdrücke sein (z.B. Enums)
2020-01-31 03:42:28 +03:00
printf("Hey, 'a' ist gleich 0!\n");
2020-02-01 04:26:58 +03:00
break; //Wenn du kein break einsetzt, so geht der Kontrollfluss
// durch die Labels
2020-01-31 03:42:28 +03:00
case 1:
printf("Huh, 'a' ist gleich 1!\n");
break;
2020-02-01 04:26:58 +03:00
// Sei vorsichtig - wenn man das `break` vergisst, werden alle
// Anweisungen ausgeführt bis das nächste `break` erscheint.
2020-01-31 03:42:28 +03:00
case 3:
case 4:
printf("Schau mal ... 'a' ist entweder 3 oder 4.\n");
break;
default:
// wenn der Ausdruck `a` auf kein Label zutrifft.
fputs("Fehler!\n", stderr);
exit(-1);
break;
}
2020-01-31 03:47:49 +03:00
////////////////////////////////////////////////
// Typenumwandlung
////////////////////////////////////////////////
// Jeder Wert in C hat einen bestimmten Typen, aber es ist möglich, ein
// Wert in einen anderen Typ umzuwandeln (mit einigen Einschränkungen).
int x_hex = 0x01; // Es ist möglich, Variablen Hexadezimalwerten zuzuweisen.
// Bei der Umwandlung zwischen Typen wird versucht, den numerischen Wert
// beizubehalten.
printf("%d\n", x_hex); // => 1
printf("%d\n", (short) x_hex); // => 1
printf("%d\n", (char) x_hex); // => 1
// Typen werden überlaufen (overflow) ohne jegliche Warnung
2020-02-01 04:26:58 +03:00
printf("%d\n", (unsigned char) 257); // => 1 (Max char=255 wenn char 8 Bit ist)
2020-01-31 03:47:49 +03:00
// Um den maximalen Wert eines `char` , `signed char` oder `unsigned char`
// herauszufinden, können die Makros `CHAR_MAX` , `SCHAR_MAX` und `UCHAR_MAX`
// aus der Header-Datei `<limits.h>` verwendet werden.
// Integer-Typen können zu Gleitkommazahlen und umgekehrt umgewandelt werden.
2024-09-06 22:52:26 +03:00
printf("%f\n", (double) 100); // %f formatiert immer zu einem `double` ...
2020-01-31 03:47:49 +03:00
printf("%f\n", (flaot) 100); // ... auch mit einem `float`
printf("%d\n", (char)100.0);
2020-01-31 05:16:16 +03:00
////////////////////////////////////////////////
// Zeiger (aka Pointer)
////////////////////////////////////////////////
// In diesem Tutorial wird das deutsche Wort Zeiger nicht verwendet, da es
// bei einer weiteren Recherche einfacher ist, wenn man von Pointern ausgeht.
2020-02-02 23:44:15 +03:00
// Außerdem ist der Begriff Pointer auch im deutschen Sprachgebrauch zu finden.
2020-01-31 05:16:16 +03:00
// Ein Pointer ist eine Variable, welche deklariert wurde, um eine Speicher-
2020-02-01 03:00:55 +03:00
// adresse zu speichern. Die Deklaration eines Pointers wird auch zeigen,
2020-01-31 05:16:16 +03:00
// auf welche Art von Daten der Pointer zeigt. Man kann die Speicheradresse
// von Variablen abrufen und dann mit diesen herumspielen.
int x = 0;
2020-02-01 04:26:58 +03:00
printf("%p\n", (void *)&x); // verwende & um die Adresse der Variable
// zu erhalten
2024-09-06 22:52:26 +03:00
// %p formatiert einen Objektpointer des Typen void*)
2020-01-31 05:16:16 +03:00
// => Gibt eine Adresse im Speicher aus
// Pointer starten mit einem * zu Beginn der Deklaration.
2020-02-02 23:02:01 +03:00
int *px, not_a_pointer; // px ist ein Pointer zu einem int.
2020-01-31 05:16:16 +03:00
px = &x; // Speichert die Adresse von x in px
printf("%p\n", (void *)px); // => Gibt eine Adresse im Speicher aus
2020-02-02 23:02:01 +03:00
printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
2020-01-31 05:16:16 +03:00
// Gibt auf einem typischen 64-Bit-System folgendes aus: "8, 4"
// Um den Wert einer Adresse, auf welche ein Pointer zeigt, herauszufinden,
// muss man vor die Variable ein * setzen, um sie zu dereferenzieren.
// Notiz: Ja, es kann verwirrend sein, dass '*' sowohl für das Deklarieren
// als auch das Derefenzieren verwendet werden kann.
printf("%d\n", *px); // => 0, der Wert von x
// Man kann den Wert, auf welchen ein Pointer zeigt, auch verändern.
// Man muss die Dereferenzierung in Klammern setzen, weil ++ eine höhere
// Priorität als * hat.
(*px)++; // Inkrementiere den Wert, auf welchen px zeigt, um 1
printf("%d\n", *px); // => 1
printf("%d\n", x); // => 1
2024-08-30 20:56:14 +03:00
// Arrays sind eine gute Möglichkeit, einen zusammenhängenden Block von
2020-01-31 05:16:16 +03:00
// Speicher zu allozieren.
2020-02-02 23:44:15 +03:00
int x_array[20]; // deklariert einen Array der Größe 20 (Größe kann
2020-01-31 05:16:16 +03:00
// nicht geändert werden.)
int xx;
2020-02-17 00:27:19 +03:00
for (xx =0; xx < 20 ; xx + + ) {
2020-01-31 05:16:16 +03:00
x_array[xx] 20 -xx;
} // Initialisiere x_array zu 20, 19, 18, ... 2, 1
2024-09-06 22:52:26 +03:00
// Deklariere ein Pointer des Typs int und initialisiere ihn, um auf `x_array`
2020-01-31 05:16:16 +03:00
// zu zeigen.
int *x_ptr = x_array;
// x_ptr zeigt jetzt auf den ersten Wert innerhalb des Arrays (int 20)
// Das funktioniert, weil Arrays oft zu Pointern reduziert werden, welche
// auf das erste Element zeigen.
// Zum Beispiel: Wenn ein Array einer Funktion mitgegeben wird oder einem
// Pointer zugewiesen wird, wird es zu einem Pointer reduziert (implizites Casting)
// Ausnahme: Wenn das Array das Argument des Operators `&` ist.
int arr[10];
2020-02-02 23:02:01 +03:00
int (*ptr_to_arr)[10] = &arr; //`& arr` ist nicht vom Typ `int *` !
2024-09-06 22:52:26 +03:00
// Es ist vom Typen "Pointer auf Array" (aus zehn `int` s)
2020-01-31 05:16:16 +03:00
// oder wenn das Array ein Stringliteral ist, welches gebraucht wird um ein
// `char` -Array zu initialisieren.
2020-02-02 23:02:01 +03:00
char other_arr[] = "foobarbazquirk";
2020-02-01 03:00:55 +03:00
// oder wenn es das Argument des `sizeof` oder `alignof` Operators ist.
2020-02-02 23:02:01 +03:00
int third_array[10];
int *ptr = third_array; // gleich wie: `int *ptr = &arr[0]`
printf("%zu, %zu\n", sizeof(third_array), sizeof(ptr));
2020-01-31 05:16:16 +03:00
// Gibt wahrscheinlich "40, 4" oder "40, 8" aus
// Pointer werden basierend auf dem Typ in- und dekrementiert
// Dies wird Pointer-Arithmetik genannt.
printf("%d\n", *(x_ptr + 1)); // => 19
printf("%d\n", x_array[1]); // => 19
// Man kann zusammenhängende Speicherblöcke auch mit der Funktion `malloc`
// aus der Standardbibliothek dynamisch allozieren. Der Funktion `malloc`
// muss ein Argument des Typs `size_t` übergeben werden, welches bestimmt,
// wie viele Bytes alloziert werden sollen. (Normalerweise geschieht dies
// aus dem Heap - dies kann auf eingebetteten Systemen unterschiedlichen sein.
// Der C Standard sagt nichts darüber.)
2020-02-02 23:02:01 +03:00
int *my_ptr = malloc(sizeof(*my_ptr) * 20);
2020-02-17 00:27:19 +03:00
for (xx = 0; xx < 20 ; xx + + ) {
2020-02-02 23:02:01 +03:00
*(my_ptr + xx) = 20 -xx; //my_ptr[xx] = 20-xx
2020-01-31 05:16:16 +03:00
} // initialisiere Speicher zu 20, 19, 18, 17, ... 2, 1 (als `int` )
// Sei vorsichtig beim Übergeben von Benutzerdefinierten Werten an `malloc` .
// Wenn du sicher sein willst, kannst du die Funktion `calloc` nutzen, welche
// (nicht wie `malloc` ) auch den Speicher nullt.
2020-02-02 23:02:01 +03:00
int *my_other_ptr = calloc(20, sizeof(int));
2020-01-31 05:16:16 +03:00
// Merke, dass es in C keinen Standard-Weg gibt, um die Länge eines dynamisch
// allozierten Arrays zu bestimmen. Auf Grund dessen sollte eine Variable
// erstellt werden, welche sich die Anzahl der Elemente im Array merkt, wenn
// die Arrays mehrmals im Programm gebraucht werden.
// Weitere Informationen stehen im Abschnitt Funktionen.
2020-02-02 23:02:01 +03:00
size_t size = 10;
int *my_array = calloc(size, sizeof(int));
2020-01-31 05:16:16 +03:00
// Füge dem Array ein Element hinzu
2020-02-02 23:02:01 +03:00
size++;
my_array = realloc(my_array, sizeof(int) *size);
2020-02-17 00:27:19 +03:00
if (my_array == NULL) {
2020-01-31 05:16:16 +03:00
// Denke daran, realloc-Fehler zu prüfen
return
}
2020-02-02 23:02:01 +03:00
my_array[10] = 5;
2020-01-31 05:16:16 +03:00
// Das Dereferenzieren von nicht alloziertem Speicher führt zu einem
// Undefinierten Verhalten.
2020-02-02 23:02:01 +03:00
printf("%d\n", *(my_ptr + 21)); // Gibt irgendwas aus.
2020-02-01 04:26:58 +03:00
// Das Programm kann auch abstürzen
2020-01-31 05:16:16 +03:00
// Nachdem du fertig mit einem Block bist, welcher `malloc` verwendet hat,
// muss der Speicher befreit werden. Ansonsten kann dieser Speicherbereich
// niemand nutzen bis dein Programm beendet wird.
// Dies wird auch als "Speicherleck" (engl: memory leak) bezeichnet.
2020-02-02 23:02:01 +03:00
free(my_ptr);
2020-01-31 05:16:16 +03:00
// Obwohl Strings normalerweise als Pointer-to-Char (Pointer zum ersten
2020-02-01 03:00:55 +03:00
// Zeichen des Arrays) repräsentiert werden, sind Strings Arrays aus `char` s.
2020-01-31 05:16:16 +03:00
// Es ist eine gute Praxis, `const char *` zu verwenden, wenn man ein
// String-Literal referenziert, da String-Literale nicht modifiziert werden
// sollten (z.B. "foo"[0] = 'a' ist ILLEGAL)
2020-02-02 23:02:01 +03:00
const char *my_str = "Das ist mein eigener String";
printf("%c\n", *my_str); // => D
2020-01-31 05:16:16 +03:00
// Dies ist nicht der Fall, wenn der String ein Array (möglicherweise mit
// einem String-Literal initialisiert) ist, welcher im beschreibbaren Speicher
// bleibt, wie zum Beispiel in:
char foo[] = "foo";
foo[0] = 'a'; // Dies ist legal, foo enthält jetzt "aoo"
2020-02-02 23:02:01 +03:00
function_1();
2020-01-31 05:16:16 +03:00
} // Ende der `main` -Funktion
2020-01-31 06:30:57 +03:00
////////////////////////////////////////////////
// Funktionen
////////////////////////////////////////////////
// Syntax einer Funktionsdeklaration
2020-02-01 03:00:55 +03:00
// < rueckgabe_wert > < funktions_name > (< args > )
2020-01-31 06:30:57 +03:00
2020-02-17 00:27:19 +03:00
int add_two_ints(int x1, int x2) {
2020-01-31 06:30:57 +03:00
return x1 + x2; // verwendet return, um einen Wert zurückzugeben
}
/*
2020-02-01 03:00:55 +03:00
Funktionen werden auf Grund des Wertes aufgerufen (call-by-value). Wenn eine
2020-02-02 23:02:01 +03:00
Funktion aufgerufen wird, sind die Argumente Kopien der ursprünglichen Werte
2020-02-01 03:00:55 +03:00
(ausgenommen Arrays). Alles, was man innerhalb einer Funktion mit den Werten
macht, hat keinen Einfluss auf die Originalwerte als die Funktion aufgerufen
2020-02-02 23:02:01 +03:00
wurde.
2020-01-31 06:30:57 +03:00
2020-02-01 03:00:55 +03:00
Verwende Pointer, um den Originalinhalt zu bearbeiten.
2020-01-31 06:30:57 +03:00
2020-02-01 03:00:55 +03:00
Beispiel:
2020-01-31 06:30:57 +03:00
*/
// Eine `void` -Funktion gibt keinen Wert zurück
2020-02-17 00:27:19 +03:00
void str_reverse(char *str_in) {
2020-01-31 06:30:57 +03:00
char tmp;
size_t ii = 0;
2020-02-02 23:02:01 +03:00
size_t size = strlen(str_in);
2020-01-31 06:30:57 +03:00
// `strlen()` ist ein Teil der C Standard-Bibliothek.
// Merke: Die Länge, welche von `strlen` zurückgegeben wird, ist ohne den
2020-02-01 03:00:55 +03:00
// Null-Byte Terminator.
2020-02-17 00:27:19 +03:00
for (ii = 0; i < size / 2 ; ii ++) { // in C99 kann man `ii` hier deklarieren .
2020-01-31 06:30:57 +03:00
tmp = str_in[ii];
2020-02-02 23:02:01 +03:00
str_in[ii] = str_in[size - ii - 1]; //#ii'tes Zeichen vom Ende her
str_in[size - ii- 1] = tmp;
2020-01-31 06:30:57 +03:00
}
}
// Merke: Die `string.h` -Headerdatei muss inkludiert werden, bevor `strlen()`
// verwendet werden kann.
/*
2020-02-01 03:00:55 +03:00
char c[] = "Das ist ein Test";
str_reverse(c);
printf("%s\n", c), => "tseT nie tsi saD"
2020-01-31 06:30:57 +03:00
*/
// Weil wir lediglich eine Variable zurückgeben können, kann zum Ändern mehrerer
// Variablen das Konzept call-by-reference verwendet werden.
2020-02-17 00:27:19 +03:00
void swap_two_numbers(int *a, int *b) {
2020-01-31 06:30:57 +03:00
int temp = *a;
*a = *b;
*b = temp;
}
2020-02-02 23:02:01 +03:00
int first = 10;
int seconde = 20;
printf("Erste Zahl: %d\n Zweite Zahl: %d\n", first, second);
swap_two_numbers(& first, &second);
printf("Erste Zahl: %d\n Zweite Zahl: %d\n", first, second);
2020-01-31 06:30:57 +03:00
// Werte sind vertauscht.
/*
Wenn man Arrays betrachtet, so werden diese immer als Pointer übergeben. Auch
wenn die Arrays statisch alloziert werden (wie zum Beispiel `arr[10]` ), werden
diese als Pointer zum ersten Element des Arrays übergeben.
2020-02-01 03:00:55 +03:00
Auch hier soll noch einmal erwähnt werden, dass es keinen Standard gibt, wie die
2020-02-02 23:44:15 +03:00
Größe eines dynamischen Arrays herausgefunden werden kann.
2020-01-31 06:30:57 +03:00
*/
2020-02-02 23:44:15 +03:00
// Die Größe des Arrays muss unbedingt mitgegeben werden.
2020-02-02 23:08:18 +03:00
// Sonst hat die Funktion keine Ahnung wie groß das Array ist.
2020-02-17 00:27:19 +03:00
void print_int_arrray(int *arr, size_t size) {
2020-01-31 06:30:57 +03:00
int i;
2020-02-17 00:27:19 +03:00
for (i = 0; i < size ; i + + ) {
2020-01-31 06:30:57 +03:00
printf("arr[%d] ist %d\n", i, arr[i]);
}
}
2020-02-02 23:02:01 +03:00
int my_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int size = 10;
print_int_array(my_array, size);
2020-01-31 06:30:57 +03:00
// Wird folgendes ausgeben: "arr[0] ist 1" usw.
2020-02-02 23:44:15 +03:00
// Wenn man auf externe Variable (außerhalb der Funktion) referenziert, sollte
2020-01-31 06:30:57 +03:00
// man das Schlüsselwort `extern` verwenden.
int i = 0;
2020-02-17 00:27:19 +03:00
void test_function() {
2020-02-01 03:00:55 +03:00
extern int i; // i braucht nun die externe Variable i
2020-01-31 06:30:57 +03:00
}
2020-02-02 23:44:15 +03:00
// Das Schlüsselwort `static` macht, dass eine Variable außerhalb der Kompilier-
2020-01-31 06:30:57 +03:00
// einheit nicht zugreifbar ist. (Auf den meisten Systemen ist eine Kompiliereinheit
2020-02-02 23:02:01 +03:00
// eine `.c` -Datei.) Das Schlüsselwort `static` kann sowohl bei globalen
2020-02-01 04:26:58 +03:00
// (zur Kompiliereinheit gehörende) Variablen, Funktionen und Funktionslokale
// Variablen angewendet werden.
2020-01-31 06:30:57 +03:00
// Wenn man `static` bei lokalen Variablen verwendet, so ist diese Variable global
// erreichbar und behält dessen Wert über Funktionsaufrufe hinweg, aber sie ist
2020-02-02 23:44:15 +03:00
// nur innerhalb der deklarierten Funktion verfügbar. Außerdem werden statische
2020-01-31 06:30:57 +03:00
// Variablen mit 0 initialisiert, wenn sie nicht mit einem anderen Startwert
// initialisiert werden.
// Es ist auch möglich, Funktionen als statisch zu deklarieren, damit diese
2020-02-02 23:44:15 +03:00
// `private` sind. Privat heißt, dass sie nur in diesem Kontekt sichtbar sind.
2020-01-31 07:14:28 +03:00
////////////////////////////////////////////////
// Benutzerdefinierte Typen und Strukturen (Structs)
////////////////////////////////////////////////
2020-02-01 03:00:55 +03:00
// `typedef` s können verwendet werden, um Typenaliase zu erstellen.
2020-02-02 23:02:01 +03:00
typedef int my_type;
my_type my_type_var = 0;
2020-01-31 07:14:28 +03:00
// Structs sind lediglich Sammlungen von Daten, die Inhalte werden
// (in der Reihenfolge wie sie geschrieben wurden) sequentiell alloziert.
2020-02-17 00:27:19 +03:00
struct rectangle {
2020-02-02 23:02:01 +03:00
int width;
int height;
2020-01-31 07:14:28 +03:00
};
// Allgemein ist es nicht so, dass folgender Ausdruck wahr ist.
2020-02-02 23:02:01 +03:00
// sizeof(struct rectangle) == sizeof(int) + sizeof(int)
2020-01-31 07:14:28 +03:00
// Dies ist so, weil potentiell ein Padding zwischen den Struktur-Inhalten
2020-02-01 03:00:55 +03:00
// möglich ist). (siehe [1, Englisch])
2020-01-31 07:14:28 +03:00
2020-02-17 00:27:19 +03:00
void function_1() {
2020-02-02 23:02:01 +03:00
struct rectangle my_rectangle;
2020-01-31 07:14:28 +03:00
// Greife auf Struct-Inhalte mit `.` zu.
2020-02-02 23:02:01 +03:00
my_rectangle.width = 10;
my_rectangle.height = 20;
2020-01-31 07:14:28 +03:00
// Du kannst Pointer zu Structs deklarieren.
2020-02-02 23:02:01 +03:00
struct rectangle *my_rectangle_ptr = &my_rectangle;
2020-01-31 07:14:28 +03:00
// Verwende Dereferenzierung, um Struct-Inhalte zu bearbeiten
2020-02-02 23:02:01 +03:00
(*my_rectangle_ptr).width = 30;
2020-01-31 07:14:28 +03:00
2020-02-01 04:26:58 +03:00
//Noch besser: Verwende die Kurzschreibweise ->, um die Lesbarkeit zu
// verbessern.
2020-02-02 23:02:01 +03:00
my_rectangle_ptr->height = 10; // Gleich wie: (*my_rectangle_ptr).height = 10;
2020-01-31 07:14:28 +03:00
}
2020-02-01 03:00:55 +03:00
// Aus Bequemlichkeitsgründen ist es möglich einem `struct` ein `typedef` hinzuzufügen.
2020-02-02 23:02:01 +03:00
typedef struct rectangle rect;
2020-01-31 07:14:28 +03:00
2020-02-17 00:27:19 +03:00
int area(rect r) {
2020-02-02 23:02:01 +03:00
return r.width * r.height;
2020-01-31 07:14:28 +03:00
}
2020-02-02 23:08:18 +03:00
// Wenn du große Structs hast, kannst du diese mit dem Pointer kopieren,
// damit große Kopiervorgänge vermieden werden.
2020-02-17 00:27:19 +03:00
int area_ptr(const rect *r) {
2020-02-02 23:02:01 +03:00
return r->width * r->height;
2020-01-31 07:14:28 +03:00
}
2020-01-31 07:15:23 +03:00
////////////////////////////////////////////////
// Funktionspointer
////////////////////////////////////////////////
/*
Zur Laufzeit sind Funktionen in einer Speicheradresse gespeichert.
Funktionspointer sind wie normale Pointer (es wird einfach eine Speicheradresse
2020-02-01 03:00:55 +03:00
gespeichert). Funktionspointer können verwendet werden, um Funktionen und
2020-01-31 07:15:23 +03:00
Handler (oder Callback-Funktionen) direkt aufzurufen.
Wie auch immer, die Syntax kann zu Beginn verwirrend wirken.
Zum Beispiel: Verwende str_reverse von einem Pointer
*/
2020-02-17 00:27:19 +03:00
void str_reverse_through_pointer(char *str_in) {
2020-01-31 07:15:23 +03:00
// Definiere eine Funktionspointer-Variable, welche f genannt wird.
void (*f)(char *); // Signatur sollte genau der Funktion entsprechen.
2020-02-01 04:26:58 +03:00
f = &str_reverse; // weise die Adresse der wirklichen Funktion zu
// (zur Laufzeit bestimmt)
2020-01-31 07:15:23 +03:00
// `f = str_reverse;` würde auch funktionieren, da Funktionen zu Pointern
// reduziert werden (ähnlich wie Arrays)
(*f)(str_in); // Die Funktion einfach mit dem Pointer aufrufen
2020-02-01 04:26:58 +03:00
// f(str_in); // Dies ist eine weitere gültige Alternative um eine Funktion
2024-09-06 22:52:26 +03:00
// aufzurufen.
2020-01-31 07:15:23 +03:00
}
/*
Solange die Signaturen der Funktionen übereinstimmen, kann man sämtliche Funktionen
2024-08-30 20:56:14 +03:00
demselben Pointer zuweisen. Funktionspointer sind auf Grund der Einfachheit und
2020-01-31 07:15:23 +03:00
Leserlichkeit normalerweise wie folgt `typedef` d
*/
2020-02-02 23:02:01 +03:00
typedef void (*my_fnp_type)(char *);
2020-01-31 07:15:23 +03:00
// Danach werden diese genutzt, um die wirkliche Pointervariable zu deklarieren.
// ..
2020-02-02 23:02:01 +03:00
// my_fnp_type f;
2020-01-31 07:15:23 +03:00
// Spezialzeichen
// Im folgenden sin die englischen Begriffe jeweils in Klammern geschrieben,
2024-08-30 20:56:14 +03:00
// da diese Begriffe auch im deutschen Sprachgebrauch verwendet werden.
2020-01-31 07:15:23 +03:00
'\a'; // Alarmzeichen (alert (bell) character)
'\n'; // Zeichen für neue Linie (newline character)
'\t'; // Tab (tab character (left justifies text))
'\v'; // Vertikaler Tab (vertical tab)
'\f'; // Neue Seite (new page (form feed))
'\r'; // Wagenrücklauf (carriage return)
'\b'; // Backspace-Zeichen (backspace character)
2020-02-01 03:00:55 +03:00
'\0'; // Null-Byte (NULL character). In C wird dieses Zeichen normalerweise am
// Ende eines Strings gesetzt.
// Beispiel: Hallo\n\0. "\0" wird per Konvention verwendet, um das Ende
// eines Strings zu kennzeichnen.
2020-01-31 07:15:23 +03:00
'\\'; // Backslash (backslash)
'\?'; // Fragezeichen (question mark)
'\''; // einfaches Anführungszeichen (single quote)
'\"'; // doppeltes Anführungszeichen (double quote)
2020-02-01 04:26:58 +03:00
'\xhh'; // Hexadezimale Zahl (hexadecimal number.) Beispiel:
// '\xb' = Zeichen für vertikalen Tab
2020-01-31 07:15:23 +03:00
'\0oo'; // Oktalzahl (octal number). Beispiel \013 = Zeichen für vertikalen Tab
//Ausgabeformatierung
"%d"; // Integer
2020-02-01 03:00:55 +03:00
"%3d"; // Integer mit einer minimalen Länge von drei Zeichen.
2020-01-31 07:15:23 +03:00
"%s"; // String
2020-02-01 03:00:55 +03:00
"%f"; // Gleitkommazahl (float)
"%ld"; // genauere Gleitkommazahl (long)
"%3.2f"; // Mindestens drei Zeichen vor und drei nach dem Komma.
2020-01-31 07:15:23 +03:00
"%7.4s"; // (Kann auch mit Strings gemacht werden)
2020-02-01 03:00:55 +03:00
"%c"; // einzelnes Zeichen (char)
2020-02-01 04:26:58 +03:00
"%p"; // Pointer. Merke: man muss den Pointer zu void umwandeln,
// bevor `printf` funktioniert.
2020-01-31 07:15:23 +03:00
"%x"; // Hexadezimal
"%o"; // Oktalzahl
"%%"; // Gibt % aus
2020-02-01 02:27:18 +03:00
////////////////////////////////////////////////
2020-02-01 03:00:55 +03:00
// Reihenfolge der Auswertung von Operatoren
2020-02-01 02:27:18 +03:00
////////////////////////////////////////////////
2020-01-31 07:15:23 +03:00
2020-02-01 02:27:18 +03:00
//-------------------------------------------------------//
// Operatoren | Assoziativität //
//-------------------------------------------------------//
// () [] -> . | linksassoziativ //
// ! ~ ++ -- + = *(type)sizeof | rechtsassoziativ //
// * / % | linksassoziativ //
// + - | linksassoziativ //
// < < >> | linksassoziativ //
// < < = > >= | linksassoziativ //
// == != | linksassoziativ //
// & | linksassoziativ //
// ^ | linksassoziativ //
// | | linksassoziativ //
// & & | linksassoziativ //
// || | linksassoziativ //
// ?: | rechtsassoziativ //
// = += -= *= /= %= & = ^= |= < < = >>= | rechtsassoziativ //
// , | linksassoziativ //
//-------------------------------------------------------//
2020-01-31 07:15:23 +03:00
2020-02-01 02:27:55 +03:00
////////////////////////////////////////////////
// Header-Dateien
////////////////////////////////////////////////
2020-01-31 07:15:23 +03:00
2020-02-01 02:27:55 +03:00
/*
Header-Dateien sind ein wichtiger Teil von C, da sie eine Verbindung zwischen
2020-02-02 23:44:15 +03:00
unterschiedlichen C-Quelldateien herstellen. Außerdem vereinfachen Header-Dateien
2020-02-01 03:00:55 +03:00
den Code und Definitionen, da diese in separaten Dateien geschrieben werden können.
2020-02-01 02:27:55 +03:00
Header-Dateien sind von der Syntax her ähnlich zu C-Quelldateien, allerdings haben
die Header-Dateien die Dateiendung `.h` . Header-Dateien können im Quellcode mit
der `#include` -Anweisung eingebunden werden z.B. `#include "beispiel.h". Die
vorherige Anweisung geht davon aus, dass sich die Header-Datei im selben Ordner
befindet wie die C-Quelldatei.
*/
// Eine sichere Möglichkeit, einen Header mehrere Male zu definieren bietet, das
// folgende Statement. Die mehrfache Definition geschieht, wenn Kreisabhängigkeiten
// bestehen.
2020-02-02 23:02:01 +03:00
#ifndef EXAMPLE_H /* Wenn EXAMPLE_H noch nicht definiert wurde */
#define EXAMPLE_H /* definiere das Makro EXAMPLE_H */
2020-02-01 02:27:55 +03:00
2024-08-30 20:56:14 +03:00
// Es können weitere Header innerhalb eines Headers eingebunden werden, was dazu
2020-02-01 02:27:55 +03:00
// führt, dass diese bereits in anderen Dateien eingebunden wurden. So kann eine
// Header-Datei in mehreren Dateien eingebunden werden. zum Beispiel:
#include <string.h>
2020-01-31 07:15:23 +03:00
2020-02-01 02:27:55 +03:00
// Wie in den Quelldateien können auch in den Header-Dateien Makros definiert
// werden und in anderen Dateien verwendet werden, welche diesen Header einbinden.
2020-02-02 23:02:01 +03:00
#define EXAMPLE_NAME "Dennis Ritchie"
2020-02-01 02:27:55 +03:00
// Funktionsmakros können auch definiert werden.
#define ADD(a, b) ((a) + (b))
// Beachte die Klammern, welche um die Argumente geschrieben wurden - diese sind
// wichtig, damit sichergestellt werden kann, dass a und b nicht unerwartet
// erweitert werden. Zum Beispiel: `MUL (x,y) (x * y)` ; Bei der Verwendung von
// `MUL(1 + 2, 3)` würde dies wie folgt erweitert werden: `(1 + 2 * 3)` , was zu
2020-02-01 03:00:55 +03:00
// einem falschen Resultat führt.
2020-02-01 02:27:55 +03:00
// Strukturen und Typendefinitionen können verwendet werden, um die Konsistenz
// zwischen unterschiedlichen Dateien beizubehalten.
2020-02-17 00:27:19 +03:00
typedef struct Node {
2020-02-02 23:02:01 +03:00
int value;
struct Node *next;
}Node;
2020-02-01 02:27:55 +03:00
// Dies kann auch mit Aufzählungen gemacht werden.
2020-02-02 23:02:01 +03:00
enum traffic_light_state {GREEN, YELLOW, RED};
2020-02-01 02:27:55 +03:00
2024-08-30 20:56:14 +03:00
// Funktionsprototypen können auch in Header-Dateien definiert werden, um die
2020-02-01 02:27:55 +03:00
// Funktion in unterschiedlichen Dateien zu verwenden, aber dies wird als schlechte
// Praxis angesehen. Definitionen sollten in einer C-Datei erstellt werden.
2020-02-02 23:02:01 +03:00
Node create_linked_list(int *value, int length);
2020-02-01 02:27:55 +03:00
2020-02-02 23:44:15 +03:00
// Außer den oben genannten Elementen, sollten weitere Definitionen in einer
// C-Datei gemacht werden. Übermäßige Includes und Definitionen sollten auch
2020-02-01 02:27:55 +03:00
// nicht einer Header-Datei gemacht werden. Stattdessen wird es empfohlen, diese
// in eine separate Header-Datei oder in eine C-Quelldatei zu schreiben.
#endif /* Ende der Präprozessordirektive */
```
2024-04-06 18:33:50 +03:00
2020-02-01 02:28:07 +03:00
## Weiterführende Literatur
Das Beste wird es sein, wenn man sich ein Exemplar des Buches
["The C Programming Language" ](https://de.wikipedia.org/wiki/The_C_Programming_Language ) besorgt.
2020-02-01 04:26:58 +03:00
Dieses Buch gilt als **das** Buch über die Programmiersprache C und wurde
von Dennis Ritchie, dem Erfinder der Programmiersprache C, und Brian Kernighan
geschrieben.
2020-02-01 02:28:07 +03:00
Sei vorsichtig, da dieses Buch mittlerweile schon etwas älter ist und gewisse
Unkorrektheiten (d.h. Ideen, welche nicht mehr als gut empfunden werden.) oder
mittlerweile geänderte Praktiken enthält. [Hinweis: Das Buch wurde auf Englisch
geschrieben, es gibt aber auch eine Übersetzung davon]
2020-02-03 01:08:55 +03:00
Eine weitere gute Ressource ist [Learn C The Hard Way ](http://learncodethehardway.org/c/ ).
2020-02-01 02:28:07 +03:00
[Englisch]
Solltest du Fragen zu C haben, so lies die FAQ [compl.lang.c Frequently Asked Questions ](http://c-faq.com ).[Englisch]
2020-02-02 23:44:15 +03:00
Außerdem ist es wichtig, eine saubere Einrückung zu verwenden. Des weiteren ist
2020-02-01 02:28:07 +03:00
es wichtig, dass der Codestil möglichst konsistent ist. Es ist wichtiger, lesbaren
Code zu schreiben als Code, welcher clever und schnell ist. Es lohnt sich ein
Blick auf den [Codestil des Linuxkernel ](https://www.kernel.org/doc/Documentation/process/coding-style.rst ) zu werfen. [Englisch]
2020-01-31 07:14:28 +03:00
[1] [Why isn't sizeof for a struct equal to the sum of sizeof of each member? ](http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member )