Größere C-Programme bestehen aus vielen Dateien. Der Präprozessor und Header-Dateien halten sie zusammen.
Der Präprozessor
Er arbeitet vor dem Compiler und ersetzt Text. Seine Anweisungen beginnen mit #:
| Anweisung | Wirkung |
|---|---|
#include <datei> / "datei" | fügt den Dateiinhalt ein |
#define NAME wert | Textersetzung |
#define MAKRO(x) ... | Makro mit Argumenten |
#ifdef, #ifndef, #if, #else, #endif | bedingte Übersetzung |
#undef NAME | Definition aufheben |
#error, #pragma | Fehlermeldung, Compiler-Hinweise |
#include <stdio.h>
#define PI 3.14159
#define QUADRAT(x) ((x) * (x)) // alle Klammern setzen!
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define VERSION "1.2"
#ifdef DEBUG
#define LOG(msg) printf("[DEBUG] %s\n", msg)
#else
#define LOG(msg) ((void) 0)
#endif
int main(void) {
printf("%.2f %d %d\n", PI * QUADRAT(2), QUADRAT(3 + 1), MAX(4, 9));
printf("Version %s, Datei %s, Zeile %d\n", VERSION, "x.c", __LINE__);
LOG("nur mit -DDEBUG sichtbar");
#if defined(__linux__)
puts("Linux");
#elif defined(_WIN32)
puts("Windows");
#else
puts("anderes System");
#endif
return 0;
}12.57 16 9 Version 1.2, Datei x.c, Zeile 16 Linux
Vordefinierte Makros: __FILE__, __LINE__, __DATE__, __func__, __STDC_VERSION__. Mit gcc -DDEBUG definierst du ein Makro von außen.
Header und Quelldateien
Teile ein Programm in Module: Die .h-Datei (Header) beschreibt die Schnittstelle (Deklarationen), die .c-Datei enthält die Umsetzung.
projekt/
├─ main.c nutzt das Modul
├─ mathe.h Schnittstelle (Prototypen, Typen)
└─ mathe.c Umsetzung/* mathe.h */
#ifndef MATHE_H /* Include-Guard: verhindert doppeltes Einbinden */
#define MATHE_H
int addiere(int a, int b);
double mittelwert(const int *a, int n);
#endif/* mathe.c */
#include "mathe.h"
int addiere(int a, int b) { return a + b; }
double mittelwert(const int *a, int n) {
double s = 0;
for (int i = 0; i < n; i++) s += a[i];
return n ? s / n : 0;
}/* main.c */
#include <stdio.h>
#include "mathe.h"
int main(void) {
int z[] = {2, 4, 6};
printf("%d %.1f\n", addiere(2, 3), mittelwert(z, 3));
return 0;
}Übersetzen:
gcc -Wall -Wextra -c mathe.c # erzeugt mathe.o
gcc -Wall -Wextra -c main.c # erzeugt main.o
gcc mathe.o main.o -o programm # Linker verbindet beides
# oder alles in einem Schritt:
gcc -Wall -Wextra main.c mathe.c -o programm- Der Include-Guard (
#ifndef ... #define ... #endif) oder#pragma onceverhindert, dass ein Header mehrfach eingefügt wird - In Headern steht nur, was andere wissen müssen: Prototypen,
typedef,struct-Definitionen,extern-Deklarationen - Variablen und Funktionen, die nur in einer Datei gebraucht werden, bekommen
static - Globale Variablen für mehrere Dateien:
extern int zaehler;im Header, Definitionint zaehler = 0;in genau einer.c-Datei
Das Programm als Ein-Datei-Beispiel
Zur Demonstration das Zusammenspiel in einer Datei (Schnittstelle oben, Umsetzung darunter):
#include <stdio.h>
/* "Header"-Teil */
int addiere(int a, int b);
double mittelwert(const int *a, int n);
/* Verwendung */
int main(void) {
int z[] = {2, 4, 6};
printf("%d %.1f\n", addiere(2, 3), mittelwert(z, 3));
return 0;
}
/* "Quelldatei"-Teil */
int addiere(int a, int b) { return a + b; }
double mittelwert(const int *a, int n) {
double s = 0;
for (int i = 0; i < n; i++) s += a[i];
return n ? s / n : 0;
}5 4.0
Make: Bauen automatisieren
Bei vielen Dateien übersetzt make nur, was sich geändert hat. Die Makefile:
CC = gcc
CFLAGS = -Wall -Wextra -std=c17 -g
programm: main.o mathe.o
$(CC) $^ -o $@
%.o: %.c mathe.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o programmmake baut, make clean räumt auf. Größere Projekte nutzen CMake, Meson oder Ninja.
Bibliotheken
- Die C-Standardbibliothek wird automatisch gelinkt; die Mathe-Bibliothek braucht
-lm - Statische Bibliothek:
ar rcs libmathe.a mathe.o, Verwenden mit-L. -lmathe - Dynamische Bibliothek (
.so,.dll):gcc -shared -fPIC mathe.c -o libmathe.so - Fremde Bibliotheken findest du mit
pkg-config --cflags --libs name
Merke
- Der Präprozessor ersetzt Text (
#include,#define,#ifdef) vor dem Compiler - Header (
.h) enthalten die Schnittstelle,.c-Dateien die Umsetzung - Include-Guards verhindern doppeltes Einbinden
- Makros sparsam und mit allen Klammern; besser
static inlineundconst makeoder CMake bauen größere Projekte automatisch
Aufgabe
Teile ein kleines Programm in liste.h, liste.c und main.c (verkettete Liste mit push und ausgeben) und baue es mit einer Makefile.