05.11.2013 Aufrufe

Skript Programmierung C/C++

Skript Programmierung C/C++

Skript Programmierung C/C++

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

private Vorlesungsmitschrift (nicht offiziell)<br />

Programmieren in C / <strong>C++</strong><br />

Kurs WS1999 – SS2001, Prof. F. Mehner<br />

Fachhochschule Südwestfalen<br />

- Campus Iserlohn -<br />

Frauenstuhlweg 31<br />

58644 Iserlohn<br />

Dieses <strong>Skript</strong> wurde in der Vorlesung „Programmieren in C/<strong>C++</strong>“ bei Prof. Mehner von mir<br />

getippt; der Inhalt wurde nicht durch Professoren geprüft und enthält ggf. einige syntaktische<br />

Fehler. Fehler, die Sie entdecken, teilen Sie mir bitte einfach per Kontaktformular auf<br />

www.ChristianSchlueter.de mit. Danke im Voraus,<br />

Christian Schlüter<br />

Stand: 2003-07-10<br />

1


Inhaltsverzeichnis<br />

Seite<br />

Kap. Inhalt, Titel<br />

1 .................. -- Deckblatt<br />

2 .................. -- Inhaltsverzeichnis<br />

3 .................. 01 Schnellkurs C<br />

11 .................. 02 Syntaxregeln und Operatoren<br />

14 .................. 03 Datentypen und Wertebereiche<br />

20 .................. 05 Funktionen<br />

29 .................. 06 Zeiger und Felder<br />

34 .................. 07 #define-Makros<br />

35 .................. 08 Vereinbarungen, Zugriffe, Strukturen<br />

38 .................. 09 <strong>C++</strong> / nicht-objektorientierte Erweiterungen<br />

41 .................. 10 Klassen<br />

46 .................. 11 Überladen von Operatoren<br />

48 .................. 12 Statische Objekte<br />

49 .................. 13 Vererbung<br />

54 .................. 14 Templates (Schablonen)<br />

61 .................. 15 Programmerzeugung mit "make"<br />

63 .................. 16 GUI – grafische Benutzeroberflächen<br />

66 .................. 17 Error Handler und Exceptions<br />

2


Kapitel 1 – Die Programmiersprache C (Schnellkurs)<br />

1.1) Programmentwicklungsschritte<br />

(Folie F1 und F2 aus dem Internet holen)<br />

1.2) Textausgabe<br />

#include <br />

void main ()<br />

{<br />

printf („\hHallo, Welt \n“);<br />

}<br />

Erläuterungen<br />

Zeile 1:<br />

Zeile 2:<br />

Zeile 3+5:<br />

Präprozessor kopiert die Datei stdio.h ein<br />

Kopf des Hauptprogramms<br />

Beginn, Ende Hauptprogramm<br />

Anmerkung: stdio.h ist eine bereits bestehende Bibliothek für Standard-Input-Output und wird<br />

vor der Übersetzung aus dem System (hier Linux) einkopiert.<br />

Anmerkung: Der Slash „\“ in einer Textausgabe hat verschiedene Funktionen, die jeweils vom<br />

folgenden Buchstaben abhängen:<br />

n new line, Neue Zeile<br />

t tabulator, Tabulator<br />

b backspace, Rücksetzen<br />

a alert, Signalton<br />

\\ Zeichen „\“<br />

Der Slash ist ein Fluchtsymbol bzw. der Beginn eines Steuerzeichens<br />

#include <br />

void main ()<br />

{<br />

int x,i;<br />

}<br />

for (i=0; i


}<br />

celsius = (5.0 / 9.0) * (fahrenheit – 32.0);<br />

printf („\n %lf °F = %lf °C \n“, fahrenheit, celsius);<br />

Anmerkung:<br />

In Zeile 6 des Programms muss bei der Division „.0“ hinter den Zahlen stehen (reelle Konstanten), da<br />

die Zahlen sonst als ganze Zahlen angesehen werden, so dass ein ganzzahliges Ergebnis erzwungen<br />

wird. Im Fall 5/9 hieße das, dass das Ergebnis 0 heisst, weil 9 = 0 Mal in 5 passt. Das gewünschte<br />

Ergebnis müsste 0.555 (Periode 5) heissen.<br />

In diesem Fall schneidet C einfach eine Stelle ab, weil der Speicherplatz beschränkt ist. Nach 15<br />

Stellen hinter dem Komma wird grundsätzlich standardmäßig abgeschnitten. Bei anderen<br />

Programmen kann gerundet oder ebenfalls geschnitten wird.<br />

double Datentyp; reele Zahl, doppelt genau<br />

printf („\n %lf °F = %lf °C \n“, fahrenheit, celsius);<br />

Auch das Prozentzeichen dient als Fluchtsymbol und korrespondiert mit den nachstehenden Werten<br />

der Variablen. In diesem Fall bezieht sich das erste %lf auf die Variable „fahrenheit“, die letzte auf<br />

„celsius“<br />

Laut Professor Mehner ist...<br />

%lf eine Formatbeschreibung für double-Größen<br />

%f (flout), reelle Zahl, einfach genau<br />

%d int (Integer), ganze Zahl<br />

%15.5lf Formatierungsanweisung, die sich wie folgt aufbaut:<br />

% Gesamtstellen.Nachkommastellen lf<br />

1.4) Die Präprozessor-Anweisungen include und define<br />

Der Präprozessor ist der erste Teil eines Compilers, der über den Quellcode geht und<br />

Ersetzungen durchführt.<br />

include-Anweisung<br />

#include <br />

Die Standard-Header-Datei stdio.h wird an Stelle dieser Zeile vollständig<br />

einkopiert<br />

#include „projekt41.h“<br />

Eigene Header-Datei im augenblicklich gültigen Arbeitsverzeichnis<br />

define-Anweisung<br />

#define Originaltext Ersetzungstext<br />

Beispiel: #define N 50<br />

#define PI 3.1415<br />

# define PI2 (2.0*PI)<br />

# define NL printf(„\n“)<br />

Anmerkung:<br />

Problem, dass auch Buchstaben in Anweisungen ersetzt würden, denn es<br />

erfolgt keine Überprüfung – deshalb sparsam gebrauchen!!!<br />

1.5) Einfache Ein- und Ausgabe<br />

4


Ausgabe: printf formatierte Ausgabe<br />

Beispiel:<br />

printf („x =%6.1lf,wi=w%lf%“,x,i);<br />

Aufbau der Teilformate: siehe Folie (F-3)<br />

Eingabe:<br />

Beispiel:<br />

scanf (Format, Variablenliste);<br />

ganze Zahl einlesen<br />

int i;<br />

printf („\n i = „);<br />

scanf („%d“, &i);<br />

Anmerkung: Das & ist ein sogenannter Adressoperator. Er ermöglicht den Schreibzugriff<br />

auf eine Variable.<br />

1.6) Ablaufsteuerung: while, for, if - else<br />

Bsp.:<br />

Temperaturrechnung<br />

°F -> °C<br />

2spaltige Tabelle<br />

Fahrenheit 0° bis 300°<br />

Schrittweite 20°<br />

#include <br />

void main()<br />

{<br />

double fahrenheit, celsius;<br />

int unten, oben, schritt;<br />

unten = 0;<br />

oben = 300;<br />

schritt = 20;<br />

fahrenheit = unten;<br />

}<br />

while (fahrenheit < oben)<br />

{<br />

celsius = (5./9.)*fahrenheit-32.0);<br />

printf("\n%5.0lf\t%5.1lf",fahrenheit,celsius<br />

);<br />

fahrenheit = fahrenheit + schritt;<br />

}<br />

while-Schleife<br />

for-Schleife<br />

if-Schleife<br />

1.7) Einfache Funktionen<br />

Mathematisch: y(x) = f(x) = x 2 – 27 + 3x<br />

x ist die unabhängige Variable<br />

5


Der nachstehende Term hinter f(x)= ist Berechnungsvorschrift<br />

Die gesamte Zeile wird Definition genannt<br />

y(3.3) = f(3.3)=...<br />

ist die Verwendung.<br />

Beispiel: °C = 5/9 (°F – 32)<br />

# include <br />

double celsius (double fahrenheit)<br />

{<br />

double erg;<br />

erg (5.0/9.0)*(fahrenheit – 32);<br />

return erg;<br />

}<br />

//Def. der Fkt celsius, Kopf<br />

//Rumpf<br />

//Rumpf<br />

//Rumpf<br />

void main ()<br />

{<br />

double fahr, cels;<br />

int unten, oben, schritt;<br />

unten = 0;<br />

oben = 300;<br />

schritt = 20;<br />

}<br />

