Einführung in C++ C++ Lektion Teil 2 Strukturen: Aufzählungstyp: Typen-Definition: struct enum typedef Der Kompilationsprozess Werkzeuge: Tmake, doxygen, emacs, valgrind 1
Das C++ Typsystem Elementare Datentypen Adressen Strukturen Pointer struct union enum Ganzzahlig Gleitkommazahlen char bool short int long float double long double C-Style Strukturen C-Strukturen fassen logisch zusammengehörende Daten zusammen struct student { int id; char name[80]; ; Structure tag Structure members Für den Entwickler id und name gehören nun zusammen. struct student erzeugt ein gemeinsames Datenfeld Für den Compiler id und name legen direkt nebeneinander im Speicher struct student ist ein komplexer Datentyp, der an Funktionen übergeben werden kann. 2
C-Syntax für Strukturen Deklaration einer C-Struktur struct [name] { <type> field1; <type> field2; [instance list]; Beispiel: struct Foo { int field1; char field2; ; Definition struct Foo foo; struct point { int a; double b; objct1, objct2; struct point objct3 = {1, 3.12 foo.field1; Datenzugriff : liest die Variable field1 der Instanz foo der struct Foo Aufzählungen enum Eine Aufzählung ist ein selbstdefinierter ganzzahliger Typ mit Schlüsselwort enum enum PrimaryColors { RED = 2, GREEN, BLUE ; PrimaryColors color = GREEN; switch(color) { case RED:... case GREEN: Default: Erste Wert (RED) wird mit 0 initialisiert Jeder weitere Konstante hat einen um ein erhöhten Wert als der Vorgänger Man kann aber auch in ersten Wert beliebig wählen (RED = 2) 3
enum Syntax Deklaration enum name { OptionName [= int], OptionName [= int], [instance list]; Definition enum Color { RED=-3, GREEN=4, BLUE=12 color, *color_ptr; enum Color c; void drawcircle (enum Color c); Alle Werte können aber auch explizit vorgegeben werden Schlüsselwort typedef Typedef bietet die Möglichkeit, Typen einen neuen Namen zu geben typedef unsigned char BYTE; unsigned char mybyte; BYTE mybyte; BYTE ist nun ein Aliasname für unsigned char Beide Definitionen von mybyte sind nun äquivalent für den Compiler. Vorteil der typdef Definition: viel aussagekräftiger (Lesbarkeit) maschinenabhängige Typen können isoliert werden. Bei der Portierung muss nur ein Parameter geändert werden. 4
Schlüsselwort typedef Typedef bietet die Möglichkeit, Typen einen neuen Namen zu geben struct Student { int id; char name[80]; ; struct Student st; typedef struct { int id; char name[80]; STUDENT; STUDENT st; Schlüsselwort typedef Vorteil Man kann schnell an einer Stelle im Code den Type einer bestimmten Variablen ändern. Gerne in Verbindung mit struct: struct point { int a; double b; ; struct point objct2; typedef struct point { int a; double b; POINT; POINT objct2; int normalnumber; int smallnumber; int normalnumber; short smallnumber; typedef int MYINT int normalnumber; MYINT smallnumber; typedef short MYINT int normalnumber; MYINT smallnumber; 5
Schlüsselwort typedef Lesbarkeit eines Programms wird verbessert, vor allem bei komplexen Datentypen (Funktionspointer): char* (*search)(float, int); search ist ein Zeiger auf eine Funktion, die zwei Eingabeparameter vom Typ float und int besitzt und die einen Zeiger auf char liefert (mehr über Zeiger in der C++ Lektion 3) typedef char* (*PTR_TO_FUNC) (float, int); Besser (kürzer): PTR_TO_FUNC search; C++ Entwicklungsprozess 6
Entwicklung mit mehreren Dateien In C++ ist es üblich, den Quelltext in mehreren Dateien zu halten Jede Datei ist dabei ein entsprechendes Modul Es gibt dabei keine Relation zwischen dem Dateiname und dem Inhalt der Datei main.cpp int add(int a, int b); int main(void) { int result, a=2, b=3; std::cout << add(a,b); return 0; utils.cpp int add(int a, int b) { return a + b; int sub(int a, int b) { return a - b; Kompilation unter Linux Der folgende Befehl führt den gesamten Kompilationsprozess durch: g++ -Wall -g file1.cpp file2.cpp -o progname Man kann auch einzelne Teilprozesse (Präprozessor, Kompilieren, Linken) einzeln durchführen: Nur Präprozessor g++ -E file.cpp Nur Kompilieren (ohne Linken) g++ -Wall -c file.cpp Alle Dateien linken g++ file1.o file2.o -o progname 7
Der Prozess des Kompilierens: Getrennte Übersetzung Präprozessor Ersetzt #define, #include Compiler Übersetzt den Quelltext in Machinencode. Der Ausgabe ist eine Objekt-Datei. Dieser Binärcode ist nicht ausführbar. Linker Nimmt alle Objekt Dateien und löst alle Referenzen auf Funktionen, Klassen und Variablen in verschiedenen Dateien auf.! "#$ Präprozessor und Compiler Präprozessor Ersetzt alle #include Anweisungen und erzeugt C++ Text. Versteht selber kein C/C++. Der erzeugte C++ Quelltext wird an den Compiler übergeben. Compiler Erwartet C/C++ Quelltext (ohne Präprozessor Anweisungen) Überprüft Syntax. Generiert und optimiert Maschine Code. Erzeugt Objekt Datei für den Linker. Die Quelltext Dateien und Zeilennummer sind immer noch bekannt. 8
Die wichtigsten GNU G++ Compiler Optionen -Wall enable all warnings -ansi turn on strict ansi compliance -v verbose mode on. (lists which directories were searched for include files/libraries) -g generate debugging symbols -c compile only (create object file) -E run the preprocessor only -s generate assembly code (instead of object code) -I<dir> prepend directory to include path -L<dir> prepend directory to library path -l<libname> library to link (looks for lib<libname>.so, or.a) in library path -o <filename> specifies output filename -O[n] specifies optimization level (default 0) -D<name>[=value] define the give preprocessor Tag Entwicklung Immer mit -Wall und -g: g++ -Wall -g <filenames...> Produktion Immer mit -Wall und O3: g++ -Wall O3 <filenames...> Linker Binder Linker Löst alle Abhängigkeiten der Objekt Dateien (*.o) auf. Erzeugt das ausführbare Programm Macht keine Code Optimierung Kennt keine Typen oder Variablen mehr Auflösung des Namens ist Compiler abhängig es ist also nicht garantiert, dass der Linker Objektdateien (*.o) von unterschiedlichen Compilern linken (binden) kann. 9
Libraries Erfinde das Rad nicht neu! Library := Menge aller vor-compilierten Sourcen (Funktionen, Klassen, Typen, ) Zusammengefasst in einer Datei: (Linux: libm.so, Windows: libm.ddl) Kein lauffähiges Programm (enthält kein main) System-Library := Library, die in bestimmten, vordefinierten Verzeichnissen installiert ist: Kommt typischerweise mit dem Betriebssystem Linux: /usr/lib Ein guter Programmierer kennt viele und die richtigen Libraries... Libraries Wie verwendet man System-Libraries? Im der C++ Datei: Header-Datei includen Beispiel: #include <math.h> Beim Kompilieren der Exe Lib linken Konvention unter Linux: -lm linkt libm.so Beispiel Math-Library linken: g++ -o myprog file1.o file2.o lm 10
Objekt-Dateien Objekt-Dateien enthalten den kompilierten Quelltext und einen Index der definierten und nicht definierten Symbolen. nm (Linux) oder dumpbin (Windows) erzeugt diese Indexliste. Statisches Linken: Der Linker sucht in allen Objekt-Dateien nach entsprechenden Symbolen und führt eine Zuweisung zwischen definierten und nicht definierten Symbolen durch Dynamisches Linken.dll (Windows) and.so (Linux) Bibliotheken, die aus Objekt-Dateien bestehen und zur Laufzeit vom Betriebssystem eingebunden werden. Objekt Datei:.o,.obj Symbole mit nm // Body.h int func(int); //declaration int func(double ); //declaration extern int global; // declaration class Body { int re; public: void volume(); ; // Body.cpp #include "Body.h" void Body::volume() { //definition re = 1; global++; int func(int k) { //definition return (k++); int func(double a) { //definition return (a*2); // helló.cpp #include <iostream> #include "Body.h" int global = 10; int main( ) { Body a; a.volume(); std::cout << example << endl; std::cout << global << endl; return 0; 11
Symbole mit nm oschenk@austin: nm Body.o 00000000 d FRAME_BEGIN 0000001c T func2 Fd 00000014 T func Fi 00000000 t gcc2_compiled. U global 00000000 T volume 4Body // Body.h int func(int); //declaration int func(double ); //declaration extern int global; // declaration class Body { int re; public: void volume(); ; // Body.cpp #include "Body.h" void Body::volume() { //definition re = 1; global++; int func(int k) { //definition return (k++); int func(double a) { //definition return (a*2); Symbole mit nm oschenk@austin: nm Body.o 00000000 d FRAME_BEGIN 0000001c T func2 Fd 00000014 T func Fi 00000000 t gcc2_compiled. U global 00000000 T volume 4Body oschenk@austin: nm hello.o 00000000 d FRAME_BEGIN U ls 7ostreamPCc U ls 7ostreami U throw U cout 00000000 t gcc2_compiled. 00000000 D global 00000000 T main U volume 4Body // helló.cpp #include <iostream> #include "Body.h" int global = 10; int main( ) { Body a; a.volume(); std::cout << example << endl; std::cout << global << endl; return 0; 12
Wer lieferte den Fehler? Error 1: test.c:1:10: #include expects "FILENAME" or <FILENAME> Error 2: /tmp/ccdoe7a7.o: In function `main': /tmp/ccdoe7a7.o(.text+0x7): undefined reference to `foo' collect2: ld returned 1 exit status Error 3: test.c: In function `main': test.c:4: `iii' undeclared (first use in this function) test.c:4: (Each undeclared identifier is reported only once test.c:4: for each function it appears in.) test.c:4: parse error before `989' Error 4: /tmp/ccwltu5r.o: In function `foo': /tmp/ccwltu5r.o(.text+0x0): multiple definition of `foo' /tmp/ccx3bpfi.o(.text+0x0): first defined here collect2: ld returned 1 exit status Makefile Der Prozess des Kompilierens wird durch ein Makefile automatisiert. Ein Makefile führt eine Liste der Abhängigkeiten der einzelnen Quelltext-Dateien und liefert Direktiven und Befehle zum automatischen Kompilieren des Projektes. Abhängigkeiten Compile Befehl Abhängigkeiten Compile Befehl Abhängigkeiten Link Befehl Das Erstellen und das Verwalten von Makefiles kann sehr aufwendig sein. Es gibt Werkzeuge zum automatischen Erzeugen von Makefile: TMake oder QMake 13
TMake TMake ist frei verfügbar unter: http://tmake.sourceforge.net/ http://www.trolltech.com/download/tmake.html Dokumentation http://docsrv.sco.com:8457/en/tmake/tmake.html to create and maintain makefiles for software projects. It can be a painful task to manage makefiles manually, especially if you develop for more than one platform or use more than one compiler. tmake automates and streamlines this process and lets you spend your valuable time on writing code, not makefiles. TMake - Beispiel test_body.pro! "#" oschenk@austin: setenv TMAKEPATH /local/tmake/lib/linux-g++ oschenk@austin: setenv PATH $PATH:/local/tmake/bin oschenk@austin: tmake test_body.pro -o Makefile oschenk@austin: make g++ -c -pipe -Wall -W -O2 -o Body.o Body.cpp g++ -c -pipe -Wall -W -O2 -o hello.o hello.cpp rm -f test_body g++ -o test_body Body.o hello.o oschenk@austin: make clean rm -f Body.o hello.o test_body rm -f core *~ TMake: generiert automatisch ein Makefile die Anhängigkeiten zwischen den Dateien werden automatisch erkannt. Mehr Infos im Tmake Manual 14
Dokumentation des C++ Code Das Verwalten und Modifizieren von grossen C++ Paketen kann sehr komplex sein. Gute Dokumentation ist deshalb sehr wichtig. Doxygen ist ein Werkzeug zur Dokumentation von C++ Quelltexten vergleichbar zu javadoc. Doxygen ist frei verfügbar unter http://www.doxygen.org Doxygen Beispiel einer Dokumentation einer Funktion /** Dokumentation of a function * @param lower lower bound of the range * @param upper upper bound of the range * @return A vector with the same size as this * vector and binary elements * @warning some detail.. * @todo.. * @bug **/ FVector findinrange(float lower, float upper) const; Doxygen analysiert die C++ Dateien und erzeugt html Dateien mit der entsprechenden Dokumentation. 15
Dokumentation Dokumentation des eigenen Quelltextes ist sehr wichtig. Während der Entwicklung kennt man alle Details des Codes. In 6 Monaten hat man alles vergessen. Um den Quelltext wieder verwenden zu können: DOKUMENTIEREN Sie den Quelltext. Jede Funktion, jeder Parameter und Return Werte müssen dokumentiert werden Alle Klasse und alle Klassenvariablem müssen dokumentitert werden Es muss auch die Funktionalität erklärt werden und warum und was man gerade macht. Englisch für Dokumentation des Quelltextes. Werkzeuge: Emacs Farbliche Hervorhebung der C++ Syntax Automatischer Texteinzug und C++ Formatierung Kontext- (sprach-) sensitive Hilfefunktion Automatisches Interface mit Ihrem Debugger (gdb) Tutorial: www.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html Michael Hagemann wird in der 2 Lektion eine kleine Einführung in die emacs Entwicklungsumgebung geben. 16
Werkzeuge: Debugging Debugger := Tool zur Inspektion der Daten und des Ablaufs eines Programmes zur Laufzeit Achtung: Debugger braucht Debug-Infos im Executable g++ -Wall -g program.cpp o program Aufruf gdb program ddd program (console) oder (GUI) Breakpoint := Haltepunkt, Ausführung des Programms bis dahin, dann Kontrolle an den User, Werkzeuge: Valgrind Valgrind ist ein leistungsfähiges Tool, um Fehler im Speicher-Management von Programmen zu finden. Softwareumgebung: Speichertest, Threadtest, Profiling, Es wird von vielen großen OpenSource-Projekten verwendet (Mozilla, OpenOffice, KDE,...) Mehr Information unter http://valgrind.org Zu testendes Programm mit Debugging-Informationen übersetzen. (Parameter "-g" beim gcc.) valgrind --tool=memcheck./programm 17
Werkzeuge: Valgrind // simple test for valgrind #include <iostream> const int N=10; // # of elements in array int main() { int *p2 = new int[n]; // allocate an int array std::cout << "Test 1: off by one" << endl; for (int i=1; i<=n; i++) // ERROR one-off in loop p2[i] = i; // err - initialize element p[n] return 0; GDB Debugger: Valgrind: oschenk@austin: g++ -Wall -g a.cpp o test_val oschenk@austin: gdb./test_val (gdb) r Starting program: test_val Test 1: off by one Program exited normally. oschenk@austin: valgrind --tool=memcheck./test_val ==32116== Invalid write of size 4 ==32116== at 0x80486C0: main (a.cpp:10) ==32116== Address 0x1BAC4050 is 0 bytes after a block of size 40 ==32116== by 0x804866F: main (a.cpp:7) 18