INE1 Präprozessor, Module ■ Prä(ääh)prozessor, Compiler und Linker, Lader ■ Der Präprozessor ■ Bedingte Übersetzung ■ Modulare Programmierung ■ Anhang: make-Utility 1 Präprozessor, Compiler, Linker, Lader School of Engineering © G. Burkert, K. Rege, ZHAW 2 von 75 Direkt ausführbarer Code vs Virtuelle Maschinen ■ C/C++ ■ Programmquelle wird in EXE/DLL übersetzt (Maschine Code) um dann direkt ausgeführt zu werden ■ Ausführbare Einheiten ■ ■ Bestehen aus (Maschinen-)Code Werden durch Funktionen des jeweiligen Betriebssystems unterstützt Unterschied Java/C# (später) ■ Programmquelle wird in .class File (sog. VM Code) übersetzt und dann anschliessend durch VM ausgeführt ■ Ausführbare Einheiten ■ ■ Bestehen aus Code der von VM ausgeführt wird; VM läuft auf verschiedenen Plattform Werden durch ein umfangreiches Laufzeitsystem (Virtual Runtime Environment: VRE) unterstützt School of Engineering © G. Burkert, K. Rege, ZHAW 3 von 75 Übersetzungs-/Ausführungsvorgang ■ Übersetzung/Ausführung in vier Phasen ■ ■ ■ ■ 1. Präprozessor: textuelle Vorverarbeitung 2. Erzeugung von Maschinen-Code (Linkbare-Einheiten): Compilation 3. Erzeugung von ausführbaren Einheiten (.exe): Linking 4. Ausführung C-Quellen Dateien .c, .h 11 Ausführbare Einheiten .exe Linkbare Einheiten .obj 22 33 Compilation Linking 44 normalerweise normalerweise nicht nichtan anZwischenprodukt Zwischenprodukt interessiert interessiert School of Engineering © G. Burkert, K. Rege, ZHAW 4 von 75 Präprozessor, Compiler, Linker, Lader ■ 1. Der Präprozessor sucht in den Quelltexten nach speziellen Befehlen, um dann im wesentlichen textuelle Ersetzungen durchzuführen ■ 2. Der Compiler wandelt den so geänderten Code in Objektcode um, der aber noch offene Aufrufe enthält ■ 3. Der Linker verbindet die noch offenen Funktionsaufrufe mit den zugehörigen Funktionen ■ 4. Der Lader lädt das Programm mit den Abhängigkeiten (Windows .DLL) in den Speicher und startet die "main" Funktion School of Engineering © G. Burkert, K. Rege, ZHAW 5 von 75 Die 3 Phasen der Übersetzung ■ Übersetzung von C/C++ in 3 Phasen ■ 1. Phase: Präprozessor (rein textuelle Substitutionen) ■ ■ Präprozessor Direktiven alle mit # Beispiele: ■ #define PI 3.1415 ■ #include <stdio.h> ■ 2. Phase: Übersetzung in Maschinencode ■ obj-Dateien: (linkbare Einheit) ■ 3. Phase: Linken der Teile zu ausführbarem Programm School of Engineering © G. Burkert, K. Rege, ZHAW 6 von 75 Phase 1: Erzeuge Quelle für Compilation Vorverarbeitung (1. Schritt) stdio.h #include<stdio.h> #include "hello.c" ...... int main () { hello.c } 11 Präprozessor School of Engineering Compilation © G. Burkert, K. Rege, ZHAW 7 von 75 Phase 1: Präprozessor-"Direktiven" Anwendung ■ Definition von Konstanten ■ Einbeziehen von anderen Quellen (Header Files) ■ Ein-/Ausblenden von Blöcken (z.B. Debug Code) #define PI 3.1415926535 #include "filename" /* from the current directory */ #include <filename> /* from the system directories */ #define DEBUG /* defines the symbol DEBUG */ #ifdef DEBUG /* code here is compiled if DEBUG is defined */ #elif defined UNIX /* code here is compiled if DEBUG is not defined, and UNIX is defined */ #else /* code here is compiled if neither DEBUG or UNIX are defined */ #endif #ifndef DEBUG /* test if symbol is not defined */ #undef DEBUG /* undefines a symbol */ School of Engineering © G. Burkert, K. Rege, ZHAW 8 von 75 Phase 1: Logische Aufteilung in C und H Files ■ C Files C-Programmquellen -> .c ■ H(eader)-Files ~ "Dateien mit Funktionsdeklarationen" -> .h ■ ■ Sammlungen von vordeklarierten (Prototyp-) Funktionen (keine Implementationen!) Definition von allgemein verwendeten Typen und Konstanten ■ System Header Files ■ ■ Beschreibung der Schnittstelle zu Betriebssystem und Bibliotheken (Funktionsprototypen) Beispiele ■ stdio.h : Standard Ein/Ausgabe ■ string.h : String Funktionen ■ Eigene Header-Files ■ zur besseren Strukturierung des Programms School of Engineering © G. Burkert, K. Rege, ZHAW 9 von 75 Phase 2: Compiler ■ Der Compiler bekommt den vom Präprozessor überarbeiteten Quelltext und setzt ihn in einen Objektcode um ■ Objektcode ■ Maschinen-Code ■ Nicht ausführbar, da noch unaufgelöste Referenzen (z.B. Bibliotheks-Funktionsaufrufe) enthalten sind ■ Meistens mit der Endung .o oder .obj School of Engineering © G. Burkert, K. Rege, ZHAW 10 von 75 Phase 2: Weitere Aufgaben des Compilers ■ Prüfung auf syntaktische und grammatische Fehler ■ Statische Typprüfung ■ Teilweise Warnung für logische Fehler ■ Anlegen von Tabellen, in denen Buch über die verwendeten Variablen geführt wird School of Engineering © G. Burkert, K. Rege, ZHAW 11 von 75 Phase 3: Linker ■ Linkbare Einheiten (.obj) werden zu ausführbaren Einheit (exe,dll) zusammengebunden ("linken") hello.obj C-Compilation 33 baba.obj Assembler tatu.obj PascalCompilation School of Engineering 33 Linking 33 Linking © G. Burkert, K. Rege, ZHAW hello.exe main() tatu.dll foo() 12 von 75 Phase 3: Linker ■ Setzt die vom Compiler generierten Objektmodule zu einem ausführbaren Programm zusammen (EXE) ■ Sucht nach Funktionsaufrufen und den dazugehörigen Funktionen ■ Überprüft, ob alle Variablen, die nur deklariert wurden (extern, s.u.), auch irgendwo definiert sind ■ Es gibt zwei Varianten von Linking ■ statisch: der Bibliotheks Code wird in das EXE hinein gepackt ■ dynamisch: der Bibliotheks Code wird nur als externe Referenz vermerkt und der Lader löst dann diese auf ■ Nach dem Linken: ausführbares Programm School of Engineering © G. Burkert, K. Rege, ZHAW 13 von 75 Phase 3: Einbinden der Standard-Bibliotheken ■ Der Linker sucht auch in speziellen Dateien, den Bibliotheken, um alle Referenzen aufzulösen ■ in Windows: Bibliotheken in sog. DLL Dateien ■ in Linus: in sog. .so Dateien ■ In C: eine Bibliothek ist eine Sammlung von Funktionen ■ Der Linker sucht in der Standard-Bibliothek nach allen verwendeten Funktionen ■ Für C sind eine Reihe Standard-Bibliotheken vorgegeben ■ Zusätzliche Bibliotheken müssen beim Linken angegeben werden School of Engineering © G. Burkert, K. Rege, ZHAW 14 von 75 Phase 4: Ausführung/Lader ■ Bringe ausführbare Einheiten zur Ausführung. ■ Lädt EXE und davon abhängige DLLs in den Speicher ■ springt zu Einsprungstelle (main) Teil Teildes desBetriebssystems Betriebssystems c:> hello.exe 44 Lader tatu.dll School of Engineering 44 © G. Burkert, K. Rege, ZHAW 15 von 75 Überblick Übersetzungs-/Ausführungsvorgang Präprozessor hello.c 11 C-Compilation hello.pre 22 Linker hello.obj 33 Lader hello.exe 44 11 hello.h Assembler baba.obj 44 33 tatu.dll baba.asm School of Engineering 22 © G. Burkert, K. Rege, ZHAW 16 von 75 Der Präprozessors School of Engineering © G. Burkert, K. Rege, ZHAW 17 von 75 Präprozessor ■ Wichtig bei allen Präprozessorbefehlen ist: Sie führen textuelle Änderungen am Quellcode aus ■ Präprozessorbefehle werden auch Direktiven genannt ■ Wird i.d.R. nicht separat ausgeführt ■ Ist Teil des C Compilers ■ Kann via Kommandozeile aber aufgerufen werden -E-E School of Engineering gcc -E ... Stop Stopafter afterthe thePräprocessing Präprocessingstage; stage;do donot notrun runthe thecompiler compiler proper. The output is in the form of Präprocessed source proper. The output is in the form of Präprocessed source code, code,which whichisissent senttotothe thestandard standardoutput. output. © G. Burkert, K. Rege, ZHAW 18 von 75 Warum ein Präprozessor? ■ Ursprünglich: Code kompakter und lesbarer machen ■ Aber: Man kann damit auch schwer zu findende Fehler in den Code einbauen... ■ In späteren C Versionen wurden Sprachmittel eingeführt, mit denen sich viele Präprozessor-Befehle vermeiden lassen ■ z.B. Konstanten nicht mehr mit #define School of Engineering © G. Burkert, K. Rege, ZHAW 19 von 75 Präprozessor ■ Der Präprozessor hat folgende Aufgaben: ■ Dateien in andere einfügen #include ■ Konstanten definieren #define ■ Makros zur Verfügung stellen #define ■ Teile des Quelltextes selektieren #ifdef, #ifndef School of Engineering © G. Burkert, K. Rege, ZHAW 20 von 75 Präprozessor-Befehle ■ Beispiel: #include "dateiname" ■ Der Präprozessor arbeitet zeilenorientiert ■ Bei einer Präprozessor-Anweisung das Zeichen "#" vorangestellt sein ■ Kein Semikolon am Ende ! School of Engineering © G. Burkert, K. Rege, ZHAW 21 von 75 Dateien einbinden ■ Befehl: #include ■ Beim Einfügen der Datei wird der Quelltext der Datei eingefügt ■ Meistens verwendet um Konstanten, Deklarationen von Typen und Funktionen (Prototypen) in einen Quelltext einzubinden ■ Zwei Varianten: #include <Dateiname> #include "Dateiname" ■ Erste Variante für das Einbinden von Header-Dateien aus Standard-Bibliotheken (in einem speziellen "Standard-Verzeichnis" abgelegt) ■ Zweite Variante für projektspezifische Header-Dateien School of Engineering © G. Burkert, K. Rege, ZHAW 22 von 75 Konstanten definieren ■ Befehl #define ■ Beispiel: #define MAXLOOP 10000 ■ Über diese Anweisung können auch Ersetzungen durchgeführt werden ■ Wird der Name irgendwo im Programmcode gefunden, wird an dieser Stelle der Ersetzungstext eingefügt, ausgenommen in Zeichenketten und Kommentaren School of Engineering © G. Burkert, K. Rege, ZHAW 23 von 75 Konstanten definieren ■ Was durch define definiert wird, wird im allgemeinen als Makro bezeichnet ■ Ein Makro ohne Parameter wird auch als symbolische Konstante bezeichnet ■ Wenn der Makroname in einem String-Literal auftaucht, wird er nicht ersetzt ■ Konvention: Namen von symbolischen Konstanten gross schreiben School of Engineering © G. Burkert, K. Rege, ZHAW 24 von 75 Konstanten definieren Beispiel Verwendung #define ANZAHL 10 int feld [ANZAHL]; /* meistens besser: */ int j = ANZAHL; const int ANZAHL=10; /* oder: */ enum {ANZAHL=10 }; School of Engineering © G. Burkert, K. Rege, ZHAW 25 von 75 Konstanten definieren ■ Wichtig: Der Präprozessor ersetzt diese Symbole rein textuell ■ Auf diese Weise können durch den Präprozessor auch leicht Fehler in den Code eingebaut werden: #define I 10 int I = 20; School of Engineering /* wird zu: int 10 = 20; */ © G. Burkert, K. Rege, ZHAW 26 von 75 Beispiel $ # # # # cpp prep.c 1 "prep.c" 1 "<built-in>" 1 "<command-line>" 1 "prep.c" int main() { double p = 3.14; // 3.14 char tx2[] = "drei"; char tx1[] = "abc ZAHL TEXT de"; return 0; } School of Engineering © G. Burkert, K. Rege, ZHAW 27 von 75 Vordefinierte Konstanten zwei zweiUnderscores Underscores __FILE__ Name der Datei, die gerade übersetzt wird __DATE__ Datum des __TIME__ Uhrzeit, zu der der Übersetzungsvorgang gestartet Übersetzungsvorgangs wurde __LINE__ Aktuelle Zeile der Datei, in der der Compiler sich gerade befindet __STDC__ Erfüllt, wenn im Standard-C Modus übersetzt wird __cplusplus Es wurde mit einem C++ Compiler compiliert __cdecl Verwende die C Aufruf Konvention School of Engineering © G. Burkert, K. Rege, ZHAW 28 von 75 Vordefinierte Konstanten /* Ausgabe der Dateiversion: */ printf("Version vom %s um %s von %s\n", __DATE__, __TIME__, __FILE__); /* Ausgabe der Zeile bei Fehlern: */ if (nenner == 0) { printf("Schwerer Fehler (Division durch Null) in " "%s, Zeile %s\n", __FILE__, __LINE__); exit(1); } School of Engineering © G. Burkert, K. Rege, ZHAW 29 von 75 Makros ■ In der #define-Direktive können nach dem Namen des Symbols auch zusätzliche Parameter in Klammern angegeben werden ■ Diese können im zu ersetzenden Text wieder auftauchen, wo sie dann textuell eingesetzt werden #define MAKRO(Argl, Arg2, ...) Ersetzungstext ■ Hinter dem Makronamen darf kein Leerzeichen folgen School of Engineering © G. Burkert, K. Rege, ZHAW 30 von 75 Makros ■ Es kann vorkommen, dass eine Anweisung nicht in eine Zeile passt ■ Dann wird das Fortsetzungszeichen "\" verwendet ■ Beispiel: #define ALERT(meintext) \ printf("###########################"); \ printf("%s", meintext); \ printf("###########################"); School of Engineering © G. Burkert, K. Rege, ZHAW 31 von 75 Beispiel $ # # # # cpp makro.c 1 "makro.c" 1 "<built-in>" 1 "<command-line>" 1 "makro.c" int main() { int k = 3, m = 34; double f = 3.1, g = 4.4; {int j; j=k; k=m; m=j; }; printf("getauscht!"); {int j; j=f; f=g; g=j; }; return 0; } School of Engineering © G. Burkert, K. Rege, ZHAW 32 von 75 Makros: Probleme ■ Mit Makros ist Vorsicht angebracht, da leicht Fehler eingebaut werden können ■ Beispiel: #define Quadrat(x) x*x Quadrat(5+3); /* ergibt 5+3*5+3 = 23 statt 64 */ ■ Besser die Argumente von Makros in Klammern schreiben: #define Quadrat(x) ((x)*(x)) School of Engineering © G. Burkert, K. Rege, ZHAW 33 von 75 Makros: Probleme ■ Noch besser: Man sollte Funktionen vorziehen, da Fehler, die über Makros entstehen, schwerer zu finden sind ■ In C99 und C++ können Präprozessor-Konstanten und -Makros meistens besser durch const und Inline-Funktionen (C++) realisiert werden "Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer." (Bjarne Stroustrup) School of Engineering © G. Burkert, K. Rege, ZHAW 34 von 75 Bedingte Übersetzung School of Engineering © G. Burkert, K. Rege, ZHAW 36 von 75 Konstanten abfragen ■ Es ist nicht nötig, einem solchen Symbol auch einen Wert zuzuweisen ■ Auch eine Definition ohne Wert kann sinnvoll sein, da abgefragt werden kann, ob ein Symbol definiert ist oder nicht ■ Befehle: #ifdef Test, ob Symbol definiert #ifndef Test, ob Symbol nicht definiert #undef Danach ist Symbol nicht (mehr) definiert School of Engineering © G. Burkert, K. Rege, ZHAW 37 von 75 Bedingte Übersetzung ■ Man kann den Präprozessor auch dazu nutzen, Teile des Quelltextes zu selektieren ■ Verhindern einer mehrfachen Übersetzung ■ Selektieren von Debugging-Code ■ Plattform übergreifende Programmierung ■ Präprozessorbefehle für diese Funktionen: #ifdef und #ifndef ■ Beide werden mit #endif abgeschlossen ■ Nach #else kann ein Alternativzweig angegeben werden School of Engineering © G. Burkert, K. Rege, ZHAW 38 von 75 Mehrfache Übersetzung/Includes verhindern ■ Beim Einbinden von Quelltexten (meist Header-Files) mit #include muss verhindert werden, dass gleiche Dateien mehrmals eingefügt werden #ifndef c_h ■ Grund: Mehrfache Definitionen sind nicht zulässig ■ Dazu wird folgende Struktur verwendet (im Header File): c.h #ifndef Dateikennung #define Dateikennung #include "c.h" #include "c.h" // Code a.h #endif b.h #include "a.h" #include "b.h" School of Engineering © G. Burkert, K. Rege, ZHAW main.c 39 von 75 Mehrfache Übersetzung/Includes verhindern ■ Dateikennung ist ein Kürzel, das in der Regel aus dem Dateinamen zusammengesetzt wird ■ Aus Dateinamen wie "meinedatei.h" wird zum Beispiel eine Dateikennung wie "MEINEDATEI_H" #ifndef MEINEDATEI_H #define MEINEDATEI_H // Code #endif School of Engineering © G. Burkert, K. Rege, ZHAW 40 von 75 Mehrfache Übersetzung/Includes verhindern #include "meinedatei.h" #include "meinedatei.h" int main (void) { // ... } #ifndef #define // Code #endif #ifndef #define // Code #endif MEINEDATEI_H MEINEDATEI_H MEINEDATEI_H MEINEDATEI_H int main (void) { // ... } School of Engineering © G. Burkert, K. Rege, ZHAW 41 von 75 Selektieren von Debugging-Code ■ Einfache Möglichkeit, zusätzlichen Code zur Fehlersuche mit in den Quelltext aufzunehmen, um ihn je nach Bedarf mit übersetzen zu lassen oder nicht ■ Anweisungen werden nur in den übersetzten Code übernommen, wenn die Konstante beim Präprozessorlauf definiert ist ■ Die Konstante kann dann am Anfang des Programmcodes oder über eine Option beim Compileraufruf definiert werden ■ Beim gcc: -Dname bzw. -Dname=wert ■ Oder in den Projektoptionen der IDE School of Engineering © G. Burkert, K. Rege, ZHAW 42 von 75 Selektieren von Debugging-Code ■ Erkennung ob Debug Code aktiviert werden soll mittels __DEBUG__ ■ Lässt man oft in der Quelle für spätere Fehlersuche stehen #ifdef __DEBUG__ printf("Initialisierung abgeschlossen.\n"); #endif School of Engineering © G. Burkert, K. Rege, ZHAW 43 von 75 Plattformabhängigkeiten ■ Quelltext für mehrere Prozessoren, Betriebssysteme, graphische Oberflächen o.ä. gleichzeitig zur Verfügung stellen #ifdef __GCC__ /* systemspezifischer Code für GNU-Compiler */ #endif #ifdef sun /* systemspezifischer Code für Sun-Compiler */ #endif #ifdef _MSC_VER /* systemspezifischer Code für Visual-Studio */ #endif School of Engineering © G. Burkert, K. Rege, ZHAW 44 von 75 Modulare Programmierung School of Engineering © G. Burkert, K. Rege, ZHAW 45 von 75 Ziele Modulare Programmierung ■ Gleichzeitige Bearbeitung/Entwicklung durch mehrere Entwickler ■ Kürzere Einarbeitungszeit von neuen Entwicklern ■ Nach Änderungen müssen nur Teile eines Projekts neu übersetzt werden ■ Teile eines Projekts können in anderen Programmiersprachen geschrieben werden ■ Klare Strukturierung des gesamten Programms ■ Verbesserung der Wiederverwendbarkeit Gute Modularisierung ist der Schlüssel für erfolgreiche Projekte. Ohne Modularisierung droht jedes Projekt früher oder später an der Komplexität zu scheitern. School of Engineering © G. Burkert, K. Rege, ZHAW 46 von 75 Prinzipien der Modularisierung ■ Geringe Vernetzung (wenig Schnittstellen) ■ Schwache Kopplung zwischen Modulen (schmale Schnittstellen) ■ Hohe Kohäsion innerhalb Modul (inhaltliche Einheit) ■ Explizite Schnittstellen (Kommunikation mit Parameterübergabe) ■ Information Hiding (Verstecken der Implementationsdetails) School of Engineering © G. Burkert, K. Rege, ZHAW 47 von 75 Zwei Stufen der Modularisierung in C 1. Stufe: Lediglich die Quellen werden modular strukturiert ■ Die für andere Quellen verwendbare Funktionen werden in Header Files als Prototypen definiert ■ Für die Verwendung dieser Quellen wird in andern Quellen lediglich dieses Header File included ■ Alle benötigten Quellen werden dann vom Compiler/Linker zu einem fertigen Programm (EXE) zusammen gefügt: "statische Bibliotheken". 2. Stufe: Zusätzlich werden die Laufzeiteinheiten modularisiert ■ Aus den Bibliotheken werden DLLs erzeugt ■ Diese DLLs werden dann zur Laufzeit vom Lader automatisch (nach-)geladen als sog. "dynamische Bibliotheken". School of Engineering © G. Burkert, K. Rege, ZHAW 48 von 75 Statische Bibliotheken School of Engineering © G. Burkert, K. Rege, ZHAW 49 von 75 Source- und Headerfiles ■ Die Deklarationen werden in Header Files durchgeführt ■ Sie enthalten die Prototypen der "von aussen sichtbaren" Funktionen ■ Header Files sind Deklarationsdateien die sowohl ins definierende als auch ins aufrufende Modul eingebunden werden ■ Header Files sind somit die Schnittstellenbeschreibung der Module ■ Diese Header-Datei wird so abgelegt, dass der Präprozessor (mittels #include) sie findet #ifndef a_h a.h #include "a.h" a.c School of Engineering © G. Burkert, K. Rege, ZHAW #include "a.h" b.c 50 von 75 Source- und Headerfiles ■ Soll in einem anderen Modul die Funktion globalFunct verwendet werden, muss diese dort mit Hilfe eines prototypen deklariert werden ■ Dasselbe gilt für die globale Variable globalVar – auch sie muss im anderen Modul vor der ersten Verwendung deklariert werden ■ Bei Funktionen einfach der Prototyp der Funktion im Header File void foo(); /* Deklaration */ ■ Bei Variablen wird zusätzlich das Schlüsselwort extern verwendet extern int globalVar; School of Engineering /* Deklaration */ © G. Burkert, K. Rege, ZHAW 51 von 75 Source- und Headerfiles /* --- Deklaration A ---------------------------- */ /* Datei: modul_a.h */ #ifndef MODUL_A_H #define MODUL_A_H extern int globalVar; /* fuer alle */ int globalFunct(int a); /* fuer alle */ #endif School of Engineering © G. Burkert, K. Rege, ZHAW 52 von 75 Source- und Headerfiles /* --- Modul A ---------------------------------- */ #include "modul_a.h" /* Definitionen */ int globalFunct(int a) { ... } /* fuer alle */ static int lokalFunct(int a) { ... } /* nur hier int globalVar = 24; /* fuer alle */ static int localVar; /* nur hier School of Engineering © G. Burkert, K. Rege, ZHAW */ */ 53 von 75 Source- und Headerfiles /* --- Modul B ---------------------------------- */ #include "modul_a.h" /* Aufruf der Funktion aus Modul A */ int result = globalFunct(42); /* Zugriff auf die Variable aus Modul A */ int var = globalVar; School of Engineering © G. Burkert, K. Rege, ZHAW 54 von 75 Sichtbarkeit von Funktionen ■ Funktionen können nur auf der äussersten Ebene eines Moduls definiert werden ■ Standardmässig gilt eine Funktion als global – das bedeutet, dass ihr Name an den Linker weitergegeben wird ■ Eine Einschränkung der Sichtbarkeit auf das aktuelle Modul ist mit Hilfe des Schlüsselworts static möglich int globalFunct(int a) { ... } /* fuer alle */ static int lokalFunct(int a) { ... } /* nur hier */ School of Engineering © G. Burkert, K. Rege, ZHAW 55 von 75 Sichtbarkeit von Variablen ■ Standardmässig sind alle Variablen global, die auf der äussersten Ebene definiert werden ■ Eine Einschränkung der Sichtbarkeit ist ebenfalls mit Hilfe des Schlüsselworts static möglich int globalVar; // fuer alle static int localVar; // nur hier ■ Lokal ist hier nicht im Sinne von lokal in einer Funktion sondern im Sinne von lokal in einem Modul (Dateiebene) gemeint School of Engineering © G. Burkert, K. Rege, ZHAW 56 von 75 Dynamische Bibliotheken School of Engineering © G. Burkert, K. Rege, ZHAW 57 von 75 Dynamische Bibliotheken ■ Dynamische Bibliotheken müssen speziell erzeugt werden ■ unter Windows mit der Endung DLL ■ Betriebssystem selber besteht aus einer Sammlung von DLLs ■ Sie stellen dem Linker (über Libs) die Funktionen zur Verfügung, ■ Es wird nicht die Funktion sondern lediglich ein Vermerk (Referenz) gespeichert ■ Der Lader löst die Referenz dynamisch auf und lädt die benötigten DLLs nach ■ Suchpfad für die DLLs ist aktuelles Verzeichnis und zusätzlich alle in der "PATH" Umgebungsvariablen vermerkte School of Engineering Dependency Walker zur Anzeige Dependency Walker zur Anzeige der Abhängigkeiten der Abhängigkeiten © G. Burkert, K. Rege, ZHAW 58 von 75 Export mit __declspec(dllexport) ■ Exportierte Funktionen müssen speziell gekennzeichnet werden #define DLL_EXPORT __declspec(dllexport) ■ Von andern Modulen importierte ensprechend #define DLL_EXPORT __declspec(dllimport) ■ Die Aufrufkonvention muss __cdecl sein ■ Am einfachsten mit einem Define (z.B. _DLL), das man über das Projekt setzt #ifdef _DLL // If accessing the data from inside the DLL #define DLL_EXPORT __declspec(dllexport) #else // If accessing the data from outside the DLL #define DLL_EXPORT __declspec(dllimport) #endif __cdecl void DLL_EXPORT foo(void); ■ Die exportierten Funktionen sind im LIB File gespeichert School of Engineering © G. Burkert, K. Rege, ZHAW 59 von 75 Übersetzung im Überblick ■ Compilierung der Bibliothek und Klient und Ausführen #include #include Compiler Compiler Namen Namender derDLL DLL vermerkt vermerkt DynLib.h DynLib.dll Beim BeimLaden Ladenwird wird die referenzierte die referenzierte DLL DLL(mit-)geladen (mit-)geladen DynLib.c DynLib.lib Beim BeimCompilieren Compilieren wird wirddas dashhFile File included included Beim BeimLinken Linkenwird wird das dasliblibFile File gebunden gebunden Lader Lader Compiler Compiler TestClient.exe TestClient.c School of Engineering © G. Burkert, K. Rege, ZHAW 60 von 75 DynLib.h #ifndef DYNLIB1_H #define DYNLIB1_H #ifdef _DLL // If accessing the data from inside the DLL #define DLL_EXPORT __declspec(dllexport) #else // If accessing the data from outside the DLL #define DLL_EXPORT __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif void __cdecl DLL_EXPORT foo(void); #ifdef } #endif __cplusplus #endif /* DYNLIB1_H */ School of Engineering © G. Burkert, K. Rege, ZHAW 61 von 75 DynLib.c und TestClient.exe ■ Implementation der Library #include "DynLib1.h" #include <stdio.h> void __cdecl foo() { printf("Hello World\n"); } ■ Und Aufruf von foo() in TestClient (separates EXE) #include <stdio.h> #include <stdlib.h> #include "DynLib1.h" int main(int argc, char** argv) { foo(); return (EXIT_SUCCESS); } School of Engineering © G. Burkert, K. Rege, ZHAW 62 von 75 Weiterer Vorteil von dynamischen Bibliotheken ■ C-Bibliotheken lassen sich von praktisch jeder Programmiersprache aus aufrufen ■ Z.B. können GUIs mit Java (oder C#) entwickelt werden ■ ■ ■ einfacher plattformunabhängig sehr gute Werkzeuge vorhanden (sog. GUI Builder) ■ Der Aufruf der C Bibliotheksfunktion in Java dann via JNI oder JNA (später) School of Engineering © G. Burkert, K. Rege, ZHAW 63 von 75 Noch Fragen? School of Engineering © G. Burkert, K. Rege, ZHAW 64 von 75 make-Utility School of Engineering © G. Burkert, K. Rege, ZHAW 65 von 75 Das make-Utility ■ Jedes etwas grössere Programm setzt sich aus einer ganzen Reihe von Dateien zusammen ■ Schon bei Veränderungen einer Datei kann es nötig sein, mehrere Module neu zu übersetzen ■ Wird z.B. ein Header-File verändert, so sollten alle Quelltextfiles, die dieses Header-File einbinden, neu übersetzt werden ■ Bei grossen Projekten kann das schnell sehr unübersichtlich werden School of Engineering © G. Burkert, K. Rege, ZHAW 66 von 75 Das make-Utility ■ Das Utility make hilft dabei, ein Programm aus mehreren Modulen unter Berücksichtigung der Abhängigkeiten zu erstellen ■ Hinweis: Das frei verfügbare und im Funktionsumfang bedeutend erweiterte GNU-Make trägt meist den Namen gmake ■ Beim Aufruf arbeitet make die Regeln einer im aktuellen Verzeichnis liegenden Makefile-Datei ab School of Engineering © G. Burkert, K. Rege, ZHAW 67 von 75 Makefile ■ Ein Makefile enthält die Regeln, wann, was und wie es auszuführen ist ■ Es besteht aus: ■ Kommentaren, beginnend mit # ■ Direktiven ■ Variablendefinitionen ■ Expliziten Regeln School of Engineering © G. Burkert, K. Rege, ZHAW 68 von 75 Makefile ■ Eine Regel ist wie folgt aufgebaut: target: dependencies <TAB> command ■ Target: Was zu erstellen ist ■ Dependencies: Legen fest, wann das zu geschehen hat, nämlich wenn eines von ihnen jünger ist als target ■ Command: Das oder die abzuarbeitenden Kommandos; wichtig ist der Tabulator am Zeilenanfang ■ Da als dependencies auch andere targets angegeben werden können ist eine Verschachtelung der Regeln möglich School of Engineering © G. Burkert, K. Rege, ZHAW 69 von 75 Makefile – Beispiel ■ Das Programm rechner wird aus den Modulen main.o, add.o, sub.o, mul.o sowie div.o gebildet ■ Alle Quelltextfiles nutzen das gemeinsame Headerfile def.h ■ Nach Änderungen an def.h sollten also auch alle Quelltextfiles neu übersetzt werden. School of Engineering © G. Burkert, K. Rege, ZHAW 70 von 75 Makefile – Beispiel # Ausschliesslich explizite Regeln # # rechner wird aus den Objektfiles (.o) zusammengebaut: rechner: add.o sub.o mul.o div.o main.o cc -o rechner add.o sub.o mul.o div.o main.o # Objektfiles loeschen clean: rm -f *.o rechner Forts. ... School of Engineering © G. Burkert, K. Rege, ZHAW 71 von 75 Makefile – Beispiel # Und so entstehen die .o-Files (def.h sowie Makefile # werden angegeben, um auch bei Veraenderungen daran # die Übersetzung zu starten): add.o: add.c def.h Makefile cc -c add.c sub.o: sub.c def.h Makefile cc -c sub.c mul.o: mul.c def.h Makefile cc -c mul.c div.o: div.c def.h Makefile cc -c div.c School of Engineering © G. Burkert, K. Rege, ZHAW 72 von 75 Makefile – Beispiel ■ Mit Hilfe von Variablen lassen sich Regeln einfacher ändern (vor allem, wenn sie an mehreren Stellen benötigt werden) ■ Beim Aufruf von make lassen sich auch Variablenzuweisungen übergeben – diese haben höhere Priorität als die Festlegungen im Makefile ■ Man muss nicht alle Regeln und Variablen angeben – make verwendet dann seine Voreinstellungen School of Engineering © G. Burkert, K. Rege, ZHAW 73 von 75 Makefile – Beispiel # Variante mit Variablen, impliziten Regeln und # gmake's Pattern-Regeln OBJS = add.o sub.o mul.o div.o main.o CC = gcc Inference Rule: xyz.o CFLAGS = -g depends on xyz.c, def.h and Makefile rechner: $(OBJS) $(CC) $(CFLAGS) -o rechner $(OBJS) The source file of the current dependency clean: rm -f *~ *.o rechner $(OBJS): %.o: %.c def.h Makefile Full name of the current target $(CC) $(CFLAGS) -c $< -o $@ School of Engineering © G. Burkert, K. Rege, ZHAW 74 von 75 Anhang: Calling Convention und Name Decoration ■ Der Name wird anhand der Aufrufkonvention "dekoriert" http://unixwiz.net/techtips/win32-callconv.html School of Engineering © G. Burkert, K. Rege, ZHAW 75 von 75