C Programmierung – Benützung des Makefiles

10 April
Termin: 8 Mai (13 Mai)

Theorie | Beispiel | Aufgaben | Anmerkungen


Theoretische Zusammenfassung:

Vom Quellcode bis zum ausführbaren Programm

Vorverarbeitung (preprocess)
Während der Vorverarbeitung erhalten wir vom Quellcode einen anderen Quellcode, in dem die Makrodefinitionen (#define) erweitert sind, und der die "include" Dateien (#include) enthaltet.

Übersetzung (compile)
Nach der Übersetzung erhalten wir vom Quellcode (z.B. C, C++) einen Objektcode. Im Fall der C oder C++ Programmen ist die Eingabe eine C oder C++ Quellcode enthaltene Datei, als Ausgabe erhalten wir eine Objektcode enthaltene Datei. Der Übersetzer wird auch die Vorverarbeitung durchführen. Die C Übersetzer unter UNIX erstellen im allgemeinen von der Eingabe-Datei eine Objektcode enthaltene Datei mit dem gleichen Namen, aber mit Erweiterung „.o”

Zusammenbindung (link)
Die Aufgabe dieser Arbeitsphase ist, wenn mehrere Objektcode enthaltene Dateien gegeben, von diesen, und eventuell vom Programm benützten Bibliotheken eine ausführbare Datei zu erstellen. Das ist notwendig, weil das Programm aus mehreren Modulen bestehen kann, die einzeln übersetzt werden. Außerdem können wir auch äußerliche Bibliotheken benützen.

Das gcc/g++ Übersetzerprogramm
Das gcc Übersetzerprogramm ist momentan einer der kräfigsten C Übersetzers. Seine, für die Übersetzung von C++ Quellcode geeignete Erweiterung ist g++, dass wir auch unter dem Namen gcc aufrufen können. Der Übersetzer wird nach der Erweiterung der Quellcode enthaltene Datei entscheiden worüber es sich handelt. „.c” Erweiterung heisst C, während „.C”, „.cc”, „.cpp”, „.c++”, usw. Erweiterungen stehen für C++ Quellcode. Dieses Übersetzerprogramm führt auch die Vorverarbeitung und Zusammenbindung durch. Wenn wir nicht jede Phase durchgehen möchten, können wir das den Übersetzer mit Hilfe von Optionen oder mit der Erweiterung der Datei bekanntgeben. Wenn nicht anders gegeben, wird der Name der ausführbaren Datei a.out. Mehrere Informationen: man gcc.

Beispiele:

Ein einziges Modul:
    gcc Bsp.C -o Bsp

Mehrere Module:
    gcc Bsp.C -c
    gcc Hilfe.cc -c
    gcc Bsp.o Hilfe.o -o Bsp

Wenn auch eine äusserliche Bibliothek benützt wird:
    gcc Bsp.C -c
    gcc Hilfe.cc -c
    gcc Bsp.o Hilfe.o -I/usr/lib/libncurses.a -o Bsp

Das make Hilfsprogramm – makefile

Im Fall der komplexeren Programmen besteht ein Programm aus mehreren Modulen. Diese Modulen sind voneinander abhängig, wenn eine davon modifiziert wird, kann das als Volge haben, dass auch andere Modulen wieder übersetzt werden sollen, und das ganze Programm wieder zusammengebunden werden soll. Zwischen den verschiedenen Modulen bestehen „Abhängigkeiten”. Wir können mit der Hilfe des make Hilfsprogramms ein grosses Projekt sehr einfach übersetzen so, dass nur die Module neu übersetzt werden, für welche das –wegen der Abhängigkeiten– nötig ist, der Programmierer muss nicht jede Änderung beachten. Das make Hilfsprogramm wird nach den, in der Makefile gegebenen Regeln und Zeitstempel der Dateien entscheiden, welche Befehle ausgeführt werden sollen.

Eine makefile enthaltet Regeln, Variablendefinitionen, Direktiven und Kommentare.

Regeln
Ein Regel bestimmt von welche Dateien der Zieldatei dieser Regel abhängig ist, bzw. mit welchem Befehl wird sie erstellt. Wenn der Zeitstempel einer der Abhängigkeit verursachenden Dateien neuer las der, der Zieldatei ist, wird der Befehl ausgeführt. Die Struktur einer Regel:

Zieldatei: Abhängigkeit verursachende Dateien
    Kommandozeile

Zieldatei: Abhängigkeit verursachende Dateien; Kommandozeile

Die Kommandozeile wird meistens in eine neue Zeile geschrieben. In diesem Fall soll die Zeile unbedingt mit einem <Tab> Charakter anfangen. Kann auch mehrere Befehle enthalten. Diese werden entweder mit „;” getrennt oder werden in eine, mit <Tab> angefangene neue Zeile geschrieben.

Variablendefinitonen
Die Variablendefiniton ist eine Zeile, die gibt eine Zeichenkette als Wert einer Variable an. Weiterhin wird das Programm den Variablenname mit dieser Zeichenkette umtauschen.

Direktiven
Eine Direktive weist das make Hilfsprogramm an, dass es, während der Makefile eingelesen wird, soll eine spezielle Aufgabe durchführen. Das kann z. B. das Einlesen einer anderen Makefile oder eine Bedingung, usw. sein.

Kommentar
Das Programm betrachtet den, nach „#” folgenden Teil als Kommentar, wird also nicht ausgewertet. Ebenfalls ist eine Leere Zeile auch Kommentar.

Wir können das make Hilfsprogramm einfach ohne Parameter aufrufen, in diesem Fall soll der Name der Makefile „makefile”, „Makefile”, „GNUmakefile” sein, oder können wir mit der Option -f den Namen der Makefile angeben.

Mehrere Informationen: info make

Beispiel – ein Makefile

# Variablendeklarationen  

CC = g++   

CCFLAGS = -Wall 

SRC = Hilfe.cc \ 
      Bsp.cc 

OBJ = Hilfe.o \ 
      Bsp.o 

PROGRAM = Bsp 

# Regeln 

$(PROGRAM) : $(OBJ) 
    $(CC) $(OBJ) -o $(PROGRAM)   

Hilfe.o : Hilfe.cc Daten.h 
    $(CC) Hilfe.cc -c $(CCFLAGS) 

Bsp.o : Bsp.cc Daten.h 
    $(CC) Bsp.cc -c $(CCFLAGS) 

# Befehle zum Löschen  

.PHONY : clean 
clean : 
    rm -f $(PROGRAM) $(OBJ) core *~

Aufgaben:

1. Schreiben Sie eine Funktion Wurzel(k,a), welche die Wurzel der Ordnung k von a mit Genaugkeit e=0.00000001 (k>2 ist eine natürliche Zahl, a ist eine positive Realzahl), als Grenzwert der Folge xn+1 = 1/k ((k-1) xn +a/xnk-1) ausrechnet, wenn x0 = a/2.
Diese Funktion wird benützt um die Summe S = Wurzel(k,x1) + ... + Wurzel(k,xn) auszurechnen, wo x1, x2, ..., xn positive Realzahlen sind, die von der Datei „input.dat” eingelesen werden.

2. Schreiben Sie eine Funktion „ggT(k,m)”, die den größten gemeinsamen Teiler von k und m ausrechnet. Diese Funktion wird benützt um den größten gemeinsamen Teiler der natürlichen Zahlen x1, x2, ..., xn, n>1 auszurechnen. Die Zahlen werden von der Datei „input.dat” eingelesen.

3. Schreiben Sie eine Funktion „kgM(k,m)”, die das kleinse gemeinsame Mehrfache von k und m ausrechnet. Diese Funktion wird benützt um das kleinse gemeinsame Mehrfache der natürlichen Zahlen x1, x2, ..., xn, n>1, auszurechnen. Die Zahlen werden von der Datei „input.dat” eingelesen.

4. Schreiben Sie eine Funktion „aProg(k,m)”, die überprüft, ob die Zahlen a, b und c eine arithmetische Progression bilden. Mit der Hilfe dieser Funktion wird entschieden ob die Zahlen x1, x2, ..., xn, n>1, eine arithmetische Progression bilden. Die Zahlen werden von der Datei „input.dat” eingelesen.

5. Schreiben Sie eine Funktion „gProg(k,m)”, die überprüft, ob die Zahlen a, b und c eine geometrische Progression bilden. Mit der Hilfe dieser Funktion wird entschieden ob die Zahlen x1, x2, ..., xn, n>1, eine geometrische Progression bilden. Die Zahlen werden von der Datei „input.dat” eingelesen.

6. Schreiben Sie eine Funktion „Test(k)”, die überprüft ob wir die gleiche Zahl wie k erhalten, wenn wir die Ziffern von k in umgekehrter Reihenfolge aufschreiben. Mit der Hilfe dieser Funktion wird die Anzahl der Zahlen mit dieser Eigenschaft von den natürlichen Zahlen x1, x2, ..., xn, n>1 ausgerechnet. Die Zahlen werden von der Datei „input.dat” eingelesen.

7. Schreiben Sie eine Funktion „perfekt(k)”, die überprüft ob die natürliche Zahl k perfekt ist, d.h. die Summe ihrer Teiler (ausgenommen die Zahl selbst) ergibt die Zahl. Z. B. 6=1+2+3. Mit der Hilfe dieser Funktion wird die Anzahl der perfekten Zahlen von den natürlichen Zahlen x1, x2, ..., xn, n>1 ausgerechnet. Die Zahlen werden von der Datei „input.dat” eingelesen.

8. Schreiben Sie eine Funktion „eNorm(x,n)”, die das euklidsche Norm der Elementen der Vektor x = (xi), i = 1, ..., n angibt (die Quadratwurzel der Quadrat-Summe der Vektorelementen). Diese Funktion wird benützt um das euklidsche Norm von k Vektoren auszurechnen. Die Vektoren werden von der Datei „input.dat” eingelesen, und das Ergebnis wird mit der Eingabedaten in die Datei „output.dat” geschrieben.

9. Schreiben Sie eine Funktion „Abstand(x,y,n)”, Welche die euklidsche Distanz der Vektoren x = (xi) und y = (yi), i = 1, ..., n (Die Quadratwurzel der Quadrat-Summe der Differenzen zwischen den Elementen mit gleicher Rang) angibt. Diese Funktion wird benützt um die Distanz von zwei Vektoren zu bestimmen. Die Vektoren werden von der Datei „input.dat” eingelesen, und das Ergebnis wird mit der Eingabedaten in die Datei „output.dat” geschrieben.


Anmerkungen:
1. Die Programmen werden in C geschrieben, und sollen wenigstens aus 2 C Modulen bestehen.
2. Die Deklarationen sollen in einer .h Headerdatei angegeben werden.
3. Für jede Aufgabe soll auch das Makefile geschrieben werden.
4. Die Dateien „input.dat” und „output.dat” sind Textdateien.
 
Dokumentation:
Makefiles – eine Einführung