Präprozessor, Module Ablauf beim Übersetzen

Werbung
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
Herunterladen