Ubungspaket 28 ¨
Module und getrenntes ¨ Ubersetzen
Ubungsziele: ¨
1. Verteilen von Programmteilen auf mehrere Dateien 2. Richtige Verwendung der Header-Dateien
3. Richtiger Umgang mit dem C-Compiler Skript:
Kapitel: 55 und 39 sowie insbesondere auch ¨ Ubungspaket 17 Semester:
Wintersemester 2021/22 Betreuer:
Thomas, Tim und Ralf Synopsis:
Im Gegensatz zu vielen klassischen Programmiersprachen bietet C die
M¨ oglichkeit, das Programm auf mehrere Dateien zu verteilen. Diese
Verteilung gestattet es, nur diejenigen Dateien zu ¨ ubersetzen, die sich
auch wirklich ge¨ andert haben; die anderen .o-Dateien k¨ onnen bleiben,
wie sie sind. Diese Option f¨ uhrt zu ¨ ubersichtlichen Quelltext-Dateien
und kann die notwendige ¨ Ubersetzungszeit bei großen Programmen
deutlich reduzieren. Allerdings tritt beim Aufteilen des Quelltextes auf
mehrere Dateien das Problem der Konsistenzhaltung auf: es kann vor-
kommen, dass eine Funktion in einer Datei anders definiert ist, als sie
Teil I: Stoffwiederholung
Aufgabe 1: Fragen zum Compiler
Wie heißen die vier Teilprogramme, die der Compiler nacheinander aufruft?
1. 2.
3. 4.
Welcher Teil des Compilers bearbeitet die #include-Direktiven?
Welche Phasen beinhaltet der eigentliche ¨Ubersetzungsvorgang?
Mittels welcher Option h¨ort der Compilervor dem Binden auf?
Gib hierf¨ur ein Beispiel.
In welcher Datei landet das Ergebnis der eigentlichen ¨Ubersetzung?
Was ”weiß“ der Compiler nach dem ¨Ubersetzen von datei.c?
Nehmen wir nun an, wir w¨urden zwei Dateien ¨ubersetzen. Was
”weiß“ der Compiler von der ersten Datei, beim ¨Ubersetzen der zweiten, wenn wir folgende Kommandos verwenden?
gcc -c datei-1.cund gcc -c datei-2.c gcc -c datei-1.c datei-2.c
Erkl¨are nochmals kurz in eigenen Worten, was die #include-Direktive macht.
Vervollst¨andige die beiden folgenden Beispiele:
#include-Direktive Effekt
#include . . . . . . . .
#include . . . . . . . .
Erkl¨are mit eigenen Worten, wof¨ur man ¨ublicherweise sogenannte .h-Dateien verwendet:
Teil II: Quiz
Aufgabe 1: ¨ Ubersetzen und Binden (Linken)
Im Folgenden gehen wir davon aus, dass wir eine Reihe von Dateien haben und diese mittels des C-Compilers ¨ubersetzen und/oder binden wollen. in der ersten Spalte steht jeweils das auszuf¨uhrende Kommando. In der zweiten soll jeweils stehen, welche Dateien ¨ubersetzt werden. In der dritten Spalte soll stehen, ob ein lauff¨ahiges Programm zusammengebunden wird und wie ggf. dessen Name lautet. Vervollst¨andige die fehlenden Spalten.
Kommando ubersetzte Dateien¨ gebundenes Programm
gcc datei-1.c . . . . . . . .
gcc datei-1.c -o badman . . . . . . . .
gcc -c a.c b.c . . . . . . . .
gcc a.o b.o . . . . . . . .
gcc -o temperatur x.c y.o . . . . . . . .
. . . . inge.c, peter.c ----
. . . . supi.c supi
Teil III: Fehlersuche
Aufgabe 1: Eine einfache verteilte Anwendung
Chefprogrammierer Dr. Modell-Hammer hat versucht, eine erste kleine verteilte An- wendung zu schreiben. Dabei hat er folgende Struktur im Sinn gehabt:
test.c main()
hello.h hello.c hello()
¨Ubersetzen:
gcc -x test test.c hello.h hello.c
In dieser Struktur befindet sich in der Datei test.c das Hauptprogramm, das seinerseits die Funktion hello() aufruft, die einen netten Begr¨ußungstext ausgibt. Diese Funktion gibt den Geburtstag, der als Struktur definiert ist, an die aufrufende Stelle zur¨uck. Leider hat auch Dr. Modell-Hammerein paar kleine Fehler gemacht . . . er ist desperate . . . Das Hauptprogramm: test.c
1 # i n c l u d e < s t d i o . h >
2 # i n c l u d e " h e l l o . c "
3
4 int m a i n ( int argc , c h a r ** a r g v )
5 {
6 s t r u c t b i r t h d a t e b i r t h = h e l l o ( " f r i d a " ) ; 7 p r i n t f ( " my b i r t h d a y is : % d . % d . % d \ n " ,
8 b i r t h . day , b i r t h . month , b i r t h . y e a r ) ;
9 }
Die Header-Datei: hello.h
1 s t r u c t b i r t h d a t e h e l l o ( c h a r * n a m e ) ;
Das Modul: hello.c
1 # i n c l u d e < s t d i o . h >
2 # i n c l u d e " h e l l o . h "
3
4 s t r u c t b i r t h d a t e { int day ; int m o n t h ; int y e a r ; };
5
6 s t r u c t b i r t h d a t e h e l l o ( c h a r * n a m e )
7 {
8 s t r u c t b i r t h d a t e b i r t h ;
9 p r i n t f ( " h e l l o % s , my d a r l i n g !\ n " , n a m e ) ;
10 b i r t h . day = 30; b i r t h . m o n t h = 2; b i r t h . y e a r = 1 9 9 1 ;
11 r e t u r n b i r t h ;
Teil IV: Anwendungen
Ziel dieses ¨Ubungspaketes ist es, ein kleines Programm zu entwickeln, das auf zwei Da- teien verteilt ist und dennoch zu einem lauff¨ahigen Programm zusammengebunden wird.
Um keine weiteren Probleme zu erzeugen, verwenden wir das Programm zum Z¨ahlen von Vokalen aus ¨Ubungspaket 24.
Vorbemerkung
Bevor wir mit der Arbeit loslegen, wiederholen wir hier als Lehrk¨orper nochmals drei wesentliche Punkte aus dem Skript:
1. Dem Compiler ist es es v¨ollig egal, wo sich welche Funktionen befinden. Er muss aber wissen, in welchen Dateien sie kodiert sind, damit er sie ¨ubersetzen und am Ende zu einem Programm zusammenbinden kann.
2. Wenn der Compiler mehrere Dateien ¨ubersetzt, egal ob mittels eines oder mehrerer Kommandos, dann ¨ubersetzt er die angegebenen Dateien nacheinander. Nach dem Ubersetzen jeder einzelnen Datei schreibt er das Ergebnis in eine Ergebnis-Datei¨ (meist eine.o-Datei) und vergisst sofort alles! Das bedeutet folgendes: Der Compiler merkt sich nicht irgendwelche Definitionen und/oder Funktionsk¨opfe; wir m¨ussen selbst daf¨ur sorgen, dass er alles zur richtigen Zeit mitbekommt.
3. Die Verwendung von .h-Dateien ist nicht notwendig aber hilft uns und dem Com- piler. In einer .h-Datei stehen ¨ublicherweise Konstantendefinitionen (beispielswei- se #define SIZE 10) und Funktionsdeklarationen (beispielsweise int my ia prt(
FILE *fp, int *a, int size );), die auch Prototypen genannt werden. Durch diese Deklarationen kann der Compiler Inkonsistenzen feststellen und uns diese mit- teilen, wodurch die Programmentwicklung f¨ur uns stark vereinfacht wird.
Aufgabe 1: Getrenntes ¨ Ubersetzen am Beispiel
1. Aufgabenstellung
3. Entwurf
Da wir diesmal die
”Programmzeilen“ trennen wollen, ben¨otigen wir ein paar ¨Uber- legungen zum Thema Entwurf, wobei wir ein wenig helfen wollen ;-)
Wir wollen also das Hauptprogramm (main) vom Z¨ahlen der Vokale (vokale()) tren- nen. Ferner wollen wir sicherstellen, dass der Compiler ¨uberall die selbe Vorstellung bez¨uglich Funktionstyp und Parameter (Signatur) von der Funktion vokale() hat.
Wie viele Dateien ben¨otigen wir?
Definiere die ben¨otigten Dateinamen:
4. Implementierung
Hier brauchen wir uns keine weiteren Gedanken machen, da wir bereits alle notwen- digen Programmzeilen in den ¨Ubungspaketen 24und 25 entwickelt haben.
5. Kodierung
Da schon alle Programmzeilen vorhanden sein sollten, k¨onnt ihr gleich am Rechner arbeiten.