for (fahr=unten; fahr


Bsp.:<br />

Feld mit Quadratzahlen belegen<br />

#include <br />

#define N 100<br />

void main ()<br />

{<br />

int q[N], i, n;<br />

n=N;<br />

for(i=0; i


for (i=0; text[i] != '\O'; i=i+1)<br />

putchar (text [i]);<br />

summgr = 0;<br />

summkl = 0;<br />

/* Text untersuchen */<br />

for (i=0; text [i] != '\O'; i=i+1)<br />

if (islower (text[i])) summkl = summkl + 1;<br />

if (isupper (text[i])) summgr = summgr + 1;<br />

/* Ergebnisse ausgeben */<br />

}<br />

printf("\n %d Kleinbuchstaben, %d Grossbuchstaben", summkl, summgr);<br />

Anmerkungen:<br />

for (i=0; (c=getchar()) != ‘\n‘; i=i+1)<br />

Die gesamte Klammer hat den Wert des Zeichens (c) und kann deshalb mit anderen Objekten<br />

verglichen werden. Ausserdem stellt sie die Zuweisung des eingelesenen Zeichens an die<br />

Variable dar.<br />

for (i=0; ((c = getchar()) != ‘\n‘); i = i+1)<br />

Wert: eingelesenes Zeichen; Prüfung auf Ungleichheit<br />

û\nû Konstante vom Typ char (8bit)<br />

Einzelzeichen<br />

0 1 2 3 4 ... 255<br />

text D i e s \0 ...<br />

ASCII 68 105<br />

text[i]=‘\0‘;<br />

if (text[i]==‘ä‘ sumkl++;<br />

1.9) Dateihandhabung<br />

Umlenkung der Ein-/Ausgabe von der Kommandozeile<br />

...>prog1.e>prog1.out<br />

>prog1.e >prog1.out<br />

Eingaben; Ausgaben<br />

Lesen und Schreiben mit fscanf und fprintf<br />

fopen ()<br />

fclose ()<br />

fscanf ()<br />

fprint ()<br />

Datei öffnen<br />

Datei schließen<br />

Lesen<br />

Schreiben ACHTUNG: Variable muss Referenz haben!!!<br />

Öffnen einer Datei<br />

#include <br />

void main ()<br />

{<br />

FILE * input;<br />

//Dateizeiger: Eingabe<br />

8


FILE * output;<br />

input = fopen (“eingabe.dat“, “r“);<br />

output = fopen (“ausgabe.dat“, “r“); ...<br />

Dateizeiger (Adressvariable), Dateinamen, Modus (r-read, w-write)<br />

Schließen einer Datei<br />

...<br />

fclose (input);<br />

fclose (output);<br />

Dateizeiger<br />

Schreiben einer Datei<br />

fprintf (output, “%ld \n“, x);<br />

Dateien, Format, Variable<br />

Beispiel: Datei Ein-/Ausgabe:<br />

#include <br />

#include <br />

#define N 100<br />

int main ()<br />

{<br />

FILE * eingabe;<br />

FILE * ausgabe;<br />

double vector[N];<br />

int i, n;<br />

/* --- Eingabedatei öffnen ---*/<br />

eingabe = fopen ("vector.dat","r");<br />

if (eingabe == NULL)<br />

{<br />

printf ("\n vector.dat kann nicht geöffnet werden!");<br />

exit(1);<br />

}<br />

/* --- Datei lesen ---*/<br />

fscanf (eingabe, "%d", &n); //Anzahl Vek.-Elemente<br />

for (i=0; i


fprintf (ausgabe, "%d", n);<br />

for (i=0; i


Kapitel 2 – Syntaxregeln und Operatoren<br />

2.1 Syntaxregeln<br />

Syntax:<br />

Satzbau, richtige Art und Weise, Sprachelemente zu Sätzen zuzuordnen.<br />

Übersetzer muss entscheiden, ob ein vorgelegtes Programm fehlerfrei ist.<br />

Dafür ist erforderlich:<br />

Syntaxregeln müssen eindeutig festgelegt sein<br />

die Bedeutung der Zeichen und der Sprachkonstruktion muss eindeutig<br />

festgelegt sein (Semantik)<br />

Eine Möglichkeit zur Formalisierung der Syntax: Backus-Naur-Form<br />

Zweck: Beschreibung von Grammatiken von Programmiersprachen<br />

#include <br />

#include <br />

#define N 100<br />

int main ()<br />

{<br />

FILE * eingabe;<br />

FILE * ausgabe;<br />

double vector[N];<br />

int i, n;<br />

/* --- Eingabedatei öffnen ---*/<br />

eingabe = fopen ("vector.dat","r");<br />

if (eingabe == NULL)<br />

{<br />

printf ("\n vector.dat kann nicht geöffnet werden!");<br />

exit(1);<br />

}<br />

/* --- Datei lesen ---*/<br />

fscanf (eingabe, "%d", &n); //Anzahl Vek.-Elemente<br />

for (i=0; i


BNF (Backus-Nauer-Form) - Formalisierung einer Grammatik mit den Bestandteilen:<br />

(1) Terminalsymbole (die gültigen bzw. verwendbaren Zeichen)<br />

(2) Nichtterminalsymbole (Bsp.: letter, statement, identifier, ...)<br />

(3) Produktionen (auch Ableitungsregeln)<br />

(4) Startsymbol (ausgezeichnetes Nichtterminalsymbol)<br />

digit ::= 0|1|2|3|4|...|9<br />

syntaktische Kategorie 10 Alternativen Terminalsymbole (nicht weiter zerlegbar)<br />

Nichtterminalsymbol<br />

::= Bedeutung "kann ersetzt werden durch"<br />

Schreibweisen in Produktion<br />

| Auswahlzeichen, Alternativen<br />

{} 1 genau 1 Term ist auszuwählen<br />

{} 0+ 0 oder mehr Term sind auszuwählen<br />

{} 1+ 1 oder mehr Term sind auszuwählen<br />

{} wahlfreier Term<br />

2.2 C-Schlüsselwörter<br />

C hat 32 Schlüsselwörter (siehe Folie F-9). Dies sind reservierte Namen, die nicht mehr als<br />

eigene Variablen angegeben werden dürfen.<br />

2.3 Namen<br />

Bestandteile:<br />

Klein- und Großbuchstaben, Ziffern, Unterstrich<br />

BNF<br />

identifier ::= {letter | underscore} 0<br />

{letter | underscore | digit} 0+<br />

!!! keine Ziffer am Anfang<br />

letter ::= lowercaseletter | uppercaseletter<br />

lowercaseletter ::= a | b | c ... /2<br />

uppercaseletter ::= A | B | C ... /2<br />

Regeln zur Namensvergabe:<br />

- am Anfang keine Ziffer<br />

- Variablen- und Funktionsnamen meist in Kleinbuchstaben<br />

- define-Konstanten meist in Großbuchstaben<br />

- mindestens die ersten 31 Zeichen werden unterschieden<br />

2.4 Ganzzahlige dezimale Konstanten<br />

BNF<br />

decimal_integer ::= 0 | positive_decimal_integer<br />

positive_decimal_integer ::= positive_digit {digit} 0+<br />

positive_digit ::= 1 | 2 | ... | 9<br />

Folge: führende Nullen nicht erlaubt!!!<br />

2.5 Zeichenkonstanten<br />

Zeichenketten : Klammerung mit "..."<br />

12


Beispiel: "abc"<br />

97, 98, 99, 0 (Byte-ASCII-Code, binäre 0 als Abschlußzeichen<br />

Einzelzeichenkonstante<br />

Klammerung durch '...' (einfache Anführungszeichen)<br />

Beispiel: char yes= 'y';<br />

2.6 Inkrement- und Dekrement-Operator<br />

++ Erhöhung einer ganzzahligen Variable um 1<br />

-- Verminderung um 1<br />

als Präfix-Operator:<br />

als Postfix-Operatoren: i+4;<br />

++i;<br />

Beispiel:<br />

for-Schleife<br />

for (Ci = 0; i < n; c + 4) ....<br />

2.7 Zuweisungsoperatoren<br />

Kurzschreibweise<br />

a += b<br />

a -= b<br />

b *= c<br />

b /= c<br />

b *= x + y +23<br />

a=b=c=0<br />

Bedeutung<br />

a=a+b<br />

a=a-b<br />

b=b*c<br />

b=b/c<br />

b=b*(x+y+23)<br />

rechte Seite wird zuerst<br />

ausgerechnet<br />

Mehrfachzuweisung<br />

2.8 Vorrang und Bindung<br />

Auswertungsreihenfolge in Ausdrücken<br />

Festlegung:<br />

(1) Klammerung<br />

(2) Beachtung der Vorrangsregeln<br />

Beispiel:<br />

if (2*a


Kapitel 3 – Datentypen und Wertebereiche<br />

Überreicht Basisdatentypen<br />

ganzzahlig: char signed char unsigned char<br />

short ...<br />

int ...<br />

long ...<br />

reell: float einfache Genauigkeit<br />

double<br />

doppelte Genauigkeit<br />

long double Kombination<br />

3.1 Datentyp char<br />

interne Darstellung (Bits 8 bit sind 1 byte)<br />

7 6 5 4 3 2 1 0<br />

0 0 1 0 0 1 0 1<br />

Verwendung:<br />

Darstellung von Zeichen (ASCII-Code) und kleinen ganzen Zahlen<br />

signed char : -128 ... +127<br />

unsigned char: 0 ... +255<br />

7 6 bis 0<br />

Vorzeichen Betrag<br />

char-Konstanten:<br />

char i, c;<br />

i = 120;<br />

c = 'c';<br />

//Achtung: dem Zeichen 'c' ist ein Wert zugewiesen<br />

3.2 Datentyp int<br />

Darstellung abhängig vom Betriebssystem und Compiler<br />

Beispiel:<br />

GNU-C, Linux, Pentium:<br />

short int<br />

int<br />

long int<br />

2 Byte<br />

4 Byte<br />

4 Byte<br />

Typ "short int":<br />

15 14 bis 0<br />

Vorzeichen Betrag<br />

signed short int:<br />

-2 15 ... +(2 15 –1)<br />

-32768 ... +32767<br />

unsigned short int<br />

0 ... (2 16 –1)<br />

0 ... 65535<br />

14


Typ "int":<br />

31 30 bis 0<br />

Vorzeichen Betrag<br />

signed int:<br />

-2 31 ... +(2 31 –1)<br />

unsigned int:<br />

0 ... +(2 32 –1)<br />

Darstellung ganzzahliger Konstanten<br />

Suffixe für unsigned-Konstanten uU<br />

Suffixe für long-Konstanten<br />

lL<br />

x = 1000000L;<br />

y = 3000000UL;<br />

Oktale Konstanten: führende 0 (Null)<br />

x = 077 Wert für 63<br />

Hexadezimale Konstanten: führende 0x oder 0X<br />

y = 0x77 Wert für 119<br />

y = 0XFF Wert für 255<br />

Überschreitung des Zahlenbereichs (an einem Beispiel):<br />

int i;<br />

i = 100000;<br />

printf ("\n %d", (i*i)/i);<br />

Das Zwischenergebnis der Klammer (i*i) ergibt 10 10 zu hoch!<br />

3.3 Reelle Zahlentypen<br />

normalisierte Darstellung<br />

5,375 10 = 4 + 1 + 1/4 + 1/8 (dezimal)<br />

101,011 2 (dual)<br />

+ 1,01011 2 * 2 +2<br />

Vorz. Mantisse<br />

interne Darstellung von Float-Zahlen<br />

31 30 ... 23 22 ... 0<br />

Vorz. Exponent +127 (8 bit) Mantisse (23 bit)<br />

Exponent wird um +127 erhöht<br />

Am Beispiel 5,375:<br />

Mantisse: 010110...0<br />

Exponent: 2 10 10000001<br />

Vorzeichen: + 0<br />

31 30 ... 23 22 ... 0<br />

0 1 0 0 0 0 0 0 1 0 1 0 1 1 0 ... ... ... ... 0<br />

größte Mantisse: 1-2 23<br />

15


größter Exponent: 127<br />

größte Zahl: 2 * 2 127 3,4 * 10 38<br />

Anzahl der binären Nachkommastellen<br />

23 Nachkommastellen binär 7 Nachkommastellen dezimal<br />

Typ: "double"<br />

80 79 ... 52 51 ... 0<br />

Vorz. Exponent 11bit Mantisse 52bit<br />

kleinste Zahl: 2.22507385850720140*10 -308<br />

größte Zahl: 1.79769313486231570*10 +308 (Vorlesung 99-12-15)<br />

Anzahl der gültigen Stellen:<br />

float 32bit ~7 Stellen<br />

double 64bit ~15 Stellen<br />

long double 80bit ~19 Stellen<br />

Darstellungsbedingte Rundungsfehler<br />

Rundungsfehler treten bei jedem Zahlensystem auf, wenn eine feste Stellenanzahl verwendet wird.<br />

10er-System 1/10 = 0,1 exakt darstellbar<br />

2er-System 1/10 = 0,00011<br />

nicht exakt darstellbar<br />

Folgen:<br />

Grundgesetze der Arithmetik zum Teil ausser Kraft!!!<br />

x0 y x+y x x+y y<br />

Beispiel:<br />

große Zahl + kleine Zahl bei 7 gültigen Stellen<br />

x = 1.0e-4;<br />

y = 1.0e+4;<br />

z = x + y;<br />

x 0 , 0 0 0 1<br />

y 1 0 0 0 0 , 0<br />

z 1 0 0 0 0 , 0 0 0 1<br />

7 Stellen<br />

Praktische Folgen:<br />

1) Summation konvergierender Folgen<br />

Bsp.: n<br />

x x<br />

e <br />

n0 n!<br />

2) Aufsteigende / absteigende Summation liefert untersch. Ergebnisse<br />

1000000<br />

Bsp.:<br />

s<br />

<br />

<br />

1<br />

n1 n!<br />

harmonische Reihe<br />

16


3) direkte Verwendung math. Identitäten nicht möglich / sinnvoll<br />

Bsp.: sin( x)<br />

1<br />

cos<br />

2 ( x)<br />

c = cos(x) berechnet, aber evtl. mit<br />

s = sqrt(1.0 – c*c);<br />

c*c darf höchstens 1.0+ sein, sonst ist die Wurzel negativ<br />

Lösung:<br />

s = 1.0 – c*c;<br />

if (s > 0.0)<br />

s = sqrt(s);<br />

else<br />

s = 0,0; //Unterdrûckung von Rundungsfehlern<br />

4) kein direkter Vergleich numerischer Größen!<br />

x = ... ;<br />

if (x == 1,75)<br />

{...}<br />

VÖLLIG UNBRAUCHBAR!!!<br />

#define EPS 1.0e-8<br />

...<br />

if (fabs(x-1,75)


Beispiel: gemischte Ausdrücke, automatische Typumwandlung<br />

int a = 7;<br />

a *= 0.5; //Wert 3<br />

float c = 8.0;<br />

c *= ½; //Wert 0(!!!), denn ganzzahlige Division von1/2 = 0!<br />

Automatische Typumwandlung bei Zuweisungen<br />

int a;<br />

float f;<br />

double d;<br />

...<br />

a = f;<br />

f = d;<br />

//evtl. Verlust der Nachkommastellen<br />

//evtl. Überlauf oder Verlust gültiger Stellen<br />

Automatische Typumwandlung bei Funktionsargumenten und Rückgabewerten<br />

Definition einer Funktion (im Funktionskopf):<br />

int f (int a)<br />

{ ... }<br />

Aufruf:<br />

double a, b;<br />

b = 0,75;<br />

a = f(b);<br />

3.4.2 Explizite Typumwandlung (type cast)<br />

Beispiel 1: int i;<br />

double y;<br />

...<br />

y = (double) (i+1);<br />

//type-cast-Operator (...)<br />

Beispiel 2: double d;<br />

int i;<br />

d = ...;<br />

d = (int) (d+0.5);<br />

//echte Rundung<br />

Beispiel 3: i = (int) (log 10(d)) + 1;<br />

//Anzahl der Vorkommastellen<br />

18


Beispiel 4: d = ...;<br />

d = ((int) (10*d))/10.0;<br />

19


Kapitel 4 – Ablaufkontrolle<br />

4.1 Vergleichsoperatoren<br />

< kleiner<br />

größer<br />

>= größer gleich<br />

== Gleichheit (oder vergleichen?!)<br />

!= Ungleichheit<br />

Das Ergebnis eines Vergleichs ist ein Wahrheitswert:<br />

0 entspricht falsch<br />

1 entspricht richtig<br />

Beispiel: Umsetzung der math. Schreibweise<br />

3 < x < 7<br />

1.) int x = 2;<br />

if (3 < x < 7)<br />

{...}<br />

wahr<br />

//0 < 7 falsch<br />

2.) Bedingung muss ausformuliert werden!!!<br />

if (3


Beispiel:<br />

if (x0)<br />

{...}<br />

Fall x=0 fehlt!!!<br />

Beispiel:<br />

if (x


i = 2 * i + x%37;<br />

}<br />

Ein Ausdruck "for ( ; ; )" entspricht "while (true)", wird also als wahr angesehen und ist<br />

sozusagen eine Endlos-Schleife.<br />

4.6 Der Komma-Operator<br />

allgemeine Form: Ausdruck, Ausdruck, ...;<br />

Beispiel:<br />

Ausführungsreihenfolge<br />

sum = 0.0;<br />

for ( i=0; i


Die Fallbezeichnungen müssen zur Übersetzungszeit eine ganzzahlige Konstante liefern,<br />

möglich:<br />

case 27: ...<br />

case 0xA: ...<br />

case 'c': ...<br />

case 7+23: ...<br />

der default-Fall kann fehlen<br />

break kann fehlen, hat aber Konsequenzen beliebter Fehler! Ggf. wird der Code des<br />

nächsten Falls ausgeführt!<br />

Beispiel zu einer Switch-Anwendung:<br />

int tag,monat,jahr;<br />

...<br />

tag=...; monat=...;<br />

switch (monat)<br />

{<br />

case 1:<br />

case 3:<br />

case 5:<br />

case 7:<br />

case 8:<br />

case 10:<br />

case 12: if (tag < 1 || tag > 31)<br />

printf ("...");<br />

break;<br />

case 4:<br />

case 6:<br />

case 9:<br />

case 11: if (tag 30)<br />

printf ("...");<br />

break;<br />

}<br />

case 2:<br />

default:<br />

if (tag28) //Schaltjahr? Y2K-Probleme!!!<br />

printf ("...");<br />

break;<br />

printf ("Falsche Monatsangabe");<br />

oder mit "int monat[13] = {0, 31, 28, 31, ..., 31}"<br />

(leeres Feld für die einfachere Umrechnung, sonst wäre z.B. Januar = 0)<br />

Beispiel: switch ersetzt if-else-Anweisung (dazu Folie 12!)<br />

int fall;<br />

fall = 0;<br />

if (a != 0) fall += 4;<br />

if (b != 0) fall += 2;<br />

if (c != 0) fall += 1;<br />

switch (fall)<br />

{<br />

case 0: ...; break;<br />

case 1: ...; break;<br />

...<br />

}<br />

23


4.9 break-Anweisung<br />

Form: break; //beendet eine Schleife oder switch-Anweisungen<br />

alternativ:<br />

int feld[10000], i, n, wert;<br />

...<br />

for (i=0; i


Kapitel 5 – Funktionen<br />

5.1 Definition einer Funktion<br />

allgemeine Form:<br />

Typ Funktionsname (Parameterliste)<br />

{<br />

Vereinbarungen<br />

Anweisungen<br />

}<br />

Wenn an Stelle von "Typ" nichts steht, wird automatisch int gewählt,<br />

aber: schlechter Programmierstil;<br />

Funktionsname ist vom Programmierer "frei" wählbar,<br />

eine Parameterliste muss nicht vorhanden sein,<br />

Beispiel: double fahr_celsius (double fahr) //Kopf mit Parameter in ( )<br />

{ //ab hier: Rumpf<br />

double celsius;<br />

celsius = (3.0/9.0) (fahr – 32);<br />

return celsius;<br />

}<br />

Anmerkung: Zwischen Kopf und Rumpf kommt nichts! In alten<br />

C-Dokumenten ist das nicht immer so, aber: nicht beachten!<br />

Beispiel: void meldung3 ()<br />

{ printf ("\n ...!"); }<br />

Beispiel:<br />

c <br />

a<br />

2<br />

b<br />

2<br />

#include <br />

#include <br />

double hypo(double a, double b)<br />

{<br />

double c;<br />

c = sqrt (a a + b b)<br />

return c;<br />

}<br />

void main ()<br />

{<br />

double x, y, z;<br />

x = 2.3;<br />

y = 7.9;<br />

z = hypo (x, y); //Aufruf der Funktion mit Übergabe von zwei<br />

} //"aktuellen Parametern" ("Argumente")<br />

Durch das z wird eine Rücksprungadresse definiert.<br />

5.2 Die return-Anweisung<br />

2 Formen: return; //verlässt ddie Funktion ohne alles<br />

return Ausdruck; //gibt v.d. Verlassen noch einen Wert zurück<br />

Beispiele: return 0;<br />

return erg;<br />

return 2 h – 33;<br />

return sqrt(a a + b b);<br />

25


Für den Rückgabewert findet ggf. eine implizite Typersetzung statt.<br />

5.3 Prototypen<br />

Prototyp:<br />

Zweck:<br />

Funktionstyp, abgeschlossen mit Semikolon; die Implementierung<br />

kann in einer anderen Datei oder weiter unten stehen.<br />

Vereinbarung der Funktion (Name, Rückgabetyp, Anzahl und Typ der<br />

Parameter) zur automatischen Überprüfung der formalen Richtigkeit<br />

der Aufrufe.<br />

Beispiel: double hypo(double a, double b); /* Prototyp */<br />

void main()<br />

{<br />

...<br />

z = hypo(x,y); /* Aufruf */<br />

5.4 Funktionsparameter mit Wertübergabe<br />

2 Arten der Parameterübergabe: - Wertpbergabe<br />

- Adressübergabe<br />

Adresse Inhalt Name<br />

101029 100 x<br />

... ... ...<br />

Bei C ist die Wertübergabe als Standardfall definiert.<br />

Eine Kopie des Parameterwertes wird an eine Funktion übergeben.<br />

Beispiel:<br />

Summe der Zahlen von 1 bis n<br />

summe_1_bis_n (int n)<br />

{<br />

int summe = 0;<br />

for ( ; n>0 ; n--) summe += n;<br />

return summe;<br />

}<br />

void main()<br />

{<br />

int n, s;<br />

n = 3;<br />

s = summe_1_bis_n (n);<br />

printf ("Summe = %d, n = %d", s, n);<br />

}<br />

Der Parameter n hat mit der Variable im Hauptprogramm nichts zu tun! Das n<br />

(mit Datentyp int) im Hauptprogramm existiert wirklich und bleibt unverändert.<br />

Die externe Prozedur erhält nur eine Kopie des Wertes und weiss daher nicht,<br />

woher der Wert kommt.<br />

5.5 Speicherklassen<br />

Grundlegende Gültigkeitsregeln für Variablen<br />

26


- eine Variable ist genau in dem Block gültig, in dem sie vereinbart ist (sog. lokale<br />

Variablen)<br />

5.5.1 Speicherklasse "auto"<br />

- Schlüsselwort "auto"<br />

- Variablen, die innerhalb eines Blockes vereinbart werden, besitzen den Speichertyp<br />

"auto" (Ersatztyp)<br />

- der zugehörige Speicher wird beim Betreten des Blockes beschafft, beim Verlassen<br />

freigegeben.<br />

5.5.2 Speicherklasse "extern"<br />

- Variablen, die ausserhalb einer Funktion vereinbart sind, haben den Speichertyp "extern"<br />

- der Speicherplatz ist permanent vorhanden (statisch)<br />

Vorteile:<br />

einfachste und unaufwendigste Möglichkeit, Werte zwischen Funktionen auszutauschen<br />

Nachteile:<br />

Gefahr des unbeabsichtigten Überschreibens!<br />

Beispiel:<br />

Summation<br />

int n = 10;<br />

double x[1000];<br />

...<br />

double summe( );<br />

{<br />

int i;<br />

double summe = 0.0;<br />

for (i=0; i


static int aufruf = 0;<br />

Initialisierung findet nur beim 1. Aufruf der Funktion statt<br />

<br />

Speicherplatz und Inhalt bleiben nach Verlassen der Funktion erhalten.<br />

5.6 Rekursive Funktionen<br />

Eine Funktion heisst rekursiv, wenn sie sich direkt oder indirekt selbst aufruft.<br />

direkt:<br />

indirekt:<br />

Bsp.:<br />

f ruft f auf<br />

f ruft g auf, g ruft dann f auf.<br />

Berechnung der Fakultät<br />

int fakultaet (int n)<br />

{<br />

if (n


Kapitel 6 – Zeiger und Felder<br />

Zum näheren Verständnis ist auch Kapitel 5 (Algorithmen) aus der Informatikvorlesungen ratsam.<br />

6.1 Eindimensionale Felder ( Folie 14)<br />

Beispiel:<br />

int a[100];<br />

Index 0...99 //große Fehlerquelle, Start NICHT bei 1!<br />

Initialisierungsmöglichkeiten<br />

Beispiel: int a[4] = {3,5,8,2}; //Vorzuweisung<br />

int b[5] = {7};<br />

//Rest wird mit 0 aufgefüllt<br />

int c[ ] = {3,4,5,9,2,8}; //ohne Längenangabe, aber der<br />

Übersetzer richtet sich autom. nach der<br />

Vorgabe in Klammern bequem<br />

6.2 Mehrdimensionale Felder ( Folie 14)<br />

Beispiel: int a[100]; //eindimensional<br />

int m[100] [100]; //zweidimensional<br />

int p[100] [100] [100]; //dreidimensional<br />

Grenzen beim Compiler bei ca. 8 bis 10 Dimensionen, aber:<br />

hoher Speicherverbrauch!!!<br />

int [10] [10];<br />

// Zeilenindex, Spaltenindex<br />

Darstellung im Speicher ( Folie 14)<br />

Im Speicher (eindimensional) liegen die Felder zeilenweise hintereinander.<br />

("Zeilenweise Linealisierung einer Matrix im Speicher")<br />

Wir haben beispielsweise 10 Zeilen und 10 Spalten<br />

i = Zeilenindex<br />

j = Spaltenindex<br />

R = 10 * i + j ( Folie 14, unten, Element [2,0] 20)<br />

= 10 * 2 + 0 = 20 ( Annahme richtig)<br />

Diese Berechnung nennt sich "Speicherabbildungsfunktion"<br />

Initialisierung zweidimensionaler Felder<br />

int a[3] [2] = {<br />

};<br />

{1,2}, // Zeile 0<br />

{7,9}, // Zeile 1<br />

{5,(-1)}, // Zeile 2<br />

6.3 Zeiger<br />

Zeiger: ist eine Speicheradresse<br />

Zeigervariable: Variabe, die Adresse speichern<br />

29


Vereinbarung von Zeigervariablen<br />

int *pa;<br />

double *pb; // Zeiger auf double-Größe<br />

int a;<br />

a = 77;<br />

pa = &a;<br />

Name Typ Adresse<br />

a int bffff8ae<br />

pa Zeiger auf int bffff8d4<br />

Operatoren<br />

& Adressoperator, beschafft die Adresse eines Objektes<br />

* Inhaltsoperator, beschafft den Inhalt des Speicherplatzes, auf<br />

den die Adresse zeigt<br />

&*... oder *&... oder *(&...) oder &(*...) heben sich gegenseitig auf. Die Variable bleibt stehen! Also *&a == &*a == a<br />

Beispiel: Verwendung einer Zeigervariablen<br />

#include <br />

void main ( )<br />

{<br />

int a;<br />

int *pa;<br />

a =77;<br />

pa =&a;<br />

printf ("\n a = %d", a);<br />

printf ("\n *pa = %d", *pa); /*Aussage hex %x)! */<br />

printf ("\n pa = %x", pa);<br />

printf ("\n &a = %x", &a);<br />

printf ("\n &pa = %x", &pa);<br />

...<br />

Ausgaben:<br />

a = 77<br />

*pa = 77<br />

pa = bffff8ac /* (Adresse von a) gespeichert in pa! */<br />

&a = bffff8ac /* direkte Adressausgabe von a*/<br />

&pa = bffff8d4 /* Adresse der Variablen pa */<br />

Adresse Inhalt Name<br />

bffff8ac<br />

a<br />

bffff8d4<br />

pa<br />

Anmerkungen:<br />

& Adresse von a<br />

pa Zeiger auf a<br />

*pa Inhalt der Variablen, auf die pa zeigt<br />

&pa Adresse der Zeigervariablen pa<br />

30


char s[] = "test";<br />

char *s = "test";<br />

//Feld mit variabler Länge anlegen und mit Inhalt und abschließender /0 füllen<br />

//das Gleiche wie oben in Zeigerschreibweise (alternativ zur Felddeklaration)<br />

Feld von Zeigern auf Arrays bedeutet also nichts anderes als ein zweidimensionales Array! Also:<br />

s [ ] [ ] = *s [ ] = **s<br />

char *p, *z;<br />

//Feld mit var. Länge ohne Inhalt<br />

p = s; //Zeiger von p zeigt (direkt) auf den gleichen Speicherbereich und Inhalt wie s.<br />

//verändert man p, verändert sich auch das originale s, also p = s = "test".<br />

p = &s[0];<br />

p = &(*(s+0));<br />

p = &*s;<br />

p = s;<br />

//entspricht der vorherigen Befehlszeile...<br />

//...denn &* oder *& oder *(&...) oder &(*...) heben sich gegenseitig auf.<br />

//p enthält auch hier den String von s, also "test".<br />

*p = 'A'; //erstes Zeichen von p wird ein 'A',...<br />

p[0] = 'A'; //...deshalb enthält p[ ] nun den String "Aest"<br />

*p++;<br />

p++;<br />

(*p)++;<br />

++*p;<br />

z = &(*p);<br />

z = p;<br />

z = p;<br />

int i = (int) *p;<br />

p = 0;<br />

//“C“ arbeitet von rechts nach links Befehle ab. Hat eine Position keinen Rückgabe-<br />

//typ, bricht die Verarbeitung ggf. mit einem Compilerfehler ab oder ignoriert dieses;<br />

// der Stern wird hier ignoriert. Der Zeiger p zeigt auf das nächste Element. p = "est"<br />

//gleicher Sachverhalt, auch ohne den Stern. 1. Zeichen wieder kürzen p = "st"<br />

//statt von rechts bearbeitet der Compiler zunächst die Klammer. Der Inhalt von p<br />

//wird also um 1 erhöht (binärer ASCII-Wert von s + 1) aus "st" wird "tt"<br />

//bedeutet das Gleiche wie (*p)++, ist aber kein guter Stil!<br />

//s.o.; &* hebt sich auf. z erhält den verbleibenden String "tt"<br />

// der gleiche Befehl wie z = &(*p). (s.o., z erhält nochmals "tt")<br />

//explizite Typkonvertierung des ASCII-Wertes von p[0] (von "t") in eine int-Variable<br />

//der Zeiger wird gesetzt auf einen ungültigen Speicherbereich. Bei einer Ausgabe<br />

//käme ein BlueScreen (Speicherzugriffsfehler) oder ein Programmabsturz.<br />

6.4 Adressübergabe bei Funktionsparametern<br />

Beispiel: Wertetausch:<br />

übliche Methode:<br />

int a, b, h;<br />

a = 5;<br />

b = 7;<br />

h = a;<br />

b = a;<br />

b = h;<br />

oder:<br />

int a, b;<br />

a=5;<br />

b=7;<br />

tausche (&a, &b);<br />

//&=Adresse übergeben<br />

void tausche (int *a, int *b) //call-by-reference<br />

31


{<br />

}<br />

int h;<br />

h = *a;<br />

//*=Inhalt der Variable holen<br />

*a = *b; //Bezugnahme auf den Inhalt,<br />

*b = h; //sog. "Inhaltsoperator"<br />

Wirkung: a = 7 und b = 5<br />

Beispiel:<br />

Koordinatentransformation (math.h für Bogenmaß Voraussetzung)<br />

x = r * cos <br />

y = r * sin <br />

= arc tan (y/x)<br />

r = sqrt (x 2 + y 2 )<br />

void polar-kart (double r, double phi, double *x, double *y)<br />

{<br />

*x = r * cos(phi);<br />

*y = r * sin(phi);<br />

}<br />

void kart_polar (double x, double y, double *r, double *phi);<br />

{<br />

*r = sqrt ( x*x + y*y);<br />

*phi = atan 2 (y,x); /* beide Argumente einzeln überg. */<br />

}<br />

oder:<br />

aber:<br />

atan (y/x)<br />

fehlende Vorzeichen, deshalb Betrachtung!<br />

6.5 Zusammenhang zwischen Feldern und Zeigern<br />

Ein Feldname ist wie ein Adresswert zu verwenden.<br />

Beispiel:<br />

int a[100], *pa;<br />

pa = a;<br />

pa = &a [0];<br />

ist gleichbedeutend!<br />

Zeiger können auch zur Feldadressierung verwendet werden.<br />

int a[100], i, Summe, *pa;<br />

pa = a;<br />

for (i=0; i < 100; i++) *pa++=i;<br />

//Feld wird mit Werten von 0 bis 99 gefüllt<br />

summe=0;<br />

for (pa = a; pa < &a[100]; pa++) summe += *pa;<br />

oder:<br />

for (i=0, i < 100, i++) summe += *(pa+i); //Adressrechnung<br />

6.6 Felder als Funktionsargumente<br />

Felder werden immer per Adresse übergeben.<br />

32


Beispiel: double summe (double a[ ], int n)<br />

{<br />

double s = 0.0;<br />

int i;<br />

for (i =0; i


Kapitel 7 – define-Makros<br />

7.1 include<br />

#include <br />

#include "projekt3.h"<br />

//Datei stdio.h aus Systemverz. wird einkopiert<br />

//Datei projekt3.h im Arbeitsverzeichnis<br />

7.2 define<br />

allgemeine Form:<br />

#define Name Ersetzungstext<br />

Beispiel: #define N 200<br />

#define SEKUNDE 1<br />

#define MINUTE (60 * SEKUNDE)<br />

#define EQ ==<br />

#define BEGIN {<br />

#define END }<br />

#define NL print ("\n")<br />

allgemeine Form:<br />

Beispiel:<br />

#define name(Parameter1, Parameter2, ...) Ers.text<br />

#define QUAD(x) ((x)*(x))<br />

.....<br />

y = QUAD (a+3);<br />

// y=((a+3)*(a+3));<br />

#define QUAD(x)(x*x)<br />

.....<br />

y=QUAD(a+3);<br />

// y=a+3*a+3;<br />

#define QUAD(x)x*x<br />

.....<br />

y=QUAD(a+3)*7; // y=a+3*a+3*7; !!!<br />

Um eine Mehrfacheinbindung zu vermeiden, benutzt man eine spezielle Anweisung:<br />

#ifndef INTERNE_HEADERBEZEICHNUNG<br />

#define INTERNE_HEADERBEZEICHNUNG<br />

#endif<br />

//fragt ab, ob Name existiert<br />

//falls nicht,erstelle<br />

//falls doch, wird überlesen bis...<br />

//diese end-Anweisung auftaucht.<br />

34


Kapitel 8 – Vereinbarungen, Zugriffe, Strukturen<br />

8.1 Vereinbarung und Komponentenzugriff<br />

Vereinbarung<br />

1. Möglichkeit<br />

struct<br />

Schlüsselwort<br />

student<br />

Name<br />

...{<br />

}<br />

char<br />

char<br />

long<br />

*nachname<br />

*vorname<br />

matr_nr<br />

Einrichtung von Variablen:<br />

struct student s1,s2,s3,st[1000];<br />

Datentyp Variablen<br />

2. Möglichkeit: anonyme Struktur<br />

struct<br />

{ int tag, monat, jahr;<br />

} gestern, heute, morgen;<br />

3 Variablen<br />

Gebrauch von type def:<br />

type def unsigned char byte;<br />

Schlüsselwort existierender Typ neuer Typname<br />

type def double laenge;<br />

type def double vektor[3]; /*Vektoren fester Länge */<br />

.....<br />

vektor vx, vy, vz;<br />

type def struct student Student;<br />

alte Bezeichnung<br />

neue Bezeichnung<br />

Zugriff auf Komponenten:<br />

Zugriffsoperator: . ( da ist ein Punkt!)<br />

Beispiel: struct student {...}; /* siehe oben */<br />

type def struct student Student;<br />

Student s1, s2, s3;<br />

s1.nachname = "Meier"; //Anonymes Char-Feld<br />

8.2 Strukturen und Funktionen<br />

Strukturen können über return aus Funktionen zurückgegeben werden (Kopieren der<br />

Struktur!)<br />

Beispiel:<br />

struct student<br />

{<br />

}<br />

char nachname[20];<br />

char vorname[20];<br />

long matr_nr;<br />

35


type def struct student Student;<br />

void print_student (Student *s);<br />

void lies_student (Student *s);<br />

void main ()<br />

{<br />

student stud[1000];<br />

lies_student (&stud[1]);<br />

print_student (&stud[1]);<br />

}<br />

void print_student (Student *s)<br />

{<br />

printf ("\n %20s, %20s, Matr.Nr. %8ld",<br />

snachname, svorname, smatr_nr);<br />

void lies_student (Student *s)<br />

{<br />

printf ("\n Nachname: ");<br />

scanf ("%s", &snachname);<br />

...<br />

}<br />

Schreibweise:<br />

(*s).nachname<br />

s->nachname<br />

(gleichwertige Schreibweise!!!)<br />

Beispiel:<br />

Vektorarithmetik<br />

Vektoren der Länge 3, dargestellt durch Strukturen<br />

struct vector 3<br />

{<br />

double x;<br />

double y;<br />

double z;<br />

};<br />

typedef struct vector3 Vek3;<br />

Vek3 createv (double x, double y, double z)<br />

{<br />

Vek3 a;<br />

a.x = x; a.y = y; a.z = z;<br />

return a;<br />

}<br />

Vek3 null( ){return createv (0,0,0);}<br />

Vek3 e_x( ) {return createv (1,0,0);}<br />

Vek3 e_y( ) {return createv (0,1,0);}<br />

Vek3 e_z( ) {return createv (0,0,1);}<br />

/* Vektor x Skalar */<br />

Vek3 VxS (Vek3 a, double s)<br />

{<br />

return createv (s*a.x, s*a.y, s*a.z);<br />

}<br />

Vek3 vaddv (Vek3 a, Vek3 b)<br />

{<br />

36


}<br />

return createv (a.x+b.x, a.y+b.y, a.z+b.z);<br />

Vek3 vsubv (Vek3 a, Vek3 b)<br />

{<br />

return createv (a.x-b.x, a.y-b.y, a.z-b.z);<br />

}<br />

double skalarprod (Vek3a, Vek3 b)<br />

{<br />

return a.x*b.x + a.y*b.y + a.z*b.z;<br />

}<br />

Vek3 Kreuzprod (Vek3 a, Vek3 b)<br />

{<br />

return createv ( a.y * b.z - a.z * b.y,<br />

a.z * b.x – a.x * b.z,<br />

a.x * b.y – a.y * b.x);<br />

}<br />

double lenght (Vek a)<br />

{<br />

return sqrt (skalarprod (a*a));<br />

}<br />

//Vektor normieren<br />

Vek3 normieren (Vek3 a)<br />

{<br />

double l;<br />

l = length(a);<br />

if (l>EPSILON) return vxs (a, 1.0/l);<br />

else return a;<br />

}<br />

void print_vek (Vek3 a)<br />

{<br />

...<br />

}<br />

Vek 3 scan_Vek (void)<br />

{<br />

...<br />

}<br />

int main ( )<br />

{<br />

Vek3 a,b,c;<br />

a = createv (1,2,3);<br />

b = createv (3,1,2);<br />

c = vaddv (a,b);<br />

...<br />

c = vaddv (vxs(a,3),vxs(b,7));<br />

c = kreuzprod (a,b);<br />

...<br />

}<br />

37


Kapitel 9: <strong>C++</strong>/ nicht-objektorientierte Erweiterungen<br />

9.1 Datentyp bool<br />

Datentyp:<br />

Konstanten:<br />

bool<br />

true, false<br />

Beispiel: bool b1, b2;<br />

b1 = false;<br />

b2 = true;<br />

b1 = 5; //entspricht true, denn 0<br />

9.2 Vereinbarungen<br />

Vereinbarungen müssen nicht mehr zwingend vor der ersten ausführbaren Anweisung eines<br />

Blockes stehen.<br />

Beispiel: void main ( )<br />

{<br />

cout


}<br />

double<br />

a* = *b;<br />

b* = h;<br />

h = *a;<br />

Beispiel 2) ...<br />

int a=5, b=7;<br />

double x=9, y=3;<br />

swap (&a, &b); // 1. swap ( )<br />

swap (&x, &y); // 2. swap ( )<br />

swap (&a, &x);<br />

// Fehler<br />

...<br />

swap<br />

9.6 new und delete – dynamische Speicherbelegung<br />

Beispiel:<br />

Wichtige Erläuterung:<br />

double *f = new double;<br />

int *feld = new int [1024];<br />

...<br />

delete f;<br />

delete [ ] feld; //Feld freigeben<br />

new ist ein Operator, der sich ans Betriebssystem wendet.<br />

feld ist der Name des Zeigers.<br />

delete löscht Rückstände aus dem Speicher<br />

9.7 iostream – Bibliothek<br />

Beispiel:<br />

#include <br />

void main ( )<br />

{<br />

int i, n=10;<br />

double a[200]<br />

for (i=0; i


Anmerkung:<br />

Bsp.:<br />

Eine Referenz ist konstant und muss bei Einrichtung einen Adresswert<br />

erhalten.<br />

void swap (int &a, int &b)<br />

{<br />

int h = a;<br />

a=b;<br />

b=h;<br />

}<br />

void main ( )<br />

{<br />

int x = 7, y = 28;<br />

...<br />

swap (x,y);<br />

...<br />

}<br />

Bsp.:<br />

double & square (double &x)<br />

{<br />

x = x * x;<br />

return x;<br />

}<br />

möglicher Aufruf: double a = 3.0, b;<br />

b = square (a);<br />

Werte (anschließend): a=9.0 und b=9.0<br />

40


Kapitel 10 - Klassen<br />

10.1 Referenzen in Funktionen<br />

Bsp.:<br />

Datenstruktur Stapel (stack)<br />

Datenkomponenten:<br />

- Feld zur Aufnahme der Daten<br />

- Stapelzeiger (top)<br />

- Feldlänge<br />

Operationen:<br />

- Initialisierung (top=0)<br />

- Element hinzufügen<br />

- Element wegnehmen<br />

- oberstes Element lesen<br />

- Anzahl der Elemente feststellen<br />

10.2 Klassendefinitionen<br />

allg. Form:<br />

Bsp.:<br />

class Name<br />

{public: öffentliche Komponenten<br />

private: private Komponenten}<br />

stack (Datei: stack.cc)<br />

#include <br />

class Stack<br />

{<br />

private: int daten;<br />

int zgr;<br />

int max;<br />

//nächstes freies Element<br />

//Feldlänge<br />

} ...<br />

public: Stack (int groesse = 100); //Konstruktor<br />

~Stack ( ) {delete daten;}; //Destruktor (inline)<br />

void init ( ) {zgr = 0;};<br />

//(inline)<br />

void push (int datum);<br />

int pop ( );<br />

int top ( );<br />

bool empty ( ) {return zgr == 0;};<br />

bool not_empty ( ) {return zgr != 0;};<br />

int height ( )<br />

{return zgr;};<br />

int max_height ( ) {return max;};<br />

//Implementierung der member-Funktionen<br />

Stack :: Stack (int groesse=100)<br />

{<br />

if (groesse


}<br />

int Stack :: pop ( )<br />

{<br />

if (zgr>0) return daten[--zgr];<br />

else {<br />

cerr 0) return daten [zgr-1];<br />

else {<br />

cerr


Erläuterungen:<br />

Objektvereinbarung<br />

- der Konstruktor wird bei der Vereinbarung eines Objektes autom. aufgerufen.<br />

- folgende Vereinbarungen sind möglich:<br />

Stack stapel1; //ohne Param.:Länge 100<br />

Stack stapel2(88); //Länge 88<br />

Stack stapel3 = Stack (900);<br />

- Es können mehrere Konstruktoren vorhanden sein;<br />

Bsp.: Stack ( ); //leerer Stapel, evtl. 1 Platz<br />

Stack (int groesse);<br />

...<br />

Verwendung<br />

Stack Stack21;<br />

//1. Konstruktor<br />

Stack Stack22 (200); //2. Konstruktor<br />

Destruktoren<br />

Funktionskomponente, die automatisch am Lebensende eines Objektes aufgerufen wird.<br />

Hauptzweck:<br />

Freigabe von dynamisch belegten Speicher<br />

Definition: ~Stack( );<br />

kein Rückgabewert, keine Parameter<br />

Implementierung: Stack:: ~Stack( )<br />

{<br />

delete daten;<br />

daten = 0;<br />

}<br />

//dyn. Speicher wieder freigeben<br />

10.4 Zuweisungsoperator<br />

...<br />

stack1 = stack2<br />

Anmerkung:<br />

Lösung:<br />

Stack-Feld von Stack1 nicht mehr zugreifbar<br />

Zuweisungsoperator = wird neu definiert<br />

Bsp.: class Stack {<br />

private: ...<br />

43


public: ...<br />

Stack & Operator = (const Stack & rechteseite);<br />

}<br />

Rückgabetyp, danach "&" für Schreibberechtigung auf die Variable<br />

Funktionsname<br />

Referenz auf einem Stack-Objekt<br />

Implementierung des Operators:<br />

-eigenen Speicher freigeben<br />

-neuen Speicher in der Größe des zu kopierenden Objektes anlegen<br />

-Feld kopieren<br />

-andere Datenkomp. kopieren<br />

Stack & Stack:: Operator = (const Stack & rechteseite)<br />

{<br />

delete daten;<br />

//eig. dyn. Speicher freigeben<br />

max = rechteseite.max;<br />

daten = new int [max]; //neuen dyn. Speicher belegen<br />

zgr = rechteseite.zgr;<br />

for (int i=0; i


Stack :: Stack operator = (const Stack & stp);<br />

alle Datenkomp. werden kopiert<br />

Referenz auf ein unveränderliches Objekt<br />

kopiert alle Datenkomponenten 1:1<br />

Anmerkung: alle 4 Funktionen können überladen werden!<br />

10.6 Friends<br />

friend-Funktionen sind keine Komp.funktionen; haben jedoch Zugriff auf die priv. Daten.<br />

Bsp.: class matrix {<br />

private: double mat [3] [3];<br />

public: ...<br />

friend vektor mult (matrix &m, vektor &v);<br />

};<br />

class vektor {<br />

private: double vek [3];<br />

public: ...<br />

friend vektor mult (matrix &m, vektor &v);<br />

};<br />

vektor mult (matrix &m; vektor &v)<br />

{<br />

vektor h;<br />

for (int i=0; i< 3, i++) {<br />

h.v [i] = 0;<br />

for (int j=0; j


Kapitel 11 – Überladen von Operatoren<br />

11.1 Unäre Operatoren (arithmetisch)<br />

Bsp.:<br />

Klasse Vektor mit 3 Komponenten<br />

class Vek3<br />

{<br />

private: double x,y,z;<br />

public: Vek3 operator + ( );<br />

Vek3 operator – ( );<br />

...<br />

}<br />

Vek3 Vek3::operator + ( ) // u = +v<br />

{<br />

return *this; //Zeiger auf sich selbst; muss klein geschrieben werden!<br />

}<br />

Vek3 Vek3::operator – ( ) // u = -v (verlangt mehrere Schritte)<br />

{<br />

return Vek3 (-x, -y, -z); //Konstruktoraufruf<br />

} //oder Vek3 h (-x, -y, -z); return h;<br />

*this ist ein Zeiger auf das eigene Objekt<br />

2. Möglichkeit: Verwendung von friend-Funktionen<br />

friends sind Funktionen, die auch auf private-Bereiche zugreifen können (oder "auf die" ?)<br />

class Vek3<br />

{<br />

...<br />

friend Vek3 operator + (const Vek3 &v);<br />

friend Vek3 operator – (const Vek3 &v);<br />

...<br />

}<br />

Vek3 operator + (const Vek3 &v)<br />

{<br />

return v;<br />

}<br />

Vek3 operator – (const Vek3 &v)<br />

{<br />

return Vek3 (-v.x, -v.y, -v.z);<br />

}<br />

11.2 Binäre Operatoren<br />

Ziel:<br />

u = v + 27.3 * (u1 + u2);<br />

1. Möglichkeit: Komponentenfunktionen<br />

Vek3 Vek3 :: operator + (const Vek3 &b)<br />

{<br />

return Vek3 (x+b.x, y+b.y, z+b.z);<br />

}<br />

46


Vek3 Vek3 :: operator – (const Vek3 &b)<br />

{<br />

return Vek3 (x-b.x, y-b.y, z-b.z);<br />

}<br />

2. Möglichkeit: friends<br />

Vek3 operator + (const Vek3 &a, const Vek3 &b)<br />

{<br />

return Vek3 (a.x + b.x, a.y + b.y, a.z + b.z);<br />

}<br />

(Für "-" analog)<br />

11.3 Arithmetische Zuweisungsoperatoren<br />

Ziel:<br />

v += b; //v,b Vektoren<br />

1. Möglichkeit: Komponentenfunktion<br />

void Vek3 :: operator += (const Vek3 &b)<br />

{<br />

x += b.x;<br />

y += b.y;<br />

z += b.z;<br />

}<br />

2. Möglichkeit:<br />

Vek3 Vek3 :: operator + (const Vek3 &b)<br />

{<br />

x += b.x;<br />

y += b.y;<br />

z += b.z;<br />

}<br />

return *this;<br />

11.4 Ein- / Ausgabeoperator<br />

Operatoren: ><br />

Überladung des Ausgabeoperators<br />

ostream& operator (ostream &out, const Vek3 &v)<br />

{<br />

out


Überladung des Eingabeoperators:<br />

istream& operator >> (istream &in, Vek3 &v)<br />

{<br />

in >> v.x >> v.y >> v.z;<br />

}<br />

oder<br />

istream& operator >> (istream &ein, Vek3 &v)<br />

{<br />

char kl, komma;<br />

in >> kl >> v.x >> komma >> v.y >> komma >> v.z >> kl;<br />

}<br />

Kapitel 12 - Statische Objekte<br />

Klassen können statische Datenkomponenten enthalten, die für die Klasse nur einmal existieren.<br />

Bsp.:<br />

Klasse für pgn-Bilder<br />

class pgn<br />

{<br />

private:<br />

int breite, hoehe, maxgrau<br />

char **pixel<br />

static int zaehler;<br />

public:<br />

pgm() {pixel = 0; zaehler**;}<br />

~pgm {...; zaehler --;}<br />

}<br />

//Konstruktor<br />

//Destruktor<br />

int pgm::zaehler = 0;<br />

Definition und Anfangswertzuweisung erfolgen außerhalb der Klasse genau einmal.<br />

48


Kapitel 13 - Vererbung<br />

13.1 Abgeleitete Klasse<br />

Ziel:<br />

Code einer vorhandenen Klasse wieder verwenden, d.h.<br />

- zusätzliche Datenkomponenten<br />

- zusätzliche Funktionskomponenten<br />

- evtl. vorhandene Funktionen überladen<br />

Deklaration einer Klasse B, abgeleitet aus der Klasse A:<br />

class B: public A { ... }<br />

Über B Objekte wird auf die public-Komponenten von A zugegriffen werden<br />

class bahn<br />

{<br />

protected: Vek3 pa; //Anfangspunkt<br />

Vek3 pe; //Endpunkt<br />

public:<br />

bahn(){};<br />

bahn (const.Vek3 &p1, const Vek3 &p2){pa = p1; pe = p2;}<br />

}<br />

void start (const Vek3 &p)<br />

void ziel (const Vek3 &p)<br />

double laenge (void)<br />

{pa = p;}<br />

{pe.p;}<br />

{return(pe-pa).laenge()}<br />

class kbogen: public bahn {<br />

protected: Vek3 pm; //mittlerer Punkt<br />

public: kbogen() {};<br />

kbogen ( const Vek3 &p1;<br />

const Vek3 &p2;<br />

const Vek3 &p3;)<br />

{pa=p1; pm=p2; pe=p3;}<br />

Ergänzung:<br />

void zwpunkt (const Vek3 &p) {pm=p;}<br />

Überladung der Funktion bahn::laenge()<br />

double laenge() {return (pe-pm).laenge() + (pm-pa).laenge();}<br />

int main()<br />

{<br />

double bahnlaenge;<br />

bahn bahn1;<br />

kbogen bogen1, bogen2;<br />

Vek3 p1 (0,2,0);<br />

Vek3 p2 (2,2,0);<br />

Vek3 p3(2,0,0);<br />

bahn1.start (p1);<br />

bahn1.ziel(p1);<br />

bogen1.start(p1);<br />

bogen1.zwpunkt(p2);<br />

bogen1.ziel(p3);<br />

}<br />

cout


Klasse kbogen Vek3 pm zusätzliche Datenkomponenten<br />

void zwpunkt() zusätzliche Methode<br />

double laenge() überlädt Fkt. der Basisklasse<br />

Hauptprog.: bahn1.laenge(); Fkt. d. Basisklasse wird aufgerufen<br />

bahn1.laenge(); Fkt. d. abgeleiteten Klasse wird aufger.<br />

13.2 Initialisierung von Klassenkomponenten<br />

bahn --> kbogen<br />

class kbogen: public bahn<br />

{<br />

...<br />

public:<br />

// -- Konstruktor 1 –<br />

kbogen() {};<br />

...<br />

}<br />

// -- Konstruktor 2 –<br />

kbogen (const Vek3 &p1, const Vek3 &p2, const Vek &p3) : bahn<br />

(p1,p3) {pm = p2;}<br />

Aufruf des Basis-Klassenkonstruktors<br />

Initialisierung der neuen Komponente<br />

kbogen bg1 (v1,v2,v3);<br />

kbogen bg2;<br />

13.3 Virtuelle Funktionen<br />

--> kbogen<br />

bahn --> gerade<br />

--> parabel<br />

--> ...<br />

Zum Beispiel: Ein Roboterarm soll eine bestimmte unförmige Form abarbeiten.<br />

Ziel:<br />

- erweiterbare Klassenhierarchie<br />

- alle abgeleiteten Funktionen laenge()<br />

- die Längen gehen bereits in Berechnungen ein, die nach einer Erweiterung nicht mehr<br />

geändert werden sollen.<br />

1. Versuch (funktioniert nicht!)<br />

class bahn {...}<br />

class kbogen: public bahn {...}<br />

int main ()<br />

{<br />

double sum;<br />

bahn *b [100];<br />

int n=6;<br />

b[0] = new bahn (p1,p2);<br />

b[1] = new bahn (p2,p3);<br />

//100 Zeiger auf Bahnobjekte<br />

50


[2] = new k.bogen (p3,p4,p4);<br />

...<br />

b[5] = new bahn (p9,p1);<br />

}<br />

sum = 0.0;<br />

for (int i=0; i laenge();<br />

...<br />

Fehlschlag! Die Längenfunktion der Basisklasse wird aufgerufen!<br />

2. Versuch: Verwendung einer virtuellen Funktion<br />

class bahn<br />

{<br />

protected:<br />

Vek3 pa, pe;<br />

public: bahn ()<br />

{};<br />

bahn (const Vek3 &p1, const Vek3 &p2)<br />

{pa = p1; pe = p2;}<br />

virtual double laenge ()<br />

{}<br />

...<br />

}<br />

class gerade: public bahn { ... }<br />

class kbogen: public bahn { ... }<br />

jeweils eigene Längenfkt.<br />

(s.o.)<br />

double bahnlaenge (bahn *b[ ], int n)<br />

{<br />

double sum = 0.0;<br />

for (int i=0; i laenge ();<br />

return sum;<br />

}<br />

int main ()<br />

{<br />

//s.o.<br />

}<br />

Anmerkungen:<br />

<br />

<br />

<br />

<br />

in bahnlaenge() werden die Funktionen laenge() der abgeleiteten Klassen<br />

aufgerufen<br />

die Funktion bahnlaenge(...) muss nicht geändert werden, wenn weitere von<br />

bahn abgeleitete Bahntypen hinzukommen<br />

Fallunterscheidungen entfallen<br />

die entsprechenden Funktionen der abgeleiteten Klassen müssen die selbe Signatur<br />

wie die Funktion der Basisklasse besitzen.<br />

Bsp.:<br />

class bahn<br />

{ ...<br />

virtual double xxx (int n);<br />

}<br />

class gerade: public bahn<br />

{ ...<br />

51


double xxx (long n);<br />

//überlädt nicht xxx in bahn!<br />

// Statischer Aufruf virtueller Funktionen<br />

double l1, l2;<br />

bahn b1;<br />

kbogen k1;<br />

...<br />

l1 = b1.laenge();<br />

l2 = k1.laenge();<br />

//bahn::laenge<br />

//kbogen::laenge<br />

//aufzurufende Funktion zur Übersetzungszeit bekannt: stat. Aufruf<br />

//sonst: dynamischer Aufruf<br />

double bahn_zeit (double v, bahn &b)<br />

{<br />

if (v > 0.0) return b.laenge() / v;<br />

//dyn. Aufruf passende Fkt. muss zur<br />

else return 0,0;<br />

//Laufzeit ermittelt werden<br />

}<br />

13.4 Abstrakte Klassen<br />

abstrakte Klassen:<br />

Basisklassen zur Ableitung weiterer Klassen<br />

Bsp.:<br />

class bahn {<br />

protected: Vek3 pa, pe;<br />

public: bahn() { }<br />

...<br />

virtual double laenge () = 0;<br />

}<br />

class gerade: public bahn {<br />

...<br />

double laenge () {...};<br />

}<br />

int main ()<br />

{<br />

bahn *b[100];<br />

bahn b23;<br />

...<br />

}<br />

//100 Zeiger<br />

//Fehler!<br />

Anmerkungen:<br />

"virtual double laenge () = 0;" ist eine rein virtuelle Funktion. Eine Implementierung<br />

ist nicht vorhanden und auch nicht vorgesehen.<br />

52


Es bedarf einer Schnittstellendefinition für abgeleitete Klassen, d.h. im Falle eines Verkaufs ist<br />

der Code und die Bibliothek sozusagen kopier- / editiergeschützt. Die Vereinbarung eines<br />

bahn-Objektes ist NICHT mehr möglich.<br />

Die virtuelle Funktion muss in abgeleiteten Klassen überladen werden.<br />

53


Kapitel 14 – Templates<br />

Templates (Schablonen) für Funktionen und Klassen, die voneinander nur in verwendeten Datentypen<br />

abweichen.<br />

14.1 Funktions-Templates<br />

Bsp: allgem. Minimumfunktion<br />

Template T Min(T u, T b)<br />

{<br />

if (a=0) return a; else return –a;<br />

}<br />

template < class T> bool IsInRange (T x, T unten, T oben)<br />

{<br />

return ( unten


};<br />

}<br />

int main ( )<br />

{<br />

bruch b1(4,9), b2(3,7), b3;<br />

b3 = b1*b2;<br />

cout


class Qelement<br />

{<br />

public:<br />

T1 element;<br />

Qelement *p_next;<br />

Qelement (const T1 &value):element(value), p_next(0) { }<br />

};<br />

Qelement *p_front, *p_end;<br />

public:<br />

friend class Qtraverse ;<br />

Queue ( ) {p_front = p_end = 0;}<br />

virtual ~Queue() { while(!empty())remove(); } //Fkt. inline definiert<br />

virtual T1 remove ( void );<br />

virtual void add ( const T1 &element );<br />

bool empty ( void ) { return p_front == 0; }<br />

}; //Ende class Queue<br />

template void Queue :: add ( const T1 &element)<br />

{<br />

Qelement *p_element = new Qelement (element);<br />

if ( empty( ) ) p_front = p_element;<br />

else p_end -> p_next = p_element;<br />

p_end = p_element;<br />

}<br />

template < class T1 > T1 Queue :: remove( void )<br />

{<br />

if ( empty( ) )<br />

{<br />

cerr element;<br />

Qelement *p_element = p_front;<br />

p_front = p_front -> p_next;<br />

delete p_element;<br />

return element;<br />

}<br />

template class Qtraverse<br />

{<br />

private:<br />

Queue *p_queue;<br />

Queue :: Qelement *p_peek;<br />

// Zeiger auf Warteschlange<br />

// Zeiger auf element<br />

public:<br />

void peek_reset() { p_peek = 0;}<br />

Qtraverse ( Queue &queue)<br />

{<br />

p_queue = &queue;<br />

peek_reset();<br />

}<br />

int peek (T1 &cur_element)<br />

{<br />

if(p_peek==0) p_peek = p_queue -> p_front; // Zeiger auf Anfang<br />

else p_peek = p_peek -> p_next;<br />

if ( p_peek == 0) return 0;<br />

cur_element = p_peek -> element;<br />

return 1;<br />

}<br />

};<br />

56


#endif<br />

//Ende der Vermeidung von Doppeleinbindungen<br />

DATEI: prk16-1.h aus dem Praktikum (getestet und lauffähig)<br />

#include "prk16-1.h"<br />

int main( void )<br />

{<br />

int n = 10000000;<br />

Queue x; //Warteschlange x<br />

Qtraverse itx ( x ); //Iterator für WS x<br />

int current, i = 0;<br />

for (i = 0; i < n; i++) x.add(i);<br />

i= 0;<br />

//Queue durchlaufen<br />

while ( itx.peek(current) )<br />

if( i++ % 100 == 0 ) cout


{<br />

Queue X;<br />

//Warteschlange<br />

qtraverse itx (x); //Iterator für Queue X<br />

int current;<br />

//laufendes Element<br />

for (int i=0;i


Verwendungsbeispiel<br />

als InputIterator:<br />

als T:<br />

*int<br />

int<br />

erzeugter Code:<br />

int* find (int* first, int* last, const int &value)<br />

{<br />

while (first != last && *first != value)<br />

first++;<br />

return first;<br />

}<br />

Anforderungen:<br />

1. Vergleich zweier Objekte (first != last)<br />

2. Inkrementierung (first ++)<br />

3. Dereferenzierung (*first != value)<br />

template < class T1 ><br />

T1 Queue < T1 > :: remove( ) //T1 wird zurückgegeben<br />

{<br />

if (empty ( ))<br />

{<br />

cerr element;<br />

element xp-element = p-front;<br />

p-front = p-front -> next;<br />

delete p-element;<br />

return element;<br />

}<br />

Container und Algorithmen:<br />

Beispiel:<br />

...<br />

vektor v(3); //Vektor v der Länge 3<br />

v[0] = 7;<br />

v[1] = v[0] + 3;<br />

v[2] = v[1] + 8;<br />

//Anwendung eines Algorithmus<br />

reverse (v.begin(), v.end());<br />

//global generische Funktion (arbeitet auf einem Bereich)<br />

Beispiel: valarray-Template<br />

Felder für numerische Operationen.<br />

Methoden erzeugen, kopieren, löschen, Wertzuweisung, Elementarzugriff, Elemente<br />

verschieben, ...<br />

Operatoren: Arithmetische Operationen, Vergleichsoperator, math. Funktionen<br />

#include <br />

59


#include <br />

int size = s;<br />

typedef valarray < double > darray;<br />

int main( )<br />

{<br />

darray v1(size), v2(size), v3(size);<br />

darray va(size);<br />

int i;<br />

for (i=0; i


Kapitel 15 – Programmerzeugung mit „make“<br />

15.1 Funktions-Templates<br />

Zweck:<br />

- automatische Prüfung von Abhängigkeiten von Programmquellen<br />

- Steuerung der Übersetzungs- und Bindungsvorgänge, der Bibliotheken-Erzeugung, der<br />

Installation von Paketen, der Verzeichnisbereinigung<br />

Beispiel: makefile<br />

#einfacher makefile zu 15.1<br />

qtest 1.0 :„qtest.cc queue_contained.tv g++ -g –Wall –c qtest1.cc –o qtest1.o"<br />

#ausführbare Datei<br />

qtest1: qtest1.o g++ -g –o qtest1.e qtest1.o<br />

wobei der große Abstand durch einen Tabulator erreicht wird und –g für den Debugger steht.<br />

–o wird für objectfiles verwendet. Alle Warnungen sind angeschaltet<br />

#Hilfsdateien löschen<br />

clean: rm –f *.o *.e *.bak<br />

Aufbau einer make-Regel:<br />

Ziel: Abhängigkeiten Abhängigkeiten<br />

Kommando<br />

wobei der Abstand wieder ein Tabulator ist<br />

Aufruf der Kommandozeile:<br />

...> make //nur make tippen<br />

...> make –f makefile<br />

15.1.1 Makros<br />

Zweck:<br />

- Verbesserung / Verkürzung der Schreibweise<br />

- sicherer (jede Inform. nur einmal)<br />

Makrodefinition:<br />

Name = Zeichenkette<br />

Verwendung:<br />

$(Name) oder ${Name}<br />

Beispiel: makefile mit Makros<br />

CC = g++ #Compiler-Name<br />

CFLAGS = –c –g –Wall<br />

LDFLAGS = –g<br />

LIBS = –lm –lg++<br />

qtest1.o:<br />

qtest1.e:<br />

clean:<br />

qtest1.cc queue container.h<br />

$(CC) $(CFLAGS) qtest1.cc qtest1.o<br />

qtest1.o queue container.h<br />

$(CC) $(LDFLAGS) –o $@ qtest1.o $(LIBS)<br />

rm –f *.o *.e *.bak *.bak~<br />

mögliche Aufrufe:<br />

nur Übersetzung von qtest1.cc<br />

make qtest1.o<br />

61


Interne Makros<br />

Binden von qtest1.e (evtl. übersetzen)<br />

make qtest1.e<br />

Hilfsdateien löschen<br />

make clean<br />

Es existiert eine Reihe vordefinierter Ziele und Makros<br />

$@ Name des augenblicklichen Zieles<br />

$< Name der abhängigen Dateien, die jünger sind als das augenblickliche Ziel (nur im<br />

Suffix-Regeln)<br />

...<br />

15.1.2 Suffix-Regeln<br />

Zweck:<br />

ähnliche Regeln zu einer Regel zusammenfassen (es ist blödsinnig, 500 ähnliche Regeln<br />

einzeln aufzustellen)<br />

Beispiel: makefile, Verwendung von Suffix-Regeln<br />

OBJS = main.o sub1.o sub2.o sub3.o<br />

LIB = /home/mueller/lib/p1.a<br />

Programm:<br />

Ausführung:<br />

$(OBJS) $(LIB)<br />

$(CC) –o $@ $(OBJS) $(LIB)<br />

make <br />

geltende Regeln<br />

.SUFFIXES: .o .c .s ...<br />

.c.o:<br />

//2 Leerzeichen<br />

$(CC) $(CFLAGS) –c $<<br />

.SUFFIXES<br />

interne Liste gültiger Dateiendungen<br />

.c.o: Ziel; Erzeugung eines Objekts aus einer gleichnamigen C-<br />

Datei<br />

$(CC)<br />

voreingestellter Compiler-Name<br />

$(CFLAGS)<br />

voreingestellter Compiler-Schalter<br />

Aufruf von make<br />

make sub2.o<br />

62


Kapitel 16 – Grafische Benutzer-Oberflächen (GUI)<br />

GUI = graphische Benutzer-Oberflächen<br />

16.1 MVC-Konzept<br />

Model<br />

View<br />

Controller<br />

Anwendung:<br />

Graphische Bibliothek (Matrix, Qt, ...)<br />

Basissystem (X-Window, ...)<br />

Treiber<br />

Hardware<br />

Programmiermodell für graphische Bibliotheken:<br />

Initialisierung – Verbindung zum X-Server<br />

Interne Datenstrukturen initialisieren (graph. Objekte, Zeichensätze, Farben,...)<br />

evtl. callback-Funktion registrieren<br />

Graphische Objekte zeigen<br />

Ereignissteuerung – Hauptschleife starten<br />

callback „Berechnung starten, graphische Objekte verschieben“<br />

Programm beenden<br />

Programmteile:<br />

1) Erzeugung graphischer Objekte<br />

GUI Toolkit<br />

direkte programmierung<br />

2) Hauptschleife (Controller)<br />

Bestandteil der Graphikbibliothek<br />

3) Anwendungsteile (Model)<br />

Berechnungen<br />

16.2 X-Windows<br />

siehe auch Folie 25!<br />

- mehrere Clients können denselben Server verwenden<br />

- X ist ereignisgesteuert<br />

Die 4 Typen von Botschaften<br />

REPLY Antwort des Servers<br />

EVENT Nachricht des Servers, z.B. Mausbewegung<br />

ERROR Fehlernachricht des Servers<br />

DISPLAY Requests und Events werden gepuffert (Verteilung der Netzlast)<br />

Request = Zeichenauftrag; Abfrage von Informationen durch den Client<br />

63


16.4 Die Qt-Bibliothek<br />

Qt <strong>C++</strong>-Klassenbibliothek UNIX(X11), Windows<br />

Folien: API Structure Overview + Kernel Classes<br />

Beispiel:<br />

Ausgabe eines Knopfes mt Beschriftung<br />

#include < qapplication.h ><br />

#include < qpushbutton.h ><br />

int main (int arg c, char **arg v) {<br />

Qapplication a (arg c, arg v);<br />

QPushButton hello („Hallo Welt!“);<br />

hello.resize (100,30);<br />

a.set MainWidget (&hello); //hello wird Hauptfenster<br />

hello.show();<br />

return a.exec();<br />

}<br />

Beispiel:<br />

Ausgabe eines Knopfes mit Funktionalität „quit“<br />

#include < qapplication.h ><br />

#include < qpushbutton.h ><br />

int main (int arg c, arg v)<br />

{<br />

Qapplication a (arg c, arg v);<br />

QPushButton hello („Hallo Welt!“);<br />

quit.resize (75,30);<br />

QObject::connect (<br />

&quit,<br />

SIGNAL(clicked()),<br />

&a,<br />

SLOT(quit());<br />

}<br />

a.set MainWidget (&quit);<br />

quit.show();<br />

return a.exec();<br />

Erläuterung zur wichtigsten (roten) Zeile:<br />

64


16.5 Signal-Slot-Mechanismus<br />

Signal:<br />

Ereignis-Mitteilungen werden anonym – d.h. ohne ausdrücklichen Empfänger<br />

– abgegeben.<br />

Das Objekt kann nicht feststellen, ob und wie das Signal verwendet wird. ( <br />

Obkekt kann als Komponente einfach wieder verwendet werden!)<br />

Slot:<br />

Slots sind Komponentenfunktionen.<br />

Ein Slot kann nicht feststellen, ob und welche Signale zugeordnet sind.<br />

Mehrfachzuordnungen sind möglich. ( Wiederverwendbarkeit!)<br />

Bsp.:<br />

Signal- und Slot-Mechanismus<br />

1) Qt-Klasse, abgeleitet von QObject (Basisobjekt)<br />

class Foo : public QObject<br />

{ Q_OBJECT Makro<br />

public:<br />

Foo( );<br />

int value( ) {return val;}<br />

public slots:<br />

void setValue (int);<br />

signals:<br />

void value (hanged (int));<br />

};<br />

private:<br />

int val;<br />

2) Implementierung des Slots<br />

void Foo :: setValue (int v)<br />

{<br />

if (v != val)<br />

{<br />

val = v;<br />

emit valueChanged (v);<br />

}<br />

}<br />

3) Verbindung zweier Objekte<br />

Foo a, b;<br />

connect (<br />

b.setValue (11);<br />

a.setValue (79);<br />

b.value ( );<br />

&a, SIGNAL (valueChanged (int)),<br />

&b, SLOT (setValue (int)));<br />

Das valueChanged-Signal von a erreicht den Slot von b<br />

65


Neue Schlüsselwörter (der Qt-Bibliothek)<br />

public slots, signal, emit, ...<br />

Signals:<br />

- zufällige Reihenfolge der Slot-Aufrufe bei Mehrfachverknüpfungen<br />

- Rückkehr aus emit, wenn alle Slots abgearbeitet sind<br />

Slots:<br />

- normale Komponentenfunktionen<br />

- Regelung der Zugriffsrechte über public, protected, private<br />

MOC (Meta Object Compiler)<br />

- erzeugt aus den zusätzlichen Qt-Schlüsselwörtern <strong>C++</strong>-Code<br />

- Aufruf über make-Datei<br />

Event-Filter:<br />

- ein Objekt, das alle Signale bekommt, die für ein anderes Objekt bestimmt sind<br />

- Filter reagiert selbst oder sendet das Signal weiter<br />

Kapitel 17 – Error- Handler und Exceptions<br />

17.1 Funktionszeiger<br />

Beispiel:<br />

#include <br />

void f0 ( ) {printf („Fkt 0 \n“)}<br />

void f1 ( ) {printf („Fkt 1 \n“)}<br />

void f2 ( ) {printf („Fkt 2 \n“)}<br />

typedef void (*FPT) ( );<br />

int main( )<br />

{<br />

FTP ftp[ ] = { f0, f1, f2};<br />

fpt[2] ( );<br />

return 0;<br />

}<br />

//Fkt.zeiger FPT<br />

<br />

66

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!