Skript Programmierung C/C++
Skript Programmierung C/C++
Skript Programmierung C/C++
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