Programmierung in C – Umfassende Zusammenfassung für Einsteiger
Einführung in die Programmiersprache C
Historischer Hintergrund
- Entstehung: C wurde in den 1970er Jahren von Dennis Ritchie bei Bell Labs entwickelt.
- Zweck: Ursprünglich für die Entwicklung des Unix-Betriebssystems konzipiert.
- Einfluss: C hat viele moderne Programmiersprachen beeinflusst, darunter C++, Java und C#.
Merkmale und Eigenschaften
- Maschinennähe: C ermöglicht eine Programmierung, die nah an der Hardware liegt, was es erlaubt, effizienteren und schnelleren Code zu schreiben.
- Portabilität: C-Programme können auf verschiedenen Hardwareplattformen und Betriebssystemen kompiliert und ausgeführt werden.
- Speicherzugriff: Direkter Zugriff auf Speicheradressen ermöglicht eine feinere Kontrolle über Ressourcen.
- Beliebtheit: Trotz ihres Alters ist C immer noch eine der am häufigsten verwendeten Programmiersprachen, insbesondere in der System- und Embedded-Programmierung.
Ist C eine überholte Sprache?
- Aktuelle Relevanz: C bleibt eine der Top 10 Programmiersprachen weltweit (Quelle: TIOBE-Index).
- Open-Source-Projekte: Viele wichtige Open-Source-Projekte sind in C geschrieben, was die anhaltende Bedeutung der Sprache unterstreicht.
- Anwendungsbereiche: C wird häufig in Bereichen eingesetzt, in denen Effizienz und Kontrolle über die Hardware entscheidend sind, wie z. B. in Betriebssystemen, Treibern und eingebetteten Systemen.
Grundlegende Konzepte und Prinzipien von C
Vertrauen in den Programmierer
- Wenige Schutzmechanismen: C setzt voraus, dass der Programmierer weiß, was er tut. Es gibt kein automatisches Bounds Checking (Überprüfung der Array-Grenzen) oder Exceptions (Ausnahmen bei Fehlern).
- Direkter Systemzugriff: C ermöglicht direkten Zugriff auf Hardware und Speicher, was sowohl mächtig als auch gefährlich sein kann, wenn man nicht vorsichtig ist.
- Kaum Abstraktion: Im Vergleich zu höhergradigen Sprachen bietet C weniger Abstraktionen, was zu einem besseren Verständnis der Abläufe auf Maschinenebene führt.
Einfachheit und Effizienz
- Kleiner Sprachumfang: C ist relativ einfach und hat einen kleinen Sprachumfang, was das Lernen und Verstehen erleichtert.
- Effiziente Ausführung: Die Priorität liegt auf der Geschwindigkeit der Programmausführung. Programme in C laufen in der Regel sehr schnell, da sie wenig Overhead haben.
- Einheitliche Methoden: Es gibt oft nur eine oder wenige Arten, eine Aufgabe zu lösen, was zu konsistenterem Code führt.
Verantwortung des Programmierers
- Sicherheit und Portabilität: Obwohl C leistungsfähig ist, liegt die Verantwortung für sicheren und portablen Code beim Programmierer.
- Fehleranfälligkeit: Ohne automatische Speicherverwaltung oder Schutzmechanismen können Fehler wie Buffer Overflows oder Speicherlecks auftreten, wenn der Programmierer nicht sorgfältig ist.
Literatur und Ressourcen
- ”C von A bis Z” von Jürgen Wolf
- Ein umfassendes Buch zur Programmiersprache C, verfügbar als Openbook unter: openbook.rheinwerk-verlag.de/c_von_a_bis_z/
- “Beej’s Guide to C Programming”
- Ein kostenloses Online-Tutorial: beej.us/guide/bgc/html/
- “Linux-UNIX-Programmierung” von Jürgen Wolf
- Fokus auf die Programmierung unter Linux und UNIX, ebenfalls als Openbook verfügbar: openbook.rheinwerk-verlag.de/linux_unix_programmierung/
- “The Linux Programming Interface” von Michael Kerrisk
- Ein tiefgehendes Buch über Systemprogrammierung unter Linux.
- Weitere Online-Ressourcen:
Erster Schritt: Hello World!
Beispielcode (hello.c)
#include <stdio.h> // Einbindung der Standard Input/Output-Bibliothek
int main() { // Hauptfunktion, der Einstiegspunkt des Programms
printf("Servus Universe!\n"); // Ausgabe einer Zeichenkette auf dem Terminal
return 0; // Rückgabewert 0 signalisiert erfolgreiche Ausführung
}Erklärungen:
#include <stdio.h>: Dieser Präprozessor-Befehl bindet die Standardbibliothek für Ein- und Ausgabe ein, die Funktionen wieprintfbereitstellt.int main(): Die Hauptfunktion, die jedes C-Programm enthält. Die Ausführung beginnt hier.printf("Servus Universe!\n");: Gibt die Zeichenkette “Servus Universe!” gefolgt von einem Zeilenumbruch auf dem Bildschirm aus.return 0;: Beendet diemain-Funktion und gibt 0 zurück, was oft als Indikator für einen erfolgreichen Programmablauf verwendet wird.
Kompilieren des Programms
gcc hello.c -o helloworld- Erklärung: Der Befehl
gccist der GNU C Compiler. Er übersetzt den Quellcodehello.cin eine ausführbare Datei namenshelloworld.
Ausführen des Programms
./helloworld-
Ausgabe:
Servus Universe! -
Erklärung: Das Programm wird gestartet, und die in
printfangegebene Nachricht wird auf dem Terminal ausgegeben.
Kommentare in C
- Einzeilige Kommentare: Beginnen mit
//und reichen bis zum Ende der Zeile.// Dies ist ein einzeiliger Kommentar - Mehrzeilige Kommentare: Beginnen mit
/*und enden mit*/. Sie können über mehrere Zeilen gehen./* * Dies ist ein * mehrzeiliger Kommentar */
Warum Kommentare wichtig sind:
- Lesbarkeit: Sie helfen, den Code für andere (und für sich selbst zu einem späteren Zeitpunkt) verständlicher zu machen.
- Dokumentation: Erläutern die Funktionsweise von Codeabschnitten oder weisen auf wichtige Details hin.
Der Präprozessor
- Funktion: Bearbeitet den Quellcode vor dem eigentlichen Kompilierungsschritt.
- Direktiven: Anweisungen, die mit
#beginnen.
Wichtige Präprozessor-Direktiven
#include: Bindet Header-Dateien ein.#include <stdio.h>#define: Definiert Makros oder Konstanten.#define PI 3.1415#ifdef/#ifndef/#endif: Bedingte Kompilierung.#ifdef DEBUG printf("Debugging Informationen\n"); #endif
Anwendungsfälle:
- Modularisierung: Erleichtert die Organisation von Code in verschiedene Dateien.
- Plattformunabhängigkeit: Ermöglicht Anpassungen für verschiedene Betriebssysteme oder Umgebungen.
- Konstanten: Verwendet, um feste Werte zu definieren, die im gesamten Programm verwendet werden.
Funktionen in C
Aufbau einer Funktion
<Rückgabetyp> <Funktionsname>(<Parameterliste>) {
// Funktionskörper
}- Rückgabetyp: Der Datentyp des Wertes, den die Funktion zurückgibt. Bei
voidgibt die Funktion keinen Wert zurück. - Funktionsname: Eindeutiger Name der Funktion.
- Parameterliste: Optional. Variablen, die der Funktion übergeben werden.
Beispiele
-
Funktion mit Rückgabewert
int addiere(int a, int b) { return a + b; }- Erklärung: Die Funktion
addierenimmt zwei ganze Zahlen als Parameter und gibt ihre Summe zurück.
- Erklärung: Die Funktion
-
Funktion ohne Rückgabewert
void ausgabe(int x) { printf("%i", x); }- Erklärung: Die Funktion
ausgabegibt den übergebenen Wert auf dem Terminal aus und hat keinen Rückgabewert.
- Erklärung: Die Funktion
Aufruf von Funktionen
- Syntax:
Rückgabewert = Funktionsname(Argumente); - Beispiel:
int resultat = addiere(5, 7); // resultat erhält den Wert 12
Warum Funktionen wichtig sind:
- Modularität: Funktionen ermöglichen es, Code in logische Blöcke zu unterteilen.
- Wiederverwendbarkeit: Einmal geschriebene Funktionen können mehrfach verwendet werden.
- Lesbarkeit: Verbessern die Struktur und Verständlichkeit des Codes.
Formatierte Ein- und Ausgabe
Ausgabe mit printf
printf("Das Ergebnis ist %i\n", variable);- Formatierungszeichen: Platzhalter, die angeben, wie die nachfolgenden Argumente formatiert werden sollen.
Wichtige Formatierungsanweisungen
%s: Zeichenkette (String)%c: Einzelnes Zeichen (Character)%ioder%d: Ganze Zahl (Integer)%f: Gleitkommazahl (Float)%lf: Gleitkommazahl mit doppelter Genauigkeit (Double)%x: Hexadezimaldarstellung%p: Zeiger (Adresse)%%: Prozentzeichen
Beispiel:
int alter = 25;
printf("Ich bin %i Jahre alt.\n", alter);Eingabe mit scanf
int scanf(const char *format, ...);-
Verwendung:
int zahl; scanf("%i", &zahl);- Achtung: Bei
scanfmuss die Adresse der Variablen übergeben werden (mit&), damit der eingegebene Wert gespeichert werden kann.
- Achtung: Bei
Hinweise:
- Eingabevalidierung:
scanfprüft nicht automatisch, ob die Eingabe dem erwarteten Format entspricht. Es ist daher wichtig, Eingaben zu validieren. - Bufferüberlauf: Bei der Eingabe von Zeichenketten kann es zu Speicherproblemen kommen, wenn die Eingabe länger als der bereitgestellte Speicherplatz ist.
Steuerzeichen (Escape-Sequenzen)
- Definition: Spezielle Zeichenfolgen, die nicht druckbare Zeichen oder spezielle Aktionen repräsentieren.
- Verwendung: Innerhalb von Zeichenketten, um z. B. Zeilenumbrüche oder Tabs einzufügen.
Wichtige Steuerzeichen
\n: Neue Zeile (Line Feed)\r: Wagenrücklauf (Carriage Return)\t: Horizontaler Tabulator\": Doppeltes Anführungszeichen\': Einfaches Anführungszeichen\\: Backslash\0: Nullzeichen (Ende einer Zeichenkette)
Beispiel:
printf("Hallo,\nwelt!\n");- Ausgabe:
Hallo, welt!
Variablen und Datentypen
Allgemeines
- Statische Typisierung: Der Datentyp einer Variablen muss bei der Deklaration festgelegt werden und kann nicht geändert werden.
- Deklaration: Einführung einer Variablen mit Angabe ihres Typs.
int zahl; - Initialisierung: Zuweisung eines Wertes bei der Deklaration.
int zahl = 5;
Grundlegende Datentypen
| Typ | Größe (Bytes) | Wertebereich |
|---|---|---|
char | 1 | -128 bis 127 (oder 0 bis 255, je nach Implementierung) |
unsigned char | 1 | 0 bis 255 |
short | 2 | -32.768 bis 32.767 |
unsigned short | 2 | 0 bis 65.535 |
int | 4 | -2.147.483.648 bis 2.147.483.647 |
unsigned int | 4 | 0 bis 4.294.967.295 |
long | 8 (auf 64-Bit Systemen) | Sehr großer Wertebereich |
float | 4 | Ca. ±3,4 × 10³⁸ |
double | 8 | Ca. ±1,7 × 10³⁰⁸ |
Hinweise:
- Plattformabhängigkeit: Die Größe der Datentypen kann je nach System variieren, insbesondere bei
int,longundpointer. - Präzision: Gleitkommazahlen (
floatunddouble) haben begrenzte Genauigkeit und können Rundungsfehler verursachen.
Beispiel: Variablendeklaration und -initialisierung
int ganzeZahl = 10;
float gleitkommaZahl = 3.14;
char buchstabe = 'A';Operatoren in C
Arithmetische Operatoren
+: Addition-: Subtraktion*: Multiplikation/: Division%: Modulo (Rest der Ganzzahldivision)
Beispiel:
int summe = 5 + 3; // summe ist 8
int rest = 10 % 3; // rest ist 1Zuweisungsoperatoren
=: Zuweisung- Kombinierte Operatoren:
+=,-=,*=,/=,%=
Beispiel:
int a = 5;
a += 3; // a ist jetzt 8Vergleichsoperatoren
==: Gleichheit!=: Ungleichheit<: Kleiner als>: Größer als<=: Kleiner oder gleich>=: Größer oder gleich
Beispiel:
if (a == b) {
// Code, falls a gleich b ist
}Logische Operatoren
&&: Logisches UND||: Logisches ODER!: Logisches NICHT
Beispiel:
if (a > 0 && b > 0) {
// Code, falls a und b beide positiv sind
}Inkrement- und Dekrementoperatoren
++: Erhöht den Wert einer Variablen um 1--: Verringert den Wert einer Variablen um 1
Beispiel:
int i = 0;
i++; // i ist jetzt 1Bitweise Operatoren
&: Bitweises UND|: Bitweises ODER^: Bitweises exklusives ODER (XOR)~: Bitweises NICHT<<: Bitweises Linksverschieben>>: Bitweises Rechtsverschieben
Anwendung von Bitoperatoren:
- Flags setzen und prüfen
- Effiziente Berechnungen
- Maskieren von Bits
Beispiel:
unsigned char flags = 0b00001111;
flags |= 0b00100000; // Setzt das fünfte BitZeiger (Pointer)
Grundkonzepte
- Definition: Ein Zeiger ist eine Variable, die die Adresse einer anderen Variable speichert.
- Deklaration:
int *p;int *pbedeutet, dasspein Zeiger auf einenintist.
Wichtige Operatoren
- Adressoperator
&: Liefert die Adresse einer Variablen.int a = 5; int *p = &a; // p speichert die Adresse von a - Dereferenzierungsoperator
*: Greift auf den Wert an der Adresse zu, auf die der Zeiger zeigt.int wert = *p; // wert ist jetzt 5
Beispiel
#include <stdio.h>
int main() {
int a = 42;
int *pa = &a;
printf("Wert von a: %d\n", a);
printf("Adresse von a: %p\n", (void*)&a);
printf("Wert von pa (Adresse von a): %p\n", (void*)pa);
printf("Wert an der Adresse, auf die pa zeigt: %d\n", *pa);
return 0;
}Erklärungen:
- Zeigerarithmetik: Zeiger können inkrementiert oder dekrementiert werden, wobei sie um die Größe des Typs verschoben werden, auf den sie zeigen.
Arrays (Felder)
Grundlagen
- Definition: Ein Array ist eine Sammlung von Elementen gleichen Datentyps, die sequenziell im Speicher angeordnet sind.
- Deklaration:
int arr[5]; // Ein Array von 5 ganzen Zahlen - Zugriff auf Elemente:
arr[0] = 10; // Setzt das erste Element auf 10 int wert = arr[0]; // Liest das erste Element
Beziehung zwischen Arrays und Zeigern
- Ein Arrayname ohne Index ist ein Zeiger auf das erste Element des Arrays.
- Beispiel:
int *p = arr; // p zeigt auf arr[0]
Iteration über ein Array
for (int i = 0; i < 5; i++) {
printf("Element %d: %d\n", i, arr[i]);
}Hinweise:
- Achtung bei der Arraygröße: C überprüft nicht automatisch, ob auf gültige Indizes zugegriffen wird. Zugriff außerhalb des Arraybereichs führt zu undefiniertem Verhalten.
- Mehrdimensionale Arrays: Arrays können mehrdimensional sein, z. B.
int matrix[3][3];
Strukturen (struct)
Verwendung
-
Definition: Eine Struktur ist ein benutzerdefinierter Datentyp, der mehrere Variablen (Felder) unterschiedlicher Typen enthält.
-
Deklaration und Definition:
struct Person { char name[50]; int alter; float groesse; }; -
Initialisierung:
struct Person person1 = {"Max Mustermann", 30, 1.80}; -
Zugriff auf Strukturmitglieder:
printf("Name: %s\n", person1.name); printf("Alter: %d\n", person1.alter);
Verwendung von typedef
-
Erleichterung: Mit
typedefkann ein Alias für den Strukturnamen erstellt werden.typedef struct { char name[50]; int alter; float groesse; } Person; Person person2 = {"Erika Musterfrau", 28, 1.65};
Warum Strukturen wichtig sind:
- Organisation von Daten: Strukturen ermöglichen es, zusammengehörige Daten zu gruppieren.
- Komplexere Datenmodelle: Sie sind die Grundlage für komplexe Datenstrukturen wie verknüpfte Listen, Bäume usw.
Aufzählungen (enum)
Verwendung
-
Definition: Eine Aufzählung ist ein benutzerdefinierter Datentyp, der eine Liste von benannten Ganzzahlkonstanten definiert.
-
Deklaration:
enum Wochentag { MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG }; -
Zuweisung und Verwendung:
enum Wochentag heute = MITTWOCH; if (heute == MITTWOCH) { printf("Es ist Bergfest!\n"); } -
Standardwerte: Standardmäßig beginnen die Werte bei 0 und erhöhen sich um 1. Die Werte können auch explizit gesetzt werden.
enum Status { OK = 0, FEHLER = -1, UNBEKANNT = 99 };
Vorteile von Aufzählungen:
- Lesbarkeit: Ersetzen von Zahlenwerten durch aussagekräftige Namen.
- Wartbarkeit: Erleichtern Änderungen, da nur die Aufzählung angepasst werden muss.
Kontrollstrukturen
Bedingte Anweisungen
if / else
if (Bedingung) {
// Code, wenn Bedingung wahr ist
} else {
// Code, wenn Bedingung falsch ist
}Beispiel:
if (alter >= 18) {
printf("Volljährig\n");
} else {
printf("Minderjährig\n");
}switch / case
- Verwendung: Für Mehrfachauswahl basierend auf dem Wert einer Variablen.
switch (ausdruck) {
case wert1:
// Code
break;
case wert2:
// Code
break;
default:
// Code
break;
}Beispiel:
int note = 2;
switch (note) {
case 1:
printf("Sehr gut\n");
break;
case 2:
printf("Gut\n");
break;
case 3:
printf("Befriedigend\n");
break;
default:
printf("Nicht bestanden\n");
break;
}Schleifen
for-Schleife
-
Syntax:
for (Initialisierung; Bedingung; Aktualisierung) { // Schleifenkörper } -
Beispiel:
for (int i = 0; i < 10; i++) { printf("%d\n", i); }
while-Schleife
-
Syntax:
while (Bedingung) { // Schleifenkörper } -
Beispiel:
int i = 0; while (i < 10) { printf("%d\n", i); i++; }
do-while-Schleife
-
Syntax:
do { // Schleifenkörper } while (Bedingung); -
Beispiel:
int i = 0; do { printf("%d\n", i); i++; } while (i < 10);
Unterschiede zwischen while und do-while:
- Bei
whilewird die Bedingung am Anfang geprüft. Die Schleife kann also auch kein einziges Mal ausgeführt werden, wenn die Bedingung von Anfang an falsch ist. - Bei
do-whilewird die Bedingung am Ende geprüft. Die Schleife wird also mindestens einmal ausgeführt.
Speicherverwaltung
Stack
- Beschreibung: Ein Bereich im Speicher, der für lokale Variablen und Funktionsaufrufe verwendet wird.
- Eigenschaften:
- Automatische Speicherverwaltung.
- Variablen auf dem Stack existieren nur während der Ausführung der Funktion.
Heap
- Beschreibung: Ein Bereich im Speicher, der für dynamische Speicherverwaltung verwendet wird.
- Eigenschaften:
- Speicher muss manuell angefordert und freigegeben werden.
- Variablen auf dem Heap existieren, bis sie freigegeben werden oder das Programm endet.
Dynamische Speicherverwaltung mit malloc und free
Speicher allokieren
void *malloc(size_t size);-
Verwendung:
int *array = (int *)malloc(10 * sizeof(int)); -
Hinweise:
mallocgibt einen Zeiger auf den Anfang des allokierten Speichers zurück.- Wenn nicht genügend Speicher verfügbar ist, gibt
mallocNULLzurück.
Speicher freigeben
void free(void *ptr);-
Verwendung:
free(array); -
Wichtig:
- Jeder mit
malloc(odercalloc,realloc) allokierte Speicher muss mitfreefreigegeben werden, um Speicherlecks zu vermeiden. - Nach dem Aufruf von
freeist der Zeiger ungültig und sollte nicht mehr verwendet werden.
- Jeder mit
Beispiel
int main() {
int n = 5;
int *zahlen = (int *)malloc(n * sizeof(int));
if (zahlen == NULL) {
printf("Speicher konnte nicht allokiert werden.\n");
return 1;
}
for (int i = 0; i < n; i++) {
zahlen[i] = i * 10;
}
for (int i = 0; i < n; i++) {
printf("%d ", zahlen[i]);
}
free(zahlen);
return 0;
}Erklärungen:
- Speicherinitialisierung: Der mit
mallocallokierte Speicher ist nicht initialisiert. Die Werte darin sind unbestimmt und sollten vor der Verwendung gesetzt werden.
Sichtbarkeit und Gültigkeitsbereich
Globale Variablen
- Definition: Variablen, die außerhalb von Funktionen deklariert sind.
- Sichtbarkeit: Über das gesamte Programm hinweg verfügbar.
- Lebensdauer: Während der gesamten Programmlaufzeit.
Lokale Variablen
- Definition: Variablen, die innerhalb von Funktionen oder Blöcken deklariert sind.
- Sichtbarkeit: Nur innerhalb des Blocks oder der Funktion.
- Lebensdauer: Von der Deklaration bis zum Ende des Blocks oder der Funktion.
Beispiel
int globaleVariable = 10; // Globale Variable
void funktion() {
int lokaleVariable = 5; // Lokale Variable
printf("%d\n", lokaleVariable);
}
int main() {
funktion();
printf("%d\n", globaleVariable);
// printf("%d\n", lokaleVariable); // Fehler: lokaleVariable ist hier nicht sichtbar
return 0;
}Parameterübergabe in Funktionen
Call by Value (Wertübergabe)
- Beschreibung: Eine Kopie des Wertes wird an die Funktion übergeben.
- Auswirkung: Änderungen am Parameter innerhalb der Funktion haben keinen Einfluss auf die Originalvariable.
Beispiel:
void verdoppeln(int x) {
x = x * 2;
}
int main() {
int a = 5;
verdoppeln(a);
printf("%d\n", a); // Ausgabe: 5
return 0;
}Call by Reference (Referenzübergabe)
- Beschreibung: Die Adresse der Variablen wird an die Funktion übergeben.
- Auswirkung: Änderungen am Parameter innerhalb der Funktion beeinflussen die Originalvariable.
Beispiel:
void verdoppeln(int *x) {
*x = *x * 2;
}
int main() {
int a = 5;
verdoppeln(&a);
printf("%d\n", a); // Ausgabe: 10
return 0;
}Erklärungen:
- Zeiger als Parameter: Um Call by Reference zu simulieren, werden Zeiger verwendet.
- Vorteile: Ermöglicht es Funktionen, mehrere Werte zurückzugeben oder große Strukturen effizient zu bearbeiten.
Rekursion
Was ist Rekursion?
- Definition: Eine Funktion ruft sich selbst auf, um ein Problem zu lösen, indem es in kleinere Teilprobleme zerlegt wird.
- Voraussetzung: Es muss eine Abbruchbedingung geben, um endlose Rekursionen zu vermeiden.
Beispiel: Berechnung der Fibonacci-Zahlen
Rekursive Implementierung
long fib_rec(long n) {
if (n <= 1) {
return n;
} else {
return fib_rec(n - 1) + fib_rec(n - 2);
}
}Erklärungen:
- Abbruchbedingung: Wenn
nkleiner oder gleich 1 ist, wirdnzurückgegeben. - Rekursiver Aufruf: Die Funktion ruft sich selbst mit den Werten
n - 1undn - 2auf.
Iterative Implementierung
long fib_it(long n) {
long a = 0, b = 1, temp;
for (long i = 2; i <= n; i++) {
temp = a + b;
a = b;
b = temp;
}
return b;
}Vergleich:
- Effizienz: Die iterative Version ist in diesem Fall effizienter, da die rekursive Version viele redundante Berechnungen durchführt.
- Stack Overflow Risiko: Bei zu vielen rekursiven Aufrufen kann der Stack überlaufen.
Kommandozeilenparameter
Hauptfunktion mit Parametern
int main(int argc, char *argv[]) {
// Code
}argc: Anzahl der übergebenen Argumente (Argument Count).argv: Array der Argumente (Argument Vector).
Verwendung
-
Zugriff auf Argumente:
for (int i = 0; i < argc; i++) { printf("Argument %d: %s\n", i, argv[i]); } -
Beispielaufruf:
./programm datei.txt 123 -
Erklärung:
argv[0]: Name des Programms (./programm)argv[1]: Erstes Argument (datei.txt)argv[2]: Zweites Argument (123)
Anwendungsfälle:
- Dateinamen übergeben
- Optionen und Flags setzen
- Eingabewerte bereitstellen
Man-Pages lesen
- Man-Pages: Systeminterne Dokumentation unter Unix/Linux-Systemen.
Verwendung
man <Thema>-
Beispiel:
man 3 printf3: Sektion 3, die sich auf Bibliotheksfunktionen bezieht.printf: Name der Funktion.
Sektionen
- Benutzerbefehle
- Systemaufrufe
- Bibliotheksfunktionen
- Geräteknoten
- Dateiformate und Konventionen
- Spiele
- Makropakete und Konventionen
- Systemadministration
Tipps:
man man: Zeigt die Man-Page zum Man-Page-System selbst.- Suche innerhalb der Man-Page: Mit
/gefolgt von dem Suchbegriff.
Ein- und Ausgabe mit Dateien
Streams und Dateien
- Definition: Ein Stream ist eine abstrakte Verbindung zwischen einem Programm und einer Datenquelle oder -senke, wie einer Datei, einem Gerät oder einem Speicherbereich.
- Dateityp: Repräsentiert durch den Typ
FILEaus der Header-Datei<stdio.h>.
Öffnen und Schließen von Dateien
-
Öffnen einer Datei:
FILE *fopen(const char *filename, const char *mode);- Modi:
"r": Lesen"w": Schreiben (Datei wird erstellt oder überschrieben)"a": Anfügen an bestehende Datei"r+": Lesen und Schreiben
- Modi:
-
Schließen einer Datei:
int fclose(FILE *stream);
Lesen und Schreiben
-
Schreiben:
fprintf(FILE *stream, const char *format, ...); -
Lesen:
fscanf(FILE *stream, const char *format, ...);
Beispiel
FILE *datei = fopen("daten.txt", "w");
if (datei != NULL) {
fprintf(datei, "Hallo Datei!\n");
fclose(datei);
} else {
printf("Fehler beim Öffnen der Datei.\n");
}Erklärungen:
- Fehlerprüfung: Es ist wichtig, zu überprüfen, ob
fopenerfolgreich war. - Pufferung: Ausgaben werden möglicherweise gepuffert und nicht sofort in die Datei geschrieben. Durch
fclosewird der Puffer geleert.
Vordefinierte Streams
stdin: Standard-Eingabestrom (z. B. Tastatureingaben)stdout: Standard-Ausgabestrom (z. B. Terminalausgabe)stderr: Standard-Fehlerstrom (für Fehlermeldungen)
Alternative Funktionen (Low-Level I/O)
-
Systemaufrufe:
open(),close(),read(),write()
-
Verwendung von Dateideskriptoren: Ganzzahlige Werte, die Dateien repräsentieren.
-
Anwendungsfälle:
- Erforderlich für spezielle Modi wie nicht-blockierende Ein-/Ausgabe.
- Mehr Kontrolle über die Ein-/Ausgabevorgänge.