Vorlesungsunterlagen als pdf-Datei

Werbung
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR– Inhalt – 01 – TH/KR – 11
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren
(4. Semester)
H. Kristl
R. Thomas
hier :
Version von R. Thomas
Stand : WS 2015/16
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR– Inhalt – 02 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Teil 1: Prozedurales Programmieren in C, Fortsetzung
Teil 2: Einführung in die objektorientierte Programmierung mit Java
Voraussetzungen : Inhalte der LV Algorithmen und Datenstrukturen
(sowie teilweise der LV Digitaltechnik)
im wesentlichen sind das :
Aufbau und Arbeitsweise eines Digitalrechners
Datendarstellung im Rechner
Entwurf und Darstellung von Algorithmen
Schritte der Softwareentwicklung
Umsetzung von Algorithmen in C-Programme
Einfache Datentypen in C
Anweisungen und Kontrollstrukturen in C
Der C-Preprozessor
Erzeugung eines C-Programms
Ausdrücke und Operatoren in C
Unterprogramme (Funktionen) in C
Arrays in C
Strings in C
Vorhang (vorangestellter Anhang) : Einige Programmierregeln
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR – Regeln – 01 – TH – 03
----------------------------------------------------------------------------------------------------------------------------------------------------
Einige Programmier-Regeln
Vorbemerkung : Im Einzelfall kann ein Verstoß gegen die folgenden Regeln zulässig (und manchmal auch
sinnvoll) sein. Jeder Verstoß muss aber gut überlegt und wohl begründet sein.
Grundregeln :
∙ Sinnvolle Zerlegung des vom Programm zu realisierenden Gesamt-Algorithmus in Teil-Algorithmen,
gegebenenfalls hierarchische Zerlegungsstruktur ( Top-Down-Design)
Implementierung der einzelnen Teil-Algorithmen durch Funktionen (Unterprogramme, Prozeduren)
∙ Strukturiert programmieren Aufbau eines (Teil-)Algorithmus (und des implementierenden ProgrammCodes) aus klaren Struktur-Elementen, die jeweils über genau einen Eingang und einen Ausgang
verfügen. Jeder (Teil-)Algorithmus muss durch ein Struktogramm formulierbar sein.
Zu verwendende Struktur-Elemente : lineare Aktion (Anweisung),
einfache Verzweigung ( z.B. if ... else), Mehrfach-Verzweigung (z.B. switch ... case),
abweisende Schleife (z.B. while ... bzw. for ...),
nichtabweisende Schleife (z.B. do ... while)
keine Sprünge (außer Unterprogramm-Aufrufe)
∙ Modular programmieren Aufteilung der Funktionalität eines Programms auf mehrere gut voneinander
abgrenzbare Module (Quelldateien, Übersetzungseinheiten)
∙ Änderungsfreundliche, übersichtliche und selbstdokumentierende Formulierung des Quellcodes
Abgeleitete Regeln :
∙ Im Regelfall keine globalen Variablen
Ausnahmen müssen wohlüberlegt und begründet sein (z.B. können modul-globale Variable sinnvoll sein,
wenn sie nach außen verborgen bleiben sollen und die Schnittstelle der – insbesondere - von außen
aufrufbaren Funktionen dadurch sinnvoll vereinfacht werden kann)
∙ Klare Funktionsschnittstellen : Alle nicht lokal definierten Variablen einer Funktion müssen über
Parameter bereitgestellt werden,
keine Verwendung globaler Variabler (Ausnahmen : s. oben)
∙ Funktionen dürfen nur am Funktionsende verlassen werden
keine "bedingten" Rücksprünge, sondern nur nur eine einzige "unbedingte" Rücksprunganweisung
Bei Funktionen, die einen Wert zurückgeben : Gegebenenfalls Definition einer Rückgabe-Variablen,
der innerhalb der Funktion an unterschiedlichen Stellen ein Rückgabewert zugewiesen wird.
∙ Keine Verwendung von Sprunganweisungen (z.B. kein goto, kein continue in Schleifen)
Ausnahme : Abschluss der einzelnen Verzweigungsfälle (case-Fälle) in einer switch-Anweisung mit break.
∙ Kein Verlassen von Schleifen unter Umgehung der Schleifensteuerung,
d.h. kein Verlassen von Schleifen mit break oder goto
∙ Keine Verwendung von Konstanten direkt mit ihrem Wert
sondern : Definition und Verwendung von symbolischen Konstanten
Dies gilt insbesondere auch für die Größenfestlegung von Arrays in C.
üblich und empfohlen : Konstantennamen bestehen nur aus Großbuchstaben
∙ Definition von symbolischen Konstanten nur an einer einzigen zentralen Stelle
(z.B. bei C-Programmen am Modulanfang oder in einer Headerdatei, falls Konstante in mehreren Modulen
verwendet wird)
∙ Für gleiche Werte unterschiedlicher Bedeutung Definition verschiedener symbolische Konstante
∙ Konsequente Formatierung des Quelltextes (Block
sinnvolles Einrücken, empfohlen : 2 Stellen/Ebene)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR-C– Inhalt –TH/KR – 05
----------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Teil 1: Prozedurales Programmieren in C, Fortsetzung
5. Datentypen in C, Teil 2
5.1. Felder (Arrays) Inhalt der LV "Algorithmen und Datenstrukturen"
5.2 Typumwandlung
5.2.1. Implizite Typumwandlung
5.2.2. Explizite Typumwandlung
5.3 Verfügbarkeit und Lebensdauer – Speicherklassen
5.4 Zeiger (Pointer)
5.4.1. Eigenschaften und Verwendung
5.4.2. Zeiger und Arrays
5.4.3. Programm-Parameter in C
5.4.4. Zeiger und Funktionen
5.5 Strukturen (strukturierter Datentyp – struct)
5.6 Definition von Datentypnamen mittels typedef
5.7 Aufzählungstypen und Aufzählungskonstante
5.8 Der sizeof-Operator
6. Dateibearbeitung in C
6.1
6.2
6.3
6.4
6.5.
Dateibearbeitungskonzept von ANSI-C
Grundlegende Funktionen zur Dateibearbeitung
Funktionen zum Datentransfer (Schreiben und Lesen)
Funktionen zur Dateiverwaltung
Beispiel zu Binärdateien
7. Ergänzungen zu Datenstrukturen in C
7.1
7.2
7.3
7.4
Dynamische Speicherallokation
Dynamische Datenstrukturen (Listen und Bäume)
Unions
Vorwärtsdeklaration von strukturierten Datentypen
8. Die C-Standardbibliothek
8.1
8.2
8.3
8.4
8.5
8.6
8.7
ANSI/ISO-C-Standardbibliothek
Überblick über die I/O-Funktionen
Funktionen zum Zeichenklassentest
Funktionen zur Stringbearbeitung
Utility-Funktionen
Mathematische Funktionen
Datums- und Zeitfunktionen
9. Ausgewählte Algorithmen
9.1.
9.2.
9.3
9.4
Sieb des Eratosthenes
Rekursion
Bestimmung von Nullstellen durch Iteration
Sortieren
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – Inhalt – TH – 07
-------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Teil 2: Einführung in die OOP mit Java
10. Grundkonzepte der Objektorientierten Programmierung
10.0.
10.1.
10.2.
10.3.
10.4.
10.5
Einführung : Grundgedanke der OOP
Objekte und Klassen
Kapselung
Vererbung
Polymorphie
Klassenbeziehungen
11. Grundlegende Eigenschaften von Java
11.1
11.2.
11.3.
11.4.
11.5.
11.6.
Java und C
Klassen und Kapselung
Programmstruktur und Startklasse
Erzeugung und Start von Java-Programmen
Packages in Java
Java-Standardbibliothek (Java API)
12. Daten und Objekte in Java
12.1.
12.2.
12.3.
12.4.
12.5.
12.6.
12.7.
12.8.
Einfache Datentypen
Referenztypen und Objekterzeugung
Konstante in Java
Wrapper-Klassen für die einfachen Datentypen
Strings
Arrays
Die Klasse Object
Beispiel : Klasse Ratio
13. Elementare Programmfunktionalitäten in Java
13.1.
13.2.
13.3.
13.4.
Zugriff zu Programmparametern
Standard-Ein- und Ausgabe
Exceptions
Dateibearbeitung
14. Vererbung und Polymorphie in Java
14.1.
14.2.
14.3.
14.4.
14.5.
Umfassende Klassendefinition
Klassenableitung
Polymorphie
Interfaces und ihre Implementierung
Programmbeispiel : Simpson-Integration
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/00/0 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
5. Datentypen in C, Teil 2
5.1. Arrays
Inhalt der LV "Algorithmen und Datenstrukturen"
5.2. Typumwandlung
5.2.1. Implizite Typumwandlung
5.2.2. Explizite Typumwandlung
5.3. Verfügbarkeit und Lebensdauer – Speicherklassen
5.4. Zeiger (Pointer)
5.4.1.
5.4.2.
5.4.3.
5.4.4.
Eigenschaften und Verwendung
Zeiger und Arrays
Programmparameter in C
Zeiger und Funktionen
5.5. Strukturen (strukturierter Datentyp – struct)
5.6. Definition von Datentypnamen mittels typedef
5.7. Aufzählungstypen und Aufzählungskonstante
5.8. Der sizeof-Operator
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/01/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Wiederholungs-Beispiel zu Arrays
● Beispiel zu eindimensionalen Arrays :
Ermittlung von Primzahlen mittels des Siebs des Eratosthenes.
s. Kap. 9, Abschnitt 1 (V-PR-C-09/01/1-TH/KR-02)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/02/1/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Implizite Typumwandlung
Bei Verknüpfungen in Ausdrücken können Operanden unterschiedlicher Datentypen auftreten.
In C wird vor der Durchführung der Operation eine Angleichung der verschiedenen Datentypen der
Operanden vorgenommen:
⇒ implizite Typumwandlung (Typkonvertierung, type conversion)
Die implizite Typkonvertierung findet in folgenden Situationen statt :
• Bei Zuweisungen wird der Wert der rechten Seite in den Typ der linken Seite umgewandelt:
aVar
=
bVar
int
int
← float
← double
Weglassen des gebrochenen Anteils
int
char
char
← long
← int
← short
Weglassen der höherwertigen Bits
float
← double
Runden oder Abschneiden (implementierungsabhängig)
float ← long, int, short, char
double ← long, int, short, char
durchgeführte Aktion
Wenn keine exakte Darstellung möglich
Runden oder Abschneiden (implementierungsabhängig)
• Bei Parameterübergabe an Funktionen:
Umwandlung des Typs des aktuellen Parameters in den Typ des formalen Parameters nach den für
die Zuweisung gültigen Regeln.
• Automatische Umwandlung in arithmetischen Ausdrücken: (integral promotion)
Vorzeichenbehaftet: char, short
→ int
Vorzeichenlos:
unsigned char, unsigned short → int (falls damit darstellbar)
unsigned int (sonst)
• Bei 2-stelligen arithmetischen Operationen wird bei unterschiedlichem Datentyp der beiden Operanden der
Wert des "niedrigeren" Typs in den "höheren" Typ umgewandelt.
Das Ergebnis der Operation ist vom Typ des "höheren" Operanden-Typs.
Dabei gilt die folgende Typ-Hierarchie:
long double
("höchster" Typ)
double
float
unsigned long int
long int
unsigned int
int
("niedrigster" Typ)
Anmerkung : Zuvor wird für short und char prinzipiell "integral promotion" durchgeführt
(s.oben).
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/02/1/2 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Regeln für die implizite Typumwandlung
Ist ein Operand "long double"
?
ja
nein
Zweiter Operand und Ergebnis sind
"long double"
Ist ein Operand "double"
?
ja
nein
Zweiter Operand und Ergebnis sind
"double"
Ist ein Operand "float"
?
nein
ja
Zweiter Operand und Ergebnis sind
"float"
integral promotion beider Operanden:
(unsigned)char,short → (unsigned)int
Ist ein Operand "unsigned long"
?
ja
nein
Zweiter Operand und Ergebnis sind
"unsigned long"
Ist ein Operand "unsigned" und
der zweite Operand "long" ?
ja
nein
Kann "long" alle "unsigned"Werte darstellen?
ja
Ist ein Operand "long"
?
ja
nein
"unsigned"-Operand und Beide Operanden und Ergebnis
"unsigned long"
Ergebnis sind "long"
nein
Zweiter Operand und
Ergebnis sind"long"
ja
Zweiter Operand und Ergebnis
"unsigned int"
Ist ein Operand
"unsigned" ?
nein
Ergebnis ist"int"
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/02/2/1 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Explizite Typumwandlung
• In manchen Fällen ist es erforderlich die Typumwandlung gezielt vorzunehmen.
Beispiel:
int j=2, k=3;
float f;
.....
f = k/j;
....
/* in f steht 1.0 */
beide Operanden sind vom Typ "int"
also ist auch das Ergebnis
der Division vom Typ "int";
die Zuweisung erfolgt erst hinterher
und führt erst dann zur impliziten
Typkonversion "int"→"float"!
Soll das "echte" Ergebnis gewünscht werden, so wird dies durch eine explizite Typumwandlung
ermöglicht.
• Die explizite Typumwandlung (type cast) erfolgt mittels des Cast-Operators, der unmittelbar vor den
Ausdruck zu setzen ist,dessen Typ konvertiert werden soll:
Cast-Operator
Ausdruck
• Cast - Operator:
unärer Operator :
in Klammern eingeschlossene Typangabe
(Typangabe)
Wirkung:
Der Wert des Ausdrucks wird in den durch den Cast-Operator festgelegten
Datentyp umgewandelt.
Achtung:
Der Datentyp der am Ausdruck beteiligten Variablen selbst bleibt unverändert.
Präzedenz:
Gleiche Priorität wie die anderen unären Operatoren.
• Anwendung :
Überall dort, wo ein ganz bestimmter Datentyp erforderlich ist, der aber durch die implizite
Typumwandlung nicht erzielt wird!
Beispiel: (vgl. oben )
int j=2, k=3;
float f;
.....
f = (float)k/j;
.....
/* in f steht 1.5 */
Der Wert der Variablen k wird
typkonvertiert ("gecastet"). Dies führt
dazu, dass vor der Ausführung der
Division j implizit typkonvertiert
wird und die Division ein "float"Ergebnis liefert.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/03/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Verfügbarkeit und Lebensdauer - Speicherklassen
● Attribut Speicherklasse
Neben der Typangabe gibt es für Objekte (Funktionen und Variable) in C als weiteres Attribut die
Speicherklasse (storage class). Sie beeinflusst folgende Merkmale von Objekten:
-
Verfügbarkeit
Lebensdauer
Weiterhin wirkt sie sich auf den Speicherort des Objekts aus
● Verfügbarkeit (Scope):
Bereiche eines Programmes, in denen zu einem Objekt zugegriffen werden kann.
C unterscheidet vier Verfügbarkeitsbereiche:
(1) gesamtes Programm
(2) Modul (File)
globale Objekte: Definition außerhalb jeder Funktion;
Funktionen sind immer global!
(3) Funktion
(4) Verbundanweisung
lokale Objekte: Definition innerhalb eines Blockes
(innerhalb einer Funktion oder
Verbundanweisung)
Lokale Objekte können nur Variablen sein!
● Lebensdauer (Duration):
Dauer der Zuordnung von Speicherplatz zu einem Objekt. Man unterscheidet:
◇ Gesamte Programmlaufzeit (static duration):
Der Speicherbereich wird dem Objekt zu Programmbeginn zugeordnet. Die Zuordnung wird erst mit dem
Programmende wieder aufgehoben.
◇ Ausführungszeit des Blockes, in dem die Definition des Objektes erfolgte (automatic duration).
Dem Objekt wird mit Eintritt des Programmes in den Block automatisch Speicher zugewiesen.
Mit Verlassen des Blockes wird diese Speicherzuweisung wieder aufgehoben.
Merke:
Globale Objekte - also auch alle Funktionen - "leben" grundsätzlich während der gesamten
Programmlaufzeit.
Lokale Objekte (Variable) besitzen eine Lebensdauer, die auf die Blockausführungszeit begrenzt
ist, sie können aber auch für die gesamte Programmlaufzeit vereinbart werden.
● Speicherklassen (storage classes):
Zur Kennzeichnung der Speicherklasse eines Objekts, bei dessen Definition bzw. Deklaration, gibt es in der
Programmiersprache C vier Speicherklassen- Spezifizierer (storage class specifier), die auch die Speicherklassennamen festlegen.
Bei Vereinbarungen müssen sie gegebenenfalls vor dem Objekttyp angegeben werden. Erfolgt keine derartige
Angabe, so gilt eine Default-Speicherklasse.
Die vier Speicherklassen(-Spezifizierer) sind:
extern - static - auto - register
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/03/2 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Speicherklassen (1)
• Speicherklasse auto (nur Variable)
◇ Verfügbarkeitsbereich : Block, der die Definition enthält
Lebensdauer :
Block-Ausführungszeit
Speicherort :
Stack
◇ Vorgabespeicherklasse für lokale Verfügbarkeit.
Speicherklasse für
▻ alle Variablen, die innerhalb eines Blockes definiert sind und nicht explizit mit "static"
oder "register" vereinbart sind;
▻ alle formalen Funktionsparameter, für die nicht explizit "register" angegeben wird.
◇ Default-Initialisierung: undefiniert
explizite Initialisierung: bei Blockeintritt
einfache Datentypen und Zeiger durch beliebige Ausdrücke;
zusammengesetzte Datentypen (arrays, structures und unions) durch konstante
Ausdrücke.
• Speicherklasse register (nur Variable)
Spezialfall der lokalen Verfügbarkeit : wie Speicherklasse auto - mit der Besonderheit, dass die
Variable in einem lokalen CPU-Speicher (Register) angelegt werden soll (Zugriffszeit!).
• Speicherklasse extern (Variable und Funktionen)
◇ Verfügbarkeitsbereich : Gesamtes Programm
Lebensdauer :
Programmlaufzeit
Speicherort :
Datensegment (Variable) bzw Codesegment (Funktionen)
◇ Vorgabespeicherklasse für globale Verfügbarkeit.
Speicherklasse für
▻ alle Funktionen,
▻ alle außerhalb jeder Funktion definierten Variablen,
für die nicht explizit "static" angegeben wird.
◇ Um Objekte verwenden zu können, die in einem anderen Modul oder im gleichen Modul später definiert
sind, sind extern -Deklarationen erforderlich.
◇ Default-Initialisierung: mit 0
explizite Initialisierung: durch konstanten Ausdruck (ausgewertet durch Compiler).
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/03/3 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Speicherklassen (2)
• Speicherklasse static (Variable und Funktionen)
◇ Es existieren zwei Formen:
▻ static local : Anwendung auf lokales Objekt
▻ static global : Anwendung auf globales Objekt
◇ Verfügbarkeitsbereich:
Lebensdauer:
Speicherort :
statisch-lokal (nur Variable)
statisch-global (Variable und Funktionen)
Block, der die Definition enthält - falls static local
Modul, das die Definition enthält - falls static global
Programmlaufzeit
Datensegment (Variable) bzw Codesegment (Funktionen)
◇ Statisch-lokale Variable behalten ihre Werte zwischen zwei Ausführungen des Blockes bei, im Gegensatz
zu Variablen der Speicherklasse "auto". Dieses Verhalten ist speziell dann interessant, wenn zwischen
zwei Aufrufen der gleichen Funktion die Variable den zuletzt ermittelten Wert beibehalten soll.
Eine eventuelle Initialisierung erfolgt nur beim ersten Eintritt in diesen Block.
◇ Statisch-globale Objekte (Variable und Funktionen) ermöglichen es, die Globalität auf das betrachtete
Modul zu beschränken. Sie können in anderen Modulen desselben Programmes nicht "gesehen" werden.
Sollen also einzelne Funktionen und globale Variable nur im eigenen Quellmodul sichtbar sein, so sind sie
mit der Speicherklasse "static" zu definieren.
◇ Default-Initialisierung: mit 0
explizite Initialisierung: durch konstanten Ausdruck (ausgewertet durch Compiler).
• Zusammenfassung Eigenschaften der Speicherklassen
Speicherklasse
auto
*) falls möglich, sonst Stack
register
static
local
Speicherort
Definitionsort
Stack
CPU-Register *)
normaler Arbeitsspeicher
innerhalb eines Blocks
(default : auto)
außerhalb jeder Funktion
(default : extern)
Verfügbarkeitsbereich
Block (Funktion bzw Verbundanweisung)
Lebensdauer
Ausführungszeit des Blocks,
in dem das Objekt definiert ist
Modul
Programm
gesamte Programmlaufzeit
**) nur mit Initialisierung
(*) darf auch fehlen
• Überblick Verwendung der Speicherklassen-Spezifizierer
auto
extern
global
register
static
extern
Variablendefinition innerhalb einer Funktion
(∗)
∗
∗
---
Variablendefinition außerhalb jeder Funktion
---
---
∗
(∗) **)
Variablendeklaration
---
---
---
∗
Funktionsdefinition
---
---
∗
(∗)
Funktionsdeklaration
---
---
∗
(∗)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/03/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsbeispiel zu Speicherklassen
/*-----------------------------------------------------/* Modul 1: stclass_m.c
/*-----------------------------------------------------/* Verfuegbarkeit, Lebensdauer und Speicherklassen
/*------------------------------------------------------
*/
*/
*/
*/
*/
#include <stdio.h>
extern void exprint(int);
static void locprint(void);
int iZ ;
Hinweis:
Funktion ist in einem
anderen Modul definiert
Hinweis:
Funktion ist später in diesem
Modul definiert und außen
nicht verfügbar
lokale auto-Variable mit
und ohne Initialisierung
int main(void)
{ int iZ=1,i;
printf("\nZahl(1): %d\n",iZ);
{ extern int iZ;
Hinweis auf verwendete globale Variable
printf("Zahl(2): %d\n",iZ++);
for(i=0;i<2;i++)
"sub"-lokale static-Variable, wird nur beim ersten Block{ static int iZ = 22;
Eintritt initialisiert und behält ihren Wert bei Wiedereintritt
exprint(iZ++);
locprint();
}
}
return 0;
}
Definition der globalen Variablen, macht
obige Vereinbarung zur Deklaration
int iZ = 33;
static void locprint(void)
{ int iZ = 49;
printf("\nZahl(loc1): %d\n",iZ++);
}
lokale auto-Variable
mit Initialisierung
/*-----------------------------------------------------/* Modul 2: stcl1.c
/*-----------------------------------------------------/* Verfuegbarkeit und Lebensdauer und Speicherklassen
/*-----------------------------------------------------#include <stdio.h>
extern int iZ;
static void locprint(void);
void exprint(register int ilZ)
{ printf("\nZahl(ex): %d %d\n",iZ, ilZ);
locprint();
}
static void locprint(void)
{ extern int iZ;
printf("\nZahl(loc2): %d\n",iZ++);
}
Funktion ist nur
in diesem Modul
verfügbar
*/
*/
*/
*/
*/
Verweis auf globale Variable, die in
einem anderen Modul definiert ist
Hinweis auf ein zweites "locprint()", das
in diesem Modul (2. Modul des gleichen
Programms) später definiert ist
Hinweis auf
verwendete globale
Variable
Funktion ist nur
in diesem Modul
verfügbar
Probelauf :
Zahl(1): 1
Zahl(2): 33
Zahl(ex): 34 22
Zahl(loc2): 34
Zahl(loc1): 49
Zahl(ex): 35 23
Zahl(loc2): 35
Zahl(loc1): 49
/*
/*
/*
/*
/*
/*
/*
/*
lokales iZ in main */
globales iZ in main verwendet */
globales iZ in exprint verwendet, "sub"-lokales iZ von main an exprint übergeben */
globales iZ verwendet in locprint aus Modul 2, das von exprint aufgerufen wird */
lokales iZ in locprint aus Modul 1 */
globales iZ in exprint verwendet, "sub"-lokales iZ von main an exprint übergeben */
globales iZ verwendet in locprint aus Modul 2, das von exprint aufgerufen wird */
lokales iZ in locprint aus Modul 1 */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/1/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger in C (1)
● Wesen und Eigenschaften von Zeigern
◇ Ein Zeiger (Pointer) ist eine Variable (Zeigervariable) oder eine Konstante (Zeigerkonstante)
Eine Zeigervariable benötigt wie andere Variable Speicherplatz.
◇ Der Wert einer Zeigervariablen (bzw eine Zeigerkonstante) ist eine Speicheradresse.
Diese Speicheradresse ist
▻ entweder die (Anfangs-) Adresse von einer – anderen – Variablen
▻ oder die (Start-) Adresse einer Funktion
⇒ Ein Zeiger z e i g t auf eine andere Variable oder auf eine Funktion
Beispiel : Die Zeigervariable zeiger_auf_x zeigt auf die Variable x
zeiger_auf _x :
x :
◇ Ein Zeiger kann auch wiederum auf eine Zeigervariable zeigen (
Zeiger auf Zeiger)
◇ Mittels Zeiger können Variable indirekt über ihre Adresse (⇒ Referenz) angesprochen werden.
◇ Zeiger sind i.a. an bestimmte Objekt-Typen gebunden.
Z.B. kann ein Zeiger auf int-Werte nur Adressen von int-Variablen als Werte annehmen.
Oder : ein Zeiger auf int-Zeiger kann nur Adressen von int-Zeiger-Variablen annehmen
Ausnahme: generic pointer: void * kann auf beliebige Objekt-Typen zeigen.
● Zeigerbezogene Operatoren
Im Zusammenhang mit Zeigern existieren zwei – zueinander inverse – unäre Operatoren:
▻ & Adressoperator
z.B.
liefert die Adresse eines Objektes
&x ⇒ Adresse der Variablen x
Adressen können Zeigervariablen zugewiesen werden:
z.B.
px sei Zeigervariable (auf int)
x
sei int-Variable
⇒
px = &x;
/* die Zeigervariable px zeigt jetzt auf die Variable x */
▻ * Objektoperator
z.B.
liefert das Objekt, auf das ein Zeiger zeigt ⇒ Dereferenzierung
*px ⇒ Objekt, auf das der Zeiger px zeigt, bzw.
Variable deren Adresse in px steht.
Zeigerobjekte können wie unter Namen referenzierte Variable verwendet werden:
z.B.
px sei Zeigervariable (auf int)
y
sei int-Variable
⇒
*px = 3;
/* Zuweisung von 3 an das Objekt von px (int-Var.),
Verwendung von px als lvalue
*/
y = *px;
/* Zuweisung des Werts des Objekts von px an y,
Verwendung von px als rvalue
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/1/2 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger in C (2)
● Vereinbarung von Zeigervariablen
erfolgt mittels Verwendung des Objektoperators *.
z.B. int *px;
/* px wird als Zeigervariable vereinbart, die auf int-Variable zeigt
/* Interpretation : *px ist vom Typ int,
⇒ px ist ein Zeiger auf int
double **ppd; /* ppd ist eine Zeigervariable, die auf double-Zeiger-Variable zeigt
/* Interpretation : **ppd ist vom Typ double
⇒ *ppd ist ein double-Zeiger
⇒ ppd ist ein Zeiger auf double-Zeiger
*/
*/
*/
*/
● Ergänzungen zur Verwendung von Zeigern
◇ Zeigerwerte (Adressen) können als Text ausgegeben werden (i.a. als Sedezimalzahl),
z.B. in die Standardausgabe mittels printf().
Umwandlungsspezifikation %p im Steuerstring
Beispiel :
int x;
int *px = &x;
printf("Adresse von x : %p\n", &x);
printf("Wert von px
: %p\n", px);
◇ Größtes Problem bei Zeigern : Verwendung von Zeigervariablen, denen kein Wert zugewiesen wurde
( nicht-initialisierte Zeiger !).
Lokale nicht-initialisierte auto-Variable besitzen einen zufälligen Wert.
Laufzeitfehler wird häufig nicht erkannt.
◇ Zeigervariablen, die keinen definierten Wert enthalten, sollte der Wert NULL (== 0, NULL-Pointer)
zugewiesen werden.
Ihre Verwendung führt dann zu einem erkennbaren Laufzeitfehler ( Null-Pointer-Exception)
Die Konstante NULL ist definiert in <stddef.h>, oft auch noch in weiteren Headerdateien
(z.B. <stdio.h> und/oder <stdlib.h>).
● Demonstrationsbeispiel zu Zeigern
Programm poindem1
siehe nächste Seite
Entwicklung der Variablenwerte des Demonstrationsbeispiels:
Variable
x:
y:
px:
py:
ppz:
Wert
Wert
Wert
Wert
Wert
Wert
Wert
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/1/3 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger in C (3)
/*----------------------------------------------------------------------- ----------*/
/* C-Quelldatei poindem1_m.c
*/
/* Modul mit main()-Funktion fuer das Programm poindem1 – Einfache Demo zu Zeigern */
/*-------------------------------------------------------------------------------- */
#include <stdio.h>
int main(void)
{ int x=1, y;
int *px, *py=NULL; /* Zeiger auf int-Variable */
int **ppz;
/* Zeiger auf int-Zeiger-Variable */
printf("\nAdresse
printf("\nAdresse
printf("\nAdresse
printf("\nAdresse
printf("\nAdresse
von
x
von
y
von px
von py
von ppz
:
:
:
:
:
%p", &x);
%p", &y);
%p", &px);
%p", &py);
%p\n", &ppz);
printf("\nWert
printf("\nWert
px=&x;
*px=5;
(*px)++;
y=*px+1;
printf("\nWert
printf("\nWert
von px (nicht-initialisiert)
: %p", px);
von py (initialisiert mit NULL) : %p", py);
/* Wertzuweisung an Zeigervariable mittels Adressoperator */
/* Modifikation des Zeigerobjekts */
/* Klammern notwendig fuer Modifikation des Zeigerobjekts */
/* Zeigerobjekt als Operand */
von x : %d",x);
von y : %d\n",y);
py=px;
*py += 3;
printf("\nWert
printf("\nWert
printf("\nWert
printf("\nWert
/* Zuweisung des Werts eines anderen Zeigers */
/* Modifikation des Zeigerobjekts */
von px :
: %p", px);
des Zeigerobjekts von px
: %d",*px);
von py :
: %p", py);
des Zeigerobjekts von py
: %d\n",*py);
py = &y;
ppz = &py;
/* Zuweisung der Adresse einer Zeigervariablen */
**ppz = 12;
/* Modifikation des Objekts des Zeigerobjekts */
printf("\nWert von ppz
: %p", ppz);
printf("\nWert des Zeigerobjekts von ppz : %p", *ppz);
printf("\nWert des Zeigerobjekts von *ppz : %d\n", **ppz);
printf("\nWert von x : %d", x);
printf("\nWert von y : %d", y);
return 0;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/1/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger in C (4)
● Operationen mit Zeigern
Auf Zeiger in C lassen sich die folgenden Operationen anwenden :
◇ Zuweisung einer Adresse, z.B. des Wertes einer anderen Zeigervariablen (desselben Typs).
siehe oben
◇ Ermittlung des Zeigerobjekts
◇ Vergleichsoperationen (==, !=, >, <, >=, <=)
▻ zwischen Zeigern gleichen Typs (sinnvoll nur für Zeiger, die auf Elemente des gleichen Arrays zeigen)
▻ sowie zwischen einem Zeiger und dem Wert 0.
◇ Folgende Arithmetische Operationen
Zeigerarithmetik
∗ Addition und Subtraktion eines ganzzahligen Wertes (einschließlich Inkrement und Dekrement).
Addition (bzw Subtraktion) von n zu einem Zeiger liefert als neuen Zeigerwert die Adresse, die um
den Speicherplatz für n Objekte seines Objekttyps größer (bzw kleiner) als sein ursprünglicher Wert
ist.
Compiler nimmt Skalierung von n entsprechend der Größe des Objekttyps vor Portabiltät
Beispiele :
double dAr[8], *pdX;
pdX = &dAr[1]; /* Zeigervariable hat den Wert der Adresse des
Arrayelements mit dem Index 1 */
pdX += 4;
/* Erhöhung von pdX um 4 Speicherplätze von double
*/
pdX++;
/* Erhöhung um den Umfang eines double-Speicherplatzes
*/
pdX = pdX – 2; /* Erniedrigung um den Umfang zweier double-Speicherplätze */
∗ Subtraktion von zwei Zeigern, die auf Objekte des gleichen Typs zeigen.
Die Operation liefert die Anzahl der Objekte, um die die Werte der beiden Zeiger voneinander
entfernt sind.
Sinnvoll anwendbar auf Zeiger, die auf Elemente des gleichen Arrays zeigen.
Anzahl der zwischen den Zeigerobjekten liegenden Elemente +1
Beispiel : Mögliche Formulierung von "strlen()"
int strLen (char sKette[])
{
char *pcStart = &sKette[0];
char *pcEnde;
pcEnde = pcStart;
while (*pcEnde != '\0')
pcEnde++;
return (pcEnde – pcStart);
}
/*
/*
/*
/*
Def. und Init. einer Zeigervariablen
Def. einer Zeigervariablen
Anfangswert vorbelegen
Ermitteln der Adresse des Stringendes
*/
*/
*/
*/
/* Differenz zwischen Start- und Endezeiger
gibt die Anzahl der dazwischen liegenden
Arrayelemente +1 wieder, das ist die
gesuchte String-Laenge
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/2/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger und Arrays in C
● Verwandschaft zwischen Zeigern und Arrays
◇ Vergleich der Verwendung von Arrays uns Zeigern :
/* Definition eines int-Arrays mit 10 Elementen */
int a[10];
a[0]
a[1]
a[i]
1. Element von a
2. Element von a
Element, das i Positionen vom Anfang entfernt ist.
⇒
⇒
⇒
/* Definition eines Zeigers auf int-Variable */
int *pa;
pa = &a[0];
pa
pa + 1
pa + i
⇒
zeigt auf das 1. Element von a
zeigt auf das 2. Element von a
zeigt auf das Element, das i Positionen von dem Element entfernt ist,
auf das pa zeigt.
⇒
⇒
⇒
*pa
*(pa+1)
*(pa+i)
ist gleich mit
ist gleich mit
ist gleich mit
a[0]
a[1]
a[i]
◇ Array-Namen werden durch den Compiler wie Zeiger auf das erste Arrayelement behandelt:
ist äquivalent mit
⇒
a
⇒
statt pa=&a[0] kann man auch
statt a[i]
kann man auch
&a[0]
pa=a
formulieren
*(a+i) formulieren
◇ Umgekehrt lassen sich Zeigervariable wie Arrays verwenden (d.h. sie lassen sich indizieren !) :
⇒
◇
statt *(pa+i)
kann man auch
pa[i]
formulieren
Jeder Array-Index-Ausdruck kann auch als PointerOffset-Ausdruck formuliert werden und umgekehrt.
◇ Eine Pointer-Variable kann auch mit negativem Index verwendet werden :
pa[-i] (i>0) bezeichnet das Element, das i Positionen (Objekte !) vor dem Element liegt, auf das
pa zeigt.
pa[-i] ist äquivalent mit *(pa-i)
◇ Es besteht jedoch ein gravierender Unterschied zwischen Array-Namen und Zeiger-Variablen :
▻ Eine Zeiger-Variable ist eine Variable, deren Wert jederzeit geändert werden darf.
z.B. int x,*px;
px = &x;
/* Veränderung ist jederzeit zulässig */
▻ Ein Array-Name ist eine Zeiger-Konstante, ihr Wert ist nicht veränderbar!
z.B. int z, y[5];
y = &z;
/* U n z u l ä s s i g */
◇ Als Funktionsparameter sind Pointer und Arrays vollkommen äquivalent (beliebig austauschbar)
an Parameterposition sind char s[] und char *s identische Formulierungen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/2/2 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zur Verwandschaft zwischen Zeigern und Arrays
/*--------------------------------------------------------------------------- */
/*
Äquivalente Formulierungen der Funktion strcopy()
*/
/*--------------------------------------------------------------------------- */
void strcopy(char strl[] ,char str2[])
{
int i=0;
while((str1[i]=str2[i]) != '\0')
i++;
}
/*--------------------------------------------------------------------------- */
void strcopy(char *strl ,char *str2)
{
int i=0;
while((str1[i]=str2[i] ) != '\0')
i++;
}
/*--------------------------------------------------------------------------- */
void strcopy(char *strl ,char *str2)
{
int i=0;
while((*(str1+i)= *(str2+i) ) != '\0')
i++;
}
/*--------------------------------------------------------------------------- */
void strcopy(char *strl ,char *str2)
{
while((*str1 = *str2 ) != '\0')
{
str1++;
str2++;
}
}
/*--------------------------------------------------------------------------- */
void strcopy(char *strl ,char *str2)
{
while (*str1++=*str2++);
}
/*--------------------------------------------------------------------------- */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/2/3 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Mehrdimensionale Arrays und Zeiger
● Der Name eines mehrdimensionalen Arrays kann als "Zeiger auf Arrays" aufgefasst werden.
Beispiel: char caF [3][4];
caF
/* Definition und damit Speicherplatzreservierung eines
Arrays von 3*4 char-Werten.
/* caF ist ein Zeiger auf ein Array (== Zeiger auf das erste
Element des Arrays) mit 3 Elementen, die ihrerseits
char-Arrays der Länge 4 sind.
*caF
*/
*/
[0][0]
[0][1]
[0][2]
[0][3]
(caF+1)
*(caF+1)
[1][0]
[1][1]
[1][2]
[1][3]
(caF+2)
*(caF+2)
[2][0]
*(caF+2)+1
[2][1]
[2][2]
[2][3]
● Daraus ergeben sich folgende Zusammenhänge :
caF + i
ist ein Zeiger auf das (i+1)-te Zeilenarray (und zeigt auf das erste
Element dieser Zeile)
*(caF + i) = caF[i]
ist das (i+1)-te Zeilenarray, genauer ein Zeiger auf das erste Element
dieses Zeilenarrays
⇒ (caF+i) und *(caF+i)
liefern immer die gleiche Adresse
*(caF+i)+j = caF[i] + j
ist ein Zeiger auf das (j+1)-te Element des (i+1)-ten Zeilenarrays
*(*(caF+i)+j) =
*(caF[i] + j) =
caF[i][j]
=
(*(caF+i))[j]
ist das (j+1)-te Element des (i+1)-ten Zeilenarrays
● Für die Parameterdeklarationen mehrdimensionaler Felder besteht folgende Äquivalenz :
char (*caMat)[4]
und
char caMat [][4]
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/2/4 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger - Arrays (1)
● Zeiger - Arrays sind Arrays deren Komponenten Zeiger sind.
Die Komponenten können auch Adressen von Arrays sein, damit ergibt sich eine Ähnlichkeit zu
mehrdimensionalen Arrays.
Beispiel:
char *paZ[3];
/* Definition und damit Speicherplatzreservierung für ein Array
mit 3 Elementen, die jeweils Zeiger auf char-Typen sind. */
/* paZ ist ein Zeiger auf ein Array ( == auf das erste Element
des Arrays) mit 3 char-Zeigern
*/
char caF1[4],
caF2[4],
caF3[4];
/* Definition und damit Speicherplatzreservierung für 3
/* char-Arrays mit jeweils der Länge 4.
*/
*/
paZ[0] = caF1;
paZ[1] = caF2;
paZ[2] = caF3;
/* Wertzuweisung an die char-Zeiger
*/
paZ
caF1
paZ+1
caF2
paZ+2
caF3
caF1[]
caF2[]
caF3[]
*(paZ+2)+1
paZ[2][1] = caF3[1]
● Man sieht folgende Analogie zwischen Zeiger-Arrays und mehrdimensionalen Arrays:
paZ+i
ist ein Zeiger auf das (i+1)-te Element des Zeiger-Arrays paZ
*(paZ+i) = paZ[i]
ist das Array, auf das das (i+1)-te Element des Zeiger-Arrays zeigt,
also ein Zeiger auf das erste Element dieses Arrays.
((paZ+i) und *(paZ+i) liefern nicht die gleiche Adresse)
*(paZ+i)+j = paZ[i]+j
ist ein Zeiger auf das (j+1)-te Element des Arrays, auf das das (i+1)te Element des Zeiger-Arrays paZ zeigt.
*(*(paZ+i)+j) =
*(paZ[i]+j)
=
paZ [i][j]
=
(*(paZ+i))[j]
ist das (j+1)-te Element des Arrays, auf das das (i+1)-te
Element des Zeiger-Arrays paZ zeigt.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/2/5 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger - Arrays (2)
● Programmbeispiel :
Das obige Beispiel geht davon aus, dass die Elemente des Zeiger-Arrays alle auf Arrays gleicher Größe
verweisen. Dies bedingt die große Ähnlichkeit zu mehrdimensionalen Arrays.
Bei häufigen Anwendungen von Zeiger-Arrays besitzen die referenzierten Arrays aber unterschiedliche
Längen, wie z.B. verschieden lange Strings.
/*------------------------------------------------------------------------*/
/* Programm: zeigarr (Modul zeigarr_m.c)
*/
/*----------------------------------------------------------------------- */
/* Aehnlichkeit und Unterschied zwischen Zeiger-Arrays u. mehrdim. Arrays */
/* Elemente des Zeiger-Arrays zeigen auf Arrays unterschiedlicher Laenge */
/*------------------------------------------------------------------------*/
#include <stdio.h>
char caTagfeld[][11] = { "Montag",
/*
"Dienstag",
/*
"Mittw",
/*
"Donnerstag", /*
"Freitag",
/*
"Samstag",
"Sonntag"
};
Dies ist ein 2-dimensionales Array
Die zweite Dimension ist fest 11,
die erste wird durch die Initialis.
festgelegt mit 7. Fuer jeden String
werden genau 11 Zeichen reserviert
*/
*/
*/
*/
*/
char *paTagzeig[] =
Dies ist ein eindimensionales
Zeigerarray mit genau 7 Zeigern
auf Strings als Elemente.
Die Strings besitzen unterschiedliche Laengen
*/
*/
*/
*/
*/
{ "Montag",
/*
"Dienstag",
/*
"Mittw",
/*
"Donnerstag", /*
"Freitag",
/*
"Samstag",
"Sonntag"
};
int main (void)
{ int i,j;
char cZa, cZb;
while ( ((printf("\nZeile ? "), fflush(stdout),
scanf("%d",&i)) >0) && (i>=0) &&
((printf("Spalte ? "), fflush(stdout),
scanf("%d",&j)) >0) && (j>=0) )
{ cZa = caTagfeld[i][j];
/* Zugriff auf das 2-dim. Array
cZb = paTagzeig[i][j];
/* Zugriff ueber das Zeiger-Array
printf("caTagfeld [%2d][%2d] = %c (= %2x hex)\n",i,j,cZa,cZa);
printf("paTagzeig [%2d][%2d] = %c (= %2x hex)\n",i,j,cZb,cZb);
}
return 0;
*/
*/
}
/* --------------------------------------------------------------------------- */
Ergebnis eines Probelaufs :
Zeile ? 0
Spalte ? 4
caTagfeld [ 0][ 4] = a (= 61 hex)
paTagzeig [ 0][ 4] = a (= 61 hex)
Zeile ? 2
Spalte ? 10
caTagfeld [ 2][10] =
(= 0 hex)
paTagzeig [ 2][10] = e (= 65 hex)
Zeile ? -1
/* Wie laesst sich dieser Unterschied
/* erklaeren ?
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/3/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programm-Parameter in C
● C bietet die Möglichkeit, einem Programm beim Start Strings als Parameter zu übergeben.
( Programm-Parameter, Kommandozeilen-Parameter).
Beim Aufruf des Programms aus der Kommandozeile sind die Programm-Parameter nach dem
Programmnamen jeweils durch Blanks getrennt anzugeben.
● Den Zugriff zu den Programm-Parametern innerhalb der Funktion main() ermöglichen zwei
Funktionsparameter, mit denen main() vom Startup-Code aufgerufen wird.
Die korrespondierenden formalen Parameter in main() werden üblicherweise mit
argc
argv
(= argument count)
(= argument vector)
und
bezeichnet :
alternative Formulierung :
int main(int argc, char *argv[])
int main(int argc, char **argv)
● Im ersten Parameter argc wird die Anzahl der Programmparameter übergeben.
Dabei zählt der Programmname selbst immer als erster Parameter.
argc ist immer mindestens == 1. (Wert, wenn es keine Programm-Parameter gibt)
● Der zweite Parameter argv ist ein Pointer auf ein char-Pointer-Array, dessen Elemente auf
die einzelnen Programm-Parameter-Strings verweisen.
Dabei zeigt das erste Element immer auf den Programmnamen.
Abgeschlossen ist das Array mit dem NULL-Pointer.
argv[0]
argv[1]
argv[argc-1]
argv[argc]
● Beispiel :
ist ein Pointer auf den Programmnamen
ist ein Pointer auf den ersten echten Programm-Parameter
ist ein Pointer auf den letzten Programm-Parameter
ist der NULL-Pointer (
argv[argc] == NULL)
Demo-Programm echopar (zeilenweise Ausgabe der einzelnen Programmparameter)
Programmaufruf :
echopar Willkommen in der C-Welt
argc = 5
argv
argv[0]
"echopar"
argv[1]
"Willkommen"
argv[2]
"in"
argv[3]
"der"
argv[4]
argv[5]
"C-Welt"
NULL
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/3/2 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiele zu Programm-Parametern
/*
/*
/*
/*
/*
------------------------------------------------------------------------Programm echopar (Modul echopar_m.c)
------------------------------------------------------------------------Ausgabe der Programm-Parameter untereinander nach stdout
-------------------------------------------------------------------------
*/
*/
*/
*/
*/
#include <stdio.h>
int main(int argc, char *argv[])
/* argc
Anzahl der Programm-Parameter
*/
/* argv
Pointer-Array auf Programm-Parameter */
{ int i;
putchar('\n');
for (i=1; i<argc; i++)
printf("%s\n",argv[i]);
return 0;
}
/*
/*
/*
/*
/*
/*
/*
------------------------------------------------------------------------Programm echopar2 (Modul echopar2_m.c)
------------------------------------------------------------------------Ausgabe der Programm-Parameter untereinander nach stdout
Alternative Verwendung des Parameters argv :
als Pointer auf char-Pointer (genauer : auf ein Array von char-Pointern)
-------------------------------------------------------------------------
#include <stdio.h>
int main(int argc, char **argv)
/* argc
Anzahl der Programm-Parameter
/* argv
Pointer auf char-Pointer-Array
{ putchar('\n');
while (--argc>0)
printf("%s\n",*++argv);
return 0;
}
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger als Funktionsparameter und Funktionswert
● Zeiger als Funktionsparameter :
Funktionsparameter in C sind grundsätzlich Wert-Parameter.
Bei der Übergabe des aktuellen Parameters wird dessen Wert in den formalen Parameter kopiert.
Für Zeiger als Parameter bedeutet dies, das die durch den Parameter übergebene Adresse als lokale
Kopie in der Funktion zur Verfügung steht.
Über sie kann zu der durch sie referierten – ausserhalb der Funktion existierenden – Variable zugegriffen
und diese damit auch verändert werden.
Parameter existieren – wie lokale auto-Variable – nur, solange die Funktion durchlaufen wird.
Beispiel (s. Programm zeigerpar) :
int mach(int iR, int *piL)
{ iR
+= 5;
/* Verändert die lokale Variable iR, nicht den an dieser
/* Position übergebenen aktuellen Paramter (hier iB) !!!
*piL += 5;
/* Verändert den Wert der Variablen auf die die lokale
/* Variable piL zeigt - also iC in main() !!
piL
= &iR;
/* piL zeigt nun auf die lokale Variable iR
return *piL;
/* gibt den Wert zurück, den iR besitzt
}
/* iR und piL hören auf zu existieren!
*/
*/
*/
*/
*/
*/
*/
int main(void)
{ int iA=0, iB=2, iC=3;
.......
iA = mach (iB, &iC); /* Übergabe einer Kopie an die formalen Parameter iR */
/* und piL
*/
......
/* iA =
; iB =
; iC =
??
*/
}
● Konsequenzen :
◇ Als aktuelle Parameter übergebene Variable werden durch die in der Funktion ausgeführten Aktionen
nicht verändert, da nur ihr jeweiliger Wert als Kopie übergeben wird
( Wertübergabe).
◇ Soll eine außerhalb der Funktion existierende Variable durch die Funktion modifiziert werden, so muss
ein Zeiger auf diese Variable als Parameter übergeben werden.
Die Übergabe eines Zeigers (== Adresse !) als Parameter wirkt wie eine Referenzübergabe für die
Variable.
◇ Variable eines zusammengesetzten Datentyps, sollten im allgemeinen stets mittels eines Zeigers übergeben werden (schnellere Parameterübergabe, Einsparung von Speicherplatz !)
Gegebenenfalls ist von Veränderungen einzelner Komponenten durch Anweisungen innerhalb der
Funktion abzusehen.
◇ Arrays lassen sich nur per Zeiger übergeben (Array-Name == Zeiger-Konstante !).
● Zeiger als Funktionswert (Rückgabewert ) :
Der Funktionswert kann ebenfalls von einem Zeigertyp sein:
int *tuwas(int *, int *); /* Funktionswert ist ein Zeiger auf ein int-Objekt */
Es ist dabei besonders darauf zu achten, dass der Rückgabewert auf noch existierende Variable verweist.
Innerhalb der Funktion verwendete lokale auto-Variable dürfen deshalb nicht referiert werden !
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/2 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger als Funktionsparameter und Funktionswert – Beispiele 1
/*------------------------------------------------------------------------- */
/* Programm: zeigarr2 (Modul zeigarr2_m.c)
*/
/*--------------------------------------------------------------------------*/
/* Modifikation des Programms zeigarr :
*/
/* Aehnlichkeit und Unterschied zwischen Zeiger-Arrays u. mehrdim. Arrays
*/
/* Elemente des Zeiger-Arrays zeigen auf Arrays unterschiedlicher Laenge
*/
/* zusätzlich Beispiel für die Verwendung von Pointer als Funktionsparameter*/
/*--------------------------------------------------------------------------*/
#include <stdio.h>
char caTagfeld[][11]= { "Montag",
/*
"Dienstag",
/*
"Mittw",
/*
"Donnerstag", /*
"Freitag",
/*
"Samstag",
"Sonntag"
};
Dies ist ein 2-dimensionales Array
Die zweite Dimension ist fest 11,
die erste wird durch die Initialis.
festgelegt mit 7. Für jeden String
werden genau 11 Zeichen reserviert
*/
*/
*/
*/
*/
char *paTagzeig[]
Dies ist ein eindimensionales
Zeigerarray mit genau 7 Zeigern auf
Strings als Elemente.
Die Strings besitzen unterschiedliche Laengen
*/
*/
*/
*/
*/
= { "Montag",
/*
"Dienstag",
/*
"Mittw",
/*
"Donnerstag", /*
"Freitag",
/*
"Samstag",
"Sonntag"
};
int iIndxLesen(int *iI, int *iJ)
{ int iRet = 1;
printf("\nZeile ? "); fflush(stdout);
if ((scanf("%d",iI)<=0) || (*iI < 0))
iRet = -1;
else
{ printf("Spalte ? "); fflush(stdout);
if ((scanf("%d",iJ)<=0) || (*iJ < 0))
iRet= -1;
}
return iRet;
}
int main (void)
{ int iRet = 1;
int i,j;
char cZa, cZb;
while (iIndxLesen(&i, &j)>= 0)
{ cZa = caTagfeld[i][j];
/*
cZb = paTagzeig[i][j];
/*
printf("caTagfeld [%2d][%2d] =
printf("paTagzeig [%2d][%2d] =
}
return 0;
}
Zugriff auf das 2-dimensionale char-Array
*/
Zugriff über das 1-dimensionale Zeiger-Array */
%c (= %2x hex)\n",i,j,cZa,cZa);
%c (= %2x hex)\n",i,j,cZb,cZb);
/*------------------------------------------------------------------------------*/
Ergebnis eines Probelaufs:
Zeile ? 0
Spalte ? 4
caTagfeld [ 0][ 4] = a (= 61 hex)
paTagzeig [ 0][ 4] = a (= 61 hex)
Zeile ? 2
Spalte ? 10
caTagfeld [ 2][10] =
(= 0 hex)
paTagzeig [ 2][10] = e (= 65 hex)
Zeile ? -1
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/3 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger als Funktionsparameter und Funktionswert – Beispiele 2
/*------------------------------------------------------------------------*/
/* Funktion: tausch
*/
/*------------------------------------------------------------------------*/
/* Zeiger als Parameter von Funktionen
*/
/* hier: Vertauschen zweier int-Werte
*/
/*------------------------------------------------------------------------*/
void tausch(int * piY, int *piZ)
{ int iZw = *piY;
/* Wert 1 merken
*/
*piY = *piZ;
/* Wert 1 mit Wert 2 überschreiben */
*piZ = iZw;
/* Wert 2 ist gemerkter Wert 1
*/
}
/*------------------------------------------------------------------------*/
/* Programm: zeigerfunc (Modul zeigerfunc_m.c)
*/
/*------------------------------------------------------------------------*/
/* Zeiger als Funktionswert
*/
/* hier: Ermitteln des maximalen Zahlenwertes in einem double-Array
*/
/* Rückgabe: Zeiger auf das größte Element
*/
/*------------------------------------------------------------------------*/
#include <stdio.h>
#define UMFANG 4
double *pdMaximum (double adF[],int iU)
{ double dMax = adF[0];
/* Merkvariable für den Maximalwert vorbelegen */
int iM=0;
/* Index des größten Elementes
*/
int i;
for(i=1;i<iU;i++)
{ if(dMax < adF[i])
{ dMax = adF[i];
/* Aktuelles Maximum
*/
iM = i;
/* und sein Index
*/
}
}
return &adF[iM];
/* Adresse des Maximalwertes
*/
}
int main(void)
{ int iA;
double adW[UMFANG] = {3.4,7.6,12.2,3.0};
printf("\nMaximalwert des Arrays: %10.3f", *pdMaximum(adW,UMFANG));
*pdMaximum(adW,4) = 0;
/* So kann auch ein Funktionsaufruf auf der
*/
/* linken Seite einer Zuweisung sinnvoll sein! */
for (iA=0;iA<UMFANG;iA++)
{ printf("\n Element %d: %10.3f",iA,adW[iA]);
}
return 0;
}
/*------------------------------------------------------------------------*/
Probelauf von zeigerfunc :
Maximalwert des Arrays:
Element
Element
Element
Element
0:
1:
2:
3:
3.400
7.600
0.000
3.000
12.200
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger auf Funktionen
• Eine Zeigervariable kann als Wert auch die Anfangsadresse einer Funktion besitzen
⇒
Zeiger auf Funktionen
Es ist möglich:
- Zeigervariable, die auf Funktionen zeigen, zu vereinbaren
- Funktionszeigervariablen Werte zuzuweisen
- Arrays von Funktionszeigern zu definieren
- Zeiger auf Funktionen als Parameter zu übergeben
- Zeiger auf Funktionen als Funktionswert zurückzugeben.
• Vereinbarung von Funktionszeigervariablen
◇ anzugeben sind :
▻ Typ des Funktionswerts der Objekt-Funktion
▻ Typ und Anzahl der Parameter der Objekt-Funktion
◇ Beispiel:
double (*pfFunc)(int) /* pfFunc ist ein Zeiger auf eine Funktion, die einen
int-Parameter erwartet und einen double-Wert
zurückliefert */
ACHTUNG: Die runden Klammern (*pfFunc) sind zwingend erforderlich – vgl. Prioritäten!
• Wertzuweisung an eine Funktionszeigervariable
◇ Wie bei Arrays, so wird auch bei Funktionen der Name allein als Zeiger auf die Funktion interpretiert
(keinen &-Operator anwenden, Funktionsname ist Adresskonstante !).
◇ Beispiel: double dF1(int);
/* Deklaration der Funktion dF1(..)
double (*pfFunc)(int); /* Definition der Funktionszeigervariablen pfFunc
pfFunc = dF1;
/* Zuweisung der Adresse der Funktion dF1(..)
/* als Wert an die Variable pfFunc
*/
*/
*/
*/
• Aufruf einer Funktion über einen Funktionszeiger
◇ Der Funktionszeiger ist mittels * zu dereferenzieren und mit einer aktuellen Parameterliste auszustatten.
◇ Beispiel: dWert = (*pfFunc)(2*i); /* Aufruf der durch pfFunc referierten Funktion */
• Funktionszeiger als Funktionswert
◇ Beispiel: int (* pGift (char *))( int); /* extrem fortgeschrittene Programmierung!!! */
pGift ist eine Funktion, die einen char-Zeiger als Parameter erwartet und einen Zeiger
auf eine Funktion zurückliefert, die ihrerseits einen int-Parameter erwartet und deren
Rückgabewert den Typ int besitzt.
• Anwendungen :
◇ Realisierung allgemeiner anwendbare Funktionen durch Übergabe von
▻ Funktionszeigern als Parameter ( z.B. Anwendung der Integration auf verschiedene Funktionen)
▻ Zeiger auf ein Array von Funktionszeigern (Realisierung von "Menüs")
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/5 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger auf Funktionen - Beispiel
∙ Beispiel :Simpson-Integration
◇ Ermittlung des Integrals einer Funktion f(x) zwischen x0 und x n
◇ Annäherung des Kurvenverlaufs von f(x) durch Parabeln, die jeweils durch 3 aufeinanderfolgende
Stützstellen verlaufen.
Stützstellen :
x0 ... x n (n gerade!)
Abstand der Stützstellen :
h = ( x n − x0 ) / n
(= Schrittweite)
◇ Der Flächeninhalt zwischen f(x) und der x-Achse (= Integral) ergibt sich näherungsweise zu:
F = h / 3 * ( f ( x0 ) + 2 *
n / 2 −1
∑
k =1
n/2
f ( x2 k ) + 4 * ∑ f ( x2 k −1 ) + f ( xn )
)
k =1
◇ Iterative Verbesserung der Genauigkeit dieses Näherungswertes durch Erhöhung der Anzahl n der
Stützstellen.
Abbruch der Iteration, wenn ein vorgegebenes Genauigkeitskriterium erreicht ist.
◇ Struktogramm :
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/6 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger auf Funktionen – Programmcode des Beispiels
/* Headerdatei simpson.h
-
Vereinbarungen
für das Programm simpint */
#ifndef SIMPSON_H_
#define SIMPSON_H_
#define
#define
#define
#define
EPSILON
ANZFUNC
Xu -1.
Xo
1.
1.e-8
3
double dKreis (double);
double dPoly (double);
double d3Eck (double);
/*
/*
/*
/*
Genauigkeit */
Anz. der zu integrierenden Funktionen */
Default fuer untere Integrationsgrenze */
Default fuer obere Integrationsgrenze */
/* zu integrierende Funktionen
*/
double dSimpson (double(*)(double), double, double); /* Integrationsfunktion */
#endif /* SIMPSON_H_ */
/*
/*
/*
/*
/*
/*
/*
/*
/*
-----------------------------------------------------------------------Modul simpson.c
Integration von Funktionen der Form y = f(x) mittels Simpsonregel
-----------------------------------------------------------------------Ein:
pdFunc - Zeiger auf Funktion der Form y = f(x)
dX0
- Startwert (erste Stuetzstelle)
dXn
- Endwert (letzte Stuetzstelle)
Aus:
Flaeche ueber der x-Achse
------------------------------------------------------------------------
#include <math.h>
#include "simpson.h"
double dSimpson( double (*pdFunc)(double), double dX0, double
{ long int i;
double
dFl = 0.;
/* vorheriger Integralwert
double
dFa = 0.;
/* aktueller Integralwert
double
dH;
/* Schrittweite
double
dX;
/* X-Wert
double
dY;
/* Funktionswert
double
dg;
/* Gewicht
double
dErr;
/* relativer Fehler
long int n = 2;
/* Intervallzahl
dXn )
*/
*/
*/
*/
*/
*/
*/
*/
/* Schleife ueber jeweils verdoppelte Intervallanzahl
*/
/* Abbruch, wenn Genauigkeit EPSILON erreicht ist
*/
do
{ dH = (dXn –dX0)/n;
/* Schrittweite bestimmen
*/
dFa = (*pdFunc)(dX0) + (*pdFunc)(dXn);
for (i=1; i < n; i++)
/* Flaeche der akt. Intervallzahl */
{ dX
= dX0 + dH*i;
dY
= (*pdFunc)(dX);
if (i%2 == 1)
dg = 4.;
else
dg = 2.;
dFa += dg*dY;
}
dFa *= dH/3;
dErr = fabs((dFa -dFl)/dFa);
n*=2;
/* Verdoppeln der Intervallzahl */
dFl = dFa;
} while ( dErr > EPSILON);
return dFa;
}
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/04/4/7 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Zeiger auf Funktionen – Programmcode des Beispiels, Forts.
/* Modul simpint_m.c
*/
/* Modul mit main()-Funktion und Beispielfunktionen für das Programm simpint*/
/* Programmbeispiel: Simpson-Integration von Funktionen der Form y = f(x)
*/
#include <stdio.h>
#include <math.h>
#include "simpson.h"
/* ------------------------------------------------------------------------ */
/* oberer Halbkreis mit Radius 1
Flaeche ergibt Pi/2.
*/
double dKreis (double dX)
{ double dR = 1.;
double dY = 0.0;
if (dX <= 1. && dX >=-1
dY = sqrt (dR*dR - dX*dX);
return dY;
}
/* ------------------------------------------------------------------------ */
/* Dreieck mit (-1,0) (0,1) (1,0)
Flaeche ergibt 1.0
*/
double d3Eck (double dX)
{ double dY = 0.0;
if (dX <= 1. && dX >= -1.)
if (dX >= 0) dY = (1 - dX);
else dY = 1 + dX;
return dY;
}
/* ------------------------------------------------------------------------ */
/* Polynom 2. Grades */
double dPoly(double dX)
{ return (dX*dX-6*dX+1);
}
/* ------------------------------------------------------------------------ */
int main(void)
{ double dXo = XO;
double dXu = XU;
double dF;
/* hier : feste Integrationsgrenzen */
/*Funktionszeiger-Array*/
double (*pfaA[ANZFUNC])(double) = {dKreis, d3Eck, dPoly};
int i=1, bBed;
do
{ printf(" Auswahl: ");
scanf("%d",&i);
bBed = i>0 && i <= ANZFUNC;
/* Formulierung der Abbruchbedingung */
if(bBed)
{ dF = dSimpson (pfaA[i-1],dXu,dXo);
printf (" Xu: %8.4f, Xo: %8.4f, F: %12.8f \n", dXu,dXo,dF);
}
} while(bBed);
return 0;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/05/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Strukturen in C (1)
● Aufbau von Strukturen
◇ Strukturen (structures) in C sind selbstdefinierte zusammengesetzte Datentypen.
Sie bestehen aus einer festen Anzahl von Komponenten, die von unterschiedlichen Typen
(auch wieder zusammengesetzte Typen) sein können.
mehrere Daten der unterschiedlichsten Art lassen sich somit in einer einzigen Variablen
zusammenfassen und damit unter einem einzigen Namen ansprechen.
Aufbau von sehr effektiv handhabbaren problemangepassten Datenstrukturen.
◇ Beispiel :
Zusammenfassung relevanter Personaldaten, wie
- Name
char name[20];
- Vorname
char vname[20];
- Geburtsdatum int jahr; char monat[4]; short tag;
- Familienstand char famstand;
◇ Die einzelnen Komponenten (Felder) einer Struktur werden nicht – wie bei Arrays – durch einen
Index sondern durch einen Namen angesprochen.
● Vereinbarung von Strukturen
◇ Auflistung der einzelnen Komponenten (Felder) unter Angabe von Typ und Namen.
◇ Eine Strukturvereinbarung kann in drei Formen geschehen:
- reine Strukturtypdefinition (ohne Angabe einer Variablenbezeichnung)
Der dabei eingeführte Typname (structure tag) kann im weiteren für die Vereinbarung von
Strukturvariablen verwendet werden.
- reine Strukturvariablenvereinbarung (ohne Angabe eines Strukturnamens)
Definition eines namenlosen Strukturtyps.
- gleichzeitige Strukturtypdefinition und Strukturvariablenvereinbarung
◇ Syntax der Strukturvereinbarung :
struct
Strukturname
{
Komponentenangabe
Variablenbezeichner
,
Komponentenangabe:
Datentypangabe
Komponentenbezeichner
,
:
;
}
;
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/05/2 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Strukturen in C (2)
● Variablenvereinbarung mit zuvor definiertem Strukturnamen:
struct
Strukturname
Variablenbezeichner
;
,
"struct Strukturname" ist die Datentypangabe !
● Beispiele :
/*Definition eines Structurtypnamens */
struct datum
{ int tag;
char monat [4];
int jahr;
};
/* Variablenvereinbarung unter Verwendung des obigen Strukturtyps */
struct datum geb_datum;
/* Variablenvereinbarung eines "namenlosen" Struturtyps
*/
struct
{ int tag;
char monat [4];
int jahr;
} geb_datum;
/* Definition eines Strukturtypnamens und Vereinbarung einer Variablen
dieses Typs */
struct datum
{ int tag;
char monat [4];
int jahr;
} geb_datum;
/* Strukturtyp mit einer Struktur als Komponente */
struct person
{ char name[20],
vname[20];
struct
{ int tag,
char monat[4];
int jahr;
} geb_datum;
char famstand;
} student, diplomand;
/* oder : Verwendung des Strukturtypnamens
struct datum */
struct person
{ char name[20],
vname[20];
struct datum geb_datum;
char famstand;
} student, diplomand;
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/05/3 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Operationen mit Strukturen
• Anwendung des Adressoperators &
Auf Strukturvariable lässt sich, ganz analog zu einfachen Variablen, der Adressoperator & anwenden.
Die so gebildete Adresse kann einer Zeigervariablen zugewiesen werden, die als Zeigervariable dieses
Strukturtyps vereinbart sein muss.
Beispiel:
struct person
/* Definition des Strukturtyps */
{ char name[20],
vname[20];
struct datum geburtstag; /* Kompon. ist selbst strukt. Datentyp */
char famstand;
}
struct person sStudent, sDiplomand, *psPersZeig;
...
psPersZeig = &sStudent;
• Zugriff zu Strukturkomponenten
◇ Direkter Zugriff zur Komponente mittels Element-Operator "."
Beispiel :
...
scanf("%s", sStudent.name);
sStudent.vorname[0] = 'F';
sStudent.geburtstag.jahr = 2001;
◇ Zugriff zur Komponente eines Struktur-Zeiger-Objektes mittels Objektelement-Operator "->".
Es gilt folgende Äquivalenz :
sptr->komp
entspricht
(*sptr).komp
Beispiel :
....
scanf("%s", psPersZeig->name);
psPersZeig->vorname[0] = 'F';
psPersZeig->geburtstag.jahr = 2001;
◇ Die Operatoren "." und "->" haben höchste Priorität (gleiche Priorität wie "()" und "[]"),
ihre Assoziativität ist von links nach rechts.
◇ Strukturkomponenten können genauso behandelt werden wie einfache Variable ihres Typs.
• Wertzuweisung an Strukturvariable
Zwischen Strukturvariablen ist eine geschlossene Zuweisung möglich:
Beispiel :
sDiplomand = sStudent;
• Initialisierung von Strukturvariablen
ist durch eine Liste von konstanten Ausdrücken – entsprechend den Komponenten – möglich.
Beispiel:
struct datum geburtstag = {23, "Mai", 1983 };
• Strukturen als Funktionsparameter und Rückgabewerte von Funktionen
Strukturen sind als Funktionsparameter und Rückgabewerte von Funktionen zulässig.
ACHTUNG : Bei größeren Strukturen ist es meist effizienter Zeiger auf Strukturen als Parameter
zu verwenden, um umfangreiches Kopieren beim Funktionsaufruf zu vermeiden!
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/05/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel zu Strukturen
/*-----------------------------------------------------------------------*/
/* Programm: complmult (Modul complmult_m.c)
*/
/*-----------------------------------------------------------------------*/
/* Darstellung komplexer Zahlen durch strukturierten Datentyp
*/
/* Komplexe Rechnung: Multiplikation
*/
/*-----------------------------------------------------------------------*/
#include <stdio.h>
/* Typdefinition zur Darstellung komplexer Zahlen */
struct complex
{ double re;
double im;
};
struct complex liesComplex( void )
{ struct complex sL;
printf ("\n Realteil:
"); scanf ("%lf",&sL.re);
printf ( " Imaginaerteil: "); scanf ("%lf",&sL.im);
return sL;
}
void ausComplex (char * text, struct complex sWert)
/* Form: (a + j*b) */
{ char cSign;
if ( sWert.im >= 0 ) cSign = '+';
else { cSign = '-'; sWert.im *= -1; }
printf("%s(%.3f %c j*%.3f)",text, sWert.re, cSign, sWert.im);
}
struct complex multComplex(struct complex sV1,struct complex sV2)
{ struct complex sErg;
sErg.re = sV1.re*sV2.re - sV1.im*sV2.im;
sErg.im = sV1.re*sV2.im + sV1.im*sV2.re;
return sErg;
}
int main (void)
{ struct complex scWert1, scWert2, scWert3;
char cZeich;
do
{ printf("Erste Zahl:"); scWert1 = liesComplex();
printf("Zweite Zahl:"); scWert2 = liesComplex();
scWert3 = multComplex(scWert1,scWert2);
ausComplex("\n",scWert1);
ausComplex(" * ",scWert2);
ausComplex(" = ",scWert3);
printf("\n weiter? (j/n) ");
do cZeich = getchar(); while (cZeich == '\n');
} while(cZeich == 'j' || cZeich == 'J');
return 0;
}
Probelauf :
Erste Zahl:
Realteil:
Imaginaerteil:
Zweite Zahl:
Realteil:
Imaginaerteil:
2.5
1.0
3.0
-1.5
(2.500 + j*1.000) * (3.000 - j*1.500) = (9.000 - j*0.750)
weiter? (j/n) n
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/05/5 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel zu Strukturen als Arraykomponenten
/*
/*
/*
/*
/*
------------------------------------------------------------------------------Programm telbuch : Suche Rufnummer aus einem Telefonbuch
------------------------------------------------------------------------------Beispiel zu Arrays deren Komponenten von einem strukturierten Datentyp sind
-------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#define MAXANZ 1000
#define MAXNL 20
#define MAXAL 40
struct
{ char
char
char
long
};
eintrag
name[MAXNL];
vorname[MAXNL];
anschrift[MAXAL];
nummer;
*/
*/
*/
*/
*/
/* maximale Anzahl von Eintraegen
*/
/* maximale Laenge von Namen u. Vornamen */
/* maximale Laenge einer Anschrift
*/
/* hier nur aus Platzgruenden keine symbol. Konst. */
/* fuer die Array-Groessen definiert
*/
int readBuch(struct eintrag * ,int ); /* Holt aktuelles Telefonbuch vom Massenspeicher - wird spaeter realisiert
Rückgabe : Anzahl gelesener Eintraege */
long lSuchNummer(struct eintrag * telbuch, int iMax)
{ char suchname[MAXNL], suchvorn[MAXNL];
int i, gefunden;
long lRet = -2L;
printf("\"Nachname Vorname\" eingeben: ");
if (scanf("%s%s",suchname,suchvorn)==2)
{ i=gefunden=0;
while(i<iMax && !gefunden)
{ gefunden = (!strcmp(telbuch[i].name, suchname)) &&
(!strcmp(telbuch[i].vorname, suchvorn));
i++;
}
if (!gefunden)
lRet = -1L;
else
lRet = telbuch[i-1].nummer;
/* Anwendung des Element-Operators */
/* oder Anwendung des Objektelement-Operators:
lRet = (telbuch+i-1)->nummer; */
}
return lRet;
}
int main (void)
{ long lNummer;
struct eintrag telefonbuch[MAXANZ];
int aktAnz = readBuch(telefonbuch, MAXANZ); /* Hole aktuelles Telefonbuch vom Massenspeicher */
while ((lNummer = lSuchNummer(telefonbuch,aktAnz)) != -2L)
{ if (lNummer >= 0)
printf("Nummer: %ld\n",lNummer);
else
printf("Nummer nicht gefunden\n");
}
return 0;
}
Hinweise zur Weiterarbeit:
1) Modifizieren Sie "lSuchNummer()" so, dass nicht die Nummer sondern der Index des
Eintrags zurückgeliefert wird.
2) Gestalten Sie nun die Ausgabe so, dass Name,Vorname, Adresse und Rufnummer
ausgegeben werden!
3) Erweitern Sie "eintrag" um die Komponente "beruf" - welche Konsequenzen hat diese
Maßnahme für den Rest des Programmes?
4) Nehmen Sie den "beruf" in die Ausgabe von 2) auf!
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/06/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Definition von Datentypnamen mittels typedef
● "Typdefinition" mittels typedef
◇ C erlaubt die Einführung eigener Typnamen mittels des Schlüsselwortes "typedef".
So eingeführte Bezeichner können dann in weiteren Vereinbarungen alleine als Typangabe (wie die
Namen der Standardtypen) verwendet werden.
⇒
Typdefinition mittels typedef
Anmerkung : Es wird kein neuer Typ, sondern nur ein neuer Name für einen existierenden Typ
definiert.
◇ Syntax der Typdefinition
typedef
Typangabe
neuer Typbezeichner
;
,
◇ Eine Typdefinition mittels typedef darf überall dort stehen, wo eine Vereinbarung stehen darf
also außerhalb jeder Funktion, im Vereinbarungsteil des Funktionsrumpfes und im Vereinbarungsteil
eines Blocks.
◇ Die Typdefinition weist eine formale Ähnlichkeit mit der Variablenvereinbarung auf:
▻ Die neue Typbezeichnung steht dort, wo in der Variablenvereinbarung der Bezeichner für die Variable
steht.
▻ Das Schlüsselwort typedef steht dort, wo in der Variablenvereinbarung die Speicherklassenangabe
steht.
▻ Aus diesem Grund wird typedef syntaktisch auch als Speicherklassen-Spezifizierer bezeichnet
(obwohl es eine ganz andere Bedeutung und Wirkung besitzt).
◇ Mittels typedef ist die Definition neuer Namen für beliebige existierende Typen, auch für Zeigertypen (inklusive Zeiger auf Funktionen) möglich.
● Anwendung
▻ Parametrisierung eines Programmes gegen Portabilitätsprobleme durch Definition von
Typnamen für maschinenabhängige Datentypen. Diese werden meist in eigenen Header-Dateien
aufgeführt.
Beispielsweise sind in der Headerdatei <stdint.h> (ANSI-C 99) Ganzzahltypen mit exakter Größe
definiert, u.a. (hier Visual-C) :
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int
uint32_t;
Bei der Portierung solcher Programme sind dann lediglich diese Header-Dateien entsprechend
anzupassen.
In mehreren Headerdateien der ANSI-C-Standardbibliothek findet sich z.B. für den "natürlichen
unsigned-Typ" die folgende Definition :
typedef unsigned int size_t;
▻ Erhöhung der Übersichtlichkeit und Lesbarkeit eines Quelltextes durch die Wahl von
problemangepassten und treffenden Typnamen selbstdokumentierend!
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/06/2 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiele zur Definition von Datentypnamen mittels typedef
● Statt
struct
{ int
char
int
};
struct
datum
tag;
monat[4];
jahr;
/* Typdefinition */
datum geb_datum;
/* Variablenvereinbarung */
kann man auch formulieren :
typedef struct
{ int tag;
char monat[4];
int jahr;
} Datum;
Datum geb_tag;
/* Typdefinition */
/* Typname !! */
/* Variablenvereinbarung */
● Die Anwendung von typedef ist nicht auf struct-Typen begrenzt, sondern ist für alle Datentypen
zulässig
Beispiele :
typedef char Wort[30];
typedef float Real;
/* Typdefinition */
/* Typdefinition */
Wort name;
Real a;
/* Variablenvereinbarung */
/* Variablenvereinbarung */
● Auch die Definition von Zeigertypen mittels typedef ist möglich (einschließlich Zeiger auf Funktionen)
Beispiele :
typedef struct
{ double re;
double im;
} Complex;
typedef Complex *ComplPtr;
/* Definition des Typs "ComplPtr" als
/* Zeiger auf "Complex"
*/
*/
typedef double (*Pfd)(char*, char*);
/* nur zur Demonstration:
*/
/* Definition des Typs "Pfd" als Zeiger auf*/
/* eine Funktion, die zwei char-Zeiger als */
/* Parameter besitzt und einen double-Wert */
/* zurückliefert
*/
Pfd myFktPtr;
/* Vereinbarung einer Variablen dieses Typs*/
Aufgabe :
Formulieren Sie den Datentyp Person mit einer Komponente des obigen Typs Datum so, dass folgende
Variablenvereinbarung möglich ist:
Person sStudent = { "Moor", "Moritz", "Muenchen",
{ 17,"Jun",1995 },
'l'
};
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/07/1 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Aufzählungstypen und Aufzählungskonstante
● Aufzählungskonstante (enumeration constants)
◇ Aufzählungskonstante ermöglichen die Definition von symbolische Ganzzahl-Konstanten und bilden
damit in vielen Fällen eine Alternative zur Festlegung symbolischer Konstanter mit Hilfe der PräprozessorDirektive #define.
◇ Aufzählungskonstante sind immer vom Typ int und werden immer gleichzeitig mit der Definition
eines Aufzählungstyps festgelegt.
● Aufzählungstypen
◇ Syntax der Aufzählungstyp-Definition:
enum
Typbezeichner
Aufzählungsliste
{
}
;
Aufzählungsliste:
Konstantenname
=
konstanter Ausdruck
,
◇ Werden nur Konstantennamen in der Aufzählungsliste angegeben, so wird dem ersten Namen der
Wert 0, dem zweiten Namen der Wert 1 usw. zugeordnet.
◇ Wird ein Konstantenname mittels "=" mit einem konstanten Ausdruck gleichgesetzt, so wird ihm
der Wert dieses Ausdrucks zugeordnet. Die nachfolgenden Konstantennamen erhalten dann in der
Reihenfolge ihrer Auflistung den jeweils nächsten int-Wert zugeordnet, wenn sie nicht selbst explizit
mit einem "=" in ihrem Wert festgelegt werden.
Aufzählungstypen bilden einen Teilbereich von int.
◇ Der Aufzählungstyp kann einen Typnamen erhalten. Damit lassen sich auch Variable dieses Typs
definieren Aufzählungsvariable.
Diese werden die in C wie Variable des Typs int behandelt.
◇ Wird kein Typname angegeben, so handelt es sich um die Definition eines namenlosen Typs.
⇒ nur Definition der Aufzählungskonstanten !
● Beispiele:
enum { MON, DIE, MIT, DON, FRE, SAM, SON };
enum Monat { JAN = 1, FEB, MAR, APR, MAI, JUN,
JUL, AUG, SEP, OKT, NOV, DEZ };
enum Monat myMonat
typedef enum Monat Month
Month wonnemonat = MAI;
enum Bool { FALSE, TRUE };
/* nur Aufzählungskonstante
*/
/* Typdefinit.: enum Monat */
/* Definition der Variablen myMonat */
/* Definition des neuen Typnamnes Month */
/* Definition und Initialisierung einer Variablen*/
/* Möglichkeit einen log. Datentyp einzuführen */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/07/2 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel : Auswertung arithmetischer Ausdrücke
● Beschreibung arithmetischer Ausdrücke
◇ Beispiel :
x = 1.7+5*(4.5-3)/((2.6+1.3)*3)-2.1
◇ Ein arithmetischer Ausdruck wie der obige kann allgemein beschrieben werden, entweder mit
den schon bekannten Syntaxdiagrammen (graphische Notation) , oder mittels einer textuellen
Notation, der sogenannten Backus-Naur-Form. :
Ausdruck
Term
Faktor
Zahl
Ziffer
::=
::=
::=
::=
::=
Term
{ + Term | - Term }
Faktor { * Faktor | / Faktor }
Zahl | ( Ausdruck )
[+|-]Ziffer {Ziffer}[.{Ziffer}]
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Dabei bedeutet:
::=
"wird definiert als"
{...} Was in geschweiften Klammern steht, darf beliebig oft wiederholt werden (bzw. auch fehlen).
[...] Was in eckigen Klammern steht, darf auch fehlen.
|
Alternative Auswahl, z. B. kann "Faktor" entweder eine Zahl oder ein Ausdruck in
runden Klammern sein.
Die Ziffern 0...9 sowie die Zeichen '(', ')', '+', '-', '*', '/' sind sogenannte
Terminalsymbole.
● Aufgabe:
Auswertung arithmetischer Ausdrücke der obigen Form mittels eines Formelinterpreters.
Es gilt – wie allgemein in der Arithmetik vorgeschrieben – auch hier "Punkt vor Strich".
● Zur Programmierung:
◇ Die obigen Definitionen – für Ausdruck, Term, Faktor und Zahl – werden nachfolgend in einer Art
"Pseudocode" formuliert, der der Programmiersprache C nachempfunden ist..
Dabei wird immer nur das jeweils nächste Eingabezeichen betrachtet, das in einer global definierten
Variablen c abgelegt wird.
Ausdruck:
{
wert = Term;
solange (c in ['+','-'])
{ falls (c == '+') wert = wert + Term;
falls (c == '-') Wert = wert - Term;
}
Ausdruck = wert;
}
Term:
{
wert = Faktor;
solange (c in ['*','/'])
{ falls (c == '*') wert = wert * Faktor;
falls (c == '/') Wert = wert / Faktor;
}
Term = wert;
}
Faktor:
{
Lies ein Zeichen c von der Eingabe;
wenn (c in ['0'..'9']) wert = Zahl;
wenn (c == '(')
{
wert = Ausdruck;
Lies schließende Klammer;
}
Faktor = Wert;
}
Zahl:
{
Lies eine Zahl mit Vorzeichen
zeichenweise ein;
}
◇ Das jeweils nächste – von Blank (' ') verschiedene – in Zeichen wird durch eine Funktion int lies()
in die globale Variable c abgelegt. Je nach Definition dieser Funktion können die den zu interpretierenden
Ausdruck bildenden Zeichen verschiedenen Eingabequellen entnommen werden (z.B. aus der StandardEingabe oder aus einem String)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/07/3 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel : Auswertung arithmetischer Ausdrücke (Forts. 1)
● Programm:
// C-Headerdatei arithmausdr.h
#ifndef ARITHMAUSDR_H_
#define ARITHMAUSDR_H_
enum Bool_t { FALSE, TRUE };
/* logischer Datentyp
float
float
float
float
int
/* Funktions-Deklarationen */
ausdruck(void);
term(void);
faktor(void);
zahl(void);
lies(void);
*/
#endif /* ARITHMAUSDR_H_ */
// C-Quelldatei arithmausdr_m.c
// Modul mit main()-Funktion fuer das Programm arithmausdr
// Programm zur Auswertung arithmetischer Ausdrücke (Formelinterpreter)
#include <stdio.h>
#include <stdlib.h>
#include "arithmausdr.h"
/* Definition globaler Variabler - Speicherklasse extern */
int c;
/* eingelesenes Zeichen
global! */
enum Bool_t fehler = FALSE;
/* globale Fehlervariable
*/
int cfehl;
/* fehlerhaftes Zeichen
global ! */
int main(void)
{ float wert;
/* Ergebniswert
*/
do
{ printf("Eingabe : "); fflush(stdout);
/* Eingabeaufforderung
*/
wert = ausdruck();
/* Einlesen und Berechnen
*/
if((c != '\n') && (c!=EOF))
{ fehler = TRUE;
cfehl = c;
while(((c=lies())!='\n') && (c!=EOF));
}
if (c!=EOF)
{ if (fehler)
{ printf("Syntaxfehler : Char:%02X\n\n",cfehl); fflush(stdout);
fehler = FALSE;
}
else
printf("Ergebnis: %0.5f\n\n",wert); fflush(stdout); /* 5 Nachpunktstellen */
}
} while (c != EOF);
/* Ende mit Ctrl-Z bzw Ctrl-D */
return EXIT_SUCCESS;
}
int lies(void)
/* Einlesen eines Zeichens, Leerzeichen uebergehen */
{ int cA;
do
{ cA = getchar();
} while(cA == ' ');
return(cA);
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/07/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel :Auswertung arithmetischer Ausdrücke (Forts 2.)
// C-Quelldatei arithmausdr_func.c
// Modul mit Funktionen fuer das Programm arithmausdr
#include "arithmausdr.h"
/* Deklaration externer Variabler */
extern int c;
/* eingelesenes Zeichen
global! */
extern enum Bool_t fehler;
/* globale Fehlervariable
*/
extern int cfehl;
/* fehlerhaftes Zeichen
global ! */
float ausdruck(void)
/* Ausdruck ::= Term { +Term || -Term }
{
float wert;
wert = term();
while (c == '+' || c == '-')
{ if (c == '+') wert = wert + term();
else wert = wert - term();
}
return wert;
}
*/
float term(void)
/* Term ::= Faktor { *Faktor || /Faktor } */
{
float wert;
wert = faktor();
while (c == '*' || c == '/')
{ if (c == '*') wert = wert * faktor();
else wert = wert / faktor();
}
return wert ;
}
float faktor(void)
/* Faktor :== Zahl || ( Ausdruck )
{
float wert=0.0;
c = lies();
/* erst hier wird gelesen !!!
if (('0' <= c && c <= '9') || (c == '-') || (c == '+'))
wert = zahl();
else if (c == '(')
/* rekursiver Aufruf !!!
{ wert = ausdruck();
if (c!=')')
/* naechstes Zeichen muss ')' sein
{ fehler = TRUE;
/* falls nicht --> Fehler
cfehl = c;
}
else
c = lies();
}
else
/* weder Zahl noch '(' --> Fehler
{ fehler = TRUE;
cfehl = c;
}
return wert;
}
// Fortsetzung der Quelldatei arithmausdr_func.c siehe nächse Seite
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/07/5 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel :Auswertung arithmetischer Ausdrücke (Forts 3.)
// Fortsetzung der C-Quelldatei arithmausdr_func.c
float zahl(void)
/* Zahl ::= [+|-] Ziffer {Ziffer} [.{Ziffer}]
{
enum Bool_t negativ = FALSE;
/* Merker fuer neg. Vorzeichen
enum Bool_t bruch
= FALSE;
/* Merker fuer Dezimalpunkt
float wert=0.0, teiler= 1.0;
if ((c == '+') || (c== '-'))
/* Vorzeichen behandeln
{ if (c == '-')
negativ = TRUE;
c = lies();
}
if ((c<'0' || c>'9') && (c!='.'))
{ fehler = TRUE;
/* keine Zahl --> Fehler
cfehl = c;
}
else
{ /* Zahl konvertieren */
while ((c >= '0' && c <= '9') || (!bruch && (c == '.')))
{ if (c == '.')
/* Dezimalpunkt
bruch = TRUE;
else
{ wert = wert * 10.0f + (c - '0');
/* weitere Stellen dazu
if (bruch) teiler = teiler*10.0f;
/* fuer Nachkommastellen
}
c = lies();
/* naechstes Zeichen
}
}
if (negativ)
wert = -wert;
return(wert/teiler);
/* teiler nur dann !=1.0, wenn bruch==TRUE
}
● Beispiel eines Probelaufs:
Eingabe:
Ergebnis:
1.7+5*(4.5-3)/((2.6+1.3)*3)-2.1
0.24103
Eingabe : 2.3++3
Ergebnis: 5.30000
Eingabe : 2.3+++3
Syntaxfehler : Char:2B
Eingabe : 4.7*(3.4-2.1
Syntaxfehler : Char:0A
Eingabe : (4.5-2+3.1)*(7.9-(3+5.6*2.4))
Ergebnis: -47.82401
Eingabe : 8.2/5*(2-7-6.3))
Syntaxfehler : Char:29
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 05/08/1 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Der sizeof-Operator in C
● unärer Operator sizeof
◇ Die Auswertung erfolgt zur Compilezeit!
◇ Der Operator dient zur Ermittlung der Größe des Speicherplatzes in Bytes, den ein Objekt des Typs
seines Operanden belegt.
• Operand kann sein:
▻ Ausdruck
z.B. sizeof
sizeof
sizeof
sizeof
i
3
(3)
(a+b)
Der Ausdruck wird nicht numerisch ausgewertet, es wird nur sein Typ bestimmt.
▻ in Klammern gesetzte Typangabe
z.B. sizeof (int)
sizeof (double)
sizeof (char *)
● Ergebnis der Operation ist vom Typ size_t
size_t ist der implementierungsabhängige "natürliche" unsigned-Ganzzahl-Typ, er ist u.a. in den
Header-Dateien <stddef.h> und <stdlib.h> definiert.
● Anwendung :
▻ Ermittlung des Platzbedarfs zusammengesetzter Datentypen, wie "structures" und "arrays"
▻ zur portablen Angabe des Platzbedarfs der Standard-Datentypen
Die genaue Größe des von Objekten belegten Speicherplatzes wird z.B. für eine dynamische
Speicherallokation oder zum Schreiben von Binärdateien benötigt.
● Beispiel :
/* Programm typesize (Modul typesize_m.c)
/* Demonstrationsprogramm zum sizeof-Operator
/* Ausgabe der Speichergrößen der einfachen Datentypen und von Pointern
*/
*/
*/
#include <stdio.h>
#define MAX 17
int main(void)
{
int i=6, aiM[MAX];
printf("\nchar
printf("short
printf("int
printf("unsigned
printf("long
printf("float
printf("double
printf("long double
printf("char-Pointer
printf("int-Pointer
printf("Arraygroesse
printf("Arraygroesse
return 0;
}
%2u Bytes\n", sizeof(char));
/*
%2u Bytes\n", sizeof(short));
/*
%2u Bytes\n", sizeof i);
/*
%2u Bytes\n", sizeof(unsigned));
/*
%2u Bytes\n", sizeof(long));
/*
%2u Bytes\n", sizeof(float));
/*
%2u Bytes\n", sizeof(double));
/*
%2u Bytes\n", sizeof(long double));
/*
%2u Bytes\n", sizeof(char*));
/*
%2u Bytes\n", sizeof(int*));
/*
%2u Bytes\n", sizeof aiM
/*
%2u Elemente\n", sizeof aiM/sizeof aiM[0]);
--> 1 */
--> 2 */
--> 4 */
--> 4 */
--> 4 */
--> 4 */
--> 8 */
--> 12 */
--> 4 */
--> 4 */
--> 68 */
/* 17 */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/00/0 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
6. Dateibearbeitung in C
6.1. Dateibearbeitungskonzept von ANSI-C
6.2. Grundlegende Funktionen zur Dateibearbeitung
6.3. Funktionen zum Datentransfer (Schreiben und Lesen)
6.4. Funktionen zur Dateiverwaltung
6.5. Beispiel zu Binärdateien
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/01/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitungskonzept von ANSI-C - Stream-I/O (1)
•
Eine Programm- Ein- und Ausgabe erfolgt üblicherweise aus/in Dateien oder von/zu Geräten (z.B. Konsole,
Drucker). In C wird kein Unterschied zwischen diesen beiden Gruppen gemacht.
Geräte werden als Dateien betrachtet und behandelt ( Dateibearbeitung).
•
Die Sprache C selbst enthält keine Elemente zur Dateibearbeitung. Jegliche Ein- und Ausgabe in C
geschieht mittels Bibliotheksfunktionen. Ihre Deklarationen sind in der Headerdatei <stdio.h>
enthalten.
•
Den in der ANSI-C-Standardbibliothek definierten Funktionen zur Dateibearbeitung ( Programm-Ein/Ausgabe) liegt das Stream-Model zu Grunde ( Stream-I/O):
Jede zu bearbeitende Datei bzw. jedes Gerät wird logisch als Stream - als
kontinuierliche geordnete Folge von Bytes - betrachtet.
•
Häufig wird der Begriff Stream mit den Begriffen Datei (file) – unter Einschluss von Geräten – gleichgesetzt.
•
Referiert wird ein Stream über einen Filepointer.
Dies ist ein Zeiger auf eine programminterne - vom Startup-Code bereitgestellte - Variable eines StrukturTyps mit dem Typnamen FILE. In Variablen dieses Typs sind die von den Bibliotheksfunktionen für die
Bearbeitung einer Datei bzw. für Ein-/Ausgabe von/zu Geräten benötigten Verwaltungsinformationen
zusammengefasst.
•
Der genaue Aufbau des Datentyps FILE ist betriebssystem- und implementierungsabhängig. Er ist in
<stdio.h> definiert. Der Aufbau braucht dem Benutzerprogramm aber nicht bekannt zu sein. Das
Benutzerprogramm sollte keinesfalls direkt zu der über einen Filepointer referierten Variablen zugreifen,
sondern nur Filepointer als aktuelle Parameter für die Bibliotheksfunktionen verwenden.
Folgende Informationen enthält eine Variable vom Datentyp FILE zumindest:
- Referenz zur Datei im Dateisystem des Betriebssystems
- Zeiger auf den Puffer, über den der Dateizugriff erfolgt
- Information über die aktuelle Bearbeitungsposition in der Datei
- Information über die Zugriffsart
- Kennungen für das Auftreten von Fehlern und das Erreichen des Dateiendes
•
Üblicherweise sind die in einem Programm zur Verfügung stehenden Variablen des Datentyps FILE in
einem Array angeordnet ( FILE-Array). Die Anzahl der Komponenten dieses Arrays legt fest, wieviele
Dateien in einem Programm gleichzeitig geöffnet sein können.
Durch das Öffnen einer Datei wird eine Komponente des FILE-Arrays belegt und der entsprechenden Datei
zugeordnet. Dabei werden bestimmte Initialisierungsinformationen eingetragen. Die Funktion zum Öffnen
der Datei liefert einen Zeiger auf die belegte FILE-Array-Komponente zurück ⇒ Filepointer.
Jeder weitere Zugriff zu dieser Datei erfolgt dann über den Filepointer.
Durch das Schließen einer Datei wird die Zuordnung zwischen Datei und FILE-Array-Komponente wieder
aufgehoben, die FILE-Array-Komponente wird freigegeben der Filepointer wird ungültig.
•
Die ersten drei Komponenten des FILE-Arrays werden generell beim Programmstart belegt und die damit
assoziierten Dateien (tatsächlich Geräte) automatisch geöffnet (Standarddateien, Standardgeräte). In
<stdio.h> sind für die entsprechenden Filepointer Namen ( Zeigerkonstante !) definiert:
- stdin ⇒ Standardeingabegerät (i.a. Tastatur)
- stdout ⇒ Standardausgabegerät (i.a. Bildschirm)
- stderr ⇒ Standardfehlerausgabe (i.a. ebenfalls Konsolenbildschirm)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/01/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitungskonzept von ANSI-C - Stream-I/O (2)
• ANSI-C unterscheidet zwei Dateiformate :
▻ Textdateien
Sie bestehen aus einer Folge von Zeilen. Jede Zeile ist logisch mit einem Newline-Zeichen
abgeschlossen.
(In der realen Datei, auf der Betriebssystemebene, kann durchaus eine andere Art des Zeilenabschlusses
vorliegen, gegebenenfalls finden beim Lesen und Schreiben Umsetzungen statt).
▻ Binärdateien
Sie bestehen aus einer Folge von Bytes und besitzen keine weitere Struktur. Beim Lesen und Schreiben
finden grundsätzlich keine Umsetzungen statt.
Dateien können im Text- oder im Binär-Modus geöffnet werden.
Die Standarddateien (Standardgeräte) sind alle im Textmodus geöffnet.
• Der Dateizugriff mittels der ANSI-C- (Stream-) I/O-Funktionen findet grundsätzlich gepuffert statt.
Dabei wird unterschieden zwischen
- Zeilenpufferung (nur für Textdateien) und
- Blockpufferung (für beliebige Dateien)
Die Puffergröße bei Blockpufferung ist durch die in <stdio.h> definierte Konstante BUFSIZ
festgelegt.
Der Puffer wird defaultmäßig vom System zur Verfügung gestellt und ist dem Programm nicht direkt
zugänglich. Es existieren aber Funktionen zum Setzen eines benutzerdefinierten Puffers. Mit diesen
Funktionen kann eine Pufferung auch ganz ausgeschaltet werden ungepufferter Dateizugriff.
• Zur Kenzeichnung eines Dateiendes wird innerhalb von C-Programmen die Konstante EOF verwendet.
Diese Konstante ist nicht identisch mit dem im jeweiligen Betriebssystem gegebenenfalls verwendeten
Dateiendezeichen. Vielmehr nehmen die jeweiligen Datei-Lesefunktionen eine entsprechende Umsetzung
vor.
• In der Standardbibliothek existieren (die Funktionsdeklarationen sind in <stdio.h> enthalten)
▻ Grundlegende Funktionen zur Dateibearbeitung , wie Öffnen und Schließen von Dateien,
Positionieren in Dateien, Beeinflussung des Dateipuffers usw.
▻ Funktionen zum Schreiben und Lesen
▻ Funktionen zur Dateiverwaltung
▻ Funktionen zur Fehlerbehandlung
• In der Header-Datei <stdio.h> sind neben
-
den Funktionsdeklarationen,
der Definition des Datentyps FILE,
der Deklaration des FILE-Arrays ,
der Definition der Standard-Filepointer
und der Definition der Konstanten BUFSIZ
insbesondere enthalten :
-
Definition der Konstanten EOF (end of file)
Definition der Konstanten NULL (⇒ NULL-Pointer)
Definition des Datentyps size_t (ein ganzzahliger Typ)
Definitionen weiterer von den Funktionen verwendeter Konstanten
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/02/1 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Grundlegende Funktionen der C-Standard-Bibliothek zur Dateibearbeitung (1)
fopen ()
-
Öffnen einer Datei
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
Aktion: "fopen()" öffnet die durch "path" bezeichnete Datei und liefert einen Pointer auf die dadurch
belegte Komponente des FILE-Arrays (⇒ Filepointer) zurück. Über diesen Filepointer erfolgt
jeder weitere Dateibezug.
Übergabeparameter: path
mode
Pfadname der Datei
Zugriffsmodus
Gueltige Angaben fuer "mode" sind :
"r"
Lesen
(Datei muß existieren)
"w"
Schreiben
(evtl. exist. Datei wird gelöscht)
"a"
Anhaengen (append)
(evtl. exist. Datei wird nicht gelöscht)
"r+"
Lesen und Schreiben
(Datei muß existieren)
*)
"w+"
Schreiben und Lesen
(evtl. exist. Datei wird gelöscht)
*)
"a+"
Lesen (an beliebiger Position) und Schreiben am Dateiende *)
*) Zwischen Lesen und Schreiben muß jeweils ein explizites Herausschreiben der Dateipuffer erfolgen
(Aufruf von fflush) oder die Dateibearbeitungsposition verändert werden (Aufruf von fpos oder
rewind).
Durch Anhängen der Zeichen 'b' oder 't' kann zusätzlich angegeben werden, ob es sich bei der zu
bearbeitenden Datei um eine Binär- oder Textdatei handelt :
"...t" Textdatei
(Default in ANSI-C)
"...b" Binärdatei
Funktionswert :
Filepointer bzw. NULL-Pointer im Fehlerfall.
fclose()
-
Schließen einer Datei
#include <stdio.h>
int fclose(FILE *fp);
Aktion: "fclose()" schreibt noch nicht herausgeschriebene Filebuffer in die durch "fp" referierte Datei,
gibt die Buffer und die FILE-Array-Komponente, auf die fp zeigt, frei und schließt die Datei.
Übergabeparameter: fp
Funktionswert :
Filepointer
im Erfolgsfall der Wert "0" bzw. im Fehlerfall der Wert EOF.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/02/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Grundlegende Funktionen der C-Standard-Bibliothek zur Dateibearbeitung (2)
fseek()
-
Verändern der Bearbeitungsposition
#include <stdio.h>
int fseek(FILE *fp, long offset, int origin);
Aktion: "fseek()" verändert die aktuelle Bearbeitungsposition der durch "fp" referierten Datei um
"offset" Bytes gegenüber der Bezugsposition "origin" ( ⇒ wahlfreier Dateizugriff ! ).
Übergabeparameter: fp
offset
origin
Funktionswert:
Filepointer
Anzahl Bytes relativ zur Bezugsposition
Bezugsposition
"0", wenn die Bearbeitungsposition wie angegeben verändert werden konnte
bzw. ein Wert ungleich "0" im Fehlerfall
bzw. undefiniert bei I/O-Geraeten.
Die Bezugsposition wird durch einen der folgenden 3 Werte fuer "origin" gekennzeichnet :
SEEK_SET (=0) Dateianfang
SEEK_CUR (=1) augenblickliche Bearbeitungsposition
SEEK_END (=2) Dateiende
Achtung : Bei Textdateien muß offset == 0 oder ein von der Funktion "ftell()" zurück
gegebener Wert sein, wobei dann origin == SEEK_SET (Dateianfang !) sein muß.
rewind()
-
Zurücksetzen auf den Dateianfang
# include <stdio.h>
void rewind(FILE *fp);
Aktion: "rewind()" setzt die durch "fp" referierte Datei auf den Anfang zurueck.
Übergabeparameter: fp
Filepointer
Funktionswert:
keiner
es gilt :
rewind(fp) == (void)fseek(fp, 0L, SEEK_SET)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/02/3 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Grundlegende Funktionen der C-Standard-Bibliothek zur Dateibearbeitung (3)
ftell()
-
Ermitteln der aktuellen Bearbeitungsposition
# include <stdio.h>
long ftell(FILE *fp);
Aktion: "ftell()" ermittelt die aktuelle Bearbeitungsposition der durch "fp" referierten Datei.
Bei Binärdateien entspricht diese Bearbeitungsposition der Anzahl Bytes relativ zum
Dateianfang.
Bei Textdateien muß das nicht notwendigerweise der Fall sein. Der dafür ermittelte Wert für
die Bearbeitungsposition dient aber als sinnvoller "offset" für den Aufruf von "fseek()".
Er stellt eine Rückkehr zur Bearbeitungsposition zum Zeitpunkt des Aufrufs von "ftell()"
sicher.
Übergabeparameter: fp
Funktionswert:
Filepointer
aktuelle Bearbeitungsposition
bei Binärdateien : Anzahl Bytes relativ zum Dateianfang
bei Textdateien : für "fseek()" verwertbare Information (s. oben)
bzw. "-1L" im Fehlerfall
bzw. undefiniert bei I/O-Geraeten
feof()
-
Überprüfung auf Dateiende
# include <stdio.h>
int feof(FILE *fp);
Aktion: "feof()" überprüft, ob für die durch "fp" referierte Datei das Dateiende-Flag gesetzt ist.
Übergabeparameter: fp
Funktionswert:
Filepointer
ein Wert !=0, wenn das Dateiende-Flag gesetzt ist.
"0", wenn das Dateiende-Flag nicht gesetzt ist.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/02/4 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Grundlegende Funktionen der C-Standard-Bibliothek zur Dateibearbeitung (4)
fflush()
-
Herausschreiben eines Dateipuffers
#include <stdio.h>
int fflush(FILE *fp);
Aktion: Wenn "fp" eine Datei referiert, die zum Schreiben oder Update - bei zuletzt durchgeführter
Schreiboperation - geöffnet wurde, veranlasst "fflush()", dass der zugehörige Dateipuffer
geleert, d.h. an das Betriebssystem zum Herausschreiben in die Datei übergeben wird; die
Datei bleibt geöffnet.
Andernfalls ist das Verhalten undefiniert.
Falls für "fp" der NULL-Pointer übergeben wird, werden die Dateipuffer aller Dateien, für
die obige Bedingung erfüllt ist, herausgeschrieben.
Übergabeparameter: fp
Funktionswert :
Filepointer
"0", wenn der Puffer erfolgreich geleert werden konnte,
bzw EOF, wenn beim Schreiben ein Fehler aufgetreten ist.
Anmerkung : Ein Dateipuffer wird automatisch geleert, wenn er voll ist, wenn die Datei geschlossen
wird oder wenn das Programm normal beendet wird.
setbuf()
-
Bereitstellen eines benutzerdefinierten Dateipuffers
#include <stdio.h>
void setbuf(FILE *fp, char *buffer);
Aktion: "setbuf()" stellt für die durch "fp" referierte Datei einen durch "buffer" referierten
benutzerdefinierten Dateipuffer zur Verfügung. "buffer" muss auf den Anfang eines charArrays der Laenge BUFSIZ (definiert in <stdio.h>) zeigen.
Ist "buffer" ==NULL, so erfolgt der Zugriff zur Datei ungepuffert.
Übergabeparameter: fp
Filepointer
buffer Pointer auf benutzerdefinierten Dateipuffer
Funktionswert :
keiner
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der C-Standard-Bibliothek zum Datentransfer (1)
fgetc()
-
Zeichenweises Lesen
getc()
-
Zeichenweises Lesen
#include <stdio.h>
int fgetc(FILE *fp);
bzw.
int getc(FILE *fp);
Aktion: "fgetc()" bzw "getc()" liest das nächste Zeichen aus der durch "fp" referierten Datei.
Übergabeparameter: fp
Filepointer
Funktionswert :
das gelesene Zeichen (als int-Wert !)
bzw. EOF bei Dateiende oder im Fehlerfall
es gilt :
getc(stdin) == getchar() !
ungetc()
-
Rückgabe eines Zeichens in den Eingabepuffer
#include <stdio.h>
int ungetc(int c, FILE *fp);
Aktion: "ungetc()" gibt das Zeichen "c" (umgewandelt in unsigned char) in den Eingabepuffer der
durch "fp" referierten Datei zurück.
Übergabeparameter: c
fp
Funktionswert :
zurueckzugebendes Zeichen
Filepointer
das zurückgegebene Zeichen bzw EOF im Fehlerfall
fputc()
-
Zeichenweises Schreiben
putc()
-
Zeichenweises Schreiben
#include <stdio.h>
int fputc(int c, FILE *fp);
bzw.
int putc(int c, FILE *fp);
Aktion: "fputc()" bzw "putc()" schreibt das Zeichen "c" (umgewandelt in unsigned char) in die durch
"fp" referierte Datei.
Übergabeparameter: c
fp
zu schreibendes Zeichen
Filepointer
Funktionswert :
das geschriebene Zeichen (als int-Wert !) bzw. EOF im Fehlerfall
es gilt:
putc(c,stdout) == putchar(c) !
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/2 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel zum zeichenweisen Lesen von Dateien
/*
/*
/*
/*
/*
/*
----------------------------------------------------------------------*/
Programm fcopy (Modul fcopy_m.c)
*/
----------------------------------------------------------------------*/
Kopiert den Inhalt der Datei "test.txt" in einen auswählbaren Stream */
--> stdout, "aus.txt", oder Datei mit angebbarem Pfadnamen
*/
----------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
void copy_from_to(FILE *, FILE *);
int main(void)
{
char *quelle = "test.txt";
char *ziel
= "aus.txt";
FILE *fpa, *fpb;
char c, *pdt, dateiname[25];
int iRet = EXIT_SUCCESS;
if ((fpa=fopen(quelle,"r")) == NULL)
{ printf("\nQuelldatei :%s: kann nicht geoeffnet werden\n\n",quelle);
iRet = EXIT_FAILURE;
}
else
{ do
{
pdt = ziel;
/* Ziel vorbelegen */
printf("Wohin soll kopiert werden?\n");
printf("(1) stdout
(2) \"aus.txt\" (3) Neue Datei (E) Exit :");
do c=getchar(); while(c == '\n');
/* Überlesen von '\n' */
switch (c)
{
case '1': copy_from_to (fpa, stdout);
break;
case '3': printf("\nZieldatei ? ");
scanf("%s",dateiname);
pdt = dateiname;
case '2': if ((fpb=fopen(pdt,"w")) == NULL)
printf("\nAuf %s ist Schreiben nicht moeglich\n\n",pdt);
else
{ copy_from_to(fpa,fpb);
fclose(fpb);
}
break;
}
rewind(fpa);
} while (c != 'e' && c != 'E');
fclose(fpa);
}
return EXIT_SUCCESS;
}
void copy_from_to(FILE *fp1, FILE *fp2)
{ int c;
while ((c = fgetc(fp1)) != EOF)
fputc(c,fp2);
}
/* Kopiert zeichenweise */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/3 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der C-Standard-Bibliothek zum Datentransfer (2)
fscanf()
-
Formatiertes Lesen
#include <stdio.h>
int fscanf(FILE *fp, const char *ctrl , ...);
Aktion: "fscanf()" liest die nächsten Zeichen aus der durch "fp" referierten (Text-)Datei, interpretiert
sie entsprechend den im Steuerstring "ctrl" vorliegenden Typ- und Formatangaben und
weist die demgemäß konvertierten Werte den durch ihre Pointer (weitere Parameter !)
referierten Variablen zu.
Übergabeparametert: fp
ctrl
...
Filepointer
String zur Steuerung des Eingabeformats (vgl. scanf())
weitere Parameter : Pointer auf Variable, die die Eingabewerte
aufnehmen sollen
Aufbau und Bedeutung von "ctrl" exakt wie bei der Funktion scanf().
Funktionswert :
die Anzahl der erfolgreich zugewiesenen Werte
bzw EOF wenn – beim Lesen des ersten Werts – versucht wurde über das
Dateiende hinaus zu lesen
es gilt :
fscanf(stdin,ctrl, ... ) == scanf(ctrl, ... )
fprintf()
-
Formatiertes Schreiben
#include <stdio.h>
int fprintf(FILE *fp, const char *ctrl, ...);
Aktion: "fprintf()" gibt die als weitere Parameter übergebenen Werte entsprechend den Formatangaben
in "ctrl" als Zeichenfolgen in die durch "fp" referierte (Text-) Datei aus.
Übergabeparametetr: fp
ctrl
...
Filepointer
String zur Festlegung des Ausgabeformats (vgl. printf())
weitere Parameter: Ausgabewerte, Anzahl und Typ entsprechend
den Konvertierungsanweisungen in "ctrl".
Aufbau und Bedeutung von "ctrl" exakt wie bei der Funktion printf().
Funktionswert :
Anzahl der ausgegebenen Zeichen
es gilt :
fprintf(stdout,ctrl, ... ) == printf(ctrl, ... )
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/4 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der C-Standard-Bibliothek zum Datentransfer (3)
fgets()
-
Zeilenweises Lesen
#include <stdio.h>
char *fgets(char *s, int n, FILE *fp);
Aktion: "fgets()" liest die nächste Zeile (einschliesslich '\n') aus der durch "fp" referierten Datei,
hoechstens jedoch (n-1) Zeichen. Die gelesenen Zeichen werden in dem durch "s" referierten
String abgelegt (einschliesslich '\n' sofern es gelesen wurde), wobei '\0' angefügt
wird.
Übergabeparameter: s
n
fp
Funktionswert :
String zum Ablegen der gelesenen Zeile (genauer: Zeiger auf String)
max Anz der zu lesenden Zeichen + 1
Filepointer
String "s" (genauer: Pointer "s" auf String)
bzw NULL-Pointer bei Erreichen des Dateiendes ohne vorheriges Lesen von
Zeichen oder im Fehlerfall
Hinweis : Anders als bei gets() wird ein eventuell gelesener '\n'-Character mit im String
abgelegt !
fputs()
-
Stringweises Schreiben
#include <stdio.h>
int fputs(const char *s, FILE *fp);
Aktion: "fputs()" schreibt den durch "s" referierten String (ohne abschließenden '\0'-Character) in
die durch "fp" referierte Datei.
Übergabeparameter: s
fp
Funktionswert :
auszugebender String
Filepointer
ein nicht-negativer Wert bei Fehlerfreiheit
bzw EOF (== -1) im Fehlerfall
Hinweis : Anders als bei puts() wird an den ausgegebenen String kein '\n'-Character
angefügt.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/5 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der C-Standard-Bibliothek zum Datentransfer (4)
fread()
-
Datenobjektorientiertes Lesen
#include <stdio.h>
size_t fread(void *bptr, size_t size, size_t count, FILE *fp);
Aktion: "fread()" liest maximal "count" Datenobjekte der Länge "size" Bytes aus der durch "fp"
referierten Datei und legt sie in dem durch "bptr" bezeichneten Buffer ab.
Übergabeparameter: bptr
size
count
fp
Pointer auf Buffer zur Ablage der gelesenen Datenobjekte
Länge eines Datenobjekts in Bytes
Anzahl der zu lesenden Datenobjekte
Filepointer
Funktionswert:
Anzahl der tatsächlich in voller Länge gelesenen Datenobjekte (diese kann im
Fehlerfall oder bei Erreichen des Dateiendes weniger als "count" sein).
Anmerkung:
Der Typ size_t ist der "natürliche" unsigned Typ
(definiert in <stdio.h> und <stdlib.h>)
fwrite()
-
Datenobjektorientiertes Schreiben
#include <stdio.h>
size_t fwrite(const void *bptr, size_t size, size_t count, FILE *fp);
Aktion: "fwrite()" schreibt "count" Datenobjekte der Länge "size" Bytes in die durch "fp" referierte
Datei. Die auszugebenden Datenobjekte werden dem Buffer entnommen, auf den "bptr" zeigt.
Übergabeparameter: bptr
size
count
fp
Pointer auf Buffer mit den zu schreibenden Datenobjekten
Länge eines Datenobjekts in Bytes
Anzahl der zu schreibenden Datenobjekte
Filepointer
Funktionswert:
Anzahl der geschriebenen Datenobjekte (diese kann im Fehlerfall weniger als
"count" sein).
Anmerkung:
Der Typ size_t ist der "natürliche" unsigned Typ
(definiert in <stdio.h> und <stdlib.h>)
Anmerkung :
Mittels der Funktionen fread()und fwrite() können typenbezogene Dateien,
die Datenobjekte im internen Maschinenformat enthalten, bearbeitet werden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/03/6 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zu den Funktionen "fwrite" und "fread"
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
----------------------------------------------------------------------Programm floatdatei (C-Modul floatdatei_m.c)
----------------------------------------------------------------------Einlesen einer Reihe von float-Werten von stdin,
Ausgabe derselben in ein "file of float",
anschliessend Einlesen der in der Datei abgespeicherten Werte
und Ausgabe nach stdout.
Zum Vergleich Ausgabe der in der Datei gespeicherten Werte als
sedezimale Bytefolge nach stdout
-----------------------------------------------------------------------
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
#include <stdio.h>
#define
#define
#define
#define
ANZ1 6
ANZ2 24
FLDATEI "daten.flt"
WTRENN(i,a) putchar((++i%a)==0 ? '\n' : ' ')
int main(void)
{
FILE *pDatei;
float fWert;
int i,iByte;
pDatei=fopen(FLDATEI,"wb"); /* Oeffnen zum Schreiben in eine Binaerdatei */
printf("\nBitte geben Sie Zahlen ein :\n");
while (scanf("%f",&fWert) != EOF)
fwrite(&fWert,sizeof(float),1,pDatei);
fclose(pDatei);
pDatei=fopen(FLDATEI,"rb"); /* Oeffnen zum Lesen aus einer Binaerdatei
*/
i=0;
printf("\nAus der Datei wiedergelesene Zahlen :\n");
while (fread(&fWert,sizeof(float),1,pDatei) != 0)
{ printf("%9.4f ",fWert);
WTRENN(i,ANZ1);
}
fclose(pDatei);
// Alternative :
pDatei=fopen(FLDATEI,"rb");
// rewind(pDatei);
i=0;
printf("\n\nDer Dateiinhalt als sedezimale Bytefolge :\n");
while ((iByte=fgetc(pDatei)) != EOF)
{ printf("%02x",iByte & 0xff);
WTRENN(i,ANZ2);
}
fclose(pDatei);
putchar('\n');
return 0;
}
Probelauf:
Bitte geben Sie Zahlen ein :
1.0 0 -1.0 2.6 3.2e-3
^Z
Aus der Datei wiedergelesene Zahlen :
1.0000
0.0000
-1.0000
2.6000
0.0032
Der Dateiinhalt als sedezimale Bytefolge :
00 00 80 3f 00 00 00 00 00 00 80 bf 66 66 26 40 17 b7 51 3b
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/04/1 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der C-Standard-Bibliothek zur Dateiverwaltung
remove()
-
Entfernen einer Datei aus dem Dateisystem
#include <stdio.h>
int remove(const char *path);
Aktion: "remove()" entfernt die durch "path" bezeichnete Datei aus dem Dateisystem.
Falls diese Datei zum Zeitpunkt des Aufrufs von "remove()" geöffnet ist, ist das Verhalten der
Funktion implementierungsabhängig.
Übergabeparameter: path
Funktionswert:
Pfadname der zu entfernenden Datei
"0", falls erfolgreich
bzw. ein Wert !="0" im Fehlerfall
rename()
-
Umbenennen einer Datei im Dateisystem
#include <stdio.h>
int rename(const char *old, const char *new);
Aktion: "rename()" benennt die durch "old" bezeichnete Datei um in "new".
Der alte Pfadname "old" ist danach ungültig.
Falls bereits eine Datei mit dem Pfadnamen "new" existiert, ist das Verhalten
implementierungsabhängig.
Übergabeparameter: old
new
alter Pfadname der Datei
neuer Pfadname der Datei
Funktionswert:
"0", falls erfolgreich
bzw. ein Wert !="0" im Fehlerfall, der alte Pfadname bleibt gültig
Anmerkung :
kann auch zum Verschieben von Dateien eingesetzt werden
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/05/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu Binärdateien
Im Praktikum (2. Übung) wird die Binärdatei "lager.dat" zur Verwaltung eines Drogerie-Lagers eingesetzt.
Die Datei enthält für jeden im Lager vorhandenen Artikel einen Datensatz (Record), der durch den Structure-Typ
struct artikel_t beschrieben wird. Diese Datei ist Datenbasis für folgende Aktionen :
int modifyLager(void);
(1) Fragt die Nummer des zu modifizierenden Datensatzes von der Konsole ab.
(2) Sucht den angegebenen Datensatz in der Datei und bietet diesen durch "modifyRec()" zur
Modifikation an.
(3) Ersetzt den alten durch den modifizierten Datensatz.
(4) Wiederholt (1)-(3) solange bis eine negative Datensatznummer eingegeben wird
oder ein Fehler beim Lesen bzw Schreiben der Datei auftritt
(5) Nicht existierende Datensatznummern werden zurückgewiesen (Ausgabe Fehlerhinweis).
Rückgabe:
1 o.k.
-1 Fehler
static int modifyRec (struct artikel_t *);
(1) Zeigt die Daten des übergebenen Artikel an.
(2) Bietet die Ist-Anzahl des Artikels (Structure-Element "bestand") zur Korrektur an.
(3) Übernimmt die neue Ist-Anzahl von der Konsole.
Übergabe:
Zeiger auf zu modifizierenden Artikel.
Rückgabe:
1 erfolgreicher Abschluss
-1 Fehler
int makeTextDatei(void);
(1) Erzeugt aus der Binärdatei "lager.dat" die Texdatei "lager.txt".
(2) Jeder Datensatz aus "lager.dat" entspricht einer geeignet formatierten "Zeile" in
"lager.txt".
Zwischen den Elementen des Artikels ist als Trennzeichen der Tabulator anzuwenden ('\t').
Rückgabe:
1 o.k.
-1 Fehler
#include <stdio.h>
#define LAGERDATEINAME "lager.dat"
#define ZIELTEXTDATEI "lager.txt"
#define MAXCHAR 32
struct artikel_t
{ int artnr;
char artname[MAXCHAR];
float preis;
int bestand;
int min;
} ;
/* Datentyp der Lagerartikel */
static int modifyRec (struct artikel_t *sM)
{ int iRet = 1
printf("Artikel: %03d %-16s %6.2f %4d:%4d\n",
sM->artnr, sM->artname, sM->preis, sM->bestand, sM->min);
printf ("Korrektur Bestand %2d nach: ",sM->bestand);
if (scanf ("%d",&(sM->bestand)) != 1)
iRet = -1;
return iRet;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 06/05/2 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu Binärdateien (Forts.)
int modifyLager (void)
{ int iRet = 1;
char *dateiName = LAGERDATEINAME;
int rnr, rerg;
long pos;
FILE *f_zeig;
struct artikel_t e_1;
if((f_zeig = fopen(dateiName,"r+b")) == NULL)
{ printf("%s kann nicht geoeffnet werden!\n",dateiName);
iRet -1;
}
else
{ do
{ printf("\nRekordnr ? ");
scanf("%d",&rnr);
if (rnr >= 0)
{ if (fseek(f_zeig,rnr*sizeof(e_1),SEEK_SET) == 0 )
{ pos = ftell(f_zeig);
rerg = fread(&e_1,sizeof(struct artikel_t),1,f_zeig);
if (rerg != 1)
{ printf("Read-Error!!\n"); iRet = -1; }
else if ( modifyRec(&e_1) == 1)
{ fseek(f_zeig,pos,SEEK_SET);
if (fwrite(&e_1,sizeof(struct artikel_t),1,f_zeig) != 1)
{ printf("Write-Error!!\n"); iRet = -1; }
}
}
}
} while (rnr>= 0 && iRet!=-1);
if (fclose(f_zeig)== EOF) iRet = -1;
}
return iRet;
}
int makeTextDatei(void)
{ int iRet = 1;
char zForm[] = "%03d\t%-16s\t%6.2f\t%4d\t%4d\n";
/* Ausgabeformat */
char *quellName = LAGERDATEINAME;
char *zielName = ZIELTEXTDATEI;
FILE *fqZeig, *fzZeig;
char erg1,erg2;
struct artikel_t e1;
if((fqZeig = fopen(quellName,"rb")) == NULL)
{ printf("%s kann nicht geoeffnet werden!\n",quellName);
iRet = –1;
}
else if((fzZeig = fopen(zielName,"wt")) == NULL)
{ printf("%s kann nicht geoeffnet werden!\n",zielName);
iRet = –1;
}
else
{ while (fread(&e1,sizeof(struct artikel_t),1,fqZeig) > 0)
fprintf(fzZeig,zForm,e1.artnr,e1.artname,e1.preis,e1.bestand,e1.min);
erg1 = fclose (fqZeig);
erg2 = fclose (fzZeig);
if (erg1 == EOF || erg2 == EOF)
{ printf("Fehler beim Datei-Schliessen ! \n"); iRet = -1; }
else
printf("Die Textdatei %s wurde erfolgreich generiert!\n",zielName);
}
return iRet;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/00/0 – TH/KR – 02
-----------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
7. Ergänzungen zu Datenstrukturen in C
7.1. Dynamische Speicherallokation
7.2. Dynamische Datenstrukturen (Listen und Bäume)
7.3. Unions
7.4. Vorwärtsdeklaration von strukturierten Datentypen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/01/1 – TH/KR – 02
-----------------------------------------------------------------------------------------------------------------------------------------------------
Dynamische Speicherallokation in C
•
Datenobjekte (Variable), die in einem Programm definiert werden, sind statische Objekte. Der Compiler sorgt
für die Bereitstellung von Speicher für diese Objekte. Ihre Anzahl und Größe (Speicherbedarf !) muss daher
bereits zum Zeitpunkt der Compilierung festliegen.
•
In vielen Fällen lässt sich aber die Größe eines Objektes (z.B. Arrays) bzw. die Anzahl der Objekte erst zur
Programmlaufzeit angeben, wobei sich deren gesamte Struktur auch noch dynamisch während der
Programmabarbeitung verändern kann. Eine statische Bereitstellung von Speicherplatz der zu erwartenden
maximalen Größe stellt in solchen Fällen nur eine Notlösung dar. Denn erstens ist damit i.a. eine mehr oder
weniger große Speicherplatzverschwendung verbunden und zweitens kann sich die dadurch getroffene
Größenbegrenzung in der Zukunft als zu gering herausstellen.
•
Zur sinnvollen Lösung des genannten Problems bietet sich die dynamische Speicherallokation (Dynamic
Storage Allocation) an:
Der für ein Objekt benötigte Speicherplatz wird nicht statisch durch
Vorkehrungen des Compilers, sondern erst bei Bedarf während der
Programmabarbeitung bereitgestellt.
Derartige - dynamisch allokierte - Objekte können nicht wie statische Objekte definiert werden. Sie werden
auch nicht über einen Namen, sondern nur über ihre Adresse angesprochen. Diese Adresse kann ihrerseits
einer statischen Pointer-Variablen zugewiesen werden, die wie üblich zu definieren ist.
•
Die dynamische Speicherbereitstellung erfolgt mittels spezieller Speicherallokationsfunktionen, die in der
Standardbibliothek (Funktionsdeklarationen in <stdlib.h>) enthalten sind :
malloc()
- Allokation eines Speicherblocks bestimmter Größe.
calloc()
- Allokation eines Speicherblocks für eine bestimmte Anzahl von Objekten einer
bestimmten Größe, Initialisierung aller Bytes des Speicherblocks mit 0.
realloc()
- Veränderung der Größe eines allokierten Speicherblocks.
Diese Funktionen liefern die Anfangsadresse des allokierten Blocks als void-Pointer (void *).
Es ist kein Type-Cast bei der Zuweisung an Pointer-Variable erforderlich.
•
•
Der für die dynamische Speicherverwaltung zur Verfügung stehende Speicherbereich wird als Heap
bezeichnet. Die Lage und Größe dieses Bereichs hängt von der Implementierung ab, folgende Varianten
sind möglich:
-
Der Heap ist Teil des Datenbereichs zwischen statischen Daten und Stack.
-
Der Heap verwendet den gesamten restlichen Arbeitsspeicher.
Die Lebensdauer dynamisch allokierten Speichers ist nicht an die Ausführungszeit eines Blocks gebunden.
Nicht mehr benötigter dynamisch allokierter Speicher ist explizit freizugeben.
Bibliotheksfunktion hierfür :
free() -
Gibt dynamisch allokierten Speicher frei.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/01/2 – TH/KR – 04
-----------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der ANSI-C-Std-Bibliothek zur dynamischen Speicherverwaltung
-
malloc()
Allokation eines Speicherblocks
#include <stdlib.h>
void *malloc(size_t size);
Aktion: "malloc()" allokiert einen Speicherblock von wenigstens der Länge "size" Bytes
(der tatsächlich allokierte Speicherblock kann infolge evtl. notwendiger Speicherplatzausrichtung größer sein).
Übergabeparameter : size
Anzahl der Bytes des zu allokierenden Blockes
Funktionswert :
Pointer auf den allokierten Speicherblock;
NULL-Pointer, falls der Speicherplatz nicht ausreicht.
Anmerkung :
Der Typ size_t ist der "natürliche" unsigned Typ (u.a. in <stdlib.h> definiert).
-
calloc()
Allokation und Initialisierung eines Speicherblocks
#include <stdlib.h>
void *calloc(size_t nobj, size_t size);
Aktion: "calloc()" allokiert einen Speicherblock von wenigstens der Länge "nobj" * "size" Bytes
(der tatsächlich allokierte Speicherblock kann infolge evtl. notwendiger Speicherplatzaus
richtung größer sein) und initialisiert alle Bytes mit 0.
Übergabeparameter: nobj
size
Funktionswert:
free()
Anzahl der Objekte, für die Speicherplatz allokiert werden soll;
Länge eines Objektes in Bytes.
Pointer auf den allokierten Speicherblock;
NULL-Pointer, falls der Speicherplatz nicht ausreicht.
-
Freigabe von Speicherplatz
#include <stdlib.h>
void free(void *ptr);
Aktion: "free()" gibt den - zuvor allokierten - Speicherblock, auf den "ptr" zeigt, wieder frei.
Übergabeparameter: ptr
Funktionswert :
keiner
Zeiger auf allokierten Speicherblock
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/01/3 – TH/KR – 03
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zur dynamischen Speicherallokation in C
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
-------------------------------------------------------------------Programm maldem (C-Modul maldem_m.c)
-------------------------------------------------------------------Demonstrationsprogramm zur Verwendung der Bibliotheksfunktionen zur
dynamischen Speicherallokation.
*/
*/
*/
*/
*/
*/
Einlesen, Sortieren und Ausgeben einer beliebigen Anzahl von
*/
double-Werten.
*/
*/
Die Funktion zum Sortieren eines Feldes von double-Werten steht
*/
in einem getrennt zu übersetzenden Modul zur Verfügung.
*/
---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
extern void dfeld_sort(double *, unsigned);
void dfeld_ein(double *, unsigned);
void dfeld_aus(double *, unsigned);
/* im Modul bubble.c def. */
int main(void)
{ int anz;
double *dfeld;
printf("\nAnzahl der double-Werte ? ");
scanf("%d", &anz);
if (anz>0)
if ((dfeld = malloc(anz*sizeof(double))) == NULL)
// oder : calloc(anz,sizeof(double))
printf("\nSpeicherplatz reicht nicht aus !\n");
else
{ dfeld_ein(dfeld, anz);
dfeld_sort(dfeld, anz);
dfeld_aus(dfeld, anz);
free(dfeld);
}
else
printf("\nAnzahl muss >0 sein !\n");
return 0;
}
void dfeld_ein(double * df, unsigned int iNbr)
{ unsigned int i;
for (i=0;i<iNbr;i++)
{ printf("Wert %2d: ",i);
scanf ("%lf", df++);
}
}
void dfeld_aus ( double * df, unsigned int iNbr)
{ unsigned int i;
printf("\nAusgabe:\n");
for (i=0;i<iNbr;i++)
printf("Wert %2d: %7.3lf\n",i,*(df++));
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/1 – TH/KR – 02
-----------------------------------------------------------------------------------------------------------------------------------------------------
Dynamische Datenstrukturen in C
•
Dynamische Datenstrukturen ändern ihre Struktur und den von ihnen belegten Speicherplatz
während der Programmausführung. Sie sind aus einzelnen Elementen (Knoten) aufgebaut, zwischen
denen üblicherweise eine bestimmte ("Nachbarschafts"-) Beziehung besteht.
Die Dynamik liegt im Einfügen neuer Knoten, Entfernen vorhandener Knoten und Änderung der
Nachbarschaftsbeziehung.
Der Speicherplatz für einen Knoten wird erst bei Bedarf zur Programmlaufzeit mittels der
Speicherallokationsfunktionen bereitgestellt → dynamische Speicherallokation. Da der Speicherort
der einzelnen Knoten im voraus nicht bekannt ist und auch für "benachbarte" Knoten weit
auseinander liegen kann, läßt sich eine ("Nachbarschafts"-) Beziehung nicht durch den Speicherort
abbilden.Vielmehr muß die Beziehung der einzelnen Knoten untereinander über Zeiger, die jeweils
auf den Speicherort des "Nachbarn" zeigen, hergestellt werden.
Jeder Knoten wird daher neben den jeweils zu speichernden Nutzdaten Zeiger auf die
jeweiligen "Nachbar"-Knoten - also auf Elemente des gleichen Typs wie der des Knotens
selbst enthalten.
Verkettete Datenstrukturen
•
Die wichtigsten dieser Datenstrukturen sind:
Lineare Listen
▻ einfach verkette Listen
▻ doppelt verkette Listen
▻ Spezialformen (bezüglich der Anwendung)
- Queue (Pufferspeicher, FIFO)
- Stack (Kellerspeicher, LIFO)
Bäume
▻ Binärbäume (zwei Nachfolger)
▻ Vielweg-Bäume (mehr als zwei Nachfolger)
Allgemeine Graphen
•
Die wichtigsten Operationen mit dynamischen Datenstrukturen sind :
•
•
•
•
•
Erzeugen eines neuen Elements
Einfügen eines Elements
Entfernen eines Elements
Suchen eines Elements
In C lassen sich die einzelnen Elemente (Knoten) durch "structures" darstellen. Zur Realisierung
verketteter Datenstrukturen müssen diese Strukturen Zeiger auf Strukturen ihres eigenen Typs
enthalten. Derartige Strukturen nennt man rekursiv.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/2 – TH/KR – 02
-----------------------------------------------------------------------------------------------------------------------------------------------------
Aufbau verketteter Datenstrukturen mittels rekursiver Strukturen
•
Einfach verkettete Liste
root
Inhalt
Inhalt
Inhalt
Inhalt
NULL
head
tail
struct listelement
{ int inhalt;
struct listelement *next;
};
•
Doppelt verkettete Liste
root
inhalt
inhalt
inhalt
inhalt
NULL
NULL
head
tail
struct listelement
{ int inhalt;
struct listelement *next;
struct listelement *back;
};
•
Binärer Baum
root
inhalt
inhalt
inhalt
inhalt
inhalt
NULL
inhalt
NULL
NULL
inhalt
NULL
NULL
inhalt
NULL
NULL
NULL
NULL
struct baumelement
{ int inhalt;
struct baumelement *rechts;
struct baumelement *links;
};
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/3 – TH/KR – 05
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu rekursiven Strukturen – Binärbaum (1)
Sortierte Speicherung von double-Werten in einem Binär-Baum
● Aufgabe :
Es soll eine beliebige Anzahl von double-Werten von der Tastatur eingelesen werden (Ende der Eingabe mit
CTRL-Z). Anschließend sind die Werte in aufsteigender Reihenfolge auf dem Bildschirm auszugeben.
Treten gleiche Werte mehrmals auf, so ist der nur einmal, zusammen mit der Anzahl seines Auftretens,
auszugeben.
● Lösung :
Bereits während des Einlesens werden die Werte sortiert in einem Binär-Baum gespeichert. Jeder Knoten
des Baums enthält den jeweiligen Wert, die Anzahl der Auftritte dieses Wertes, sowie je einen Pointer auf
einen rechten (für größeren Folgewert) und einen linken Folgeknoten (für kleineren Folgewert).
◇ Typdefinition des Knotens :
struct dknoten
{ double wert;
int anzahl;
struct dknoten *rechts;
struct dknoten *links;
};
◇ Die Speicherung erfolgt so, dass sich jeder Wert der kleiner als ein Knotenwert ist, im linken Unterbaum
und jeder Wert der größer ist, im rechten Unterbaum dieses Knotens befindet.
◇ Die Ausgabe erfolgt mittels einer (rekursiven) Funktion, die beginnend mit dem Wurzelknoten zuerst den
linken Unterbaum, dann den Wurzelknoten und dann den rechten Unterbaum ausgibt.
◇ Beispiel für Eingabe : 1.2
4.5
3.1
0.8
6.3
3.1
-5.8
4.0
4.5
2.8
1.2
root
1
4.5
2
0.8
1
NULL
-5.8
1
6.3
3.1
1
2
NULL
4.0
NULL
2.8
1
NULL
1
NULL
NULL
NULL
NULL
NULL
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/4 – TH/KR – 04
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu rekursiven Strukturen – Binärbaum (2)
● Programm
/* Programm baumsortd (C-Modul baumsortd_m.c)
Sortierte Ausgabe (aufsteigende Wertefolge) einer beliebigen Anzahl in
beliebiger Reihenfolge ueber stdin eingegebenen Folge von double-Werten
nach stdout. Speicherung der Werte in einem Binär-Baum */
#include <stdio.h>
#include <stdlib.h>
typedef struct knoten knoten_t;
struct knoten
{ double wert;
int anzahl;
knoten_t *rechts;
knoten_t *links;
};
/* Vorwärtsdeklaration von knoten_t
/* Typ-Definition des Knotens
knoten_t * psEinfBaum(knoten_t *, double);
void vBaumAus(knoten_t *);
/* Funktionsdeklarationen
*/
*/
*/
int main(void)
{ knoten_t *wurzel = NULL;
double d;
printf("\nBitte Zahlen eingeben !\n");
while (scanf("%lf",&d)!=EOF)
wurzel = psEinfBaum(wurzel,d);
printf("\nSortierte Folge der eingegeben Zahlen :\n\n");
vBaumAus(wurzel);
return 0;
}
knoten_t * psEinfBaum(knoten_t *pk, double a)
/* Einfügen Wert in Baum
/* pk Zeiger auf akt. Knoten des Baums
/* a
neuer einzufuegender Wert
{ if (pk==NULL)
/* neuer Wert ist einzufuegen
{ pk=malloc(sizeof(knoten_t));
pk->wert = a;
pk->anzahl = 1;
pk->links = pk->rechts = NULL;
}
else if (a==pk->wert)
/* Wert bereits vorhanden
pk->anzahl++;
else if (a<pk->wert)
/* Wert ist kleiner als Knotenwert
pk->links = psEinfBaum(pk->links,a);
else
/* Wert ist groesser als Knotenwert
pk->rechts = psEinfBaum(pk->rechts,a);
return pk;
}
*/
*/
*/
*/
*/
*/
*/
void vBaumAus(knoten_t *pk)
/* Rekursive Ausgabe der Baumwerte
*/
{ if (pk!=NULL)
/* pk Zeiger auf akt. Knoten des Baums */
{ vBaumAus(pk->links);
printf("%13.6lf",pk->wert);
if (pk->anzahl > 1)
printf(" (%2d)",pk->anzahl);
putchar('\n');
vBaumAus(pk->rechts);
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/5 – TH/KR – 04
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu rekursiven Strukturen – Binärbaum (3)
● Probelauf
Ergebnis eines Probelaufs des Programms baumsortd :
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/6 – TH/KR – 05
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu rekursiven Strukturen – Binärbaum (4)
● Iterative Lösung für das Einfügen eines Knotens
/*
/*
/*
/*
/*
/*
----------------------------------------------------------------------Einfügen eines Wertes in den Baum
pk Zeiger auf akt. Knoten des Baums
a
neuer einzufuegender Wert
Iterative Variante
(enthalten im Modul baumsort_iter_m.c)
-----------------------------------------------------------------------
knoten_t * psItEinfBaum(knoten_t *pk, double a)
{ knoten_t *pka, *pkn, *pkneu;
int erfolg;
if (pk==NULL)
// 1. Wurzel untersuchen
{ pk = malloc(sizeof(knoten_t));
pk->wert = a;
pk->anzahl = 1;
pk->links = pk->rechts = NULL;
}
else
{ pka = pkn = pk;
// 2. Knoten untersuchen
erfolg = 0;
do
{ if (pka->wert == a)
{ pka->anzahl++;
erfolg = 1;
}
else if (pka->wert > a)
{ pkn = pka->links;
if (pkn == NULL)
{ pkneu = malloc(sizeof(knoten_t));
pkneu->wert = a;
pkneu->anzahl = 1;
pkneu->links = pkneu->rechts = NULL;
pka->links=pkneu;
erfolg = 1;
}
}
else
{ pkn = pka->rechts;
if (pkn==NULL)
{ pkneu = malloc(sizeof(knoten_t));
pkneu->wert = a;
pkneu->anzahl = 1;
pkneu->links = pkneu->rechts = NULL;
pka->rechts = pkneu;
erfolg = 1;
}
}
pka = pkn;
} while (erfolg == 0);
}
return pk;
}
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/7 – TH/KR – 06
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu dynamischen Datenstrukturen – Lineare Liste (1)
Modifikation der im Praktikum gestellten Aufgabe zur Erstellung einer Bestellliste durch Auswertung einer
Lagerbestandsdatei (Binärdatei "lager.dat") : Ablage der nachzubestelleden Artikel in einer linearen
vorwärts verketteten Liste. Realisierung der folgenden Funktionen :
struct artlst_t *makeOrderList(char *);
(1) Öffnet die durch den Parameter referierte Lagerdatei.
(2) Liest den nächsten Datensatz ein.
(3) Übergibt den Artikel falls die aktuelle Stückzahl geringer ist als die Mindestmenge an
"putInList()", gemeinsam mit der Wurzel der Liste.
(4) Wiederholt (2)-(3) bis alle Datensätze gelesen und geprüft sind.
Übergabe:
Zugriffspfad der Lagerdatei
Rückgabe:
Pointer auf Wurzelknoten (bei erfolgreicher Listenerzeugung)
NULL-Pointer, falls keine Liste erzeugt wurde (z.B. kein Öffnen der Lagerdatei)
static struct artlst_t *putInList(struct artlst_t *, struct artikel_t);
(1) Allokiert Speicherplatz für einen Listenknoten.
(2) Kopiert den übergebenen Artikel in den Listenknoten und
(3) hängt den neuen Knoten am Ende der Liste an.
Übergabe:
Zeiger auf ersten Listenknoten (Wurzelknoten).
Artikel, der zu übernehmen ist.
Rückgabe:
Pointer auf Wurzelknoten (ggf. neu allokierter Knoten).
bzw NULL-Pointer, falls ein Fehler aufgetreten ist.
void prtOrderList(struct artlst_t *);
(1) Gibt den Inhalt der Liste knotenweise in die Standardausgabe aus.
Übergabe:
Pointer auf Wurzelknoten der Liste.
Rückgabe:
void remOrderList(struct artlst_t *);
(1) Gibt den allokierten Speicherplatz für jedes Listenelement wieder frei.
Übergabe:
Pointer auf Wurzelknoten der Liste.
Rückgabe:
// Inhalt der C-Headerdatei lagfunc3.h :
#define MAXCHAR 40
#define ANZ
5
#define LAGERDATEINAME "lager.dat"
struct artikel_t
{ int artnr;
char artname[MAXCHAR];
float preis;
int bestand;
int min;
};
/* Datentyp der Lagerartikel
*/
struct artlst_t
{ struct artikel_t sArtikel;
struct artlst_t *next;
};
/* Datentyp der Listenknoten
*/
/* Rekursive Datenstuktur!!!
*/
struct artlst_t *makeOrderList(char*);
void prtOrderList(struct artlst_t *);
void remOrderList(struct artlst_t *);
/* Funktionsprototypen */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/8 – TH/KR – 06
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu dynamischen Datenstrukturen – Lineare Liste (2)
// C-Modul lagfunc3.c
#include <stdio.h>
#include <stdlib.h>
#include "lagfunc3.h"
static struct artlst_t *putInList(struct artlst_t *, struct artikel_t);
struct artlst_t *makeOrderList(char * quellPfad)
{ int speicherVorh = 1;
FILE *fqZeig;
struct artikel_t e1;
struct artlst_t *root=NULL;
struct artlst_t *erg;
if((fqZeig = fopen(quellPfad,"rb")) == NULL)
printf("%s kann nicht geoeffnet werden!\n",quellPfad);
else
{ while (fread(&e1,sizeof(struct artikel_t),1,fqZeig) > 0 && speicherVorh)
{ if(e1.bestand < e1.min)
{ erg = putInList(root,e1);
if (erg == NULL)
{ printf("Speicher nicht ausreichend!!!\n");
speicherVorh = 0;
//Speicher fehlt
}
else
root=erg;
}
}
fclose (fqZeig);
}
return root;
}
static struct artlst_t *putInList(struct artlst_t *wrzl, struct artikel_t sG)
{ struct artlst_t *pRet = wrzl;
struct artlst_t *pakt, *palt;
pakt = malloc (sizeof(struct artlst_t));
if (pakt == NULL)
pRet = NULL;
else
{ pakt->sArtikel = sG;
pakt->next = NULL;
if(wrzl == NULL)
// Liste war noch leer
pRet = pakt;
// neues Element --> erstes Listenelemnt
else
{ palt = wrzl;
while (palt->next != NULL)
// Ende der Liste suchen
palt = palt->next;
palt->next = pakt;
// neues Element an das Ende der Liste
}
}
return pRet;
}
// Fortsetung s. nächste Seite
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/02/9 – TH/KR – 07
-----------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel zu dynamischen Datenstrukturen – Lineare Liste (3)
// C-Modul lagfunc3.c, Fortsetzung
void prtOrderList(struct artlst_t *wrzl)
{ const char *ueber = "Lfd.Nr. Art.Nr. Artikel
BestellAnz Einzelpr.";
const char *ueber2= " Gesamtpr.\n";
const char *line = "------------------------------------------------------";
const char *line2 = "-------------\n";
const char *form = " %3d
%03d
%-18s %3d
%8.2f
%9.2f\n";
const char *sform = "
Summe: %14.2f\n\n";
int lfdnr = 0;
float gesamtpreis = 0.0;
float gesamtartikelpreis;
int bestellmenge;
struct artlst_t *palt;
printf(ueber); printf(ueber2);
printf(line); printf(line2);
palt = wrzl;
while (palt != NULL)
{ bestellmenge = ANZ*palt->sArtikel.min - palt->sArtikel.bestand;
gesamtartikelpreis = bestellmenge * palt->sArtikel.preis;
gesamtpreis += gesamtartikelpreis;
printf(form,++lfdnr,palt->sArtikel.artnr,palt->sArtikel.artname,
bestellmenge, palt->sArtikel.preis, gesamtartikelpreis);
palt = palt->next;
}
printf(line); printf(line2);
printf("
"); printf(sform,gesamtpreis);
}
void remOrderList(struct artlst_t * wrzl)
{ struct artlst_t *pmrk, *palt;
palt = wrzl;
while (palt != NULL)
{ pmrk = palt;
palt = palt->next;
free(pmrk);
// Speicher wieder freigeben
}
}
// C-Modul lager3_m.c (Programm lager3)
// Modifikation der 2. Aufgabe des Prakt. Programmieren
#include <stdio.h>
#include <stdlib.h>
#include "lagfunc3.h"
int main(void)
{ struct artlst_t *root = NULL;
char *ldatei = LAGERDATEINAME;
if((root = makeOrderList(ldatei)) == NULL)
printf("Fehler beim Oeffnen der Lagerdatei!!\n");
else
{ printf("Bestellliste :\n");
prtOrderList(root);
remOrderList(root);
}
return 0;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/03/1 – TH/KR – 04
-----------------------------------------------------------------------------------------------------------------------------------------------------
Unions (Variante Strukturen) in C
● Aufbau von Unions
◇ Variante Strukturen – in C Unions genannt – sind Strukturen, deren Komponenten alle an der gleichen
Anfangsadresse im Arbeitspeicher beginnen.
Die Komponenten einer Union liegen nicht wie bei einer normalen Struktur hintereinander
– an verschiedenen Adressen – im Arbeisspeicher, sondern sie liegen "übereinander" im gleichen
Speicherbereich, der am Unionanfang beginnt.
d.h. der Inhalt des gleichen Speicherbereichs wird je nach union-Komponente unterschiedlich
interpretiert.
◇ Beispiel :
struct
{ int i;
double f;
char *pc;
} svar;
svar
i
union
{ int i;
double f;
char *pc;
} uvar;
uvar
i
f
pc
f
pc
◇ Die einzelnen Komponenten einer Union können durchaus unterschiedlich lang sein.
Der von der Union belegte Speicherplatz richtet sich nach der längsten Komponente.
● Vereinbarung von Unions
Die Vereinbarung von Unions (Typdefinition und Variablenvereinbarung) erfolgt analog zur Vereinbarung
von Strukturen. Lediglich das Schlüselwort struct wird durch das Schlüsselwort union ersetzt.
● Initialisierung von Unions
nur für die 1. Komponente mit konstantem Ausdruck zulässig.
● Operationen mit unions
Operationen mit Unions sind die gleichen, die mit Strukturen möglich sind:
▻ Anwendung des Adressoperators
▻ Zugriff zu Union-Komponenten
- mittels des Element-Operators . und
- und mittels des Objektelement-Operators ->
▻ Wertzuweisung: ganze Unions geschlossen
▻ Verwendung als Funktionsparameter und Funktionswerte: sowohl durch Union-Pointer als auch durch
Unions direkt.
● Interpretation von Unions
Eine Union-Variable kann als eine Variable aufgefasst werden, die Werte unterschiedlichen Typs
annehmen kann. Die jeweils richtige Interpretation durch entsprechende Wahl der Komponente obliegt dem
Programmierer.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/03/2 – TH/KR – 03
-----------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel zu Unions in C
/*----------------------------------------------------------------------*/
/* Programm floatbyte (C-Modul floatbyte_m.c)
*/
/*----------------------------------------------------------------------*/
/* Ausgabe der internen Darstellung von float-Werten als sedezimale
*/
/* Bytefolge (MS-Byte zuerst)
*/
/*----------------------------------------------------------------------*/
#include <stdio.h>
union floathex
{ float f;
char b[sizeof(float)];
};
/* unabhaengig von interner Darstellung */
int main(void)
{ union floathex flvar;
float r;
int i;
printf("\nInterne Darstellung von FLOAT-Werten als Bytefolge");
printf(" (MS-Byte zuerst).\n");
printf("Bitte geben Sie Zahlenwerte ein :\n\n");
while (scanf("%f",&r)!=EOF)
{ flvar.f=r;
for (i=sizeof(float)-1; i>=0; i--)
printf("%02x ",flvar.b[i]&0xff);
printf("\n\n");
}
return 0;
}
● Probelauf:
Interne Darstellung von FLOAT-Werten als Bytefolge (MS-Byte zuerst).
Bitte geben Sie Zahlenwerte ein :
0
00
00
00
00
1.0
3f 80
00
00
-1.0
bf 80
00
00
2.6
40 26
66
66
2.6e-6
36 2e
7b
a9
^Z
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C– 07/04/1 – TH/KR – 04
-----------------------------------------------------------------------------------------------------------------------------------------------------
Vorwärtsdeklaration von Struktur-Typen
● In ANSI-C ist es möglich, einen struct- oder union-Typ vorwärts zu deklarieren.
Diese Typ-Vorwärtsdeklaration besteht lediglich aus dem Wortsymbol struct bzw. union und dem
Struktur- bzw. Union-Name. Die Auflistung der Komponenten fehlt.
● Syntax :
struct
Strukturname
union
Unionname
;
● Anwendung :
Die Typ-Vorwärtsdeklaration ermöglicht die Definition von struct- bzw. union-Typen, die
gegenseitig aufeinander Bezug nehmen wobei wenigstens einer der Typnamen bereits für einen
anderen struct- bzw. union-Typ in einem übergeordneten Block vereinbart ist.
● Beispiel :
// Programm vorwdekl (C-Modul vorwdekl_m.c)
// Demonstrationsbeispiel zur Vorwaertsdeklaration
#include <stdlib.h>
#include <stdio.h>
struct st2
{ int ba;
int lu;
};
int main(void)
{ struct st2;
struct st1
{ struct st2* sp2;
int a;
};
struct st2
{ struct st1 sp1;
int b;
};
/* Typ-Vorwaertsdeklaration, setzt globale */
/* Vereinbarung von struct st2 ausser Kraft */
/* Pointer auf lokal definierte sruct st2
*/
/* lokale Definition von struct s2 */
struct st1 mys1 = { NULL, 7};
struct st2 mys2 = { mys1, 5 };
mys1.sp2 = &mys2;
printf("%d\n", (mys1.sp2)->b);
return 0;
}
/* ohne Typ-Vorwaertsdeklaration falsch */
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/00/0 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
8. Die C-Standardbibliothek
8.1. ANSI/ISO-C-Standardbibliothek
8.2. Überblick über die IO-Funktionen
8.3. Funktionen zum Zeichenklassentest
8.4. Funktionen zur Stringbearbeitung
8.5. Utility-Funktionen
8.6. Mathematische Funktionen
8.7. Datums- und Zeitfunktionen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/01/1 – TH/KR – 06
---------------------------------------------------------------------------------------------------------------------------------------------------------
ANSI/ISO-C-Standardbibliothek
•
Mit ANSI-ISO-C wurde nicht nur die Sprache C (die selbst keinerlei Funktionen enthält) sondern auch eine
Standard-Bibliothek genormt.
Eine ausschließliche Verwendung von Standard-Bibliotheks-Funktionen gewährleistet die Portabilität von
C-Programmen.
•
Die Standard-Bibliothek umfasst im wesentlichen
Ein-/Ausgabe-Funktionen
Funktionen zum Zeichenklassentest
Funktionen zur String- (und Zeichenarray-)bearbeitung
Diverse Utility-Funktionen (z.B. zur Speicherallokation)
Mathematische Funktionen
Datums- und Zeitfunktionen
Diagnose-Funktion
Funktionen zum Zugriff zu einer variablen Parameterliste
Funktionen zur Ermöglichung nicht-lokaler Sprünge
Funktionen zur Signal-Behandlung
Funktionen zur Auswahl länderspezifischer Darstellungen
-
•
Die Anwendung einiger Funktionen setzt speziell definierte Konstanten und Typen voraus.
•
Einige der "Funktionen" sind tatsächlich nicht als Funktion sondern als Makro realisiert.
•
Mit der Standard-Bibliothek eng assoziiert ist eine Gruppe von Standard-Header-Dateien, diese enthalten:
notwendige Extern-Deklarationen der Bibliotheksfunktionen (Function Prototypes)
die Definitionen von Makros, die anstelle von Funktionen realisiert sind
die Definitionen der speziellen Konstanten und Typen, die für die Anwendung der Funktionen benötigt
werden
die Definitionen weiterer implementierungsabhängiger Größen (z.B. Fehlernummern, numerische
Konstante)
die Externdeklarationen einiger Systemvariablen
•
Funktionell zusammengehörige Definitionen und Deklarationen sind jeweils in einer spezifischen HeaderDatei zusammengefaßt.
Einige Definitionen und Deklarationen sind auch mehrfach, d.h. in mehreren Header-Dateien, enthalten.
•
Jede Bibliotheks-Funktion ist mit einer oder mehreren Header-Dateien assoziiert.
Diese Header-Datei(en) ist (sind) bei Verwendung der Funktion mittels #include <datname> in das
Quellprogramm einzubinden.
•
Die Header-Dateien sind so aufgebaut, daß sie ohne Probleme in jeder beliebiger Reihenfolge und auch
mehrfach in ein C-Quellmodul eingebunden werden können.
•
Die ANSI/ISO-C-Norm C90 (einschließlich Amendment 1) schreibt die Namen und den Inhalt von
18 Header-Dateien vor ( Standard-Header-Dateien).
Die neuere Norm C99 fügt 6 weitere Header-Dateien hinzu.
•
Reale ANSI-C-Implementierungen enthalten i.a. darüberhinaus weitere Header-Dateien entsprechend einer
Erweiterung des Funktionsumfangs der Bibliothek.
Außerdem können einzelne Standard-Header-Dateien auch weitere - in der Norm nicht vorgeschriebene Deklarationen und Definitionen enthalten.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/01/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Die Standard-Header-Dateien von ANSI/ISO-C (1)
<assert.h>
Deklaration einer Diagnose-Funktion bzw.
Definition eines die Funktion ersetzenden Makros
<ctype.h>
Deklaration von Funktionen zum Zeichenklassentest bzw.
Definition ersetzender Makros
<errno.h>
Deklaration der System-Fehlervariablen und Definition von
System-Fehlercodes
<float.h>
Definition gleitkommaarithmetikbezogener Konstanten
<iso646.h>
Definition von Ersatzdarstellungen einiger Operatorsymbole
<limits.h>
Definition von Konstanten zur Beschreibung des Wertebereichs der Ganzzahl-Typen
<locale.h>
Definition von Konstanten und eines Typs sowie
Deklaration von Funktionen zur Auswahl länderspezifischer
Darstellungen
<math.h>
Deklaration mathematischer Funktionen
<setjmp.h>
Deklaration von Funktionen zur Realisierung nicht-lokaler
Sprünge
<signal.h>
Definition von Konstanten und Deklaration von Funktionen
zur Signal-Behandlung
<stdarg.h>
Definition von Typen und Makros zum Zugriff zu einer
variablen Parameterliste
<stddef.h>
Definition einiger Typen und der Konstanten NULL
<stdio.h>
Definition von Konstanten und Typen, Deklaration von
Funktionen zur Ein- und Ausgabe (I/O-Funktionen)
<stdlib.h>
Definition von Konstanten und Typen, sowie Deklaration
diverser Utility-Funktionen
(z.B. String-Konvertierungsfunktionen, Speicherverwaltungsfunktionen, Environment-Funktionen, Such- und
Sortier-Funktionen, Integer-Arithmetik-Funktionen,
Multibyte-Zeichen-Funktionen)
<string.h>
Deklaration von Funktionen zur Stringbearbeitung und
Zeichenarraybearbeitung
<time.h>
Definition von Konstanten und Typen, Deklaration von Zeitund Datums-Funktionen
<wchar.h>
Definition von Konstanten und Makros sowie Deklaration von
Funktionen zur Bearbeitung von "wide characters" (Ein/Ausgabe, Stringbearbeitung)
<wctype.h>
Deklaration von Funktionen zum Test und Klassifizieren von
"wide characters"
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/01/3 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Die Standard-Header-Dateien von ANSI/ISO-C (2)
Mit der neueren C-Norm C99 (1999 verabschiedet als ISO/IEC 9899:1999, ergänzt 2001 durch Technical
Corrigendum 1 TC1, 2004 durch Technical Corrigendum 2 TC2 und 2007 durch Technical Corrigendum 3 TC3, in
dieser Version bezeichnet als ISO/IEC 9899:TC3) wurden – neben einer Reihe von Änderungen und Ergänzungen
der Sprache selbst – auch die Standardbibliothek erweitert und 6 weitere Header-Dateien hinzugefügt.
<complex.h>
Definition von Makros und Deklaration von Funktionen zur
Unterstützung komplexer Arithmetik
<fenv.h>
Definition von Makros sowie Deklaration von Typen und
Funktionen, die einen Zugriff zur
implementierungsabhängigen Floating Point Umgebung
ermöglichen
<inttypes.h>
Einbinden von <stdint.h> und Bereitstellung ergänzender
Verwendunsgmöglichkeiten für die darin deklarierten Typen
<stdbool.h>
Definition von Makros zur einfacheren Verwendung des in der
Sprache definierten logischen Datentyps _Bool
<stdint.h>
Deklaration von Ganzzahl-Datentypen mit besonderen
Eigenschaften hinsichtlich ihrer Darstellungsbreite,
Definition von Makros zur Bezeichnung des jeweiligen
Minimal- und Maximalwertes dieser Ganzzahl-Datentypen
<tgmath.h>
Einbinden von <math.h> und <complex.h>,
Definition typ-generischen Makros zur Anwendung der in
diesen Header-Dateien deklarierten mathematischen
Funktionen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/02/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Überblick über die I/O-Funktionen der ANSI-C-Standardbibliothek (1)
(Funktionsdeklarationen in <stdio.h>)
•
Funktionen zur Dateibearbeitung (ohne Schreiben und Lesen)
int fclose (FILE *fp);
Schließen einer Datei
int fflush (FILE *fp);
Herausschreiben eines Dateipuffers
int fgetpos (FILE *fp, fpos_t *ptr);
Ermitteln der aktuellen Bearbeitungsposition
FILE *fopen (char *path, char *mode);
Öffnen einer Datei
FILE *freopen (char *path, char *mode,
FILE *fp);
Erneutes Öffnen einer offenen Datei
int fseek (FILE *fp, long offs,
int org);
Verändern der Bearbeitungsposition
int fsetpos (FILE *fp, fpos_t *ptr);
Verändern der Berabeitungsposition
long ftell (FILE *fp);
Ermitteln der aktuellen Bearbeitungsposition
void rewind (FILE *fp);
Zurücksetzen auf den Dateianfang
void setbuf (FILE *fp, char *buf);
Bereitstellen eines
benutzerdefinierten Dateipuffers
int setvbuf (FILE *fp, char *buf, int
mode, size_t size);
Bereitstellen eines benutzerdefinierten Dateipuffers
•
Funktionen zur Dateiverwaltung
int remove (char *path);
Löschen einer Datei
int rename (char *oldpath, char
*newpath);
Umbenennen einer Datei
FILE *tmpfile (void);
Erzeugen und Öffnen einer temporären
Datei
char *tmpnam (char s[L_tmpnam]);
Erzeugen eines noch nicht vorhandenen
Dateinamens
•
Funktionen zur Fehlerbehandlung
void clearerr (FILE *fp);
Rücksetzen des Fehler- und des EOFFlags
int feof (FILE *fp);
Überprüfung des EOF-Flags
int ferror (FILE *fp);
Überprüfung des Fehler-Flags
void perror (char *s);
Ausgabe von Fehlermeldungen nach
stderr
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/02/2 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Überblick über die I/O-Funktionen der ANSI-C-Standardbibliothek (2)
(Funktionsdeklarationen in <stdio.h>)
•
Funktionen zur Ein-und Ausgabe von/nach stdin und stdout
int getchar (void);
zeichenweise Eingabe von stdin
char *gets (char *s);
zeilenweise Eingabe von stdin
int printf (char *ctrl, ...);
formatierte Ausgabe nach stdout
int putchar (int c);
zeichenweise Ausgabe nach stdout
int puts (char *s);
zeilenweise Ausgabe nach stdout
int scanf (char *ctrl, ...);
formatierte Eingabe von stdin
int vprintf (char *ctrl, va_list arg);
formatierte Ausgabe nach stdout
•
Funktionen zur Ein-/Ausgabe beliebiger Dateien
int fgetc (FILE *fp);
zeichenweise Eingabe
char *fgets (char *s, int n, FILE *fp);
zeilenweise Eingabe
int fprintf (FILE *fp,char *ctrl, ...);
formatierte Ausgabe
int fputc (int c, FILE *fp);
zeichenweise Ausgabe
int fputs (char *s, FILE *fp);
stringweise Ausgabe
size_t fread (void *ptr, size_t size,
size_t count, FILE *fp);
datenobjektorientierte Eingabe
int fscanf (FILE *fp, char *ctrl, ...);
formatierte Eingabe
datenobjektorientierte Ausgabe
size_t fwrite (void *ptr, size_t size,
size_t count, FILE *fp);
int getc (FILE *fp);
zeichenweise Eingabe
int putc (int c, FILE *fp);
zeichenweise Ausgabe
int ungetc (int c, FILE *fp);
Rückgabe eines Zeichens in Eingabepuffer
int vfprintf (FILE * fp, char *ctrl,
va_list arg);
formatierte Ausgabe
•
Funktionen zur Ein-/Ausgabe in/aus Strings
int sprintf (char *s, char *ctrl, ...);
format. Ausgabe in einen String
int sscanf (char *s, char *ctrl, ...);
format. Eingabe aus einem String
int vsprintf (char *s, char *ctrl,
va_list arg);
format. Ausgabe in einen String
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/03/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der ANSI-C-Standardbibliothek zum Zeichenklassentest
Bei Verwendung einer der folgenden Funktionen (die häufig als Makros realisiert sind) muß die zugehörige
Standard-Header-Datei mittels #include <ctype.h> in das Quellmodul eingebunden werden.
•
Alle Funktionen haben einen int-Übergabeparameter. Der übergebene aktuelle int-Parameter muß in den
Wertebereich von unsigned char fallen oder EOF sein.
•
Funktionen zur Überprüfung eines Zeichens auf eine bestimmte Klassenzugehörigkeit
Funktionswert:
•
- != 0 (TRUE), wenn der übergebene aktuelle Parameter in die
jeweils überprüfte Zeichenklasse fällt,
- == 0 (FALSE), wenn der übergebene aktuelle Parameter nicht
in die überprüfte Zeichenklasse fällt
- einen undefinierten Wert, wenn der übergebene aktuelle
Parameter nicht als unsigned char darstellbar und nicht
gleich EOF ist.
int isalnum (int c);
Überprüfung auf Buchstabe (Groß- oder Klein-)
oder (Dezimal-)Ziffer
int isalpha (int c);
Überprüfung auf Buchstabe (Groß- oder Klein-)
int iscntrl (int c);
Überprüfung auf Steuerzeichen
int isdigit (int c);
Überprüfung auf (Dezimal-)Ziffer
int isgraph (int c);
Überprüfung auf darstellbare Zeichen,
ausgenommen BLANK (SPACE)
int islower (int c);
Überprüfung auf Klein-Buchstaben
int isprint (int c);
Überprüfung auf darstellbare Zeichen,
einschließlich BLANK (SPACE)
int ispunct (int c);
Überprüfung auf darstellbare Zeichen, ausgenommen BLANK, Buchstaben und (Dezimal-)Ziffern
int isspace (int c);
Überprüfung auf BLANK (SPACE), FF, NEWLINE,
CR, HT und VT
int isupper (int c);
Überprüfung auf Groß-Buchstaben
int isxdigit (int c);
Überprüfung auf Hexadezimal-Ziffer
Funktionen zur Zeichenumwandlung
Funktionswert:
- das umgewandelte Zeichen, falls eine Umwandlung relevant
ist,
- den als aktuellen Parameter übergebenen Wert, falls eine
Umwandlung nicht relevant ist.
int tolower (int c);
Umwandlung von Groß- in Klein-Buchstaben
int toupper (int c);
Umwandlung von Klein- in Groß-Buchstaben
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/1 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der ANSI-C-Standardbibliothek zur Stringbearbeitung
Überblick (1)
● Funktions-Deklarationen (Function Prototypes) in : <string.h>
● Funktionen zum Kopieren von Strings und Zeichenarrays
void *memcpy(void *s1, const void *s2,
size_t n);
Kopieren von n Zeichen vom Array s2
in das Array s1
void *memmove(void *s1, const void *s2,
size_t n);
Kopieren von n Zeichen vom Array s2
in das Array s1(Überlappung ist
zulässig)
char *strcpy(char *s1, const char *s2);
Kopieren des Strings s2 in den
String s1.
char *strncpy(char *s1, const char *s2,
size_t n);
Kopieren von maximal n Zeichen vom
String s2 in den String s1 (ggf.
kein Abschluß mit '\0')
● Funktionen zum Aneinanderhängen von Strings
char *strcat(char *s1, const char *s2);
Anhängen von String s2 an den
String s1
char *strncat(char *s1, const char *s2,
size_t n);
Anhängen von maximal n Zeichen
des Strings s2 an den Str. s1
(Abschluß immer mit '\0')
● Funktionen zum Vergleich von Strings und Zeichenarrays
int memcmp(const void *s1,
const void *s2, size_t n);
Vergleich von n Zeichen der Arrays
s1 und s2
int strcmp(const char *s1,
const char *s2);
Vergleich der Strings s1 und s2
int strncmp(const char *s1,
const char *s2, size_t n);
Vergleich der Strings s1 und s2
(max. n Zeichen)
int strcoll(const char *s1,
const char *s2);
Vergleich der Strings s1 und s2
gemäß dem mit setlocale() festgelegten länderspezifischen Zeichencode
size_t strxfrm(char *s1, const char *s2,
size_t n);
Code-Transformation von maximal n
Zeichen des Strings s2 in den
String s1
Quellcode: länderspezifisch
Zielcode : der von strcmp()
verwendete Zeichencode
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen der ANSI-C-Standardbibliothek zur Stringbearbeitung
Überblick (2)
•
•
Funktionen zum Suchen in Strings und Arrays
void *memchr(const void *s, int c,
size_t n);
Durchsuchen von max n Zeichen des Arrays
s nach dem ersten Auftritt des Zeichens c
char *strchr(const char *s, int c);
Durchsuchen des Strings s nach dem ersten
Auftreten des Zeichens c
char *strrchr(const char *s, int c);
Durchsuchen des Strings s nach dem
letzten Auftreten des Zeichens c
char *strpbrk( const char *s1,
const char *s2);
Durchsuchen des Strings s1 nach dem
ersten Auftreten irgendeines der im
String s2 enthaltenen Zeichen
char *strstr( const char *s1,
const char *s2);
Durchsuchen des Strings s1 nach dem
ersten Auftreten des Strings s2
size_t strspn( const char *s1,
const char *s2);
Ermittlung der Länge des Anfangs des
Strings s1, der nur aus in s2 enthaltenen
Zeichen besteht
== Index des 1. Auftritts eines Zeichens
in s1, das mit keinem Zeichen in s2
übereinstimmt
size_t strcspn( const char *s1,
const char *s2);
Ermittlung der Länge des Anfangs des
Strings s1, der nur aus in s2 nicht
enthaltenen Zeichen besteht
== Index des 1. Auftritts eines Zeichens
in s1, das mit irgendeinem Zeichen in s2
übereinstimmt
char *strtok(char *s1,
const char *s2);
Zerlegen des Strings s1 in die Teilstrings, die durch im String s2 enthaltene Zeichen getrennt werden.
Aufeinanderfolgende Aufrufe liefern
jeweils den Pointer auf den nächsten
Teilstring bzw den NULL-Pointer (Ende!).
Ab dem 2. Aufruf muss für s1 der NULLPointer übergeben werden.
Funktion zum Füllen eines Arrays mit einem Zeichen
void *memset(void *s, int c,
size_t n);
•
Funktion zur Ermittlung der Länge eines Strings
size_t strlen(const char *s);
•
Ablage des Zeichens c in n aufeinanderfolgende Elemente des (char-) Arrays s
Ermittlung der Länge des Strings s
Funktion zur Ermittlung einer Fehlermeldung
char *strerror(int errnum);
Ermittlung der Fehlermeldung, die zur
Fehlernummer errnum gehört
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/3 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Ausgewählte Stringbearbeitungsfunktionen (1)
strcpy
-
Kopieren eines Strings
#include <string.h>
char *strcpy(char *str1, const char *str2);
Aktion: "strcpy()" kopiert den String "str2" in den String "str1"
Übergabeparameter : str1
str2
Funktionswert :
String, in den kopiert wird (Zielstring)
String, der kopiert wird (Quellstring)
Pointer auf den Zielstring, d.h. "str1" (== 1.Parameter)
strcat
-
Konkatenation zweier Strings
#include <string.h>
char *strcat(char *str1, const char *str2);
Aktion: "strcat()" hängt den String "str2" an den String "str1" an.
(der resultierende String "str1" wird mit dem '\0'-Zeichen abgeschlossen)
Übergabeparameter : str1
str2
Funktionswert:
strcmp
String, an den angehängt wird
String, der angehängt wird
Pointer auf den resultierenden String, d.h. "str1" (== 1.Parameter)
-
Vergleich zweier Strings
#include <string.h>
int strcmp(const char *str1, const char *str2);
Aktion: "strcmp()" vergleicht den String "str1" lexikographisch mit dem String "str2"
Übergabeparameter : str1
str2
erster zu vergleichender String
zweiter zu vergleichender String
Funktionswert :
wenn "str1" kleiner "str2" ist
wenn "str1" gleich "str2" ist
wenn "str1" größer "str2" ist
<0
==0
>0
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/4 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Ausgewählte Stringbearbeitungsfunktionen (2)
strlen
-
Ermittlung der Länge eines Strings
#include <string.h>
size_t strlen(const char *str);
Aktion: "strlen()" ermittelt die Länge des Strings "str" in Anzahl Zeichen
(ohne abschliessendes '\0'-Zeichen)
Übergabeparameter : str
String, dessen Länge ermittelt werden soll
Funktionswert :
Länge des Strings "str" in Anzahl Zeichen
Anmerkung :
Der Typ size_t ist der "natürliche" unsigned-Typ (definiert in <string.h>)
strchr
-
Durchsuchen eines Strings nach einem Zeichen
#include <string.h>
char *strchr(const char *str, int c);
Aktion: "strstr()" durchsucht den String "str" nach dem ersten Auftreten des Zeichens "c".
Übergabeparameter : str
C
Funktionswert:
zu durchsuchender String
Suchzeichen
Pointer auf die Stelle im String "str", an der das Zeichen "c" erstmals auftritt
bzw NULL-Pointer, wenn das Zeichen "c" nicht im String "str" enhalten ist
strstr
-
Durchsuchen eines Strings nach einem anderen String
#include <string.h>
char *strstr(const char *str1, const char *str2);
Aktion: "strstr()" durchsucht den String "str1" nach dem ersten Auftreten des Strings "str2".
Übergabeparameter : str1
str2
Funktionswert:
zu durchsuchender String
Suchstring
Pointer auf die Stelle im String "str1", an der der String "str2" beginnt bzw
NULL-Pointer, wenn der String "str2" nicht im String "str1" enhalten ist
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/5 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Ausgewählte Zeichenarray-Bearbeitungsfunktionen (1)
memcpy
-
Kopieren einer Zeichenfolge bestimmter Länge
#include <string.h>
void *memcpy(void *s1, const void *s2, size_t n);
Aktion: "memcpy()" kopiert "n" Zeichen aus dem durch "s2" referierten – als Zeichenfolge aufgefassten – Datenobjekt (Quell-Speicherbereich) in das duch "s1" referierte – als Zeichenfolge
aufgefasste – Datenobjekt (Ziel-Speicherbereich).
Übergabeparameter : s1
s2
n
Pointer auf Ziel-Datenobjekt ( Zielzeichenfolge)
(Ziel-Speicherbereich, Speicherbereich in der kopiert wird)
Pointer auf Quell-Datenobjekt (Quellzeichenfolge)
(Quell-Speicherbereich, Speicherbereich aus dem kopiert wird)
Anzahl der zu kopierenden Zeichen
Funktionswert :
Pointer auf den Ziel-Speicherbereich, d.h. "s1" (== 1.Parameter)
Anmerkungen:
1. Quell- und Ziel-Speicherbereich dürfen sich nicht überlappen
2. Der Typ size_t ist der "natürliche" unsigned-Typ (definiert in <string.h>)
memcmp
-
Vergleich zweier Zeichenfolgen bestimmter Länge
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
Aktion: "memcmp()" vergleicht die ersten "n" Zeichen des durch "s1" referierten – als Zeichenfolge
aufgefassten –Datenobjekts lexikographisch mit den ersten "n" Zeichen des durch "s2"
referierten – als Zeichenfolge aufgefassten – Datenobjekts
Für den Vergleich werden die Zeichen als unsigned char interpretiert.
Übergabeparameter : s1
s2
n
Pointer auf das erste zu vergleichende Datenobjekt (erste Zeichenfolge)
Pointer auf das zweite zu vergleichende Datenobjekt (zweite Zeichenf.)
Anzahl der zu vergleichenden Zeichen
Funktionswert :
wenn das erste unterschiedliche Zeichen bei der durch "s1" referierten
Zeichenfolge kleiner als bei der durch "s2" referierten Zeichenfolge ist.
wenn die ersten "n" Zeichen in beiden Zeichnfolgen gleich sind
wenn das erste unterschiedliche Zeichen bei der durch "s1" referierten
Zeichenfolge größer als bei der durch "s2" referierten Zeichenfolge ist.
<0
==0
>0
Anmerkung :
Der Typ size_t ist der "natürliche" unsigned-Typ (definiert in <string.h>)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/04/6 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Ausgewählte Zeichenarray-Bearbeitungsfunktionen (2)
memchr
-
Suchen eines Zeichens in einer Zeichfolge bestimmter Länge
#include <string.h>
void *memchr(const void *s, int c, size_t n);
Aktion: "memchr()" durchsucht die ersten "n" Zeichen des durch "s" referierten – als Zeichenfolge
aufge fassten – Datenobjekts nach dem ersten Auftreten des – in unsigned char umgewandelten – Zeichens "c".
Übergabeparameter : s
c
n
Pointer auf das zu durchsuchende Datenobjekt
(zu durchsuchende Zeichenfolge)
(= Speicherbereich, der durchsucht wird)
Zeichen, nach dem gesucht wird
Anzahl der bei der Suche zu berücksichtigenden Zeichen
(== Länge der zu durchsuchenden Zeichenfolge)
Funktionswert :
Pointer auf die Stelle, an der das Zeichen "c" erstmals auftritt
bzw NULL-Pointer, wenn das Zeichen "c" nicht enhalten ist
Anmerkung :
Der Typ size_t ist der "natürliche" unsigned-Typ (definiert in <string.h>)
memset
-
Füllen eines Speicherbereichs mit einem Zeichen
#include <string.h>
void *memset(void *s, int c, size_t n);
Aktion: "memset()" setzt die ersten "n" Zeichen des durch "s" referierten Speicherbereichs auf den
Wert des – in unsigned char umgewandelten – Zeichens "c"
Übergabeparameter : s
c
n
Pointer auf den zu füllenden Speicherbereich
Zeichen, mit dem derSpeicherbereich gefüllt wird
Länge des zu füllenden Speicherbereichs (in Bytes)
Funktionswert :
Pointer auf gefüllten Speicherbereich (== Parameter "s")
Anmerkung :
Der Typ size_t ist der "natürliche" unsigned-Typ (definiert in <string.h>)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Allgemeine Utility-Funktionen der ANSI-C-Standardbibliothek (1)
● In der Standard-Header-Datei <stdlib.h> sind die Deklarationen einer Reihe von Funktionen enthalten,
die man zusammenfassend als Allgemeine Utility-Funktionen bezeichnen kann. Diese Funktionen lassen
sich in die folgenden Gruppen einteilen :
▻ Funktionen zur Umwandlung von Strings in numerische Werte
▻ Funktionen zur Erzeugung von Pseudo-Zufallszahlen
▻ Funktionen zur dynamischen Speicherverwaltung
▻ Environment -Funktionen
▻ Such- und Sortierfunktionen
▻ Integer-Arithmetik -Funktionen
▻ Funktionen zur Verarbeitung erweiterter Zeichensätze (nicht in allen Implementierungen enthalten)
● Bei Verwendung dieser Funktionen ist die Standard-Header-Datei <stdlib.h> einzubinden :
#include <stdlib.h>
● In dieser Header-Datei sind auch die folgenden Typen und Konstanten definiert:
▻ Typ div_t
ein Structure-Typ für den Funktionswert von div().
typedef struct {int quot, rem;} div_t;
▻ Typ ldiv_t
ein Structure-Typ für den Funktionswert von ldiv().
typedef struct {long quot, rem;} ldiv_t;
▻ Typ size_t
der "natürliche" unsigned-Typ, d.h. der Typ, den der sizeofOperator liefert.
▻ Typ wchar_t
ein ganzzahliger Typ, dessen Wertebereich den größten vom Compiler
unterstützten Zeichensatz darstellen kann(nur definiert, wenn die
Funktionen zur Verarbeitung erweiterter Zeichensätze implementiert
sind).
▻ Konstante EXIT_FAILURE
ein ganzzahliger Wert zur Kennzeichnung einer fehlerhaften
Programmbeendigung (Rückgabewert von exit()).
▻ Konstante EXIT_SUCCESS
ein ganzzahliger Wert zur Kennzeichnung einer erfolgreichen
Programmbeendigung (Rückgabewert von exit()).
▻ Konstante MB_CUR_MAX
ein positiver ganzzahliger Wert, der angibt aus wieviel Bytes ein MultiByte-Character maximal bestehen kann (nur definiert, wenn die
Funktionen zur Verarbeitung erweiterter Zeichensätze implementiert
sind).
▻ Konstante RAND_MAX
die größte von rand() erzeugte Pseudo-Zufallszahl (ganzzahlig),
dieser implementierungs-abhängige Wert muss mindestens 32767
sein.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Allgemeine Utility-Funktionen der ANSI-C-Standardbibliothek (2)
•
•
•
Funktionen zur Umwandlung von Strings in numerische Werte
double atof(const char *s);
Umwandlung des Strings s in einen doubleWert
int atoi(const char *s);
Umwandlung des Strings s in einen intWert
long atol(const char *s);
Umwandlung des Strings s in einen longWert
double strtod(const char *s,
char **endp);
Umwandlung des Strings s in einen doubleWert, Speicherung eines Pointers auf das
erste nicht umgewandelte Zeichen von s in
*endp (nur wenn endp != NULL)
long strtol(const char *s,
char **endp, int base);
Umwandlung des Strings s in einen
longWert mit der Basis base, Speicherung
eines Pointers auf das erste nicht
umgewandelte Zeichen von s in *endp (nur
wenn endp != NULL)
unsigned long strtoul(char *s,
char **endp, int base);
Umwandlung des Strings s in einen
unsigned long-Wert mit der Basis base,
Speicherung eines Pointers auf das erste
nicht umgewandelte Zeichen von s in *endp
(nur wenn endp != NULL)
Funktionen zur Erzeugung von Pseudo-Zufallszahlen
int rand(void);
Erzeugung einer ganzzahligen PseudoZufallszahl im Bereich 0 .. RAND_MAX
void srand(unsigned int seed);
Setzen des Initialisierungswertes für die
durch rand() erzeugte Zufallszahlenfolge
(default : 1)
Funktionen zur dynamischen Speicherverwaltung
void *calloc(size_t nobj,
size_t size);
Allokation eines Speicherblocks für nobj
Objekte je der Länge size,
Initialisierung aller Speicherplätze mit
0
void free(void *p);
Freigabe des Speicherbereichs auf den p
zeigt
void *malloc(size_t size);
Allokation eines Speicherblocks derLänge
size (keine Initialisierung)
void *realloc(void *p, size_t size);
Änderung der Größe eines bereits vorher
allokierten - durch p referierten
Speicherblocks - auf den neuen Wert size
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/3 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Allgemeine Utility-Funktionen der ANSI-C-Standardbibliothek (3)
•
•
•
Environment-Funktionen
void abort(void);
Anormale Beendigung eines Programms
int atexit(void (*fcn)(void));
Registrierung der Funktion (*fcn)() als
eine Funktion, die bei Programmbeendigung mittels exit() aufgerufen wird
void exit(int status);
Normale Beendigung eines Programms
char *getenv(const char *name);
Ermittlung des zur Environment-Variablen
name gehörenden Strings (Wert der
Environment-Variablen)
(implementierungsabhängig)
int system(const char *s);
Übergabe des - ein Kommando bedeutenden - Strings s an die Betriebssystem-Umgebung zur Ausführung
Such- und Sortier-Funktionen
void *bsearch(const void *key, const
void *base, size_t n,
size_t size,
int (*cmp)(const void *,
const void *));
Durchsuchen eines in aufsteigender
Ordnung sortierten - durch base referierten - Feldes von n Objekten der
Länge size auf Übereinstimmung mit dem
Suchschlüssel *key.
Der Vergleich erfolgt mittels der
Funktion (*cmp)().
void qsort(void *base,
size_t n, size_t size,
int (*cmp)(const void *,
const void *));
Sortieren des - durch base referierten Feldes von n Objekten der Länge size in
aufsteigender Ordnung mittels der
Vergleichsfunktion (*cmp)().
Integer-Arithmetik-Funktionen
int abs(int i);
Ermittlung des Absolutwertes von i
(int-Wert)
div_t div(int dend, int disor);
Ermittlung des ganzzahligen Quotienten
und des Restes der Division von
dend durch disor (int-Werte)
long labs(long n);
Ermittlung des Absolutwertes von n
(long-Wert)
ldiv_t ldiv(long dend, long disor);
Ermittlung des ganzzahligen Quotienten
und des Restes der Division von
dend durch disor (long-Werte)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/4 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Funktionen zur Erzeugung von Pseudo-Zufallszahlen
rand
-
Erzeugung einer ganzzahligen Pseudo-Zufallszahl
#include <stdlib.h>
int rand(void);
Aktion: "rand()" ermittelt die nächste Pseudo-Zufallszahl aus einer durch einen Initialisierungswert
festgelegten ganzzahligen Pseudo-Zufallszahlenfolge. Die Pseudo-Zufallszahlen liegen im
Bereich 0 .. RAND_MAX.
Übergabeparameter : keiner
Funktionswert :
die erzeugte Pseudo-Zufallszahl
Anmerkung :
Sollen die Pseudo-Zufallszahlen in einem anderen Bereich als
0 .. RAND_MAX liegen, müssen sie durch eine geeignete Abbildungsfunktion
in den gewünschten Bereich transformiert werden.
Beispiel : Sollen die Pseudo-Zufallszahlen im Bereich 0 .. ZMAX liegen
(ZMAX < RAND_MAX), ist auf die erzeugten Zahlen die Operation
"Modulo (ZMAX+1)" anzuwenden.
srand
-
Setzen des Initialisierungswertes einer Pseudo-Zufallszahlenfolge
#include <stdlib.h>
void srand(unsigned int seed);
Aktion: "srand()" setzt den Initialisierungswert für eine durch den fortlaufenden Aufruf von "rand()"
erzeugbare Pseudo-Zufallszahlen-Folge auf den Parameter "seed".
Ein bestimmter Wert von "seed" führt immer zur Erzeugung der gleichen Zahlenfolge.
Übergabeparameter : seed
Initialisierungswert für eine Pseudo-Zufallszahlenfolge
Funktionswert :
keiner
Anmerkung :
Der Default-Initialisierungswert für eine Pseudo-Zufallszahlenfolge ist 1,
d,h. der Aufruf von "rand()" ohne vorherigen Aufruf von "srand()" führt zu
derselben Pseudo-Zufallszahlenfolge, wie sie der vorherige Aufruf von "rand()"
mit dem Parameter 1 bewirken würde.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/5 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zur Erzeugung von Pseudo-Zufallszahlen (1)
/* --------------------------------------------------------------------Programm randdemo (C-Modul randdemo_m.c)
--------------------------------------------------------------------Demonstrationsprogramm für die Verwendung von rand() und srand()
Es werden MAX_ANZ_ZZ Pseudo-Zufallsszahlen zwischen 0 und einem
angebbaren Maximalwert erzeugt,von diesen werden nur die ersten
anz Zahlen (anz ebenfalls angebbar) ausgegeben.
Über die Gesamtheit der erzeugten Zahlen wird die Wahrscheinlichkeitsverteilung ausgegeben.
--------------------------------------------------------------------- */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_ANZ_ZZ
10000000L
int main(void)
{
int zz;
int mzz;
int mdis;
unsigned int sd;
long* ip;
int i;
long l;
printf("\nErzeugung von %ld Pseudo-Zufallszahlen :\n", MAX_ANZ_ZZ);
printf("\nZufallszahlenbereich : 0 .. ? ");
scanf("%d", &mzz);
printf("\nInitialisierungswert der Folge ? ");
scanf("%u", &sd);
printf("\nAnzahl der auszugebenden Zahlen ? ");
scanf("%d", &mdis);
ip = calloc(mzz+1,sizeof(long));
/* Alternative :
ip = malloc((mzz+1)*sizeof(long));
memset(ip, '\0', (mzz+1)*sizeof(long));
*/
putchar('\n');
srand(sd);
for (l=0; l<MAX_ANZ_ZZ; l++)
{
zz=rand()%(mzz+1);
if (l< mdis)
printf("%d ", zz);
++ip[zz];
}
putchar('\n');
printf("\nWahrscheinlichkeitsverteilung : \n");
for (i=0; i<= mzz; i++)
printf("\n%4d : %.6f", i, (double)ip[i]/MAX_ANZ_ZZ);
printf("\n\n");
return 0;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/6 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zur Erzeugung von Pseudo-Zufallszahlen (2)
Beispiel für einen Programmlauf
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/7 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Ausgewählte Environment-Funktionen
exit
-
Beendigung des Programms
#include <stdlib.h>
void exit(int status);
Aktion: "exit()" beendet das laufende Programm (normale Beendigung).
Vor der Rückkehr zum Aufrufer des Programms (z.B. der Kommandoprozessor des Betriebssystems) werden alle gegebenenfalls mit "atexit()" festgelegten Funktionen aufgerufen, sowie
alle noch nicht herausgeschriebenen Datei-Schreib-Puffer herausgeschrieben und alle offenen
Dateien geschlossen.
Der Parameter "status" wird als Program Return Code dem Aufrufer des Programms
übergeben (auf der BS-Ebene kann er mit geeigneten System Calls ermittelt werden).
I.a. ist es üblich (aber nicht verpflichtend) eine fehlerfreie Programmbeendigung mit dem
Return Code 0 zu kennzeichnen, Werte != 0 stehen i.a. für Fehlergründe, die zur
Programmbeendigung geführt haben
Übergabeparameter : status
Rückgabewert des Programms (Program Return Code)
Funktionswert :
keiner, da die Funktion nicht zurückkehrt
Anmerkung :
Die Funktion "exit()" sollte i.a. – wenn überhaupt – nur aufgerufen werden,
wenn das Programs aus einer anderen Funktion als "main()" heraus beendet
werden soll (was aber einen – nur wenn wohl durchdacht auch tolerierbaren –
Verstoß gegen Grundsätze einer strukturierten Programmierung darstellt).
Normalerweise gibt es keinen Grund, "exit()" von "main()" aus aufzurufen.
(eine Beendigung von "main()" bewirkt das gleiche wie der Aufruf von "exit()")
system
-
Ausführung eines Programms durch das Betriebssystems
#include <stdlib.h>
int system(const char *s);
Aktion: "system()" ruft den Kommandoprozessor des Betriebssystems auf und übergibt diesem die
durch "s" referierte Kommandozeile zur Ausführung.
Übergabeparameter : s
Funktionswert :
Pointer auf die Kommandozeile zum Aufruf des Programms
(Programmpfad einschließlich Programmparameter)
- falls für "s" der NULL-Pointer übergeben wird :
==0, wenn kein Kommandoprozessor verfügbar ist
!=0, wenn ein Kommandoprozessor verfügbar ist
- falls für "s" ein Wert != NULL übergeben wird :
ein implementierungsabhängiger Wert
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/8 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Such- und Sortierfunktionen
bsearch
-
Durchsuchen eines in aufsteigender Ordnung sortierten Arrays
#include <stdlib.h>
void *bsearch(const void *key, const void *base, size_t n,
size_t size, int (*cmp)(const void*, const void*));
Aktion: "bsearch()" durchsucht das durch base referierte Array nach dem Auftritt eines Elements,
das mit dem durch key referierten Suchschlüssel übereinstimmt. Für den Vergleich des
Suchschlüssels mit den einzelnen Array-Elementen ruft die Funktion die durch cmp referierte
Vergleichsfunktion auf (Pointer auf Suchschlüssel wird dabei als erster Parameter übergeben).
Die Vergleichsfunktion muss folgende Funktionswerte erzeugen :
<0, wenn das erste Element kleiner als das zweite ist (Suchschlüssel < Array-Element)
==0, wenn beide Elemente gleich sind (Suchschlüssel == Array-Element)
>0, wenn das erste Element größer als das zweite ist (Suchschlüssel > Array-Element)
Übergabeparameter : key
base
n
size
cmp
Funktionswert :
qsort
Pointer auf Suchschlüssel
Pointer auf das zu durchsuchende Array
Arraygröße (== Anzahl der Array-Elemente)
Größe eines Array-Elementes in Bytes
Pointer auf Vergleichsfunktion, die für das Suchen verwendet wird
- Pointer auf das gefundene Element bei Erfolg
- NULL-Pointer bei Misserfolg
-
Sortieren eines Arrays in aufsteigender Ordnung
#include <stdlib.h>
void qsort(void *base, size_t n, size_t size,
int (*cmp)(const void*, const void*));
Aktion: "qsort()" sortiert das durch base referierte Array nach dem Quick-Sort-Verfahren.
Für den Vergleich von jeweils zwei Elementen ruft die Funktion die durch cmp referierte
Vergleichsfunktion auf. Dieser werden die Pointer auf die zu vergleichenden Elemente als
Parameter übergeben. Sie muss folgende Funktionswerte erzeugen :
<0, wenn das erste Element kleiner als das zweite ist
==0, wenn beide Elemente gleich sind
>0, wenn das erste Element größer als das zweite ist
Übergabeparameter : base
n
size
cmp
Funktionswert :
keiner
Pointer auf das zu sortierende Array
Arraygröße (== Anzahl der Array-Elemente)
Größe eines Array-Elementes in Bytes
Pointer auf Vergleichsfunktion, die für das Sortieren verwendet wird
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/05/9 – TH/KR – 01
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsbeispiel zur Bibliotheksfunktion qsort()
/* ----------------------------------------------------------------------C-Modul qsortdemo1_m.c
----------------------------------------------------------------------Einfaches Demo-Programm zur Bibliotheksfunktion qsort()
Sortieren von double-Werten, die von der Standard-Eingabe eingelesen
werden, Ausgabe der sortierten Werte in die Standard-Ausgabe
----------------------------------------------------------------------- */
#include <stdlib.h>
#include <stdio.h>
#define MAXANZ 10
int lesenDoubleWerte(double*, int);
void ausDoubleWerte(double*, int);
int vglDouble(const void *, const void *);
int main(void)
{ int anz;
double darr[MAXANZ];
anz = lesenDoubleWerte(darr, MAXANZ);
qsort(darr, anz, sizeof(double), vglDouble);
ausDoubleWerte(darr, anz);
return 0;
}
int vglDouble(const void *d1, const void *d2)
{ int iRet = 0;
if (*(double*)d1<*(double*)d2)
iRet = -1;
else if (*(double*)d1>*(double*)d2)
iRet = 1;
return iRet;
//return *(double*)d1 - *(double*)d2; // geht nicht !!! Warum ???
}
int lesenDoubleWerte(double* dfeld, int iAnz)
{ unsigned int i = 0;
int ende = 0;
printf("Eingabe von double-Werten (maximal %d) :\n", iAnz);
while (i<iAnz && !ende)
{ printf("Wert %2d : ",i); fflush(stdout);
if (scanf ("%lf", dfeld++)<=0)
ende = 1;
else
i++;
}
return i;
}
void ausDoubleWerte(double* dfeld, int iAnz)
{ unsigned int i;
printf("\nAusgabe der sortierten Werte :\n");
for (i=0;i<iAnz;i++)
{
printf("Wert %2d : %7.3lf\n",i,*(dfeld++));
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/06/1 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Mathematische Funktionen der ANSI-C-Standardbibliothek (1)
•
Die ANSI-C-Standardbibliothek stellt auch eine Anzahl häufig benötigter mathematischer Funktonen zur
Verfügung. Die entsprechenden Funktions-Deklarationen befinden sich in der Header-Datei <math.h>.
•
Alle mathematischen Funktionen erzeugen einen Funktionswert vom Typ double. Die Parameter
sind i.a. ebenfalls vom Typ double.
•
Alle Funktionen erkennen Bereichsüberschreitungen sowohl für die übergebenen aktuellen Parameter als
auch für den Funktionswert :
- liegt ein aktueller Parameterwert außerhalb des zulässigen Bereichs ( domain error), so wird die
globale Fehlervariable errno auf den Wert EDOM gesetzt. Der Funktionswert ist in diesem Fall
implementierungsabhängig.
- liegt der sich ergebende Funktionswert außerhalb des darstellbaren Wertebereichs des Typs double
( range error), so wird die globale Fehlervariable errno auf den Wert ERANGE gesetzt.
- Als Funktionswert wird in einem derartigen Fall zurückgegeben :
- der Wert 0, wenn ein Bereichs-Unterlauf auftritt, bzw.
- der Wert HUGE_VAL (gegebenenfalls mit einem Vorzeichen versehen),wenn ein BereichsÜberlauf auftritt.
•
Die Deklaration der Fehlervariablen errno und die Definition der Konstanten EDOM und ERANGE ist in
der Standard-Header-Datei <errno.h> enthalten.
•
In <math.h> ist außerdem der Makro HUGE_VAL definiert:
double-Wert, der zur Kennzeichnung des Überschreitens des darstellbaren Wertebereichs dient (kann
"unendlich" repräsentieren). Die Definition kann als Konstante oder als Variable erfolgen.
•
Exponential- und logarithmische Funktionen
double exp(double x);
Exponentialfunktion ex ("e hoch x")
double frexp(double x, int *ex);
Zerlegung von x in die normalisierte
Darstellung
x = m*2n
mit 0.5 <= m < 1
m: Funktionswert
n: Ablage in *ex
Sonderfall: x=0 ? m=0 und n=0
double ldexp(double x, int n);
Funktion x*2n
double log(double x);
natürlicher Logarithmus ln(x), x>0
double log10(double x);
dekadischer Logarithmus lg(x), x>0
double modf(double x, int *ip);
Zerlegung von x in einen ganzzahligen
und einen gebrochenen Anteil;
gebrochener Anteil: Funktionswert
ganzzahliger Anteil: Ablage in *ip
double pow(double x, double y);
xy ("x hoch y")
double sqrt(double x);
Positive Qaudratwurzel von x, x>=0
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/06/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Mathematische Funktionen der ANSI-C-Standardbibliothek (2)
•
Trigonometrische und hyperbolische Funktionen
Die trigonometrischen Funktionen arbeiten mit Parametern im Bogenmaß.
Deren Wert sollte nicht zu weit ausserhalb des Intervalls [-2π ... 2π] liegen, da die Funktionen für
Parameter mit großem Betrag häufig ungenaue Werte liefern.
Gegebenenfalls sind die Parameter modulo π zu übergeben.
•
double acos(double x);
Arcus-Cosinus von x (Hauptwert)
-1 <= x <= 1
Funktionswert: [0 .. π]
double asin(double x);
Arcus-Sinus von x (Hauptwert)
-1 <= x <= 1
Funktionswert : [-π/2 .. π/2]
double atan(double x);
Arcus-Tangens von x (Hauptwert)
Funktionswert : [-π/2 .. π/2]
double atan2(double y, double x);
Arcus-Tangens von y/x (Hauptwert)
Funktionswert : [-π .. π]
Die Ermittlung des Quadranten, in dem
der Funktionswert liegt, erfolgt unter
Berücksichtigung des Vorzeichens beider
Parameter.
double cos(double x);
Cosinus von x
double cosh(double x);
Cosinus Hyperbolicus von x
double sin(double x);
Sinus von x
double sinh(double x);
Sinus Hyperbolicus von x
double tan(double x);
Tangens von x
double tanh(double x);
Tangens Hyperbolicus von x
Sonstige mathematische Funktionen
double ceil(double x);
kleinste ganze Zahl >= x
double fabs(double x);
Absolutwert von x
double floor(double x);
größte ganze Zahl <=x
double fmod(double x, double y);
Gleitpunkt-Rest der Division x/y
Funktionswert hat das Vorzeichen von x
Falls y=0 ? Funktionswert implementierungsabhängig
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/1 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Datums- und Zeitfunktionen (1)
•
Die ANSI-C-Standard-Bibliothek stellt Funktionen, die einen Zugriff zur Systemuhr ermöglichen, zur
Verfügung :
- Funktionen zur Ermittlung von Zeiten und Zeitdifferenzen
- Funktionen zur Umwandlung von Zeitdarstellungen
- Funktionen zur Darstellung von Zeiten als Strings
•
Es werden drei verschiedenen Zeitdarstellungen verwendet :
- Prozessorzeit (processor time), ausgedrückt in Systemzeit-Perioden.
- Kalenderzeit (calendar time), gemäß dem Gregorianischen Kalender, in einer
implementierungsspezifischen unstrukturierten Darstellung.
- Ortszeit (local time), die für eine spezifische Zeitzone geltende Kalenderzeit, gegebenenfalls modifiziert
als Sommerzeit (daylight saving time), in einer strukturierten Darstellung.
•
Die entsprechenden Funktionsdefinitionen befinden sich in der Standard-Header-Datei <time.h>. Diese
ist daher bei Verwendung der Funktionen mittels #include <time.h> einzubinden.
In <time.h> sind außerdem definiert :
Konstante
Anzahl der Perioden (ticks) der Systemzeit pro Sekunde
Typ
CLOCKS_PER_SEC
(== CLK_TCK)
clock_t
Typ
time_t
Arithmetischer Datentyp zur unstrukturiertenDarstellung der
Kalendertzeit
(meist als long definiert)
Typ
Arithmetischer Datentyp zur Darstellung der Prozessorzeit
(meist als long definiert)
Structure-Typ zur strukturierten Darstellung der Kalenderzeit
(Ortszeit)
struct tm
Der Typ struct tm muss wenigstens die folgenden Komponenten enthalten :
int
int
int
int
int
int
int
int
int
tm_sec;
tm_min;
tm_hour;
tm_mday;
tm_mon;
tm_year;
tm_wday;
tm_yday;
tm_isdst;
/*
/*
/*
/*
/*
/*
/*
/*
/*
Sekunden nach der Minute
Minuten nach der Stunde
Stunden seit Mitternacht
Tag des Monats
Monate seit Januar
Jahre seit 1900
Tage seit Sonntag
Tage seit 1.Januar
Sommerzeit-Flag
[0
[0
[0
[1
[0
*/
*/
*/
*/
*/
*/
[0 .. 6] */
[0 ..365] */
*)
*/
*) Für den Wert des Sommerzeit-Flags tm_isdst gilt :
- >0
- =0
- <0
wenn die Sommerzeit in Kraft ist
wenn die Sommerzeit nicht in Kraft ist
wenn die Information nicht verfügbar ist
..
..
..
..
..
59]
59]
23]
31]
11]
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/2 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Datums- und Zeitfunktionen (2)
•
Funktionen zur Ermittlung von Zeiten und Zeitdifferenzen
clock_t clock(void);
Ermittlung der Prozessorzeit, die seit
Programmstart vergangen ist, in
Systemzeit-Perioden (ticks).
clock()/CLK_TCK ist die Zeit in
Sekunden.
Funktionswert = (clock_t)-1, wenn die
Prozessorzeit nicht verfügbar ist.
time_t time(time_t *timer);
Ermittlung der Kalenderzeit in einer
implementierungsspezifischen Darstellung.
Falls timer!=NULL wird der Funktionswert
auch *timer zugewiesen.
Funktionswert = (time_t)-1, wenn Kalenderzeit nicht verfügbar ist.
double difftime(time_t t2, time_t t1); Bildung der Zeitdifferenz t2 - t1 ausgedrückt in Sekunden (t2 und t1
enthalten Zeiten in implementierungsspezifischer Darstellung!)
•
Funktionen zur Umwandlung von Zeitdarstellungen
struct tm *gmtime(const time_t *tp);
Umwandlung der in implementierungsspezifischer Darstellung vorliegenden
Kalenderzeit *tp in eine strukturierte
Darstellung der Coordinated Universal
Time (UTC).
Falls die UTC nicht ermittelt werden
kann, ist der Funktionswert = NULL
struct tm *localtime(const time_t *tp); Umwandlung der in implementierungsspezifischer Darstellung vorliegenden
Kalenderzeit *tp in eine strukturierte
Darstellung der Ortszeit
time_t mktime(struct tm *tptr);
Umwandlung der in strukturierter Darstellung vorliegenden Ortszeit *tptr in
die implementierungsspezifische Darstellung der Kalenderzeit.
Die Werte der einzelnen Strukturkomponenten müssen nicht normiert sein.
Die Funktion führt zusätzlich ihre
Normierung durch und berechnet die
Werte für die Komponenten tm_wday und
tm_yday.
Falls keine darstellbare Kalenderzeit
ermittelt werden kann erzeugt die
Funktion den Wert (time_t)-1.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/3 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Datums- und Zeitfunktionen (3)
•
Funktionen zur Darstellung von Zeiten als Strings
char *asctime(const struct tm *tptr);
Umwandlung der in strukturierter Darstellung vorliegenden Ortszeit *tptr in
einen String der folgenden Form:
"Sun Apr 14 11:23:22 1991\n\0"
Der Funktionswert ist Pointer auf diesen
String.
char *ctime(const time_t *tp);
Umwandlung der in implementierungsspezifischer Darstellung vorliegenden
Kalenderzeit *tp in einen String der
folgenden Form:
"Sun Apr 14 11:23:22 1991\n\0"
Der Funktionswert ist Pointer auf diesen
String.
ctime(tp) ist äquivalent zu
asctime(localtime(tp)).
size_t strftime( char *s, size_t smax,
const char *fmt,
const struct tm
*tptr);
Ablage von in *tptr enthaltenen Zeitund Datumsinformationen in den String s
gemäß den im String fmt enthaltenen
Formatspezifikationen.
Jeder sonstiger Text in fmt wird nach s
übernommen. Der String s darf maximal
smax Zeichen lang werden.
Funktionswert:
- Anzahl der in s abgelegten Zeichen
ohne abschließendes '\0'), wenn
diese <=smax ist.
- 0, wenn s länger als smax Zeichen
werden würde.
Der Inhalt von s ist in diesem Fall
undefiniert.
Format-Spezifier für strftime():
%a
%A
%b
%B
%c
%d
%H
%I
%j
%m
%M
%p
%S
%U
%w
%W
%x
%X
%y
%Y
%Z
%%
abgekürzter Name des Wochentags
ausgeschriebener Name des Wochentags
abgekürzter Name des Monats
ausgeschriebener Name des Monats
geeignete Darstellung der lokalen Zeit (Datum und Zeit)
Tag des Monats als Dezimalzahl (01 .. 31)
Stunde als Dezimalzahl (24-Std-Uhr) (00 .. 23)
Stunde als Dezimalzahl (12-Std-Uhr) (01 .. 12)
Tag des Jahres als Dezimalzahl (001 .. 386)
Monat als Dezimalzahl (01 .. 12)
Minute als Dezimalzahl (00 .. 59)
PM oder AM (oder landesspezifisches Äquivalent)
Sekunde als Dezimalzahl (00 .. 59)
Woche des Jahres dezimal Sonntag ist der erste Tag der Woche) (00 .. 53)
Wochentag als Dezimalzahl (Sonntag = 0) (0 .. 6)
Woche des Jahres dezimal(Montag ist der erste Tag der Woche) (00 .. 53)
geeignete Darstellung des lokalen Datums
geeignete Darstellung der lokalen Zeit
(Jahr - Jahrhundert) als Dezimalzahl (00 .. 99)
Jahr als Dezimalzahl (einschließlich Jahrhundert)
Name der Zeitzone, falls ein Name existiert
Das Zeichen %
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/4 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Datums- und Zeitfunktionen (4)
● Zusammenhänge zwischen den Datums- und Zeitdarstellungen
gmtime()
time()
implementierungsspezifische
unstrukturierte
Kalenderzeit
(Typ : time_t)
strukturierte
Kalenderzeit
(UTC)
(Typ : struct tm)
localtime()
mktime()
strukturierte
Kalenderzeit
(Ortszeit)
(Typ : struct tm)
asctime()
ctime()
asctime()
Kalenderzeit
in
String-Darstellung
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/5 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsbeispiel zu den ANSI-C-Datums- und Zeitfunktionen (1)
/*--------------------------------------------------------------------*/
/* Programm timedemo (C-Modul timedemo_m.c)
*/
/*--------------------------------------------------------------------*/
/* Demonstrationsprogramm zur Verwendung der Zeit- und Datumsfunk*/
/* tionen der ANSI-C-Standardbibliothek
*/
/*--------------------------------------------------------------------*/
#include <time.h>
#include <stdio.h>
#include <string.h>
void proc_cal_time_out(clock_t, time_t);
void warten(void);
void loc_time_out(struct tm *);
int main(void)
{
time_t zeit1,zeit2;
clock_t clk1, clk2;
double ctsec, ttdiff;
struct tm *lzeit;
char zeit_str[80];
clk1=clock();
zeit1=time(NULL);
proc_cal_time_out(clk1, zeit1);
warten();
clk2=clock();
zeit2=time(NULL);
proc_cal_time_out(clk2, zeit2);
ctsec=(double)(clk2-clk1)/CLK_TCK;
ttdiff=difftime(zeit2, zeit1);
printf("\nWartezeit : %f Sek. (ermittelt ueber Prozessorzeit)",ctsec);
printf("\n
%f Sek. (ermittelt ueber Kalenderzeit)\n",ttdiff);
lzeit=localtime(&zeit2);
loc_time_out(lzeit);
strcpy(zeit_str,asctime(lzeit));
printf("\nZeit-String : %s\n",zeit_str);
return 0;
}
void proc_cal_time_out(clock_t ct, time_t tt)
{
printf("\nProzessorzeit = %ld Ticks",ct);
printf("\nKalenderzeit = %ld\n",tt);
}
void warten(void)
{
printf("\nWarten bis Eingabe CR ");
getchar();
}
void loc_time_out(struct tm *ltp)
{
printf("\nOrtszeit : Jahr
= %d",ltp->tm_year);
printf("\n
Monat = %d",ltp->tm_mon);
printf("\n
Tag
= %d",ltp->tm_mday);
printf("\n
Zeit
= %d:%d:%d",ltp->tm_hour,ltp->tm_min,
ltp->tm_sec);
printf("\nSommerzeit-Flag
: %d\n",ltp->tm_isdst);
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 08/07/6 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsbeispiel zu den ANSI-C-Datums- und Zeitfunktionen (2)
Beispiel für einen Programmlauf
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/00/0 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
9. Ausgewählte Algorithmen
9.1. Sieb des Eratosthenes
9.2. Rekursion
9.3. Bestimmung von Nullstellen durch Iteration
9.4. Sortieren
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/01/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sieb des Eratosthenes (1)
● Algorithmus zur Ermittlung von Primzahlen
● Grundprinzip :
● Modifikation : Die Zahl 2 ist die einzige gerade Primzahl.
Alle weiteren Primzahlen sind ungerade.
Beschränkung auf die Untersuchung der ungeraden Zahlen >= 3,
d.h. das Sieb enthält nur alle ungeraden Zahlen >= 3.
Annahme : Die Zahlen sind linear ihrer Größe nach im Sieb angeordnet
Sie besitzen eine Position
Zahlen im Sieb :
3
5
7
9
11
13
15
17
19
21
23
25
27
29
31 ...
Position im Sieb :
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14 ...
Zusammenhang zwischen einer Zahl z im Sieb und ihrer Position i :
z = 2*i + 3
prim sei eine Primzahl.
Da sich im Sieb nur ungerade Zahlen befinden, können auch nur ungerade Vielfache der Primzahl
enthalten sein (jedes gerade Vielfache ist eine gerade Zahl).
Damit beschränkt sich das Entfernen auf das Entfernen der ungeraden Vielfachen der Primzahl.
Für die zu entfernenden Zahlen gilt somit :
zahl = prim
zahl neu = zahl alt + 2 * prim
Für die Position der zu entfernenden Zahlen ergibt sich :
2 * ineu + 3 = 2 * ialt + 3 + 2 * prim
ineu = ialt + prim
Die Positionsdifferenz der zu entfernenden Zahlen zahl ist gleich der Primzahl prim
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/01/2 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sieb des Eratosthenes (2)
● Realisierung des Siebs durch ein Array von logischen Werten.
Der Index eines Array-Elements entspricht der Position einer Zahl im Sieb und legt damit
eindeutig die entsprechende Zahl fest.
Der Wert eines Array-Elements (TRUE oder FALSE) gibt an, ob sich die zugeordnete Zahl im
Sieb befindet (TRUE) oder ob sie aus dem Sieb entfernt wurde (FALSE).
Struktogramm des modifizierten Algorithmus :
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/01/3 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sieb des Eratosthenes (3)
● Programmcode :
/*
/*
/*
/*
/*
-------------------------------------------------------------------Programm primzahl (C-Modul sieberathost_m.c)
-------------------------------------------------------------------Primzahlenermittlung mittels Sieb des Eratosthenes
--------------------------------------------------------------------
*/
*/
*/
*/
*/
#include <stdio.h>
#define
#define
#define
#define
#define
SIZE
TRUE
FALSE
ANZZEIL
WRITELN
802
1
0
12
putchar('\n')
int main(void)
{ int i,k;
int iPrim, iAnzahl;
char cFlags[SIZE+1];
/* Sieb */
printf("\nPrimzahlen zwischen 3 und %d\n\n",2*SIZE+3);
iAnzahl=0;
for (i=0; i<=SIZE; i++)
cFlags[i]=TRUE;
for (i=0; i<=SIZE; i++)
if (cFlags[i])
/* zugehoerige Zahl im Sieb ? */
{ iPrim=2*i+3;
for (k=i; k<=SIZE; k=k+iPrim)
cFlags[k]=FALSE;
/* zugehoerige Zahl aus Sieb entfernen */
iAnzahl++;
printf("%6d",iPrim);
/* Ausgabe Primzahl */
if (iAnzahl%ANZZEIL == 0) WRITELN;
}
return 0;
}
● Programmausgabe :
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/02/1 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Rekursion (1)
• Rekursion ist ein grundlegendes Prinzip zur Formulierung von Algorithmen.
Auch bei Datenstrukturen wird dieses Prinzip angewendet.
• Rekursion liegt vor, wenn die Definition eines Objektes unter Verwendung des Objekts selbst
erfolgt :
Datenstrukturen :
Die Definition enthält Referenzen auf Strukturen des eigenen Typs
Algorithmen (Funktionen) : Ein (Teil-)Algorithmus (eine Funktion) ruft sich während seiner/
ihrer Ausführung selbst auf
• Bei Algorithmen (Funktionen) unterscheidet man :
▻ direkte Rekursion :
Der Funktionsaufruf ist direkt in der Funktion enthalten
(-->rekursive Funktion)
▻ indirekte Rekursion : Der Funktionsaufruf erfolgt indirekt über wenigstens eine weitere
Funktion. Z.B. f() ruft b() auf und b() ruft ihrerseits f() auf.
(-->rekursiv verwendete Funktion)
• Eine Alternative zur Rekursion ist die Iteration.
Hierunter versteht man die wiederholte Ausführung einer Anweisungsfolge, wobei die Anzahl der
Wiederholungen durch bestimmte Variable gesteuert wird.
• Bei jedem erneuten Aufruf (Inkarnation) einer rekursiven bzw rekursiv verwendeten Funktion
werden die lokalen auto- und register-Variablen und die formalen Parameter neu angelegt, d.h. auf
dem Stack wird für sie neuer Speicherplatz allokiert (bzw werden die alten register-Variablen auf
dem Stack zwischengespeichert).
▻ zu den verschiedenen Funktionsinkarnationen gehören verschiedene Variablen- und
Parametersätze. (Problem : Gefahr des Stack-Überlaufs)
▻ Statisch-lokale Variable werden dagegen nicht jedesmal neu angelegt. Sie existieren nur einmal
und sind in allen Inkarnationen zugänglich.
• Eine rekursive bzw rekursiv verwendete Funktion wird erst beendet, wenn alle in ihr bzw. durch sie
erfolgten erneuten Funktionsaufrufe ihrerseits beendet worden sind. Da jede sinnvolle Rekursion
einmal enden muss (Zeit, Stack !) muss die Funktion eine Abbruchbedingung enthalten, die zu
keinem erneuten Funktionsaufruf führt.
Die Abbruchbedingung muss durch wenigstens eine Variable gesteuert werden, mit der Information
von einer Funktionsinkarnation zur nächsten übergeben wird. Hierfür lässt sich eine lokale statische
Variable oder ein Parameter einsetzen.
• Forderungen an eine rekursive bzw. rekursiv verwendete Funktion:
Die Abbruchbedingung muss bei jedem Funktionsaufruf überprüft werden.
Wenigstens eine der Variablen, die die Abbruchbedingung steuern, muss bei jedem erneuten
Funktionsaufruf einen neuen Wert, im Sinne einer Annäherung an die Abbruchbedingung,
bekommen.
Die Abbruchbedingung muss nach einer sinnvollen Anzahl von Funktionsaufrufen erfüllt sein.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/02/2 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Rekursion (2)
• Beispiel für einen rekursiven Algorithmus :
Ermittlung der Fakultät fac(n)=(n!)
rekursive Definition : fac(0) = 1
fac(n) = n * fac(n-1)
Formulierung als C-Funktion :
unsigned fac (unsigned n)
{
if (n==0)
/* Abbruchbedingung */
return(1);
else
return(n*fac (n-1));
}
Untersuchung der Funktionsaufrufe und Funktionswerte für fac(4) :
Funktionsaufrufe
Funktionswerte
fac(4)
24 <┐
│
│
└> 4*fac(3)
6 <┐
│
│
└> 3*fac(2)
2 <┐
│
│
└> 2*fac(1)
1 <┐
│
│
└> 1*fac(0) ---> 1
• Anwendung rekursiver Algorithmen
◇ Rekursive Algorithmen sind häufig leichter zu formulieren und klarer zu verstehen als
entsprechende iterative Algorithmen.
◇ Trotzdem ist Rekursion nicht in jedem Fall vorteilhafter. Häufig führt sie weder zu einem
merklich geringeren Codeumfang noch zu einer besseren Speicherausnutzung (Stack !).
Vielfach ist sogar die Laufzeit wegen der vielfachen Funktionsaufrufe länger.
◇ Andererseits lassen sich manche Probleme mittels eines rekursiven Algorithmus wesentlich
effektiver – auch bezüglich des Zeitaufwands – lösen, als mit einem iterativen Algorithmus.
Beispiel : Potenz n einer reellen Zahl x
iterativ : (n-1) Multiplikationen
rekursiv : ld n Multiplikationen
In einem solchen Fall ist i.a. Rekursion vorzuziehen.
◇ Weiterhin sollte Rekursion dann angewendet werden,
▻ wenn ein iterativer Algorithmus nur sehr schwierig und umständlich zu formulieren ist, ein
entsprechender rekursiver Algorithmus dagegen leicht zu finden und anschaulich zu verstehen
ist.
▻ wenn die zugrundeliegenden Datenstrukturen ihrerseits rekursiv sind (z.B. lineare Listen,
Bäume usw.).
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/02/3 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Dezimal-Dualwandlung mittels rekursiver Funktion
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
-----------------------------------------------------------------Programm dezdualrek (C-Modul dezdualrek_m.c)
-----------------------------------------------------------------Modifikation des Programms dezdual zur Dezimal-Dualwandlung
positiver ganzer Zahlen
*/
*/
*/
*/
*/
*/
Ausgabe einer positiven Integerzahl als Dualzahl ohne Zwischen*/
speicherung der ermittelten Dualziffern
*/
Einsatz einer rekursiven Funktion
*/
------------------------------------------------------------------ */
#include <stdio.h>
#define BASIS
2
void printdual(unsigned i)
{ unsigned zahl;
if ((zahl=i/BASIS) != 0)
printdual(zahl);
putchar(i%BASIS + '0');
return;
}
int main(void)
{ unsigned zahl;
while ((printf("\npositive ganze Dezimalzahl ? "),
scanf("%d",&zahl)) != EOF)
{
printf("\numgewandelt in Dualzahl
: ");
printdual(zahl);
putchar('\n');
}
return 0;
}
Beispiel für einen Programmlauf :
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/1 – TH/KR – 02
---------------------------------------------------------------------------------------------------------------------------------------------------------
Nullstellen berechnen – Iterationsverfahren (1)
Betrachtet wird das folgende elektrische Netzwerk:
UBR
IR
f(UB)
IB
R
UQ
B
UB
UB
Das Bauelement B ist nichtlinear und besitze folgende Kennlinie:
IB = k * UB
Gesucht ist die Spannung UB in Abhängigkeit von UQ der Spannungsquelle. Es gilt IR = IB und mit
UR = UQ – UB erhält man also:
UQ −U B
R
= k * UB
Mathematisch betrachtet ist die Lösung dieser Gleichung die Bestimmung der Nullstelle der Funktion:
f (U B ) = k * U B −
U Q −U B
R
Für die numerische – und damit mittels Programm realisierbare – Bestimmung der Nullstelle einer
Funktion f(x) bieten sich folgende drei Verfahren an:
(1) Intervallhalbierung
(2) Regula Falsi (Sekantenverfahren)
(3) Newton-Verfahren
Allgemein gilt:
Gibt es für f(x) im Intervall a ≤ x0 ≤ x1 ≤ b zwei Funktionswerte mit entgegengesetztem Vorzeichen, gilt
also f(x0) * f(x1) < 0, so exisitiert ein f(xn) = 0.
Vor.: f(x) ist definiert und stetig innerhalb a ≤ xn ≤ b.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/2 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Nullstellen berechnen – Iterationsverfahren (2)
(1) Intervallhalbierung:
f(x)
a
a'=k
x
b
Es sind zwei Anfangswerte a,b so vorzugeben, dass gilt f(a)*f(b)<0.
Das Intervall wird halbiert
k = ½ * (a+b)
und wie folgt verkleinert:
ist f(k)*f(a)<0, so wird als neues Intervall [a,k] gewählt, d.h. b durch k ersetzt,
ist f(k)*f(b)<0, so wird als neues Intervall [k,b] gewählt, d.h. a durch k ersetzt.
Dieses Verfahren wird fortgesetzt, bis das Abbruchkriterium erfüllt ist:
|a-b| < ε
(2) Regula Falsi (Sekantenverfahren):
Der Schnittpunkt der Geraden durch die beiden Funktionswerte f(a) und f(b) mit der x-Achse
ergibt den neuen Stützpunkt a'.
f(x)
f(b)
a'
a
b
f(a)
Es gilt :
f(b)/(b-a') = -f(a)/(a'-a)
Umformung ergibt : a'=a–f(a)*(b-a)/(f(b)-f(a))
Abbruchkriterium:
|f(a')| < ε
x
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/3 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Nullstellen berechnen – Iterationsverfahren (3)
(3) Das Newton-Verfahren:
Beginnend bei einem Anfangswert x0 wird der Schnittpunkt x1 der Tangente an der Stelle f(x0)
mit der x-Achse ermittelt. Die Tangente an der Stelle f(x1) liefert den nächsten Näherungswert.
Abbruchkriterium:
|xν+1-xν| < ε
f(x)
x1
x0
x
Es gilt:
Umformung ergibt :
f'(xν) = f(xν)/(xν - xν+1)
xν+1 = xν-f(xν)/f'(xν)
Im interessierenden Intervall muß gelten:
Für den Startwert x0 gilt also:
|f(xν)*f''(xν)/(f'(xν))2| < 1
|f(x0)*f''(x0)| < (f'(x0))2
Ist die nötige Ableitung der Funktion f'(x0) nicht bekannt, so kann sie durch den Differenzenquotienten angenähert werden:
f'(x0) ≈ [f(x0+dh)-f(x0)]/dh
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/4 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (1)
/* C-Headerdatei nullst.h
/* Konstantendefinitionen und Funktionsdeklarationen
/* für das Demo-Programm zur Nullstellenbestimmung
*/
*/
*/
#ifndef __NULLST_H
#define __NULLST_H
#define EPSILON 1.e-7
#define MAXITER 100
/* Genauigkeit der Nullstelle
/* maximale Anzahl Iterationen
*/
*/
/* Newton-Verfahren */
int dNewton(double, double *, double (*)(double));
/* Intervallhalbierung */
int dIntHalb (double ,double, double *, double (*)(double));
/* Regula Falsi – Trapezverfahren */
int dRegFals (double ,double, double *, double (*)(double));
#endif
/* ---------------------------------------------------------------------/* C-Modul IntHalb.c
/* Nullstellenbestimmung mittels Intervallhalbierung
/*----------------------------------------------------------------------/* Übergabe:
/*
dA:
Startwert der Nullstellenbestimmung
/*
dB:
Startwert der Nullstellenbestimmung
/*
pRes: Zeiger auf x-Wert der Nullstelle, für die Rückgabe
/*
pFunc: Zeiger auf Funktion, deren Nullstelle bestimmt wird
/* Rückgabe:
/*
Anzahl der Iterationen
/*
-1 im Fehlerfall
/* Defines:
/*
EPSILON geforderte Genauigkeit
/*
MAXITER maximale Anzahl durchzuführender Iterationen
/*----------------------------------------------------------------------#include <math.h>
#include "nullst.h"
int dIntHalb (double dA,double dB, double *pRes, double (*pFunc)(double))
{ double dX;
int iIter=0;
if((*pFunc)(dA)*(*pFunc)(dB)>0) /* ungueltige Startwerte */
iIter = -1;
else
{ do
{ dX = (dA + dB)/2.0;
if ((*pFunc)(dX)*(*pFunc)(dB) < 0)
dA = dX;
else
dB = dX;
iIter++;
} while ((fabs(dB-dA) > EPSILON) && (iIter < MAXITER));
if (iIter >= MAXITER)
/* Begrenzung der Iterationen */
iIter = -1;
else *pRes = dX;
}
return iIter;
}
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/5 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (2)
/*---------------------------------------------------------------------/* C-Modul RegFals.c
/* Nullstellenbestimmung mittels Regula Falsi
/*---------------------------------------------------------------------/* Übergabe:
/*
dA:
Startwert 1 der Nullstellenbestimmung
/*
dB:
Startwert 2 der Nullstellenbestimmung
/*
pRes: Zeiger auf x-Wert der Nullstelle, für die Rückgabe
/*
pFunc: Zeiger auf Funktion, deren Nullstelle bestimmt wird
/* Rückgabe:
/*
Anzahl der Iterationen
/*
-1 im Fehlerfall
/* Global:
/* Defines:
/*
EPSILON geforderte Genauigkeit
/*
MAXITER maximale Anzahl durchzuführender Iterationen
/*---------------------------------------------------------------------#include <math.h>
#include "nullst.h"
int dRegFals (double dA,double dB, double *pRes, double (*pFunc)(double))
{ double dFa,dFb,dFx;
int iIter=0;
if((*pFunc)(dA)*(*pFunc)(dB)>0) /* ungueltige Startwerte */
iIter = -1;
else
{ dFb = (*pFunc)(dB);
do
{ dFa = (*pFunc)(dA);
dA = dA - dFa*(dB-dA)/(dFb-dFa);
dFx = (*pFunc)(dA);
iIter++;
} while ((fabs(dFx) > EPSILON) && (iIter < MAXITER));
if (iIter >= MAXITER)
iIter = -1;
else
*pRes = dA;
}
return iIter;
}
/* Begrenzung der Iterationen */
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/6 – TH/KR – 07
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (3)
/*----------------------------------------------------------------------/* C-Modul Newt.c
/* Nullstellenbestimmung nach Newton
/*----------------------------------------------------------------------/* Übergabe:
/*
dX0
Startwert der Nullstellenbestimmung
/*
pRes
Zeiger auf x-Wert der Nullstelle, für die Rückgabe
/*
pFunc: Zeiger auf Funktion, deren Nullstelle bestimmt wird
/* Rückgabe:
/*
Anzahl der Iterationen
/*
-1 im Fehlerfall
/* Global:
/* Defines:
/*
EPSILON
geforderte Genauigkeit
/*
MAXITER
maximale Anzahl durchzuführender Iterationen
/*----------------------------------------------------------------------#include <math.h>
#include "nullst.h"
/* Vorwaertsdeklaration */
double d_Func (double (* dFunc)(double),double dX);
int dNewton (double dX0, double *pRes, double (*pFunc)(double))
{ double dXi, dXialt;
double dFi;
double dFs;
int iIt = 0;
dXi = dXialt = dX0;
do
/* Berechnung des neuen Funktionswerts */
{ dFi = (*pFunc)(dXi);
dFs = d_Func(pFunc,dXi);
dXialt = dXi;
if(fabs(dFs) > 0)
dXi = dXi - dFi/dFs;
iIt++;
} while ((iIt<=MAXITER) && (fabs(dFs) >= 1.e-10)
&& (fabs(dXi-dXialt) >= EPSILON)) ;
if (fabs(dFs) < 1.e-10)
iIter = -1;
else if (iIt > MAXITER)
iIter = -1;
else
*pRes=dXialt;
return iIt;
/* verschwindende Ableitung abgefangen */
/* Begrenzung der Iterationen */
}
/* numerische Ableitung einer Funktion: Differenzenquotient */
double d_Func (double (* dFunc)(double),double dX)
{ /* dX:
Stelle der Ableitung
*/
/* dFunc: abzuleitende Funktion */
double dFs;
double dH = 1.e-6;
dFs = ((*dFunc)(dX+dH) - (*dFunc)(dX))/dH;
return dFs;
}
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/7 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (4)
/* C-Modul funcs.c
Funktionen, deren Nullstelle gesucht wird
einige Beispiele:
*/
#include <math.h>
double dFunc1 (double dX)
{ double dF;
dF = dX*dX - 6*dX + 7;
return dF;
}
// Polynom
double dFunc2 (double dX)
// trig. Funktion
{ double dF;
dF = cos(dX)*cos(dX) - dX*dX;
return dF;
}
double dFunc3 (double dUb) // Vergleiche Eingangsbeispiel
{ double dF;
double dK = 0.005;
double dUq = 2.5, dR = 1e2;
dF = dK*sqrt(dUb) - (dUq-dUb)/dR;
return dF;
}
double dFunc4 (double dUd) // B ist Diode (Shockley-Modell)
{ double dF;
double dIss = 10e-9, dUt = 50e-3;
double dUg = 1.5, dRg = 1e3;
dF = dIss*(exp(dUd/dUt) - 1) - (dUg-dUd)/dRg;
return dF;
}
/* C-Headerdatei funcs.h
Funktionsdeklarationen einiger Funktionen, von denen
die Nullstelle ermittelt werden soll
*/
#ifndef _FUNCS_H
#define _FUNCS_H
double
double
double
double
#endif
dFunc1
dFunc2
dFunc3
dFunc4
(double
(double
(double
(double
dX);
dX);
dUb);
dUd);
//
//
//
//
Polynom
trig. Funktion
Vergleiche Eingangsbeispiel
B ist Diode (Shockley-Modell)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/8 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (5)
/* C-Modul nullst_m.c - Modul mit main()-Funktion für das Programm nullst
Programmbeispiel:Nullstellenermittlung mit unterschiedlichen Verfahren
Modul enthält auch einige zu untersuchende Funktionen
*/
#include
#include
#include
#include
<stdio.h>
<math.h>
"nullst.h"
"funcs.h"
int main(void)
{ double (*pFunc)(double); /* Zeiger auf die Funktion, für die die
Nullstelle zu ermitteln ist */
double dXa= 4.0;
/* Startwert 1 */
double dXb= 10.0;
/* Startwert 2 */
double dXn;
/* Position der Nullstelle */
int iTer;
/* Anzahl Iterationen
*/
char sel;
int valid;
pFunc = dFunc1;
/* Vorbelegung Funktionszeiger */
do
{ printf("Welche Funktion: (1)..(4)? ");
do sel = getchar(); while(sel == '\n');
valid = 1;
switch (sel)
{ case '1': dXa=4.; dXb=10.0;/* Startwerte */
pFunc = dFunc1; /* Auswahl der Funktion */
break;
case '2': dXa=-10.; dXb=0.0;
pFunc = dFunc2;
break;
case '3': dXa=9.0; dXb=0.25;
pFunc = dFunc3;
break;
case '4': dXa=0.2; dXb=0.8;
pFunc = dFunc4;
break;
default : valid = 0;
break;
}
if (valid && sel != EOF)
{ if((iTer = dNewton (dXa,&dXn,pFunc)) >= 0)
printf ("N-Nullstelle: x=%-11.5lf y=%14.5le nach %3d Iterationen\n",
dXn, (*pFunc)(dXn),iTer);
else
printf("Fehler - Newton!\n");
if((iTer = dIntHalb (dXa,dXb,&dXn,pFunc)) >= 0)
printf ("H-Nullstelle: x=%-11.5lf y=%14.5le nach %3d Iterationen\n",
dXn, (*pFunc)(dXn), iTer);
else
printf("Fehler - Int.-Halb.!\n");
if((iTer = dRegFals(dXa,dXb,&dXn,pFunc)) >= 0)
printf ("R-Nullstelle: x=%-11.5lf y=%14.5le nach %3d Iterationen\n\n",
dXn, (*pFunc)(dXn), iTer);
else
printf("Fehler - Reg.Fals.!\n\n");
}
} while (sel != EOF);
return 0;
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/03/9 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Nullstellenermittlung mit unterschiedlichen Verfahren (6)
Probelauf
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/1 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch direktes Austauschen – Bubble Sort
● Aufgabe: Sortierung eines Arrays a mit n Elementen in aufsteigender Reihenfolge.
● Verbale Beschreibung des Algorithmus:
Beginnend am Arrayende werden Arrayelemente ausgetauscht, falls der Wert des Elementes mit dem
niedrigeren Index größer ist als der des Elementes mit dem höheren Index. Damit wird an die Stelle
des niedrigsten Index das kleinste Arrayelement plaziert. Mit dem restlichen Teilarray wird
entsprechend verfahren.
● Struktogramm:
● Beispiel:
n=5
j
1
2
3
4
i
4
3
2
1
4
3
2
4
3
4
a0
3
3
3
3
1
1
1
1
1
1
1
a1
2
2
2
1
3
3
3
2
2
2
2
a2
5
5
1
2
2
2
2
3
3
3
3
a3
1
1
5
5
5
4
4
4
4
4
4
a4
4
4
4
4
4
5
5
5
5
5
5
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/2 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch direktes Einfügen
● Aufgabe: Sortierung eines Arrays a mit n Elementen in aufsteigender Reihenfolge.
● Verbale Beschreibung des Algorithmus:
Beginnend bei einem Vergleichselement werden alle Elemente mit kleinerem Index um eine Position
verschoben, sofern ihr Wert größer als der des Vergleichselementes ist. Ansonsten wird das
Vergleichselement an der betrachteten Position direkt eingefügt. Der Vergleich startet an der 2.
Arrayposition und wird für alle folgenden Positionen wiederholt.
● Struktogramm:
● Beispiel:
n=5
j
1
2
3
4
i
0
-1
1
2
1
0
-1
3
2
a0
3
2
2
2
2
1
1
1
a1
2
3
3
3
3
2
2
2
2
a2
5
5
5
5
3
3
3
3
3
a3
1
1
1
1
5
5
5
5
4
a4
4
4
4
4
4
4
4
4
5
5
x
2
2
5
1
1
1
1
4
4
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/3 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch direktes Auswählen
● Aufgabe: Sortierung eines Arrays a mit n Elementen in aufsteigender Reihenfolge.
● Verbale Beschreibung des Algorithmus:
Beginnend beim ersten Element wird der Index jenes Arrayelementes gesucht, das den minimalen
Wert besitzt. Mit diesem direkt ausgewählten Element wird das erste Element vertauscht. Dieses wird
fortgesetzt mit dem Teilarray beginnend mit dem zweiten Arrayelement usw.
● Struktogramm:
● Beispiel:
j
0
1
2
3
n=5
i
1
2
3
4
2
3
4
3
4
4
5
a0
3
3
3
3
1
1
1
1
1
1
1
1
a1
2
2
2
2
2
2
2
2
2
2
2
2
a2
5
5
5
5
5
5
5
5
5
3
3
3
a3
1
1
1
1
3
3
3
3
3
5
5
4
a4
4
4
4
4
4
4
4
4
4
4
4
5
aimin
3
2
2
1
2
2
2
5
3
5
4
imin
0
1
1
3
1
1
1
2
3
3
4
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/4 – TH/KR – 05
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch austauschendes Suchen
● Aufgabe: Sortierung eines Arrays a mit n Elementen in aufsteigender Reihenfolge.
● Verbale Beschreibung des Algorithmus:
Beginnend bei dem Element mit niedrigstem Index werden die Folgeelemente durchsucht und mit
dem betrachteten Element ausgetauscht, falls das Folgeelement kleiner ist. Dies wird bis zum
Arrayende fortgesetzt.
● Struktogramm:
● Beispiel:
n=5
j
i
a0
a1
a2
a3
a4
0
1
3
2
5
1
4
2
2
3
5
1
4
3
2
3
5
1
4
4
1
3
5
2
4
2
1
3
5
2
4
3
1
3
5
2
4
4
1
2
5
3
4
3
1
2
5
3
4
4
1
2
3
5
4
4
1
2
3
5
4
1
2
3
4
5
1
2
3
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/5 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch Zerlegen – Quick Sort (1)
● Aufgabe: Sortierung eines Arrays a mit n Elementen in aufsteigender Reihenfolge.
● Verbale Beschreibung des Algorithmus:
Das Array wird in zwei Teile zerlegt, von denen der erste nur Elemente enthält, die kleiner als ein
Vergleichswert (Pivot) sind, der zweite Teil enthält nur Elemente die größer oder gleich dem
Vergleichswert sind. Das Verfahren wird auf die Teilarrays erneut angewendet, bis die resultierenden
Teilarrays aus nur noch einem Element bestehen.
● Struktogramm:
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/6 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Sortieren durch Zerlegen – Quick Sort (2)
● Beispiel:
n = 8;
l: Index des ersten Elements, r: Index des letzten Elements
l
0
r
7
0
4
1
4
1
3
5
7
i
0
1
2
3
5
0
0
1
1
2
3
4
1
1
2
3
5
6
7
j
7
7
6
5
4
4
2
0
4
4
3
3
3
3
2
1
7
6
5
a0
3
3
3
3
a1
6
2
2
2
a2
5
5
-7
-7
a3
4
4
4
0
a4
1
1
1
1
3
-7
2
2
-7
3
0
0
1
1
-7
-7
2
2
3
1
0
0
1
3
3
3
2
0
1
1
0
2
-7
0
1
2
a5
0
0
0
4
a6
-7
-7
5
5
a7
2
6
6
6
pivot
4
4
4
4
1
1
3
4
4
5
5
6
6
4
5
6
5
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/7 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm : Vergleich verschiedener Sortierverfahren (1)
/*
/*
/*
/*
/*
/*
/*
------------------------------------------------------------------------------Programm sortdemo (C-Modul sortdemo_m.c - Modul mit main()-Funktion)
------------------------------------------------------------------------------Demoprogramm zur Untersuchung und zum Vergleich verschiedener Sortierverfahren
Sortiert werden jeweils Zufalls-double-Werte, die mittels der Bibliotheksfunktion rand() erzeugt werden
-------------------------------------------------------------------------------
#include
#include
#include
#include
<stdio.h>
<ctype.h>
<stdlib.h>
<time.h>
#define ANZ_VAL 20000
#define PROZEILE 7
void
void
void
void
void
void
BubbleSort(double*,int);
DirectInsert(double *, int);
DirectSelect(double *, int);
ExchangingSeek(double *, int);
QuickSort(double *, int);
ANSI_qs( double *, int);
void displSelection();
void fillArray(double*, int);
void show(double *daF,int iAnz);
int main (void)
{ static double daSortDat[ANZ_VAL] = {0};
unsigned char cSelect;
clock_t start, finish;
double diffzeit;
int iLae;
iLae = sizeof(daSortDat)/sizeof(daSortDat[0]);
do
{ displSelection();
do cSelect = getchar(); while (cSelect == '\n');
if(toupper(cSelect) != 'E')
{ fillArray(daSortDat, iLae);
start = clock();
switch (cSelect)
{ case '1': BubbleSort(daSortDat,iLae);
break;
case '2': DirectInsert(daSortDat,iLae);
break;
case '3': DirectSelect(daSortDat,iLae);
break;
case '4': ExchangingSeek(daSortDat,iLae);
break;
case '5': QuickSort(daSortDat,iLae);
break;
case '6': ANSI_qs(daSortDat,iLae);
break;
}
finish = clock();
show(daSortDat,5);
show(daSortDat+iLae-5,5);
diffzeit = (double)(finish-start)/(CLOCKS_PER_SEC/1000.);
printf ("Start: %d - Stop: %d - Diff(msec): %.2f\n\n",
(int)start, (int)finish, diffzeit); fflush(stdout);
}
} while (toupper(cSelect) != 'E');
return 0;
}
*/
*/
*/
*/
*/
*/
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/8 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm : Vergleich verschiedener Sortierverfahren (2)
/* Fortsetzung des C-Moduls sortdemo_m.c
von main() verwendete Hilfsfunktionen
*/
void displSelection()
{ printf("->Auswahl\n");
printf("(1) Bubble
printf("(3) DirectSelect
printf("(5) Quick Sort
printf("(E) EXIT \n");
}
(2) DirectInsert\n");
(4) Austauschendes Suchen\n");
(6) ANSI-qs\n");
fflush(stdout);
void fillArray(double* daSortDat, int anz)
{ int i;
srand(123);
for (i=0; i<anz; i++)
daSortDat[i] = rand();
}
void show(double *daF,int iAnz)
{ int i;
for (i=0; i<iAnz; i++)
{ printf("%-10.2f",daF[i]);
if((i+1)%PROZEILE == 0) putchar('\n');
}
putchar('\n');
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/9 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm : Vergleich verschiedener Sortierverfahren (3)
/* C-Modul sortfunc.c
Definition von Funktionen, die verschiedene Sortierverfahren implementieren
Sortiert werden "double"-Arrays in aufsteigender Reihenfolge
*/
#include <stdlib.h>
/* Bubble-Sort - Sortieren durch direktes Austauschen */
void BubbleSort(double *daF, int iAnz)
{ int j,i;
double dHlp;
for (j=1;j<iAnz;j++)
for (i=iAnz-1;i>=j;i--)
if (daF[i-1] > daF[i])
{ dHlp = daF[i-1];
daF[i-1] = daF[i];
daF[i] = dHlp;
}
}
/* Sortieren durch direktes Einfuegen */
void DirectInsert(double *daF, int iAnz)
{ int i,j;
double dHlp;
for (j=1;j<iAnz;j++)
{ dHlp = daF[j];
i=j-1;
while(dHlp<daF[i] && i>=0)
{ daF[i+1]=daF[i];
i--;
}
daF[i+1] = dHlp;
}
}
/* Sortieren durch direktes Auswählen */
void DirectSelect(double *daF, int iAnz)
{ int i,j,imin;
double dHlp;
for (j=0;j<iAnz-1;j++)
{ imin=j;
for (i=j+1;i<iAnz;i++)
if(daF[i] < daF[imin])
imin=i;
dHlp = daF[imin];
daF[imin] = daF[j];
daF[j] = dHlp;
}
}
/* Sortieren durch austauschendes Suchen */
void ExchangingSeek(double *daF, int iAnz)
{ int i,j;
double dHlp;
for (j=0;j<iAnz-1;j++)
{ for (i=j+1;i<iAnz;i++)
if(daF[i] < daF[j])
{ dHlp = daF[i];
daF[i] = daF[j];
daF[j] = dHlp;
}
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/10 – TH/KR – 04
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm : Vergleich verschiedener Sortierverfahren (4)
/* Fortsetzung des C-Moduls sortfunc.c */
/* Sortieren durch Zerlegen - Quick Sort (Hoare) */
void quickSort(double *daF, int iStart, int iEnd)
// Aufruf siehe unten
{ int i,j;
double dPivot, dHlp;
i=iStart;
// Index des 1. Elements
j=iEnd;
// Index des letzten Elements
dPivot = daF[(iStart+iEnd)/2];
do
{ while ( daF[i] < dPivot ) i++;
while ( daF[j] > dPivot ) j--;
if(i<j)
{ dHlp = daF[i];
daF[i] = daF[j];
daF[j] = dHlp;
i++;
j--;
}
else if (i==j)
{ i++;
j--;
}
} while (i<=j);
if(iStart<j) quickSort(daF,iStart,j);
if(i<iEnd)
quickSort(daF,i,iEnd);
}
/* Aufruffunktion fuer Quick Sort */
void QuickSort(double *daF, int iAnz)
{ quickSort(daF, 0, iAnz-1);
}
// Startindex und Endindex!!
// von qsort() benoetigte Vergleichsfunktion
int vgl(const double * dEins, const double *dZwei)
{ int iRet = 0;
if(*dEins > *dZwei)
iRet = 1;
else if (*dEins < *dZwei)
iRet = -1;
return iRet;
}
*/
// von qsort() benoetigte Vergleichsfunktion mit norm-kompatibler Parameterliste
int vgl(const void * dEins, const void *dZwei)
{ int iRet = 0;
if(*(double*)dEins > *(double*)dZwei)
iRet = 1;
else if (*(double*)dEins < *(double*)dZwei)
iRet = -1;
return iRet;
}
*/
/* Quick Sort mittels Bibliotheksfunktion qsort() */
void ANSI_qs( double *daF, int iAnz)
{ qsort(daF,iAnz,sizeof(daF[0]),vgl);
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–C – 09/04/11 – TH/KR – 03
---------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm : Vergleich verschiedener Sortierverfahren (5)
Beispiel für einen Probelauf
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – Inhalt – TH – 07
-------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Teil 2: Einführung in die OOP mit Java
10. Grundkonzepte der Objektorientierten Programmierung
10.0.
10.1.
10.2.
10.3.
10.4.
10.5
Einführung : Grundgedanke der OOP
Objekte und Klassen
Kapselung
Vererbung
Polymorphie
Klassenbeziehungen
11. Grundlegende Eigenschaften von Java
11.1
11.2.
11.3.
11.4.
11.5.
11.6.
Java und C
Klassen und Kapselung
Programmstruktur und Startklasse
Erzeugung und Start von Java-Programmen
Packages in Java
Java-Standardbibliothek (Java API)
12. Daten und Objekte in Java
12.1.
12.2.
12.3.
12.4.
12.5.
12.6.
12.7.
12.8.
Einfache Datentypen
Referenztypen und Objekterzeugung
Konstante in Java
Wrapper-Klassen für die einfachen Datentypen
Strings
Arrays
Die Klasse Object
Beispiel : Klasse Ratio
13. Elementare Programmfunktionalitäten in Java
13.1.
13.2.
13.3.
13.4.
Zugriff zu Programmparametern
Standard-Ein- und Ausgabe
Exceptions
Dateibearbeitung
14. Vererbung und Polymorphie in Java
14.1.
14.2.
14.3.
14.4.
14.5.
Umfassende Klassendefinition
Klassenableitung
Polymorphie
Interfaces und ihre Implementierung
Programmbeispiel : Simpson-Integration
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/00/0 – TH – 01
------------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
10. Grundkonzepte der Objektorientierten Programmierung
(OOP)
10.0. Einführung : Grundgedanke der OOP
10.1. Objekte und Klassen
10.2. Kapselung
10.3. Vererbung
10.4. Polymorphie
10.5. Klassenbeziehungen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/00/1 – TH – 02
------------------------------------------------------------------------------------------------------------------------------------------------------------
Einführung
• Grundgedanke der OOP:
OOP ist eine Softwareentwicklungsmethodik, deren Grundidee aus der Simulationstechnik stammt :
In dieser werden die Objekte der realen Welt sowie deren Beziehungen durch entsprechende Strukturen im
Rechner abgebildet.
In der OOP wird dieses Prinzip auf alle Arten von Informationen und Abläufen – auch auf solche abstrakter
Natur – angewendet.
Der Aufgabenbereich eines zu lösenden Problems wird in Objekte und die zwischen ihnen bestehenden
Beziehungen zerlegt. Diese werden in einem das entsprechende Problem lösenden Programm nachgebildet.
Ein OOP-Programm besteht somit im wesentlichen aus einer Anzahl miteinander in Beziehung stehender
Objekte.
Diese Denk- und Vorgehensweise ermöglicht es, auch sehr umfangreiche und komplexe Aufgaben auf einem
relativ hohem Abstraktionsniveau erfolgreich zu bearbeiten.
Sie nutzt die intellektuellen Fähigkeiten aus, die der Mensch zur Bewältigung der ihn umgebenden Komplexität
entwickelt hat.
Dies sind im wesentlichen die Fähigkeiten des Abstrahierens, des Klassifizierens und des Generalisierens.
Auf ihnen beruhen die Grundkonzepte der OOP :
◈
Bildung von Objekten
◈
Abstraktion der Objekte durch Klassen
◈
Kapselung
◈
Vererbung
(
Klassifizieren)
◈
Polymorphie
(
Generalisieren)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/01/1 – TH – 03
------------------------------------------------------------------------------------------------------------------------------------------------------------
Grundkonzepte der OOP : Objekte und Klassen (1)
• Konkrete Objekte:
◇ Objekte sind in einem bestimmten Sinn abgeschlossene Einheiten, die durch zwei Aspekte gekennzeichnet
sind :
▻ Sie besitzen einen (inneren) Zustand
▻ und sie verfügen über Fähigkeiten, d.h. sie können bestimmte Operationen – aktiv oder passiv – ausführen
Diese Fähigkeiten und damit das dadurch bestimmte Verhalten können von außen angefordert, aktiviert
werden. Die Aktivierung der Fähigkeiten kann eine Zustandsänderung bewirken.
◇ Beispiel :
Willies Uhr
Zustand :
aktuelle Zeit
Fähigkeiten : Uhr stellen
Zeit fortschalten (ticken)
Zeit darstellen
• Objekte in der OOP
◇ In der OOP stehen Objekte im Vordergrund.
Sie bilden die grundlegenden Strukturierungseinheiten eines OOP-Programmms
Dabei kann ein Objekt sehr konkret aber auch beliebig abstrakt sein, es kann ein statisches Gebilde
(z.B. ein Auto), oder einen dynamischen Ablauf (Vorgang) beschreiben (z.B. ein Tennisspiel).
Der (innere) Zustand des Objekts wird durch Datenstrukturen (Datenkomponenten), seine Fähigkeiten
– die von ihm ausführbaren Operationen – werden durch Funktionen (Prozeduren) beschrieben.
Die Datenkomponenten werden auch als Attribute, die Funktionen (Memberfunktionen) als Methoden
bezeichnet.
◇ Ein Objekt verbindet also Daten und den zu ihrer Bearbeitung dienenden Code zu einer Einheit.
Die von einem Objekt ausführbaren Methoden (= Funktionen) sind Bestandteil des Objekts und nur als
solche relevant.
Dies steht im Gegensatz zur konventionellen (prozeduralen, imperativen) Programmierung, bei der Daten
und Code getrennt sind, wobei der Code (Prozeduren, Funktionen) eigenständig ist und im Vordergrund
steht : Code wird auf Daten angewendet.
◇ In der OOP wird eine Methode für ein Objekt aufgerufen, in dem an das Objekt – i.a. durch ein anderes
Objekt – eine entsprechende Nachricht (Botschaft) geschickt wird : Das Objekt interpretiert die Nachricht
und reagiert mit der Ausführung einer zugeordneten Operation (Methode).
Zwischen den Objekten bestehen also Kommunikationsbeziehungen.
Ein OOP-Programm besteht im wesentlichen aus einer Ansammlung miteinander kommunizierender
und dadurch interagierender Objekte.
◇ Der OOP-Ansatz erfordert eine andere Vorgehensweise bei der Problemlösung :
Statt einer Top-Down-Zerlegung des Problems ( hierarchische Modularisierung) müssen die relevanten
Objekte (Aufbau und Verhalten) des Problems und die zwischen ihnen bestehenden Beziehungen ermittelt
werden ( aufgaben- und kommunikationsorientierte Zerlegung)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/01/2 – TH – 03
------------------------------------------------------------------------------------------------------------------------------------------------------------
Grundkonzepte der OOP : Objekte und Klassen (2)
• Klassen
◇ Der Aufbau, die Eigenschaften und Fähigkeiten (Verhaltensweisen) von Objekten werden durch
Klassen beschrieben.
◇ Eine Klasse legt die Datenkomponenten (Datenstruktur) und die Methoden zur Bearbeitung der Daten
(Memberfunktionen) für eine Menge gleichartiger Objekte – d.h. Objekte mit gemeinsamen Merkmalen
und gleichen Fähigkeiten, die diese von anderen Objekten unterscheiden – fest.
Abstraktion
◇ Ein spezielles Objekt der durch eine Klasse definierten Objektmenge wird auch Instanz genannt.
Es unterscheidet sich von einem anderen Objekt (einer anderen Instanz) der gleichen Klasse nur durch
seinen jeweiligen Zustand, d.h. den Werten seiner Datenkomponenten.
⇒ Die Klasse entspricht dem Datentyp prozeduraler Programmiersprachen,
während eine Instanz (ein spezielles Objekt dieser Klasse) einer Variablen entspricht.
◇ Eine Klasse kann formal als Erweiterung einer Struktur (struct-Typ) in C aufgefasst werden.
Gegenüber einer nur aus Datenkomponenten bestehenden Struktur besitzt eine Klasse zusätzlich Funktionskomponenten.
◇ Beispiel : Klasse Uhr
Uhr
Datenkomp :
actTime
Funktionskomp :
setTime(...)
tick()
displayClock()
◇ Jedes Objekt (Instanz) einer Klasse hat einen eigenen (inneren) Zustand.
Die Datenkomponenten existieren für jedes Objekt (Unterschied zu Modulen der prozeduralen
Programmierung).
Sie werden erst geschaffen, wenn das Objekt generiert wird ( "Variablendefinition").
◇ Die Methoden (Funktionen) existieren dagegen nur einmal pro Klasse.
Sie werden durch die Definition der Klasse ( "Typdefinition") geschaffen.
Auch wenn es gar keine Objekte dieser Klasse gibt, existieren die Methoden.
Jedes Objekt einer Klasse arbeitet mit demselben Code ( Code Sharing)
Beim Aufruf einer Methode für ein spezielles Objekt, wird dieser eine Referenz auf das Objekt als
verborgener Parameter übergeben.
Dadurch wird die Methode an ein spezielles Objekt gekoppelt
Sie wird damit quasi zu einem Bestandteil des Objekts
Dies ermöglicht der Methode, zu sämtlichen Komponenten (insbesondere also auch zu den Datenkomponenten) dieses Objekts zuzugreifen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/02/1 – TH – 01
------------------------------------------------------------------------------------------------------------------------------------------------------------
Grundkonzepte der OOP : Kapselung
• Kapselung (Encapsulation):
◇ Der Benutzer eines Objekts (allg. : Anwenderprogramm, speziell : anderes Objekt) braucht seinen genauen
Aufbau nicht zu kennen.
Ihm müssen lediglich die Methoden, die er für eine Interaktion mit dem Objekt benötigt, d.h. über die er
die Fähigkeiten des Objekts aktivieren und dessen Zustand verändern kann, bekannt zu sein.
Von der internen Darstellung der den jeweiligen Objektzustand festlegenden Daten braucht er dagegen
keinerlei Kenntnis zu haben.
◇ Nur die Funktionen (= Methoden) eines Objekts, die zu seiner Verwendung tatsächlich benötigt werden,
werden allgemein zugänglich, d.h. öffentlich (public), gemacht.
Sie bilden das Interface (Protokoll), über das zu dem Objekt kontrolliert zugegriffen werden kann, d.h. sie
bilden seine Schnittstelle zur "Außenwelt".
Die Daten (u. gegebenenfalls reine Hilfs- und Verwaltungsfunktionen) sind nur Komponenten des Objekts
selbst zugänglich, d.h. privat (private).
Der "Außenwelt" gegenüber bleiben sie verborgen Sie sind nach außen gekapselt.
Hierdurch wird sichergestellt, dass zu einem Objekt nur über eine wohldefinierte Schnittstelle zugegriffen
Werden kann. Klassen-Schnittstelle
Zugriffe zu Interna, die nur zur internen Realisierung und Verwaltung des Objekts dienen, sind nicht möglich
Vermeidung von Fehlern durch Verhinderung eines direkten und unkontrollierten Zugriffs
⇒ Trennung von Interface (Schnittstelle) und Implementierung.
Methoden
Daten
Methoden
◇ Die Kapselung bewirkt außerdem eine Datenabstraktion (data abstraction) :
Eine Datenstruktur ist - nach außen - nicht mehr an eine bestimmte Implementierung gebunden, sondern wird
allein über die auf sie anwendbaren Operationen (Methoden, Funktionen) definiert.
( abstrakter Datentyp, ADT)
Eine Änderung der Implementierung - bei gleichbleibendem Interface – hat keinen Einfluß auf den
Anwendungscode.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/03/1 – TH – 04
------------------------------------------------------------------------------------------------------------------------------------------------------------
Grundkonzepte der OOP : Vererbung
• Vererbung (Inheritance) :
◇ Weitergabe von Eigenschaften (Daten und Funktionen) eines Objekts an ein anderes Objekt.
Zusätzlich zu den ererbten Eigenschaften kann ein Objekt neue spezifische Eigenschaften (Daten und
Funktionen) besitzen bzw. bestimmte Eigenschaften modifizieren.
Schaffung einer neuen Art von Objekten durch Erweiterung einer bestehenden Art von Objekten.
◇ Die Vererbung führt zum Aufbau von Klassenhierarchien :
Eine neue Klasse wird aus einer - oder mehreren - bereits definierten Klasse(n) abgeleitet.
vorhandenene Klasse : Basisklasse, Elternklasse, Oberklasse
neue Klasse :
abgeleitete Klasse, Kindklasse, Unterklasse, Subklasse
Die abgeleitete Klasse erbt die Daten und Methoden der Basisklasse(n).
Dabei können geerbte Methoden abgeändert ("überschrieben") werden.
Zusätzlich kann die abgeleitete Klasse neue Daten und Methoden besitzen.
Abänderung und Ergänzung im Sinne einer weiteren Spezialisierung ( Klassifizierung)
◇ Beispiel : Ableitung der Klasse UhrMitDatum von der Klasse Uhr
neue Datenkomponenten :
neue Funktionskomponenten :
geänderte Funktionskomponenten :
actDate
setDate(), setClock()
tick(), displayClock()
Uhr
Datenkomp :
actTime
Funktionskomp :
setTime(...)
tick()
displayClock()
UhrMitDatum
neue Datenkomp :
actDate
neue Funkt.komp :
setDate(...)
setClock(...)
modifiz. Funkt.komp : tick()
displayClock()
◇ Ein Objekt einer abgeleiteteten Klasse kann immer auch als – spezielles - Objekt der Basisklasse(n)
betrachtet werden : z.B. ist jedes UhrMitDatum-Objekt auch ein Uhr-Objekt.
◇ Einfache Vererbung : Ableitung einer Klasse von nur einer Basisklasse.
Mehrfachvererbung : Ableitung einer Klasse von mehreren Basisklassen
(nicht von allen OOP-Sprachen unterstützt, u.a. auch nicht von Java)
◇ Durch Vererbung übertragene Methoden (Funktionen) existieren nur einmal ( Code Sharing).
Dies erleichtert Änderungen und Erweiterungen an bestehenden Klassenhierarchien.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/04/1 – TH – 04
------------------------------------------------------------------------------------------------------------------------------------------------------------
Grundkonzepte der OOP : Polymorphie
• Prinzip der Polymorphie (Polymorphism) :
◇ Verwendung des gleichen Namens für unterschiedliche - aber miteinander verwandte - Dinge.
(griech. : Vielgestaltigkeit)
◇ Polymorphie in der OOP ermöglicht, dass verschiedenartige Objekte (unterschiedlicher aber durch Vererbung miteinander verwandter Klassen) unter einem gemeinsamen Oberbegriff (Basisklasse) betrachtet
und bearbeitet werden können.( Generalisierung)
Beispiel : Sowohl Objekte der Klasse Uhr als auch Objekte der Klasse UhrMitDatum lassen sich als
Uhr-Objekte behandeln.
● Überschreiben und Überladen von Methoden
◇ In der Basisklasse definierte Funktionen können in abgeleiteten Klassen mit dem gleichen Namen und
der gleichen Parameterliste (d.h. gleichem Interface) erneut definiert werden.
Überschreiben von Funktionen (Function Overwriting), virtuelle Funktionen.
.
◇ Dadurch wird das durch den Aufruf einer derartigen Funktion bewirkte Verhalten eines Objekts in der
abgeleiteten Klasse abgeändert.
Der gleiche (Funktions-)Name kann somit zur Spezifizierung einer Gruppe ähnlicher - aber doch unterschiedlicher – Operationen (Methoden) verwendet werden.
Die gleiche durch den Funktions-Namen ausgedrückte Botschaft kann - an unterschiedliche Objekte
gerichtet – zum Aufruf unterschiedlicher Methoden führen.
Die in einem konkreten Fall ausgeführte Operation (Methode) hängt von der tatsächlichen Klasse des
Objekts ab, an das die Botschaft gerichtet ist ( "Ein Interface - mehrere Methoden").
Also nicht die Botschaft, d.h. der Aufrufer, bestimmt, welche Methode (Funktion) ausgeführt wird, sondern
der Empfänger der Botschaft.
◇ Beispiel : Die Botschaft "displayClock()" an ein UhrMitDatum-Objekt gerichtet, bewirkt die Ausgabe der Uhrzeit und des Datums, während sie bei einem Uhr-Objekt nur zur Ausgabe der Uhrzeit führt.
⇒
Polymorphie erlaubt durch die Bereitstellung eines gemeinsamen Interfaces die einheitliche
Verarbeitung unterschiedlicher Objekte, die über gemeinsame Grundfähigkeiten verfügen.
Dadurch wird die Beherrschung einer größeren Komplexität ermöglicht.
◇ Beim Aufruf virtueller (überschreibbarer) Methoden wird die Zuordnung der tatsächlich aufgerufenen
Methode zur Botschaft (Methodenname) erst zur Laufzeit vorgenommen. Dies bezeichnet man als
"späte Bindung" ("late binding").
Bei einer Zuordnung bereits zur Compilezeit spricht man von "früher Bindung" ("early binding").
◇ In einer erweiterten Betrachtungsweise ermöglicht Polymorphie auch das – nicht nur in der OOP eingesetzte – Überladen von Funktionen (Methoden) und Operatoren :
▻ Überladen von Funktionen (Function Overloading) :
Mehrere Funktionen können den gleichen Namen besitzen, sofern ihre Parameterliste (Signatur)
unterschiedlich ist.
Durch den gleichen Namen wird auch hier eine Art Standard-Interface (das sich aber nicht auf die
Parameter erstreckt) bereitgestellt, über das sich mehrere unterschiedliche aber meist miteinander
verwandte Methoden aufrufen lassen.
Die speziell angewandte Methode hängt hier von den beim Aufruf übergebenen Daten (Parametern) ab.
▻ Überladen von Operatoren (Operator Overloading) :
Operatoren können zur Anwendung auf unterschiedliche Datentypen umdefiniert werden.
Definition spezieller Operatorfunktionen (in Java nicht möglich)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/05/1 – TH – 02
------------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenbeziehungen (1)
• Vererbungsbeziehung
◇ Beziehung zwischen Klassen, deren Komponenten sich teilweise überdecken
◇ Eine abgeleitete Klasse erbt die Eigenschaften und Fähigkeiten (Komponenten) der Basisklasse(n).
"ist"-Beziehung ein Objekt der abgeleiteten Klasse ist auch ein Objekt der Basisklasse(n)
◇ Ordnungsprinzip bei der Spezifikation von Klassen.
Generalisierung / Spezialisierung
◇ Ein Spezialfall der Vererbung ist die Implementierung (Basisklasse definiert nur ein Zugriffsinterface)
• Nutzungsbeziehungen
◇ Unter einer (statischen) Nutzungsbeziehung versteht man eine in einem konkreten Anwendungsbereich
geltende Beziehung zwischen Klassen, deren Instanzen voneinander Kenntnis haben und die dadurch
miteinander kommunizieren können
Nutzungsbeziehungen sind notwendig für die Interaktion von Objekten
◇ Assoziation
- Spezielle Beziehung zwischen Klassen bzw Objekten, bei der die Objekte unabhängig voneinander
existieren und lose miteinander gekoppelt sind
Beispiel : einem Objekt wird die Referenz auf ein anderes Objekt in einer Methode als Parameter
übergeben und nur in dieser Methode verwendet
- Navigationsrichtung : legt die Kommunikationsrichtung und die Richtung, in der ein Objekt der einen
Klasse ein Objekt der anderen Klasse referieren kann, fest.
bidirektional (Kommunikation in beiden Richtungen möglich) oder unidirektional
- Multiplizität (multiplicity) : bezeichnet die mögliche Anzahl der an der Assoziation beteiligten
Instanzen einer Klasse.
◇ Aggregation
- Spezielle Beziehung zwischen Klassen bzw Objekten, bei der die Objekte der einen Klasse Bestandteile
(Komponenten) eines oder mehrerer Objekte der anderen Klasse sind.
zwischen den Objekten besteht eine feste Kopplung
- "hat"-Beziehung bzw "ist Teil von"-Beziehung
- Das "umschließende" Objekt bildet einen Container für das bzw die enthaltene(n) Objekt(e)
- Aggregation kann als Spezialfall der Assoziation aufgefaßt werden.
- eine Aggregation ist i.a. eine unidirektionale Beziehung (Navigation vom umschließenden Objekt zu den
Komponenten-Objekten)
Je nach dem Grad der Kopplung unterscheidet man :
▻ einfache Aggregation
Das umschließende Objekt und die Komponenten sind nicht existenzabhängig
Eine Komponente kann zusätzlich noch weiteren umschließenden Objekten der gleichen oder einer
anderen Klasse zugeordnet sein.
Bei Löschung des umfassenden Objekts bleiben die Komponenten unabhängig davon erhalten.
Beispiel : Objekt wird vom umfassenden Objekt erzeugt, Referenz darauf wird anderem Objekt übergeben
▻ echte Aggregation (Komposition, composite aggregation)
Die Komponenten können nur einem umfassenden Objekt zugeordnet sein und nur innerhalb diesem
existieren. Bei Löschung des Aggregats werden auch die Komponenten gelöscht.
Beispiel : Objekt wird von umfassenden Objekt erzeugt und nur dort verwendet
◇ Anmerkung :
In der Praxis kann es im Einzelfall sehr schwierig sein, zwischen Assoziation und Aggregation und den
verschiedenen Formen der Aggregation zu unterscheiden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/05/2 – TH – 02
------------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenbeziehungen (2)
• Klassendiagramm
◇ Die (statischen) Beziehungen (Vererbungsbeziehungen und Nutzungsbeziehungen) zwischen den in einem
OOP-System zusammenwirkenden Klassen lassen sich durch Klassendiagramme beschreiben.
◇ Ein Klassendiagramm ist eines der in der UML (Unified Modelling Language) zusammengefassten graphischen Darstellungsmittel.
Es ermöglicht die Erstellung eines detaillierten statisches Systemmodells
◇ Zur Erhöhung der Übersichtlichkeit kann die Gesamtheit der Klassen eines Systems auf mehrere
Teildiagramme aufgeteilt sein
• Anmerkung :
◇ Die dynamischen Beziehungen zwischen den Objekten eines OOP-Systems werden durch Sequenzdiagramme, einer anderen Diagrammart der UML, beschrieben. Sie werden hier nicht weiter betrachtet.
• Elemente eines Klassendiagramms
Klassenname
KlasseA
Datenkomponenten
1
*
KlasseB
Funktionskomponenten
Klasse
Assoziation (bidirektional)
KlasseA
KlasseA
1
n
KlasseB
Assoziation (unidirektional)
KlasseB
KlasseA
Vererbung
1
1
KlasseB
(einfache) Aggregation
InterfaceA
KlasseA
KlasseB
KlasseB
Implementierung
Komposition (echte Aggregation)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 10/05/3 – TH – 03
------------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenbeziehungen (3)
• Beispiele für einfache Klassendiagramme
Textdateifilterung
Test reeller Zweipole
Bestellverwaltung
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/00/0 – TH – 01
-----------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
11. Grundlegende Eigenschaften von Java
11.1. Java und C
11.2. Klassen und Kapselung
11.3. Programmstruktur und Startklasse
11.4. Erzeugung und Start von Java-Programmen
11.5. Packages in Java
11.6. Java-Standardbibliothek (Java API)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/01/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Java und C (1)
• Java-Entwicklung
◇ Java wurde von der Fa. Sun entwickelt. Die Entwicklung begann im Jahr 1990.
1995 wurde Java erstmals der Öffentllichkeit vorgestellt (Sun World 95).
Seit 1996 stellt die Fa. Sun (jetzt Oracle) "offizielle Java-Versionen" in Form von Java Development Kits
kostenlos zum Download bereit.
Die Java Development Kits enthalten alle für die Entwicklung von Java-Programmen benötigten Tools
(Compiler, Interpreter (Java Virtual Machine, JVM), weitere Dienstprogramme und die Standardbibliothek)
▻ 1996
(Jan.) Freigabe des Java Development Kits 1.0 (JDK 1.0)
▻ 1997
JDK 1.1
▻ 1998
JDK 1.2, kurze Zeit später umbenannt in Java 2 Platform
▻ 2000
JDK 1.3 (Offizieller Name : Java2 System Development Kit 1.3, J2SDK 1.3)
▻ 2002
JDK 1.4 (J2SDK 1.4) (06.02.2002)
▻ 2004
JDK 1.5 (JDK 5.0)
(30.09.2004)
▻ 2006
JDK 1.6 (JDK 6.0)
(11.12.2006)
▻ 2011
JDK 1.7 (JDK 7.0)
(28.07.2011)
▻ 2014
JDK 1.8 (JDK 8.0)
(18.03.2014)
• Java2-Plattformen
► Java2 Standard Edition (J2SE)
► Java2 Enterprise Edition (J2EE), zusätzliche Komponenten zur Entwicklung unternehmensweiter verteilter
Anwendungen (u.a. Enterprise Java Beans, EJB)
► Java2 MicroEdition (J2ME), eingeschränkter Sprachstandard für den Einsatz in Geräten wie Mobiltelefonen,
PDAs usw
► Java Card Platform
• Java – ein C-Derivat
◇ Java kann als Derivat der Sprache C betrachtet werden.
Die Syntax und großenteils die Semantik von Java entsprechen weitgehend der Syntax und Semantik von C.
Anweisungen und Variablenvereinbarungen werdem im wesentlichen wie in C formuliert.
◇ Im Unterschied zu C++, einer ebenfalls von C abgeleiteten hybriden (prozedural und objektorientiert verwendbaren) Programmiersprache, ist Java als rein objekt- (bzw klassen-) orientierte Sprache konzipiert worden.
◇ Außer von C ist Java auch stark von C++ und weiteren objektorientierten Programmiersprachen wie Oberon,
Ada, Eiffel, Objective-C beeinflusst worden.
◇ Zahlreiche fehleranfällige oder redundante Bestandteile /Eigenschaften von C/C++ wurden nicht
implementiert. Wo sinnvoll und erforderlich sind diese durch "bessere" Konzepte ersetzt.
◇ Ein weiterer elementarer Unterschied zwischen C und Java liegt in der Anwendbarkeit der erzeugten
Programme :
- Ein C-Compiler erzeugt Maschinen-Code, C-Programme laufen direkt, aber nur auf der jeweiligen
Hardware, für die sie erzeugt wurden.
- Ein Java-Compiler erzeugt einen maschinenunabhängigen Byte-Code. Dieser kann als Maschinencode
einer virtuellen Maschine aufgefasst werden. Java-Programme müssen mittels eines Interpreters
(Java Virtual Machine, JVM) ausgeführt werden.
Ein einmal erzeugtes Java-Programm läuft damit aber auf jeder Hardware, für die eine JVM existiert.
Motto von Sun : Write Once , Run Anywhere
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/01/2 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Java und C (2)
• In Java nicht implementierte C-Eigenschaften
Im wesentlichen fehlen in Java die folgenden C- Eigenschaften bzw Sprachelemente :
▻ Pointer und Pointerarithmetik
▻ Funktionspointer
▻ Speicherklassen (auto, register, extern)
▻ globale Variable
▻ freie Funktionen
▻ die strukturierten Typen struct und union
▻ Bitfelder
▻ Datentyp long double
▻ vorzeichenlose Datentypen (kein unsigned und damit auch kein signed)
▻ Nachbildung von logischen Werten durch int-Werte
▻ Einführung neuer Typnamen mittels typedef
▻ Sprunganweisung goto
▻ sizeof-Operator
▻ Komma-Operator
▻ ->-Operator
▻ Präprozessoranweisungen (Textersatz (Makros), bedingte Kompilierung)
▻ Header-Dateien
▻ Extern- und Vorwärtsreferenzen
▻ Dynamische Allokation von Variablen einfacher Datentypen
▻ Datentyp wchar_t (ein in C in Headerdateien mittels typedef definierter Typ)
▻ Datentypen long long, _Bool, sowie komplexe und imaginäre Datentypen (C99)
▻ inline-Funktionen (C99)
• In Java geänderte Eigenschaften von C (C90)
▻ Die Trennung eines Blockes in Vereinbarungsteil (am Anfang) und Anweisungsteil existiert in Java nicht
Vereinbarungen und Anweisungen können beliebig gemischt werden.
▻ Der Anfangsausdruck im Kopf einer for-Anweisung kann durch eine initialisierte Variablendefinition
ersetzt werden keine vorherige Definition einer "Laufvariablen" notwendig
Beispiel :
for (int i=1; i<=end; i++)
// ...
▻ In der Parameterliste einer parameterlosen Funktion darf das Schlüsselwort void nicht angegeben
werden. Die Parameterliste muß leer bleiben.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/01/3 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Vergleich der Schlüsselworte und Identifier von Java und C
• Schlüselworte (key words)
reine CSchlüsselworte
++) C-99
Schlüsselworte,
die in C und Java
enthalten sind
*) in Java reserviert,
aber nicht
verwendet
neue
Schlüsselworte
von Java
auto
extern
inline
register
restrict
signed
sizeof
struct
typedef
union
unsigned
_Bool
_Complex
_Imaginary
break
case
char
const
*)
continue
default
do
double
else
enum
float
for
goto
if
int
long
return
short
static
switch
void
volatile
while
abstract
assert
boolean
byte
catch
class
extends
false
**)
final
finally
implements
import
instanceof
interface
native
new
null
**)
package
private
protected
++)
++)
*)
++)
++)
++)
public
strictfp
super
synchronized
this
throw
throws
transient
true
**)
try
**) genau genommen handelt es sich hierbei in Java nicht um ein Schlüsselwort,
sondern um eine Konstante (literal)
• Identifier
◇ Identifier (Namen) in Java werden nach den gleichen Regeln wie in C gebildet :
▻ Sie bestehen aus Buchstaben, dem Underscore ('_') und Ziffern
▻ Sie müssen mit einem Buchstaben oder dem Underscore ('_') beginnen
▻ Groß- und Klein-Buchstaben sind unterschiedlich
▻ Sie dürfen nicht wie ein reserviertes Wort (einschliesslich false, true und null) lauten
◇ Zusätzlich dürfen Java-Identifier das Dollarzeichen ('$') enthalten.
Dieses wird wie ein Buchstabe behandelt (Beginn mit '$' ist zulässig)
◇ Als Buchstaben sind alle Unicode-Buchstaben (also auch andere als im lateinischen Alphabet) zulässig
◇ Java-Identifier dürfen aus beliebig vielen Zeichen bestehen.
◇ Konventionen (nicht verpflichtend) bezüglich der Schreibweise :
▻ Variable : nur Kleinbuchstaben, z. Tl. aber auch Unterteilung in Folge-Abschnitte (Beginn mit Groß-Bu)
▻ Konstante : nur Großbuchstaben
▻ Funktionen : Beginn mit Klein-Bu, häufig Unterteilung in Abschnitte, die jeweils mit Groß-Bu beginnen
▻ Typen (Klassen) : Beginn mit Groß-Bu, Unterteilung in Abschnitte, die jeweils mit Groß-Bu beginnen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/01/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Operatoren in Java
• Operator-Hierarchie von Java
Priorität
Operator
Operation
1
[]
()
.
++ (Postfix)
-- (Postfix)
Indizierung
Funktionsaufruf
Komponentenzugriff
Increment
Decrement
l
2
++ (Prefix)
-- (Prefix)
+
!
~
Increment
Decrement
Identität
Negation (arithmetisch)
Negation (logisch)
bitweises Komplement
lr
3
new
(type)
Objekterzeugung
Typkonvertierung (Cast)
lr
4
* / %
Multiplikation / Division / Modulus
l
r
5
+
-
Addition bzw Konkatenation (Strings)
Subtraktion
l
r
6
<<
>>
>>>
Linkssschieben
Rechtsschieben, Nachschieben des Vorzeichens
Rechtsschieben, Nachschieben von 0
l
r
*)
*)
Assoziativität
r
7
< <=
> >=
instanceof *)
Vergleich (kleiner / kleiner gleich)
Vergleich (größer / größer gleich)
Typprüfung
l
r
8
== !=
Vergleich (gleich / ungleich)
l
r
9
&
bitweises UND
l
r
10
^
bitweises EXOR
l
r
11
|
bitweises ODER
l
r
12
&&
logisches UND
l
r
13
||
logisches ODER
l
r
14
?:
bedingte Auswertung
lr
15
=
*= /= %=
+= -=
<<= >>=
>>>=
*)
&= ^= |=
Zuweisung
Zuweisung mit Verknüpfungsoperation
lr
*) neue Operatoren in Java
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/02/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassen und Kapselung (1)
• Klassen – Grundbausteine eines Java-Programms
◇ Im vorigen Kapitel wurde ein OOP-Programm im wesentlichen als eine Ansammlung miteinander kommunizierender Objekte definiert.
Objekte werden durch Klassen beschrieben. Eine Klasse definiert die jeweiligen Eigenschaften (den Aufbau)
und das Verhalten (die Funktionalität) der von ihr instanzierbaren Objekte.
◇ Klassen sind die elementaren Bestandteile jedes Java-Programms.
Sämtlicher Code eines Java-Programms befindet sich innerhalb von Klassen.
Ein Programm kann aus beliebig vielen Klassen bestehen.
• Aufbau von Klassen
◇ Eine Klasse ist aus Klassenkomponenten aufgebaut. Diese werden in einer Klassendefinition festgelegt.
Klassenkomponenten können sein :
- Datenkomponenten (Membervariable) Variablendefinitionen
Die Gesamtheit der Datenkomponenten beschreibt den Aufbau und – mit ihren jeweiligen konkreten
Werten in einem Objekt – den Zustand der Objekte ( Objektvariable).
- Funktionskomponenten (Memberfunktionen) Funktionsdefinitionen
Diese legen das Verhalten, die Fähigkeiten der Objekte fest ( Objektfunktionen).
◇ Klassen können formal als eine Erweiterung der Structure-Typen von C aufgefasst werden.
◇ Eine Klasse kann auch Komponenten (sowohl Daten- als auch Funktionskomponenten) enthalten, die nicht
objekt-spezifisch sind, sondern den Zustand und das Verhalten der Klasse selbst beschreiben.
Sie sind durch den Modifizierer static gekennzeichnet.
statische Datenkomponenten (Klassenvariable) bzw statische Funktionskomponenten
(Klassenfunktionen)
◇ Zusätzlich kann eine Klassendefinition
- Konstruktoren enthalten
Konstruktoren sind spezielle klassenspezifische Funktionen, die bei der Objekterzeugung automatisch
aufgerufen werden. Sie tragen immer den Klassennamen und besitzen keinen Rückgabetyp (auch nicht
void). Sie werden nicht durch static gekennzeichnet.
Sie werden in Java nicht zu den Memberfunktionen und nicht zu den Klassenkomponenten gerechnet.
Ihre primäre Aufgabe besteht in der Initialisierung der Datenkomponenten des erzeugten Objekts.
Konstruktoren können Parameter besitzen ( Initialisierungswerte). Ein parameterloser Konstruktor wird
auch als no-arg-Konstruktor bezeichnet.In einer Klasse können mehrere Konstruktoren – mit jeweils
unterschiedlicher Parameterliste – definiert sein. Falls eine Klassendefinition keinen Konstruktor enthält,
wird vom Compiler implizit ein Default-Konstruktor (parameterlos) bereitgestellt.
◇ Eine Klassendefinition kann noch weitere Bestandteile enthalten (s. später)
• Kapselung
◇ Das Konzept der Kapselung wird in Java durch die Festlegung von Zugriffsberechtigungen zur Klasse
selbst und zu den Klassenkomponenten realisiert.
◇ Dies erfolgt durch die Angabe von Zugriffs-Modifizierern. Hierfür existieren folgende Möglichkeiten :
- kein Zugriffs-Modifizierer : Zugriffsberechtigung "package".
Ein Zugriff besteht nur von innerhalb des Packages (eine Art Namensraum), in dem die Klasse definiert ist.
- Modifizierer public : Ein Zugriff ist von überall her möglich (öffentlicher Zugriff).
- Modifizierer private (nur für Klassenkomponenten) : Zugriff nur von innerhalb der Klasse, in dem
die jeweilige Komponente definiert ist.
- Modifizierer protected (nur für Klassenkomponenten) : Zugriff beschränkt auf die Klasse selbst,
auf abgeleitete Klassen und auf das Package
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/02/2 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassen und Kapselung (2)
• Vereinfachte Syntax der Klassendefinition
(vollständiges Syntax-Diagramm s. später)
class
Klassen-Name
{
Komponentendefinition
public
}
Konstruktordefinition
;
◇ Komponentendefinition : Variablen- oder Funktionsdefinition
Beide können beliebig gemischt werden
▻ Variablendefinition (Definition von Datenkomponenten)
Typangabe
Variablenname
Feld-Modifizierer
Initialisierer
=
;
,
Datenkomponenten können bei der Definition initialisiert werden (mit einem typkonformen Ausdruck)
Feld-Initialisierer
▻ Funktionsdefinition (Definition von Memberfunktionen), vereinfacht
Rückgabetyp
MethodenModifizierer
Methoden-Name
(
Methoden-Kopf
{
Formal-Parameterliste
)
throws-Klausel
Anweisung
}
MethodenRumpf
▻ Die wichtigsten Feld- bzw Methoden-Modifizierer sind (es gibt noch weitere) :
- die Zugriffs-Modifizierer public, private und protected
- static
: Die Komponente ist eine statische Klassenkomponente
(klassenspezifische Komponente)
- final : Bei Datenkomponenten : Komponente ist eine Konstante
Bei Memberfunktionen : Methode kann in abgeleiteten Klassen nicht überschrieben werden
▻ Wenn eine Methode keine Parameter besitzt, ist die Formal-Parameterliste leer (kein void !!!)
▻ Die throws-Klausel deklariert Exceptions, die von der Funktion geworfen werden können (s. später)
◇ Konstruktordefinition : analog zur Funktionsdefinition, mit folgenden Unterschieden :
▻ als (Konstruktor-) Modifizierer sind nur die Zugriffs-Modifizierer zulässig
▻ kein Rückgabetyp
▻ Methodenname == Klassenname
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/02/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassen und Kapselung (3)
• Beispiel einer einfachen Klassendefinition :
public class Uhr
{
private long acttime;
public Uhr()
{ acttime = 0;
}
// Datenkomponente
// Konstruktor
public void setTime(long time)
{ acttime = time;
}
public long tick()
{ ++actTime;
return acttime;
}
// Funktionskomponenten
public void displayClock()
{
/* ... */
}
}
Anmerkung : Der Klassenname allein dient als Typ-Bezeichnung für Variablendefinitionen
• Zugriff zu Klassenkomponenten
◇ Ein Zugriff von ausserhalb der Klasse ist nur entsprechend der durch den jeweiligen Zugriffs-Modifizierer
festgelegten Berechtigung möglich.
Zu private-Komponenten ist ein Zugriff von außen grundsätzlich nicht zulässig
◇ Klassenkomponenten (sowohl Daten- als auch Funktionskomponenten) werden mittels des – aus C für den
Struktur-Komponenten-Zugriff bekannten – Komponentenzugriff-Operators . ausgewählt :
▻ objektspezifische Komponenten über eine Objekt-Referenz
Beispiel :
myClck sei eine Referenz auf ein Uhr-Objekt
myClck.displayClock();
// Methodenaufruf
Interpretation : An das durch myClock referierte Objekt wird die Botschaft displayClock
geschickt. Das Objekt reagiert daraufhin mit der Ausführung der zugeordneten Methode.
▻ klassenspezifische Komponenten (static !!!) über den Klassennamen.
Beispiel :
System ist eine Bibliotheksklasse, die u.a. die statische Methode
getProperties() besitzt.
System.getProperties()
// Methodenaufruf
◇ Von innerhalb eines Objekts einer Klasse (Memberfunktionen, Konstruktoren) ist der Zugriff zu allen
Komponenten (auch private) allein mit deren Namen möglich.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/03/1 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Struktur von Java-Programmen (1)
• Modulkonzept
◇ Ein Java-Programm besteht nur aus Klassen.
◇ Jede Klasse muss in einer eigenen Quelldatei vollständig definiert und implementiert werden.
Ausnahme von der eigenen Quelldatei : eingebettete Klassen (nested classes).(hier nicht betrachtet)
Der Quelldatei-Hauptname muss wie der (Haupt-)Klassenname lauten.
Die Quelldatei-Extension ist .java.
Beispiel : Klasse Welcome
Quelldateiname : Welcome.java
◇ Eine Quelldatei ist die Übersetzungseinheit (Übersetzungs-Modul).
Für jede enthaltene Klasse wird vom Compiler eine eigene Byte-Code-Datei erzeugt.
Die Byte-Code-Datei für die (Haupt-)Klasse bekommt den Hauptnamen der Quellcode-Datei und die
Extension .class
Beispiel : Aus Welcome.java erzeugt der Compiler Welcome.class
Nur bei eingebetteten Klassen : Die Hauptnamen der übrigen Byte-Code-Dateien sind aus den Namen der
Haupt-Klasse und der eingebetteten Klassen zusammengesetzt.
• Programmstruktur
◇ Ein Linken der getrennt übersetzten Module (Klassen-Dateien) zu einer – ausführbaren – Programm-Datei
findet in Java nicht statt.
◇ Ein Java-Programm ist damit konzeptionell nicht in einer einzigen Datei zusammengefasst. Vielmehr
besteht es – entsprechend den im Programm eingesetzten Klassen – aus einer oder mehreren Dateien.
Anmerkung : Es gibt Möglichkeiten der Zusammenfassung zu einer Archiv-Datei (hier nicht betrachtet)
◇ Eine dieser Dateien muss die Start-Klasse des Programms enthalten.
◇ Ausgehend von der Start-Klasse werden die übrigen Klassen des Programms referiert.
• Programmarten
◇ Es werden zwei Java-Programmarten unterschieden :
▻ Applikations-Programme
▻ Applets
◇ Applikationsprogramme
"Normale" Java-Programme. Sie werden durch direkten Start der Virtuellen Maschine (JVM) ausgeführt.
Hierfür ist dieser beim Start die Start-Klasse des Programms als Parameter zu übergeben
◇ Applets
Diese Programme werden innerhalb eines Java-fähigen Web-Browsers (oder im Kontext eines anderen
"applet viewers") ausgeführt. Ein derartiger Browser (bzw "applet viewer") enthält eine – gegebenenfalls über
ein Plug-In eingebundene – JVM. Der Applet-Code befindet sich üblicherweise auf einem Server und wird
durch ein <Applet>-Tag im HTML-Code einer Web-Seite referiert. Stößt der Browser beim Interpretieren
des HTML-Codes auf ein derartiges <Applet>-Tag, startet er seine JVM. Diese lädt von der angegebenen
URL den Byte-Code der Start-Klasse des Applets und führt ihn aus.
Applets sind damit – i.a. kleine – Programme, die in einfacher Art und Weise über das Internet verteilt werden
können. Da Applets prinzipiell unzuverlässigen Code enthalten können, unterliegt ihre Ausführung strengen
Sicherheitsrestriktionen.
Der Start und die Ausführung eines Applets unterscheidet sich aber erheblich von Start und Ausführung eines
Applikations-Programms.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/03/2 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Struktur von Java-Programmen (2)
• Start-Klasse eines Java-Applikations-Programms
◇ Die der JVM beim Start zu übergebene Klasse muss – neben möglicherweise beliebig vielen anderen
Methoden – eine statische main()-Methode besitzen.
Diese main()-Methode muss öffentlich zugänglich (public) und vom Typ void sein.
Als formalen Parameter muß sie ein String-Array besitzen.
Sie stellt den Startpunkt der Programmausführung dar.
◇ Beispiel für ein minimales Java-Programm :
// Welcome.java
public class Welcome
{
public static void main(String[] args)
{
System.out.println("Welcome to the world of Java");
}
}
◇ I.a. ist ein Java-Programm nicht klassenorientiert sondern objektorientiert. D.h. im Programm werden
Objekte erzeugt, die miteinander kommunizieren.
Die Erzeugung des "ersten" Objekts erfolgt dann in der main()-Methode der Start-Klasse :
▻ Entweder durch Instantiierung einer anderen Klasse (Start-Klasse dient nur zum Programmstart)
▻ oder durch Instantiierung der eigenen Klasse (Start-Klasse enthält auch spezifische Programmfunktionalitäten)
◇ Ausgehend vom ersten Objekt werden dann die weiteren Objekte erzeugt.
◇ Beispiel für ein minimales Java-Programm mit Objekterzeugung :
// Willkommen.java
public class Willkommen
{
public Willkommen()
{
// nur zur Demonstration
}
public void begruessung()
{
System.out.println("Willkommen bei Java");
}
public static void main(String[] args)
{
Willkommen gruss = new Willkommen();
gruss.begruessung();
}
// Konstruktor
// Memberfunktion
// Erzeugung eines Objekts
}
◇ Anmerkung :
Häufig wird auch bei Klassen, die in der späteren Verwendung nicht als Start-Klassen dienen sollen, eine
statische main()-Methode vorgesehen. Diese Methode enthält dann i.a. Code, der das – weitgehend
isolierte – Testen der Klasse ermöglicht.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/04/1 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Erzeugung und Start von Java-Programmen
• Vom Quellprogramm zur Programmausführung
Quell-Datei
(z.B.Welcome.java)
Java-Compiler
weitere
Byte-Code-Dateien
javac
Byte-Code-Datei
(z.B.Welcome.class)
Java-Interpreter
(Java Virtual Machine)
java
• Aufruf des Java-Compilers (Kommandozeilen-Tool javac des JDK)
◇ Der Compiler sucht die zu übersetzende Quelldatei ausgehend vom aktuellen Arbeitsdirectory.
Befindet sich die Datei in einem anderen Directory, ist ein entsprechender Zugriffspfad anzugeben.
◇ Der Name der zu übersetzenden Quelldatei ist einschließlich der Extension .java anzugeben.
◇ Beispiel :Quelldatei Welcome.java im Directory E:\Java\Vorl
E:\Java\Vorl>javac Welcome.java
◇ Der Compiler erzeugt – bei Fehlerfreiheit – eine Byte-Code-Datei (hier : Welcome.class) im
Verzeichnis der Quelldatei.
◇ Fehler werden zusammen mit der Zeilennummer und dem Quelldateinamen gemeldet.
◇ Werden in einer Quelldatei weitere Klassen referiert und findet der Compiler keine entsprechenden
.class-Dateien, versucht er diese durch zusätzliches Übersetzen der entsprechenden .java-Dateien
zu erzeugen. (Ausnahme : Klassen der Standardbibliothek)
• Aufruf des Java-Interpreters (Kommandozeilen-Tool java des JDK)
◇ Dem Java-Interpreter ist der Klassenname (nicht der Dateiname, d.h. keine Extension !) der Start-Klasse als
Programm-(Kommandozeilen-)Parameter zu übergeben.
◇ Der Java-Interpreter sucht nach der Byte-Code-Datei der Start-Klasse im aktuellen Directory und – falls
dort nicht vorhanden – in den Directories, die in der Environment-Variablen CLASSPATH enthalten sind.
◇ Beispiel : Byte-Code-Datei Welcome.class (mit Klasse Welcome) im Verzeichnis E:\Java\Vorl
CLASSPATH-Variable ist nicht gesetzt.
E:\Java\Vorl>java Welcome
◇ Werden von der Start-Klasse weitere Klassen benötigt, sucht der Interpreter die entsprechenden Byte-CodeDateien im aktuellen Directory bzw in den Directories der CLASSPATH-Variablen
(Ausnahme : Klassen der Standardbibliothek)
◇ Wird eine benötigte Byte-Code-Datei nicht gefunden, wird die Exception NoClassDefFoundError
erzeugt.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/05/1 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Packages in Java (1)
• Packages als Namensräume
◇ Klassen lassen sich in Java in Pakete (Packages) gruppieren.
Packages können als Namensräume (name spaces) aufgefasst werden, in denen irgendwie zusammengehörige Klassen zusammengefasst sind (z.B. zu einem bestimmten Projekt gehörend, von einem bestimmten
Entwickler erzeugt, zu einer bestimmten Bibliothek gehörend usw).
◇ Packages haben einen Namen und können hierarchisch strukturiert (Package Unter-Package) sein.
Package-Namen können aus mehreren Bestandteilen bestehen, die durch einen Punkt ('.') vonein ander getrennt sind.
◇ Damit lässt sich eine Klasse über einen vollständigen Klassennamen ansprechen.
Dieser besteht aus dem eigentlichen Klassennamen, dem der durch einen Punkt (.) abgetrennte
Package-Name vorangestellt ist.
voll-qualifizierter Klassenname
Package-Name
Beispiel :
.
Klassenname
Klasse Date im Package java.util
voll-qualifizierter Klassenname : java.util.Date
◇ Mittels Packages lassen sich Namenskonflikte, insbesondere zwischen Klassen unterschiedlicher Hersteller
(Bibliotheken), weitgehend vermeiden.
◇ Empfohlene Konvention zur Vergabe von Package-Namen :
Beginn des Package-Namens mit dem invertierten Internet-Domain-Namen des Herstellers.
Beispiel : Fa. SESA, Internet-Domain-Name : sesa.com
Package-Name : com.sesa.wmf
Es ist auch üblich, aus Gründen der Übersichtlichkeit den Top-Level-Domain-Namen wegzulassen.
◇ Klassen, die explizit keinem Package zugeordnet sind, befinden sich im namenlosen Package (unnamed
package). Für die Verwendung in Bibliotheken sind derartige Klassen nicht geeignet.
• Packages als Directory-Struktur
◇ Ein strukturierter Package-Name wird in eine entsprechende Directory-Struktur abgebildet.
◇ Beispiel : Klassen-Dateien des Packages com.sesa.wmf sind im Verzeichnis com\sesa\wmf
◇ Das Start-Verzeichnis einer derartigen Directory-Struktur muss sich im aktuellen Verzeichnis oder in
einem über die CLASSPATH-Variable festgelegten Verzeichnis befinden.
◇ Package-Namen ermöglichen damit eine strukturierte und dadurch übersichtliche Ablage von KlassenDateien.
◇ Klassen-Dateien des namenlosen Package müssen direkt im aktuellen Directory bzw in einem durch die
CLASSPATH-Variable festgelegten Verzeichnis liegen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/05/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Packages in Java (2)
• Package-Zuordnung einer Klasse
◇ Hierzu dient die package-Vereinbarung
package
Beispiel :
Package-Name
;
package hm.ee.vorl;
◇ Die package-Vereinbarung muss am Beginn einer Quelldatei (erste Vereinbarung) stehen.
Sie legt fest, dass die nachfolgend definierte Klasse zu dem angegebenen Package gehört.
• Verwendung von Klassen des eigenen Packages
◇ Eine Klasse kann eine andere Klasse ihres eigenen Packages allein mit dem einfachen Klassennamen
verwenden.
◇ Jede Klasse eines Packages hat Zugriff zu den anderen Klassen desselben Packages, auch wenn sie nicht
explizit public deklariert sind. (Zugriffsberechtigung "package")
• Verwendung von Klassen aus anderen Packages
◇ Eine Klasse kann zu Klassen aus einem anderen Package nur zugreifen, wenn diese explizit public
deklariert sind.
◇ Für die Verwendung bestehen prinzipiell zwei unterschiedliche Möglichkeiten :
▻ Verwendung des voll-qualifizierten Namens.
▻ Importieren der Klassen und Verwendung der einfachen Klassennamen
◇ Verwendung des voll-qualifizierten Namens :
Beispiel : Verwendung der Klasse Date aus dem Package java.util
// Datum1.java
public class Datum1
{
public static void main(String[] args)
{ java.util.Date now = new java.util.Date();
System.out.println(now);
}
}
Nachteil : Klassennamen können sehr lang und damit unhandlich werden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/05/3 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Packages in Java (3)
• Verwendung von Klassen aus anderen Packages, Forts.
◇ Importieren von Klassen aus anderen Packages : mittels der import-Deklaration
Diese existiert in zwei Formen :
import
import
voll-qualifizierter Klassenname
Package-Name
.
Importieren einer einzelnen Klasse
;
*
;
Importieren aller Klassen eines
Packages (type import on demand),
aber nicht von dessen Unter-Packages
◇ Beispiel zum Importieren einer Klasse :
Verwendung der Klasse Date aus dem Package java.util
// Datum2.java
import java.util.Date;
// oder : import java.util.*;
public class Datum2
{
public static void main(String[] args)
{ Date now = new Date();
System.out.println(now);
}
}
◇ Anmerkung : Importieren bedeutet kein Einbinden von Code anderer Klassen.
• Die Zugriffsberechtigung "package"
◇ In Java haben alle Klassenkomponenten einer Klasse, die nicht explizit als public, protected oder
private gekennzeichnet sind, die Zugriffsberechtigung "package". Default-Zugriffsberechtigung
◇ Diese Zugriffsberechtigung erlaubt den Zugriff durch alle Klassen desselben Packages.
◇ Die Zugriffsberechtigung zu einem Package erstreckt sich nicht auf dessen eventuelle Unter-Packages.
Klassen eines Packages haben keinen Zugriff zu "package"-Komponenten von Klassen eines zugehörigen
Unter-Packages (u. umgekehrt).
◇ Die Zugriffsberechtigung protected umfasst in Java auch die Zugriffsberechtigung "package"
Zu Klassenkomponenten mit der Zugriffsberechtigung protected können neben den abgeleiteten Klassen
auch alle Klassen desselben Packages zugreifen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/05/4 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Packages in Java (4)
• Beispiel zur Zugriffsberechtigung "package"
◇ Definition einer Klasse in einem Unter-Package :
package vorl.pack1;
public class PackAccDemo
{ private int val = 123;
void showValPack()
{ System.out.println ("package-Zugriff
}
// Zugriffsberechtigung package
: " + val);
protected void showValProt()
{ System.out.println ("protected-Zugriff : " + val);
}
public void showValPub()
{ System.out.println ("public-Zugriff
}
: " + val);
}
◇ Definition einer verwendenden Klasse in demselben Package
Zugriff zu allen Komponenten der verwendeten Klasse mit der Zugriffsberechtigung
"package", protected oder public
package vorl.pack1;
class PackAccDemoUse1
{
public static void main(String[] args)
{ PackAccDemo demo = new PackAccDemo();
demo.showValPack();
demo.showValProt();
demo.showValPub();
}
}
◇ Definition einer verwendenden Klasse in einem übergeordneten Package
Zugriff nur zu Komponenten der verwendeten Klasse mit der Zugriffsberechtigung public
package vorl;
import vorl.pack1.*;
class PackAccDemoUse3
{
public static void main(String[] args)
{ PackAccDemo demo = new PackAccDemo();
//demo.showValPack();
// Zugriff zu package nicht zulässig
//demo.showValProt();
// Zugriff zu protected nicht zulässig
demo.showValPub();
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/06/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Java-Standardbibliothek (1)
• Allgemeines
◇ Die Java-Plattform stellt auch eine Laufzeit-Bibliothek (API) zur Verfügung.
◇ Diese sehr umfangreiche Java Standard-Bibliothek enthält zahlreiche nützliche Klassen für diverse
Anwendungsbereiche.
Eine Reihe dieser Klassen implementieren wesentliche Spracheigenschaften und sind damit sehr eng
mit der Sprache selbst verknüpft.
◇ Die Klassen der Standardbibibliothek sind auf verschiedene APIs aufgeteilt und in Paketen (Packages)
zusammengefasst. Dabei ist die Zuordnung der einzelnen Klassen zu den verschiedenen Paketen nicht immer
ganz stimmig, insbesondere stimmen die API-Grenzen nicht immer mit den Paketgrenzen überein.
◇ Zu vielen der Haupt-Paketen existieren Unter-Pakete.
◇ Die Pakete der im JDK enthaltenen Java Standard-Bibliothek lassen sich in drei Gruppen einteilen :
▻ Standard-Pakete. Sie gehören zu jeder Java-Plattform
▻ Standard-Erweiterungs-Pakete. Sie stellen erweiterte und ergänzende Funktionalitäten zur Verfügung
und müssen nicht unbedingt auf jeder Java-Plattform zur Verfügung stehen
▻ Ergänzende Pakete von Dritt-Herstellern. Sie ergänzen und erweitern ebenfalls die Funktionalität der
Bibliothek.
◇ Die Java Standard-Bibliothek ist ständig erweitert worden. (JDK 1.0 8 Pakete, JDK 1.4 über 130 Pakete,
JDK 5.0 über 170 Pakete, JDK 6.0 203 Pakete, JDK 7.0 209 Pakete, JDK 8.0 217 Pakete)
◇ Sun stellt in der das JDK begleitenden Dokumentation eine ausführliche Beschreibung der Klassen zur
Verfügung (Java Platform API Specification)
• Überblick über die Standard-Pakete
◇ Die Namen der Standard-Pakete beginnen mit java.
◇ Die folgende Tabelle gibt nur einen Überblick über die Haupt-Pakete dieser Gruppe
java.applet
Applets
java.awt
Abstract Windowing Toolkit : Erzeugung von GUIs, 11 Unter-Pakete
java.beans
Java Beans (Komponenten-Architektur von Java), ein Unter-Paket
java.io
Ein-/Ausgabe mittels Streams, Dateien und Serialisierung
java.lang
Elementare Sprachunterstützung, 6 Unter-Pakete
java.math
Unterstützung von Ganzzahl- und Gleitpunkt-Arithmetik
java.net
Unterstützung von Netzwerkanwendungen
java.nio
New I/O Package, verbesserte I/O-Unterstützung, 7 Unter-Pakete, ab JDK 1.4
java.rmi
Remote Method Invocation, Entfernte Objekt-Kommunikation, 4 Unter-Pakete
java.security
Sicherheits-Framework, 4 Unter-Pakete
java.sql
Datenbankzugriff
java.text
Erweiterte Textdarstellung und –bearbeitung, Internationalisierung, ein Unter-Paket
java.time
Div. zeit- und datum-bezogene Klassen, 4 Unterpakete, ab JDK 8.0
java.util
Utility-Klassen, Collection Framework, spezielle Datenstrukturen, 11 Unter-Pakete
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 11/06/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Java-Standardbibliothek (2)
• Überblick über die Standard-Erweiterungs-Pakete
◇ Die Namen der Standard-Erweiterungs-Pakete beginnt mit javax.
◇ Die folgende Tabelle gibt nur einen Überblick über die Haupt-Pakete dieser Gruppe (hier : Standard Edition)
javax.accessibility
Unterstützung spezieller I/O-Geräte (z.B. für Braille-Zeichen)
javax.activation
Unterstützung der Objekt-Aktivierung
javax.activity
spezielle Exception-Klassen im Zusammenhang mit der CORBA-Serialisierung
javax.annotation
Unterstützung von Annotations, ein Unter-Paket
javax.crypto
Unterstützung kryptographischer Operationen, zwei Unter-Pakete
javax.imageio
I/O von Bilddateien, 6 Unter-Pakete
javax.jws
Unterstützung von Web Services, ein Unter-Paket
javax.lang.model
Unterstützung der Modellierung der Sprache Java, drei Unter-Pakete
javax.management
Java Management Erweiterungen, 8 Unter-Pakete
javax.naming
Zugriff zu Namensdiensten, vier Unter-Pakete
javax.net
Ergänzung zur Netzwerkunterstützung, ein Unter-Paket
javax.print
Print Service API, Zugriff zu Print Services, drei Unter-Pakete
javax.rmi
Ergänzung zum RMI-API, zwei Unter-Pakete
javax.script
Scripting API (Definition von Java Scripting Engines)
javax.security....
Ergänzung zum Sicherheits-Framework, besteht nur aus 8 Unter-Paketen
javax.sound....
Sound API, besteht nur aus 4 Unter-Paketen
javax.sql
Ergänzung zum Datenbankzugriffs-API (serverseitig), drei Unter-Pakete
javax.swing
Swing Toolkit, Erweiterungen zur GUI-Erzeugung, 17 Unter-Pakete
javax.tools
Interfaces und Klassen für Tools zum Starten aus Programmen heraus
javax.transaction
Unterstützung von Transaktionen, ein Unter-Paket
javax.xml
Zugriff zu XML-Dateien, 34 Unter-Pakete
• Ergänzende Pakete von Drittherstellern
◇ Hierzu gehören zahlreiche Pakete unterhalb der org.omg.-Hierarchie.
Sie stellen im wesentlichen CORBA-Unterstützung zur Verfügung
◇ Weitere Pakete befinden sich unter den Hierachien org.w3c. und org.xml.
Sie bieten Unterstützung für den Zugriff zu XML-Dateien.
◇ Das Paket org.ietf.jgss stellt ein Framework zur Nutzung von Sicherheitsdiensten (Authentifizierung,
Daten-Sicherheit, Daten-Integrität usw) unterschiedlichster Mechanismen (wie z.B. Kerberos) zur Verfügung
• Verwendung der Java Standard-Bibliothek
◇ Zur Verwendung von Komponenten der Standard-Bibliothek in eigenen Programmen werden entsprechende
import-Vereinbarungen benötigt, wenn nicht voll-qualifizierte Namen verwendet werden sollen.
Ausnahme : Das Paket java.lang ist so eng mit der Sprache verknüpft, dass es automatisch durch den
Compiler importiert wird. Eine explizite import-Anweisung ist daher für Komponenten aus diesem
Paket nicht erforderlich.
◇ Der Java-Compiler und der Java-Interpreter finden den Zugriffspfad zu den Bibliotheks-Dateien
automatisch. Er muss daher nicht in die CLASSPATH-Variable aufgenommen werden (ab JDK 1.2).
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/00/0 – TH – 03
-----------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
12. Daten und Objekte in Java
12.1. Einfache Datentypen
12.2. Referenztypen und Objekterzeugung
12.3. Konstante in Java
12.4. Wrapperklassen für die einfachen Datentypen
12.5. Strings
12.6. Arrays
12.7. Die Klasse Object
12.8. Beispiel : Klasse Ratio
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/01/1 – TH – 03
-------------------------------------------------------------------------------------------------------------------------------------------------------
Einfache Datentypen in Java (1)
• Allgemeines zu Datentypen
◇ Java ist eine streng typisierte Sprache. Das bedeutet, dass jede Variable und jeder Ausdruck einen Typ hat,
der zur Compilezeit bekannt sein muss.
◇ In Java muß jede Variable immer einen definierten Wert haben: Membervariable werden automatisch
mit einem Default-Wert initialisiert, sofern ihnen nicht explizit ein Wert zugewiesen wird.
Bei lokalen Variablen verhindert der Compiler, dass sie ohne explizite Initialisierung bzw Wertzuweisung
verwendet werden (Definite Assignment).
◇ In Java gibt es zwei Kategorien von Datentypen :
▻ einfache (primitive) Datentypen (primitive types)
▻ Referenz-Typen (reference types)
• Überblick über die einfachen Datentypen
◇ Variable eines einfachen Datentyps enthalten einen Wert ihres jeweiligen Typs.
◇ Java kennt acht einfache Datentypen
◇ Für jeden Datentyp ist der Wertebereich und damit die Größe des belegten Speicherplatzes eindeutig
– unabhängig von der jeweiligen Plattform – festgelegt.
◇ Zu jedem primitiven Datentyp ist in der Standard-Bibliothek eine Wrapper-Klasse definiert (s. später).
◇ Überblick :
Typname Größe
Art des Typs
Wertebereich
Default-Wert WrapperKlasse
byte
1 Byte
vorzeichenbeh. ganze Zahl
-128 ... +127
0
Byte
short
2 Bytes
vorzeichenbeh. ganze Zahl
-215
0
Short
31
... +2 -1
0
Integer
... +263-1
0
Long
'\u0000'
Character
0.0
Float
0.0
Double
false
Boolean
... +215-1
31
int
4 Bytes
vorzeichenbeh. ganze Zahl
-2
long
8 Bytes
vorzeichenbeh. ganze Zahl
-263
char
2 Bytes
Unicode-Zeichen
alle Unicode-Zeichen
float
4 Bytes
Gleitpunktzahl (reelle Zahl) +/-3.4028...*1038
double
8 Bytes
Gleitpunktzahl (reelle Zahl) +/-1.7976...*10
boolean 1 Bit *) logischer Wert
false, true
308
*) Die belegte Speichergröße ergibt sich durch die kleinste adressierbare Speichereinheit (meist 1 Byte)
◇ Der Datentyp float entspricht dem 32-Bit-IEEE-Format (single precision)
Der Datentyp double entspricht dem 64-Bit-IEEE-Format (double precision)
◇ Der Datentyp char zählt (mit byte, short, int und long) zu den ganzzahligen Datentypen
(integral types).
◇ Die ganzzahligen Datentypen bilden zusammen mit den Gleitpunkttypen (floating point types )
(float und double) die numerischen Typen (numeric types).
◇ Der logische Datentyp boolean muß überall dort verwendet werden, wo ein logischer Operand
erforderlich ist (logische Operatoren, Steuerausdrücke in den Steueranweisungen, erster Ausdruck im
bedingten Auswerte-Operator).
Ein Ersatz durch ganzzahlige Typen (Werte 0 und !=0) ist nicht zulässig.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/01/2 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Einfache Datentypen in Java (2)
• Darstellung der Werte der einfachen Datentypen im Quelltext (Konstante, literals)
◇ Die Wertedarstellung entspricht im wesentlichen der von C
◇ Datentypen byte, short, int und long (ganzzahlige Datentypen ohne char) :
▻ Darstellung als Dezimalzahl (Ziffern 0 bis 9, Beginn nicht mit 0)
▻ Darstellung als Oktalzahl : Beginn mit 0 (nur Ziffern 0 bis 7)
▻ Darstellung als Sedezimalzahl : Beginn mit 0x oder 0X (Ziffern 0 bis 9, a bis f, A bis F)
▻ ab JDK 1.7 : Darstellung als Binärzahl : Beginn mit 0b oder 0B (Ziffern 0 und 1)
▻ Ziffernfolgen sind grundsätzlich (ohne Suffix) vom Typ int.
Allerdings muss der dargestellte Wert innerhalb des für int zulässigen Bereichs liegen.
▻ Ziffernfolgen mit dem Suffix l oder L sind vom Typ long
▻ Jeder ganzzahlige Wert, kann mit dem Vorzeichen – oder + versehen werden.
◇ Datentypen float und double :
▻ Exponential- oder Dezimalbruchdarstellung
▻ ohne Suffix oder mit Suffix d oder D : Datentyp double
▻ mit Suffix f oder F : Datentyp float
▻ Zusätzlich kann eine Gleitpunktzahl mit dem Vorzeichen – oder + versehen werden.
◇ Datentyp char :
▻ Darstellung eines Einzelzeichens in einfachen Hochkommata (single quotes), z.B.: 'A', 'Π', ':'
▻ Darstellung durch eine C-kompatible Escape-Sequenz in einfachen Hochkommata:
'\b'
'\t'
'\n'
'\r'
'\f'
'\''
'\"'
'\\'
'\ooo'
Backspace (BS)
Horizontaler Tabulator (Horizontal Tab, HT)
Zeilenwechsel (Newline, Linefeed, LF)
Carriage Return (CR)
Seitenwechsel (Form Feed, FF)
Ersatzdarstellung für einfaches Hochkomma (single quote)
Ersatzdarstellung für doppeltes Hochkomma (double quote)
Ersatzdarstellung für Fluchtsysmbol \
Oktal-Escape-Sequenz, 1 bis 3 Oktalziffern, maximaler Wert : 0377
Anmerkung : Die C-Escape-Sequenzen '\v', '\a', '\?' und '\xhh' sind in Java
nicht implementiert.
▻ Darstellung durch eine Unicode-Escape-Sequenz in einfachen Hochkommata : '\uhhhh'
(h Sedezimalziffer),
z.B. : '\u0041' (== 'A'), '\u00dc' (== 'Ü'), '\uffff'
◇ Für einige Grenzwerte (z.B. größter und kleinster darstellbarer positiver Wert) der numerischen
Datentypen sind in den entsprechenden Wrapper-Klassen (Package java.lang) symbolische
Konstanten (als public static final) definiert. s. später
◇ Datentyp boolean
▻ Es existieren nur die beiden Konstanten true und false
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/01/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Einfache Datentypen in Java (3)
• Typkonvertierungen bei einfachen Datentypen
◇ Typkonvertierungen (Casts) zwischen allen ganzzahligen Typen (einschließlich char) und den
Gleitpunkt-Typen sind grundsätzlich zulässig.
◇ Implizite Typkonvertierungen bei typgemischten zweistelligen arithmetischen Operationen vor
Ausführung der Operation (numeric promotion) :
▻ Nur ganzzahlige Operanden (Integer-Operation), kein Operand vom Typ long :
Alle Nicht–int-Operanden werden in den Typ int umgewandelt.
Die Operation wird als int-Operation ausgeführt und liefert ein int-Ergebnis
▻ Nur ganzzahlige Operanden (Integer-Operation), ein Operand vom Typ long :
Der andere Operand wird in den Typ long umgewandelt.
Die Operation wird als long-Operation ausgeführt und liefert ein long-Ergebnis
▻ Ein Operand ist vom Typ double.
Der andere Operand wird in den Typ double umgewandelt.
Die Operation wird als double-Operation ausgeführt und liefert ein double-Ergebnis
▻ Ein Operand ist vom Typ float und der andere nicht vom Typ double.
Der andere Operand wird in den Typ float umgewandelt.
Die Operation wird als float-Operation ausgeführt und liefert ein float-Ergebnis
◇ Implizite Typkonvertierungen zwischen numerischen Typen bei Wertzuweisungen (u. Initialisierungen)
Nur zulässig, wenn sichergestellt ist, dass keine Größen-Information verloren gehen kann (Genauigkeit
darf verloren gehen) :
▻ Ganzzahl-Werte an Ganzzahl-Variable eines größeren Typs.
▻ Ganzzahl-Werte an Gleitpunkt-Variable
▻ float-Werte an double-Variable
▻ int-Konstante an byte-, short- oder char-Variable, wenn die int-Konstante sich auch als
Wert des kleineren Datentyps darstellen lässt (für char : Zeichencode zwischen 0 und 65535).
◇ Alle anderen Umwandlungen zwischen numerischen Typen, wenn also Information verloren gehen
könnte, sind nur explizit möglich (Cast-Operator !).
◇ Zwischen dem Typ boolean und den numerischen Datentypen sind keinerlei Typkonvertierungen
zulässig (auch nicht explizit).
◇ Werte eines numerischen Datentyps lassen sich mit einem String-Operanden (Bibliotheks-Klasse
String) mittels des Konkatenations-Operators (+) vernüpfen. In einem derartigen Fall wird der
numerische Wert in seine dezimale Text-Darstellung umgewandelt und mit dem String-Operanden
zu einem neuen String zusammengefasst.
Beispiel :
double dv = 74.25;
System.out.println("Wert von dv : " + dv);
Ausgabe : Wert von dv : 74.25
◇ Analog lassen sich Werte des Typs boolean mit einem String-Operanden mittels des
Konkatenations-Operators (+) verknüpfen. In einem derartigen Fall wird der logische Wert in seine
Textdarstellung (entweder "true" oder "false") umgewandelt und mit dem StringOperanden zu einem neuen String zusammengefasst.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/01/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Java-Utility-Klasse Math
• Allgemeines zur Klasse Math
◇ Diese nicht instanzierbare und nicht ableitbare Klasse definiert zwei Konstante und eine Reihe statischer
Methoden zur Durchführung grundlegender numerischer Berechnungen.
◇ Die Klasse befindet sich im Package java.lang.
• Mathematische Konstante
◇ public static final double E
// Eulersche Zahl
◇ public static final double PI
// Kreiskonstante
• Statische Memberfunktionen Klasse Math (Auswahl)
public static type abs(type x)
▻ Ermittlung des Betrags (Absolutwerts) des Arguments
Funktionswert
▻ mehrfach überladen für verschiedene Typen type : double, float, int und long
public static double atan2(double y, double x)
▻ Ermittlung des arctan(y/x) im Bogenmaß (= Phasenwinkel der Polarkoordinaten des Punktes mit den
kartesichen Koordinaten (x,y)) Funktionswert
public static double cos(double x)
▻ Ermittlung des Cosinus des Winkels x (x im Bogenmaß)
Funktionswert
public static double sin(double x)
▻ Ermittlung des Sinus des Winkels x (x im Bogenmaß)
Funktionswert
public static double tan(double x)
▻ Ermittlung des Tangens des Winkels x (x im Bogenmaß)
Funktionswert
public static double exp(double x)
▻ Ermittlung von
ex
Funktionswert
public static double log(double x)
▻ Ermittlung des natürlischen Logarithmus von x
Funktionswert
public static double log10(double x)
▻ Ermittlung des dekadischen Logarithmus von x
Funktionswert
public static double sqrt(double x)
▻ Ermittlung der positiven Quadratwurzel von x
Funktionswert
▻ Für x<0 wird NaN als Funktionswert zurückgegeben
public static double pow(double a, double b)
▻ Ermittlung von
ab
Funktionswert
public static double random()
▻ Ermittlung einer Pseudo-Zufallszahl r im Bereich (0.0 <= r < 1.0)
Funktionswert
Hinweis : Die Konstanten und Methoden müssen mit ihrem vollqualifizierten Namen verwendet werden (z.b: Math.PI)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/02/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Referenz-Typen und Objekterzeugung in Java (1)
• Eigenschaften von Variablen eines Referenz-Typs ("Variablen eines Klassentyps")
◇ Beispiel : Uhr
Uhr myClk;
sei der Name einer Klasse (Klassentyp)
Definition einer "Variablen dieses Klassentyps"
◇ Die Definition einer "Variablen eines Klassentyps" bewirkt lediglich die Bereitstellung des Speicherplatzes für die Aufnahme der Adresse eines Objekts dieser Klasse, nicht jedoch für das Objekt selbst.
Dieses muss vielmehr gesondert erzeugt werden.
Variable mit dieser Eigenschaft bezeichnet man als Referenz-Variable (= Variable eines Referenz-Typs)
◇ Referenz-Variable in Java referieren Objekte, d.h. Instanzen einer Klasse.
Objekte können in Java nicht direkt als Werte verwendet werden (z.B. als Funktions-Parameter), sondern
nur über eine Referenz.
◇ Variable eines Referenz-Typs (Referenz-Variable) werden zwar häufig als "Objekt-Variable" bezeichnet,
tatsächlich sind sie aber Pointer-Variable, die auf ein Objekt verweisen
Ihr Wert ist eine Speicheradresse.
Bei ihrer Verwendung werden sie automatisch dereferenziert. (Anwendung des Punkt- (.) Operators
zum Komponentenzugriff).
◇ Eine Referenz-Variable kann immer nur auf Objekte eines bestimmten Typs bzw bestimmter Typen
(Polymorphie !) zeigen.
Formal werden drei Arten von Referenz-Variablen unterschieden :
▻ Klassen-Typen (sie verweisen auf Objekte der entsprechenden Klasse oder auf Objekte davon
abgeleiteter Klassen)
▻ Interface-Typen (sie verweisen auf Objekte, die das jeweilige Interface implementieren)
▻ Array-Typen (sie verweisen auf Arrays, Arrays sind in Java ebenfalls – spezielle – Objekte)
◇ Bezüglich ihres Anwendungs-Zwecks kann man zwei Klassen-Kategorien unterscheiden :
▻ Value-Typen : Der Inhalt der Objekte (= Wert der Datenkomponenten) ist das Wesentliche.
Er repräsentiert den Wert eines Objekts und der ist entscheidend für dessen Verwendung
(analog zu den einfachen Datentypen)
▻ Entity-Typen : Der Inhalt eines Objekts ist nicht das Wesentliche. Objekte werden nicht als Werte
betrachtet. Sie bieten im wesentlichen Dienste an, gegebenenfalls durch die Benutzung referierter anderer
Objekte. Diese Dienste sind entscheidend für ihre Verwendung.
◇ Die für Referenz-Variable definierten Vergleichs-Operatoren == und != beziehen sich auf die
Referenzen (Adressen) und nicht auf den Inhalt der referierten Objekte.
=> Vergleich auf Identität der Objekte und nicht auf deren inhaltliche Gleichheit
Gleiche Referenzen bedeuten natürlich auch Gleichheit der referierten - identischen - Objekte.
Ungleiche Referenzen bedeuten aber nicht, dass auch die referierten - nicht identischen - Objekte
ungleich sein müssen.
◇ Als spezielle Referenz-Konstante ist der Wert null definiert.
Eine Referenz-Variable mit diesem Wert zeigt auf nichts (also auf kein Objekt).
Einer lokalen Referenz-Variablen, die auf nichts zeigen soll, muss dieser Wert explizit zugewiesen werden.
Beispiel :
String sa = null;
Für Referenz-Variable, die Member-Variable sind, ist null der ohne explizite Initialisierung zugewiesene Default-Wert.
◇ Mit dem auf Referenz-Variable anwendbaren Operator instanceof lässt sich überprüfen, ob das
referierte Objekt von einem bestimmten Typ ist (s. später).
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/02/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Referenz-Typen und Objekterzeugung in Java (2)
• Erzeugung von Objekten
◇ Objekte werden grundsätzlich nur namenlos dynamisch auf dem Heap alloziert.
◇ Hierzu dient ein new-Ausdruck : new-Operator mit nachgestellten Konstruktor-Aufruf.
new
Konstruktor-Aufruf
◇ Konstruktoren sind spezielle klassenspezifische Funktionen, die zur Erzeugung eines Objekts benötigt
werden. Sie tragen i.a. den Namen der Klasse des zu erzeugenden Objekts (Ausnahme s. Arrays).
Konstruktoren können Parameter besitzen.
◇ Der new-Ausdruck liefert die Adresse des erzeugten Objekts zurück, die dann einer Referenz-Variablen
als Wert zugewiesen werden kann.
Beispiel :
String sv = new String("Hallo !");
◇ In Java gibt ein in der JVM im Hintergrund laufender automatischer Garbage Collector den allozierten
Speicher für ein Objekt wieder frei, wenn keinerlei Referenz mehr auf das Objekt existiert
Eine explizite Freigabe des dynamisch allozierten Speichers ist daher weder notwendig noch überhaupt
möglich In Java existiert daher kein Speicherfreigabe-Operator (wie z.B. delete in C++).
• Die this-Referenz
◇ In jeder objektspezifischen Methode eines Objekts steht eine Referenz auf das Objekt selbst
zur Verfügung Schlüsselwort this
this-Referenz
◇ Verwendung
▻ als Rückgabewert einer Methode.
▻ als Parameter in aufgerufenen Methoden anderer Objekte, die eine Referenz auf das aufrufende
Objekt benötigen
▻ Verwendung zum Ansprechen der Datenkomponenten des eigenen Objekts
Nur sinnvoll, bei gleichnamigen Funktions-/Konstruktor-Parametern
▻ innerhalb eines Konstruktors Aufruf eines anderen Konstruktors der eigenen Klasse
◇ Beispiel :
public class Punkt
{
private double x;
private double y;
public Punkt()
{ this(0,0); }
public Punkt(double x, double y)
{ this.x = x; this.y = y; }
public Punkt move (double dx, double dy)
{ x+=dx; y+=dy;
return this;
}
public void printPunkt()
{ System.out.print(this); }
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/03/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Konstante in Java
• Der Modifizierer final für Daten
◇ Er legt fest, dass der entsprechende Datenwert nicht geändert werden kann
◇ Anwendbar – sowohl für einfache Datentypen als auch für Referenztypen – auf
▻ statische Datenkomponenten
▻ nichtstatische Datenkomponenten
▻ lokale Variable (in Funktionen)
◇ Für Referenztypen legt final lediglich fest, dass die Referenz nicht änderbar ist, d.h. das referierte
Objekt kann nicht gewechselt werden.
Ob der Zustand dieses referierten Objekts änderbar ist, wird dadurch nicht bestimmt.
• Symbolische Konstante
◇ Sind objektunabhängig.
Definition als nicht-änderbare statische Datenkomponenten (Klassen-Konstante).
Modifizierer static und final.
Wenn sie allgemein verwendbar sein sollen zusätzlicher Modifizierer public.
◇ Initialisierung mit dem durch sie repräsentierten Wert bei der Definition.
◇ Es ist üblich – aber nicht verpflichtend – , Konstanten-Namen mit Großbuchstaben zu bilden.
◇ Beispiele aus der Java-Standard-Bibliothek
public final class Integer
{
public static final int MAX_VALUE = 0x7fffffff;
public static final int MIN_VALUE = 0x80000000;
// ...
}
public final class Math
{
public static final double PI = 3.14159265358979323846;
// ...
}
◇ Verwendung ausserhalb ihrer definierenden Klasse mit ihrem vollqualifizierten Namen:
klassenname.KONSTANTENNAME
• Objektspezifische und lokale Konstante
◇ Objektspezifische Konstante : Definition als nichtstatische nichtänderbare Datenkomponenten
Modifizierer final (und meist private)
Initialisierung bei der Definition oder durch den Konstruktor, danach nicht mehr änderbar ( Fehler !)
Alle nichtstatischen Datenkomponenten final : Objektzustand nicht änderbar (konstantes Objekt)
◇ Lokale Konstante in Funktionen: Modifizierer final vor der Typangabe
Beispiel :
final int ANZ = 10;
Sofern diese nicht bei ihrer Definition initialisiert werden (blank final), kann ihnen später einmal ein
Wert zugewiesen werden
Jeder Versuch einer weiteren Wertzuweisung führt zu einem Compilerfehler.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/04/1 – TH – 06
--------------------------------------------------------------------------------------------------------------------------------------------------------
Wrapper-Klassen für die einfachen Datentypen (1)
• Verwendung von Werten einfacher Datentypen wie Objekte
◇ Werte einfacher Datentypen und Objekte werden prinzipiell unterschiedlich dargestellt und referiert.
Dadurch lassen sich Werte einfacher Datentypen nicht direkt in Situationen, in denen Objekte benötigt
werden, verwenden (z.B. als Argument in vielen Methoden, aktueller Typ bei generischen Datentypen)
◇ Um eine derartige Verwendung trotzdem zu ermöglichen, existieren Wrapper-Klassen für alle einfachen
Datentypen. Ein Objekt einer Wrapper-Klasse kapselt einen Wert des korrespondierenden einfachen Datentyps. Dieser Wert wird bei der Objekterzeugung festgelegt (Konstruktor-Parameter) und kann später nicht
mehr geändert werden ( objektspezifische Konstante).
• Die Wrapper-Klassen im Überblick
◇ Die Wrapper-Klassen befinden sich im Package java.lang
◇ Alle Wrapper-Klassen – außer Character – verfügen über mindestens 2 Konstruktoren :
▻ Ein Konstruktor besitzt einen Parameter vom zu kapselnden einfachen Typ.
Er dient zur Umwandlung : Wert Objekt
▻ Der zweite Konstruktor besitzt einen String-Parameter.
Der String muss die textuelle Repräsentation des zu kapselnden Wertes sein.
◇ Zur Ermittlung des von einem Wrapper-Klassen-Objekt gekapselten Wertes stellen die Wrapper-Klassen
sogenannte "Getter"-Methoden zur Verfügung (Umwandlung Objekt Wert):
primitivtyp primitivtypValue()
◇
einfacher Datentyp
Wrapper-Klasse
"Getter"-Methode
char
Character
public char charValue()
byte
Byte
public byte byteValue()
short
Short
public short shortValue()
int
Integer
public int intValue()
long
Long
public long longValue()
float
Float
public float floatValue()
double
Double
public double doubleValue()
boolean
Boolean
public boolean booleanValue()
◇ Beispiele für die Umwandlung Wert ⇔ Objekt
Integer iobj
Double dobj
long lval1
Long lobj
=
=
=
=
new Integer(27);
new Double("3.14");
-34567L;
new Long(lval1);
int ival
double dval
long lval2
= iobj.intValue();
= dobj.doubleValue();
= lobj.LongValue();
Wert
Objekt
Objekt
Wert
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/04/2 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Wrapper-Klassen für die einfachen Datentypen (2)
• Umwandlung von Strings in dargestellte Werte
◇ Die Wrapper-Klassen stellen auch statische Methoden zur Verfügung, die einen String, der die textuelle
Repräsentation eines Werts eines einfachen Datentyps ist, in den dargestellten Wert umwandeln.
("Parser"-Methoden)
Falls der String nicht umwandelbar ist, wird von den Methoden eine NumberFormatException
geworfen.
◇ Überblick über die "Parser"-Methoden :
▻ Klasse Byte
: public static byte
▻ Klasse Short
: public static short parseShort(String str)
▻ Klasse Integer
: public static int
parseInt(String str)
▻ Klasse Long
: public static long
parseLong(String str)
▻ Klasse Float
: public static float parseFloat(String str)
▻ Klasse Double
: public static double parseDouble(String str)
▻ Klasse Boolean
: public static boolean parseBoolean(String str)
Der String "true" (unabhängig von Groß-/Kleinschreibung) liefert true,
alle anderen Strings liefern false (Methode existiert erst ab dem JDK 5.0)
◇ Beispiele :
double dv
String is
int iv
boolean bv
=
=
=
=
parseByte(String str)
Double.parseDouble("55.4321");
new String("98765");
Integer.parseInt(is);
Boolean.parseBoolean("richtig");
• Grenzwerte der numerischen Datentypen
◇ Für alle numerischen Datentypen sind in den entsprechenden Wrapper-Klassen (Package java.lang)
die folgenden symbolischen Konstanten (als public static final) definiert :
größter darstellbarer Wert
kleinster darstellbarer Wert (ist negativ) bei Ganzzahl-Typen
bzw kleinster darstellbarer positiver Wert bei Gleitpunkt-Typen
MAX_VALUE
MIN_VALUE
◇ Für die Datentypen float und double sind in den Klassen Float und Double u.a. zusätzlich
definiert :
NaN
NEGATIVE_INFINITY
POSITIVE_INFINITY
Not-a-Number (Repräsentation eines ungültigen Werts, z.B. 0/0)
negativ unendlich
positiv unendlich
◇ Verwendung dieser Konstanten immer nur zusammen mit dem jeweiligen Klassennamen
(voll-qualifizierter Name) :
z.B. : double dv = Double.MIN_VALUE;
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/04/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Wrapper-Klassen für die einfachen Datentypen (3)
• Autoboxing/-unboxing (Implizite Umwandlung zwischen Werten und Objekten)
◇ Bis zum JDK 1.4 mußten Umwandlungen zwischen den Werten einfacher Datentypen und Wrapper-KlassenObjekten explizit mittels Konstruktoren und "Getter"-Methoden durchgeführt werden (s. oben).
◇ Mit dem JDK 5.0 wurde das Konzept des Autoboxing/-unboxing eingeführt.
Dieses ermöglicht eine implizite (automatische) Umwandlung zwischen Werten und Objekten.
Werte einfacher Datentypen und Wrapper-Objekte können völlig kompatibel zueinander
verwendet werden.
◇ Wert
Objekt (Autoboxing) :
Beispiel 1 :
Beispiel 2 :
◇ Objekt
int iVal1 = 12;
Integer iObj;
iObj = iVal1;
// implizite Erzeugung eines neuen Integer-Objekts
Double dObj = 0.75; // implizite Erzeugung eines neuen Double-Objekts
Wert (Autounboxing) :
Beispiel 1 :
int iVal2 = iObj;
// implizites De-Wrapping
Beispiel 2 :
double dVal = dObj; // implizites De-Wrapping
◇ Autoboxing/-unboxing findet überall statt, wo Werte einfacher Datentypen bzw Wrapper-Objekte benötigt
werden.
Damit können z.B. mit Wrapper-Objekten auch "direkt" arithmetische Operationen vorgenommen
werden.
Beispiele :
Integer iObj1 = 10;
Integer iObj2 = iObj1 + 5;
++iObj1;
◇ Autoboxing findet allerdings nur dann statt, wenn keine andere implizit mögliche Typkonvertierung
anwendbar ist.
Derartige Situationen können z.B. bei überladenen Funktionen auftreten.
Beispiel :
void func(double dv);
void func(Integer io);
. . .
func(5) führt zum Aufruf der ersten Methode (func(5.0)) (int
double)
• Interpretation des Bitmusters von Gleitpunktwertdarstellungen als ganze Zahl
◇ Hierfür stehen in der Klasse Float bzw Double entsprechende statische Methoden zur Verfügung.
◇ Klasse Float : public static int floatToIntBits(float value)
Interpretation des Bitmusters des float-Wertes value als int-Wert
◇ Klasse Double : public static long doubleToLongBits(double value)
Interpretation des Bitmusters des double-Wertes value als long-Wert
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Strings in Java - Allgemeines
• Darstellung von Strings
◇ Strings in Java bestehen aus Unicode-Zeichen.
Wie in C besteht eine String-Konstante (string literal) aus einer Folge von 0 oder mehr Zeichen, die in
doppelte Hochkommata (double qoutes) eingeschlossen sein müssen.
Die Zeichen können auch durch Escape-Sequenzen dargestellt werden.
Beispiele :
"\nHallo"
"\u00ea\u0256"
◇ Strings sind in Java Objekte.
String-Konstante werden durch Objekte der Bibliotheks-Klasse String dargestellt.
Der Inhalt derartiger Objekte ist unveränderlich.
Zur Darstellung von veränderlichen Strings dienen die Bibliotheks-Klassen StringBuffer (threadsicher) und StringBuilder (ab JDK 5.0, nicht thread-sicher, aber schneller)
◇ Eine String-Variable ist – wie alle "Objekt-Variablen" in Java – eine Referenz-Variable. Sie enthält eine
Referenz auf ein String-Objekt.
Eine Zuweisung eines neuen Strings an eine String-Variable ("Variable vom Typ String")
bedeutet nicht, dass das referierte String-Objekt geändert wird, sondern dass die String-Variable
ein anderes String-Objekt referiert.
Beispiel :
String str = "Hausboot";
// ...
str = "Segelyacht";
// str zeigt auf ein anderes Objekt !
• Direkte Sprachunterstützung für Strings
◇ In der Sprache Java selbst sind einige Mechanismen zur Erzeugung und Verwendung von Strings
implementiert, die im Zusammenhang mit der Klasse String zur Anwendung kommen
◇ Beim Auftritt einer String-Konstanten im Quelltext erzeugt der Compiler ein String-Objekt, das mit
der String-Konstanten initialisiert wird.
Beispiele : System.out.println("Schoene Gruesse !");
Der Compiler erzeugt ein String-Objekt mit dem Inhalt "Schoene Gruesse !" und
übergibt eine Referenz (Adresse) auf dieses Objekt der Methode println(...)
String s1 = "Das alte Europa";
Der Compiler erzeugt ein String-Objekt mit dem Inhalt "Das alte Europa" und
weist der Variablen s1 eine Referenz (Adresse) auf dieses Objekt zu
◇ Für String-Objekte ist der Operator + überladen als Konkatenations-Operator.
Er erzeugt ein neues String-Objekt, das aus den beiden Operanden zusammengesetzt ist.
Beispiel :
s1 = s1 + " hatte Recht !";
◇ Der + -Operator wirkt auch dann als Konkatenations-Operator, wenn nur ein Operand vom Typ
String ist. In einem derartigen Fall wird der andere Operand implizit in ein String-Objekt
umgewandelt und mit dem String-Operanden zu einem neuen String-Objekt konkateniert.
Die implizite Konvertierung in ein String-Objekt (string conversion) findet für jeden einfachen Typ
und für jeden Referenz-Typ (Klasse !) statt. Ist die in einer Objekt-Variablen gespeicherte Referenz gleich
null, wird der String "null" erzeugt.
Beispiel :
System.out.println("Heute ist : " + new java.util.Date());
◇ Eine String-Konkatenation kann auch mit dem += -Operator realisiert werden. Wenn hier der rechte
Operand kein String-Objekt ist, wird er implizit in ein solches konvertiert.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Strings in Java - Beispiele zur direkten Sprachunterstützung
• Klasse StringDemo1 als Demonstrationsbeispiel
public class StringDemo1
{
public void demo()
{ System.out.println("\n\"Hallo\"");
System.out.println("\u00ea\u0256\u00df\u00fc");
String s2 = "Hausboot";
// ...
s2 = "Segelyacht";
System.out.println(s2);
System.out.println("Schoene Gruesse !");
String s1 = "Das alte Europa";
s1 = s1 + " hatte Recht !";
s1 += 25;
int i3 = 248;
s1+=i3;
System.out.println(s1);
String s3 = "Ein Test ";
int i1 = 1;
int i2 = 2;
System.out.println(s3+i1+i2);
System.out.println("Heute ist : " + new java.util.Date());
String neu = null;
System.out.println("Der String neu lautet : " + neu);
}
public static void main(String[] args)
{
StringDemo1 strdem1 = new StringDemo1();
strdem1.demo();
}
}
Ausgabe des Programms :
"Hallo"
ê?ßü
Segelyacht
Schoene Gruesse !
Das alte Europa hatte Recht !25248
Ein Test 12
Heute ist : Tue Jul 23 17:17:19 CEST 2013
Der String neu lautet : null
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/3 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse String in Java (1)
• Allgemeines zur Klasse String
◇ Standard-Klasse zur Darstellung von nicht veränderlichen Strings.
◇ Bestandteil des Package java.lang
◇ Von der Klasse String können keine anderen Klassen abgeleitet werden (Die Klasse ist final)
◇ Die Klasse String implementiert das Interface CharSequence
◇ Die Klasse String enthält zahlreiche Konstruktoren und Methoden zur Verwendung von Strings, u.a.
- zur expliziten Erzeugung von String-Objekten (Konstruktoren),
- zur Ermittlung der Stringlänge
- zum lesenden Zugriff zu String-Elementen,
- zum String-Vergleich
- zum Suchen in Strings
- zur Extraktion von Teilstrings,
- zum Kopieren von Strings mit Umwandlung aller Buchstaben in Großbuchstaben bzw Kleinbuchstaben.
• Konstruktoren der Klasse String (Auswahl)
public String()
Erzeugt ein String-Objekt, das den Leerstring enthält
public String(String str)
Erzeugt ein String-Objekt, das eine Kopie des Inhalts
von str enthält (Copy-Konstruktor)
public String(char[] acs)
Erzeugt ein String-Objekt, das die Zeichenfolge aus
acs enthält.
Die Zeichen werden in das neu erzeugte Objekt kopiert.
public String(byte[] abs)
Erzeugt ein String-Objekt, das mit der Zeichenfolge, die
durch Decodierung der byte-Werte von abs entsteht,
initialisiert ist.
Die Decodierung erfolgt für den Default-Zeichensatz der
Implementierung
public String(StringBuffer buf) Erzeugt ein String-Objekt, das mit der in buf bzw
public String(StringBuilder bld) bld enthaltenen Zeichenfolge initialisiert ist.
Die Zeichen werden in das neu erzeugte Objekt kopiert.
◇ Hinweis :
Die explizite Erzeugung eines String-Objekts bei Initialisierung mit einer String-Konstanten ist
ineffizient :
String str = new String("Wer rastet, der rostet");
Dies führt zunächst zur impliziten Erzeugung eines String-Objektes das mit der String-Konstanten
"Wer rastet, der rostet" initialisiert ist.
Anschliessend wird zur expliziten Erzeugung eines weiteren String-Objekts der Copy-Konstruktor
aufgerufen, dem eine Referenz auf das zuerst erzeugte Objekt übergeben wird. Eine Referenz auf das
explizit erzeugte zweite Objekt wird dann der String-Variablen str zugewiesen.
Vorzuziehen ist daher die direkte "Initialisierung" einer String-Variablen mit einer String-Konstanten.
In diesem Fall wird nur ein String-Objekt – implizit – erzeugt
String str = "Wer rastet, der rostet";
Entsprechendes gilt für die Wertzuweisung an String-Variable.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/4 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse String in Java (2)
• Memberfunktionen Klasse String (Auswahl Teil 1)
public int length()
▻ Gibt die Länge eines String-Objekts (= die Anzahl der enthaltenen Unicode-Zeichen) als Funktionswert
zurück
public char charAt(int index)
▻ Liefert das Zeichen mit dem Index index als Funktionswert zurück.
▻ Der zulässige Indexbereich reicht von 0 (erstes Zeichen) bis length()-1 (letztes Zeichen).
▻ Erzeugung einer IndexOutOfBoundsException, wenn ein unzulässiger Index übergeben wird.
public boolean contains(CharSequence s)
// ab dem JDK 5.0 vorhanden
▻ Überprüfung, ob die Zeichenfolge s (z.B. Inhalt eines String-Objekts) im aktuellen Objekt enthalten ist
▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false
public int indexOf(int ch)
▻ Rückgabe des Index des ersten Auftritts des – als int-Wert übergebenen – Zeichens ch als
Funktionswert.
▻ Falls das Zeichen ch nicht enthalten ist, wird der Wert –1 zurückgegeben.
▻ Beispiel : "hamburger".indexOf('r') liefert 5
public int indexOf(int ch, int from)
▻ Funktionalität wie int indexOf(int ch), allerdings beginnt die Suche am Index from
▻ Beispiel : "hamburger".indexOf('r', 6) liefert 8
public int indexOf(String str)
▻ Rückgabe des Index des ersten Auftritts des Teilstrings str.
▻ Der zurückgegebene Funktionswert ist der Index des ersten Zeichens des gefundenen Teilstrings.
▻ Falls der Teilstring str nicht enthalten ist, wird der Wert –1 zurückgegeben.
public int indexOf(String str, int from)
▻ Funktionalität wie int indexOf(String str), allerdings beginnt die Suche am Index from
public
public
public
public
int
int
int
int
lastIndexOf(int ch)
lastIndexOf(int ch, int from)
lastIndexOf(String str)
lastIndexOf(String str, int from)
▻ Ähnlich wie die Funktionen indexOf(...).
▻ Nur Ermittlung des Index des letzten Auftritts des Zeichens ch bzw des Teilsstrings str.
▻ Beispiel : "hamburger".lastIndexOf("rindfleisch") liefert -1
public boolean isEmpty()
// ab dem JDK 6.0 vorhanden
▻ Gibt true zurück, wenn das aktuelle String-Objekt leer (d.h. seine Länge==0) ist,
andernfalls false
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/5 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse String in Java (3)
• Memberfunktionen Klasse String (Auswahl Teil 2)
public boolean endsWith(String suffix)
▻ Überprüfung, ob das aktuelle Objekt mit dem String suffix endet.
▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false
public boolean startsWith(String prefix)
▻ Überprüfung, ob das aktuelle Objekt mit dem String prefix beginnt.
▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false
public boolean equals(Object obj)
▻ Überprüfung, ob das übergebene Objekt ein String-Objekt ist und gleiche Länge und gleichen Inhalt wie
das aktuelle Objekt hat.
▻ Falls ja, wird true als Funktionswert zurückgegeben, andernfalls false
public int compareTo(String str)
▻ Durchführung eines lexikographischen Vergleichs zwischen dem aktuellen String-Objekt und dem
String-Objekt str
▻ Der Vergleich wird mit den Codewerten der einzelnen Zeichen bzw der Stringlänge ausgeführt
▻ Funktionswert : - 0, wenn beide String-Objekte gleich sind
- <0, wenn das aktuelle Objekt "kleiner" als str ist
- >0, wenn das aktuelle Objekt "größer" als str ist
▻ Beispiel : "haben".compareTo("hat") liefert –18
public String substring(int beg, int end)
▻ Rückgabe des Teilstrings, der am Index beg beginnt und am Index end-1 endet, als neues StringObjekt (genauer : als Referenz auf ein neu erzeugtes String-Objekt mit dem entsprechenden Inhalt des
Teilstrings).
▻ Erzeugung einer IndexOutOfBoundsException, falls wenigstens einer der übergebenen Indizees
unzulässig ist oder beg>end ist.
▻ Beispiel : "hamburger".substring(4,8) liefert "urge"
public String toUpperCase()
▻ Rückgabe eines neuen String-Objekts, in dem alle Buchstaben in Großbuchstaben konvertiert sind
public String replace(char alt, char neu)
▻ Rückgabe eines neuen String-Objekts, das aus dem aktuellen Objekt durch Ersatz aller Auftritte des
Zeichens alt durch das Zeichen neu und der Übernahme alle übrigen Zeichen entsteht.
▻ Ist das Zeichen alt überhaupt nicht im aktuellen Objekt enthalten, wird kein neues Objekt erzeugt,
sondern das aktuelle Objekt zurückgegeben.
public String trim()
▻ Rückgabe einer neu erzeugten Kopie des aktuellen Objekts, bei der alle führenden und endenden Blanks
sowie ASCII-Steuerzeichen (Zeichencode <= '\u0020') entfernt sind
▻ Falls sowohl das erste als auch das letzte Zeichen des aktuellen String-Objekts einen Zeichencode
> '\u0020' besitzen, wird keine Kopie erzeugt, sondern das aktuelle Objekt zurückgegeben
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/6 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse String in Java (4)
• Explizite Umwandlung in eine String-Darstellung
◇ In der Klasse String existiert eine mehrfach überladene statische Memberfunktion zur Umwandlung
von Werten der einfachen Datentypen sowie von Objekten beliebiger Klassen in eine String-Darstellung
Beispiel für die Umwandlung von double-Werten :
public static String valueOf(double d)
◇ Für jede Klasse ist in Java die folgende Memberfunktion definiert, mit der ein Objekt der jeweiligen Klasse
in ein String-Objekt (textuelle Repräsentation des Objekts !) umgewandelt wird :
public String toString()
Inhalt und Aufbau des Strings kann für jede Klasse geeignet festgelegt werden.
Falls das nicht der Fall ist, wird eine Default-Darstellung verwendet (s. später, Klasse Object)
◇ Erzeugung formatierter Strings (z.B. für die formatierte Darstellung von Zahlenwerten) mittels der
statischen Memberfunktion der Klasse String (s.a. Kap. 13.2, Standard-Ein- und Ausgabe) :
public static String format(String form, Object... args)
▻ Parameter :
form
Formatierungs-String,
Aufbau im wesentlichen wie bei printf(...) in C.
args
auszugebende Objekte/Werte (Autoboxing!) entsprechend
den Formatangaben im Formatierungs-String
▻ Funktionswert : Erzeugter formatierter String
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/05/7 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse String in Java (5)
• Demonstrationsbeispiel zur Klasse String
public class StringDemo2
{
public void demo()
{ String str = "Aller Anfang";
str = str + " ist";
str += " schwer";
System.out.println(str);
int len = str.length();
System.out.println("Laenge des Strings : " + len);
int idx = 6;
System.out.println(idx+1 + ". Zeichen : " + str.charAt(idx));
System.out.println("Index von \"ist\" : " + str.indexOf("ist"));
char ch = 'r';
System.out.println("letzter Index von " + ch + " : " + str.lastIndexOf(ch));
int ie = idx+6;
System.out.print("Teilstring (" + idx + ',' + ie + ") : ");
System.out.println(str.substring(idx, ie));
System.out.println(str.toUpperCase());
int diff = "haben".compareTo("hat");
System.out.println("Vergleich von \"haben\" und \"hat\" liefert : "+diff);
java.util.Date now = new java.util.Date();
String s10 = now.toString();
String s11 = String.valueOf(now);
System.out.println(s10);
System.out.println(s11);
if (s10.equals(s11))
System.out.println("Strings sind gleich !");
else
System.out.println("Strings sind ungleich !");
String pi_4st = String.format("%.4f", Math.PI);
System.out.println("PI : " + pi_4st);
}
public static void main(String[] args)
{
StringDemo2 strdem2 = new StringDemo2();
strdem2.demo();
}
}
Ausgabe des Programms :
Aller Anfang ist schwer
Laenge des Strings : 23
7. Zeichen : A
Index von "ist" : 13
letzter Index von r : 22
Teilstring (6,12) : Anfang
ALLER ANFANG IST SCHWER
Vergleich von "haben" und "hat" liefert : -18
Tue Jul 23 18:19:30 CEST 2013
Tue Jul 23 18:19:30 CEST 2013
Strings sind gleich !
PI : 3,1416
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Arrays in Java (1)
• Allgemeine Eigenschaften
◇ Arrays sind in Java Objekte.
Array-Typen sind – namenlose – Klassen.
Von einer Array-Klasse können keine weiteren Klassen abgeleitet werden.
◇ Als Element-Typ ist jeder beliebige Typ (einfacher Datentyp oder Referenz-Typ) zulässig.
◇ Die Auswahl eines Array-Elements erfolgt wie in C :
Ein Array-Element wird über einen Index, der vom Typ int sein muss, ausgewählt.
Der Index des ersten Elements hat den Wert 0.
◇ Array-Variable sind Referenz-Variable. Sie verweisen auf Array-Objekte.
Die Definition einer Array-Variablen erzeugt noch kein Array-Objekt, d.h. sie alloziert keinen
Speicherplatz für die Array-Elemente.
◇ Eine Array-Variable legt nur den Element-Typ , nicht aber die Länge eines Arrays (d.h. die Anzahl
seiner Elemente) fest. Sie kann daher auf Arrays beliebiger Länge zeigen.
◇ Die Länge eines Arrays wird dynamisch bei seiner Erzeugung (z.B. mittels eines new-Ausdrucks)
festgelegt. Anschließend ist sie nicht mehr veränderbar.
◇ Ein Array kann auch die Länge 0 besitzen. Ein derartiges Array ist ein echtes Array-Objekt.
Sinnvoll können derartige Arrays beispielsweise als Rückgabewert von Funktionen (genauer : Referenz
darauf) auftreten.
◇ Jedes Array besitzt die öffentliche Datenkomponente
public final int length;
Sie enthält die Länge des Arrays.
◇ Ein Array-Zugriff wird zur Laufzeit auf Zulässigkeit überprüft.
Der Versuch des Zugriffs zu einer nicht existierenden Array-Komponente (ungültiger Index) führt zum
Werfen der Exception ArrayIndexOutOfBoundsException.
• Vereinbarung von Array-Variablen
◇ Angabe eines Array-Typs :
Element-Typ
[]
◇ Diese Typangabe ist bei der Vereinbarung von Array-Variablen zu verwenden.
Beispiele : int[] ia;
// ia kann auf Array-Objekte zeigen, deren Elemente int-Werte sind
String[] names;
// names kann auf Array-Objekte zeigen, deren Elemente
// Referenzen auf String-Objekte sind
◇ Element-Typ eines Arrays kann wiederum ein Array sein mehrdimensionale Arrays.
Die Vereinbarung mehrdimensionaler Arrays erfolgt analog zu eindimensionalen Arrays.
Beispiele : double[][] kmat; // kmat kann auf Array-Objekte zeigen, deren Elemente Refe// renzen auf Array-Objekte sind, die double-Werte enthalten
String[][] seite;
// seite kann auf Array-Objekte zeigen, deren Elemente
// Referenzen auf Array-Objekte sind, die Referenzen auf
// String-Objekte enthalten.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Arrays in Java (2)
• Definition von Array-Objekten mittels new-Ausdruck
◇ Andere Syntax als bei Nicht-Array-Objekten : kein Konstruktoraufruf !
Stattdessen ist der Elementtyp zusammem mit der Länge des Arrays anzugeben.
Die einzelnen Elemente werden mit ihrem Defaultwert initialisiert.
Beispiele : ia = new int[10];
// Speicherallokation für ein Array-Objekt, das 10 int// Elemente hat.
// Alle Elemente sind mit 0 initialisiert.
names = new String[3]; //
//
//
//
Speicherallokation für ein Array-Objekt, das
3 Elemente hat, die Referenzen auf String-Objekte
sind.
Alle Elemente sind mit null initialisiert.
◇ Die Definition eines Array-Objekts mittels eines new-Ausdrucks kann natürlich auch zusammen mit der
Vereinbarung einer Array-Variablen erfolgen.
Beispiel :
float[] pol = new float[6];
// pol zeigt auf ein alloziertes float-Array
// mit 6 Elementen (alle mit 0.0 initialisiert)
◇ Bei mehrdimensionalen Arrays kann im new-Ausdruck die Länge aller Dimensionen angegeben
werden.
Es reicht aber aus, nur die Länge der ersten (am weitesten links stehenden) Dimension festzulegen.
Die Längen der übrigen Dimensionen müssen dann später festgelegt werden.
Beispiel :
kmat = new double[5][4]; //
//
//
//
//
//
Speicherallokation für ein Array-Objekt, das
5 Elemente hat,
die auf gleichzeitig allozierte double-Arrays, die
alle 4 Elemente haben, zeigen.
Die Elemente der double-Arrays sind mit 0.0
initialisiert
Das obige Beispiel ist eine abkürzende Schreibweise für die folgende ausführlichere Formulierung :
kmat = new double[5][];
//
//
//
//
Speicherallokation für ein Array-Objekt, das
5 Elemente hat,
die auf double-Arrays zeigen können, aber alle mit
null initialisiert sind.
for (int i=0; i<kmat.length; i++)
kmat[i] = new double[4];
// Speicherallokation für 5 double-Arrays der
// Länge 4.
// Alle Elemente dieser Arrays sind mit 0.0
// initialisiert.
◇ Als Vorteil der Java-Implementierung von mehrdimensionalen Arrays ergibt sich, dass die Element-Arrays
(Arrays in zweiter (und gegebenenfalls höherer) Dimension eine unterschiedliche Länge besitzen können.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Arrays in Java (3)
• Definition von Array-Objekten mittels Array-Initialisierer (array initializer)
◇ Alternative zum new-Ausdruck : Angabe eines Array-Initialisierers bei der Variablenvereinbarung
◇ Syntax für Array-Initialisierer :
{
Initialisierungs-Ausdruck
}
,
◇ Ein Array-Initialisierer bewirkt die Allokation von Speicherplatz für ein Array-Objekt und initialisiert
die einzelnen Elemente mit den angegebenen Initialisierungs-Ausdrücken.
◇ Die Länge des Arrays wird nicht angegeben .Sie ergibt sich aus der Anzahl der Initialisierungsausdrücke
Beispiel :
String[] farben = { "rot", "gruen", "blau", "gelb", "weiss" };
// Vereinbarung der Array-Variablen farben. Diese zeigt auf ein gleichzeitig alloziertes
// Array-Objekt, dessen 5 Elemente mit den Referenzen auf die angegebenen Farb-Strings,
// für die ebenfalls Speicher alloziert wird, initialisiert werden.
◇ Bei mehrdimensionalen Arrays können Array-Initialisierer geschachtelt werden (InitialisierungsAusdrücke sind wiederum Array-Initialisierer)
◇ Array-Initialisierer können auch zusammen mit einem new-Ausdruck verwendet werden. Sie sind dem
new-Ausdruck nachzustellen, eine Array-Länge darf nicht angegeben werden. (sinnvoll zur Erzeugung
anonymer initialisierter Arrays)
• Unterstützende Klassen der Standard-Bibliothek
◇ In der Java-Standardbibliothek sind zwei Klassen definiert, die statische Memberfunktionen zur
Unterstützung der Verwendung von Arrays zur Verfügung stellen
Beide Klassen können nicht instanziiert werden.
◇ Klasse java.lang.reflect.Array
Sie stellt statische Methoden zur dynamischen Erzeugung von und zum Zugriff zu Arrays zur Verfügung.
◇ Klasse java.util.Arrays
Sie stellt statische Methoden zur Bearbeitung von Arrays zur Verfügung.
Im wesentlichen handelt es sich um Methoden
- zum Suchen in Arrays
- zum Sortieren von Array-Komponenten
- zum Vergleich von Arrays
- zur Zuweisung an Array-Bereiche
◇ Die Klasse java.lang.System definiert zum Kopieren von Arraybereichen die statische Methode
public static void arraycopy(Object src, int sPos, Object dest, int dPos, int len)
Quell-Objekt (src) und Ziel-Objekt (dest) müssen Arrays sein
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zu Arrays in Java
• Quellcode des Programms (Klasse ArrayDemo)
public class ArrayDemo
{
public void demo()
{ int[] ia;
ia = new int[6];
System.out.println("int-Array : " + ia);
for (int i=0; i<ia.length; i++)
System.out.println(i + " : " + ia[i]);
String[] names = /* new String[]*/ { "Walter", "Franz", "Ilka" };
System.out.println("String-Array :" + names);
for (int i=0; i<names.length; i++)
System.out.println(i + " : " + names[i]);
double[][] kmat = new double[4][3];
System.out.println("2-dimensionales double-Array : " + kmat);
for (int i=0; i<kmat.length; i++)
System.out.println(i + " : " + kmat[i]);
System.out.println("erstes Zeilen-Array :");
for (int i=0; i<kmat[0].length; i++)
System.out.println(i + " : " + kmat[0][i]);
System.out.println("weiteres 2-dimensionales double-Array :");
double[][] gmat = new double[4][];
for (int i=0; i<gmat.length; i++)
gmat[i] = new double[i+1];
for (int i=0; i<gmat.length; i++)
{ System.out.print(i + " : ");
for (int j=0; j<gmat[i].length; j++)
System.out.print(gmat[i][j]+ " ");
System.out.println();
}
}
public static void main(String[] args)
{ new ArrayDemo().demo();
}
}
• Ausgabe des Programms
int-Array : [I@108786b
0 : 0
1 : 0
2 : 0
3 : 0
4 : 0
5 : 0
String-Array :[Ljava.lang.String;@119c082
0 : Walter
1 : Franz
2 : Ilka
2-dimensionales double-Array : [[D@1add2dd
0 : [D@eee36c
1 : [D@194df86
2 : [D@defa1a
3 : [D@f5da06
erstes Zeilen-Array :
0 : 0.0
1 : 0.0
2 : 0.0
weiteres 2-dimensionales double-Array :
0 : 0.0
1 : 0.0 0.0
2 : 0.0 0.0 0.0
3 : 0.0 0.0 0.0 0.0
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/5 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die vereinfachte for-Anweisung in Java
• Iteration über alle Elemente eines Arrays (und eines Containers)
◇ Zum sequentiellen Durchlaufen der Elemente eines Arrays wird üblicherweise die – auch aus C bekannte –
(Standard-) for-Anweisung eingesetzt.
Zum Elemente-Zugriff erfordert sie die explizite Definition und Verwendung einer "Laufvariablen".
◇ Beispiel :
double[] da;
// ...
for (int i=0; i<da.length; i++)
System.out.println(da[i]);
◇ Analoges gilt für das Durchlaufen eines Containers (in Java Collection genannt).
Anstelle der Laufvariablen tritt hier ein Iterator-Objekt.
• Vereinfachte for-Anweisung (Enhanced for Loop, For-Each-Schleife)
◇ Sie wurde mit dem JDK 5.0 zum sequentiellen Durchlaufen aller Elemente eines Arrays (bzw einer
Collection) eingeführt
◇ Bei ihr wird auf die explizite Definition einer Laufvariablen (bzw eines Iterator-Objekts) verzichtet.
◇ Syntax :
Array-Objekt
for
(
Element-Typ
Var-Name
:
)
Anweisung
Collection-Objekt
◇ Beispiel :
double[] da;
// ...
for(double el : da)
System.out.println(el);
◇ Wirkung : "Für jedes Element el in da "
Der Element-Typ-Variablen el wird in jedem Schleifendurchlauf das jeweils nächste Element des Arrays
da zugewiesen.
◇ Die Gültigkeit der Element-Typ-Variablen el ist auf den Schleifenrumpf begrenzt.
◇ Der vom Compiler erzeugte Code benötigt und verwaltet auch hier für den Elementzugriff eine Laufvariable (bzw ein Iterator-Objekt). Diese wird vom Compiler implizit erzeugt.
• Anwendbarkeit der vereinfachten for-Anweisung
◇ Sie ist kein allgemeiner Ersatz für die (Standard-) for-Anweisung
◇ Sie kann nur zum sequentiellen Durchlaufen aller Elemente eines Arrays (bzw einer Collection) in Vorwärtsrichtung eingesetzt werden (vom ersten zum letzten Element, ohne Überspringen einzelner Elemente)
◇ Sie kann nicht eingesetzt werden, wenn
▻ ein Element aus dem Array (der Collection) entfernt oder modifiziert werden soll
▻ innerhalb einer Schleife mehrere Arrays (Collections) parallel bearbeitet werden sollen
◇ Ihr Einsatz in geschachtelten Schleifen ist jedoch möglich
Beispiel : Durchlaufen eines zweidimensionalen Arrays
double[][] gmat;
// ...
for (double[] row : gmat)
for (double el : row)
System.out.println(el);
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/06/6 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zur vereinfachten for-Anweisung in Java
• Quellcode des Programms (Klasse ForEachDemo)
// ForEachDemo.java
public class ForEachDemo
{
public void demo()
{
double[] da;
da = new double[6];
for (int i=0; i<da.length; i++)
da[i] = i;
System.out.println("\ndouble-Array (eindimensional) : ");
for(double el : da)
System.out.println(el);
String[] names = { "Walter", "Franz", "Ilka" };
System.out.println("\nString-Array : ");
for (String str : names)
System.out.println(str);
System.out.println("\ndouble-Array (zweidimensional) :");
double[][] gmat = new double[4][];
for (int i=0; i<gmat.length; i++)
gmat[i] = new double[i+1];
for (double[] row : gmat)
{ for (double el : row)
System.out.print(el + "
");
System.out.println();
}
}
public static void main(String[] args)
{ new ForEachDemo().demo();
}
}
• Ausgabe des Programms
double-Array (eindimensional) :
0.0
1.0
2.0
3.0
4.0
5.0
String-Array :
Walter
Franz
Ilka
double-Array (zweidimensional) :
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/1 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse Object in Java (1)
• "Mutter" aller Klassen
◇ In Java existiert nur eine Klassenhierarchie.
Wurzel dieser Hierarchie ist die Klasse Object.
Alle Klassen haben – direkt oder indirekt – diese Klasse als Basisklasse.
Auch Klassen, die scheinbar nicht abgeleitet sind, d.h. die ohne Angabe einer Basisklasse definiert sind,
sind tatsächlich implizit direkt von Object abgeleitet.
◇ Da eine Variable eines Klassen-Typs auch auf Objekte abgeleiteter Klassen zeigen kann (Polymorphie !),
kann eine Variable vom Typ Object Objekte eines beliebigen Klassen-Typs (einschliesslich ArrayTyps) referieren.
◇ Die Klasse Object ist im Package java.lang enthalten.
• Memberfunktionen der Klasse Object, Allgemeines
◇ Die Klasse Object definiert insgesamt elf Memberfunktionen, die wegen der Vererbung in allen
Klassen zur Verfügung stehen.
◇ Sechs dieser Memberfunktionen sind nicht überschreibbar.
Die übrigen lassen sich in abgeleiteten Klassen überschreiben. In Abhängigkeit von der jeweiligen Klasse
kann für eine sinnvolle Funktionalität ein Überschreiben notwendig sein.
◇ Die Memberfunktionen von Object zerfallen in zwei Kategorien :
- Allgemeine Utility-Funktionen (6)
- Methoden zur Thread-Unterstützung (5)
Hier werden nur 5 der 6 Utilility-Funktionen vorgestellt :
▻ Class<?> getClass()
▻ String toString()
▻ Object clone()
▻ boolean equals(Object obj)
▻ int hashCode()
• Memberfunktionen der Klasse Object, Auswahl (1)
public final Class<?> getClass()
▻ Diese – nicht überschreibbare – Funktion gibt eine Referenz auf das Objekt der Klasse Class
zurück, das die tatsächliche Klasse des aktuellen Objekts repräsentiert
▻ Anmerkungen :
1. Jede Klasse (sowie jedes Interface, jeder einfacher Datentyp und void) wird durch ein Objekt
der – generischen – Klasse Class repräsentiert.
Ein Class-Objekt wird automatisch durch die JVM beim Laden der von ihm repräsentierten
Klasse erzeugt.
2. Neben vielen anderen Informationen stellt ein Class-Objekt auch den – vollqualifizierten –
Klassennamen der repräsentierten Klasse zur Verfügung.
Dieser lässt sich mittels der folgenden Class-Memberfunktion ermitteln :
public String getName()
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/2 – TH – 08
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse Object in Java (2)
• Memberfunktionen der Klasse Object, Auswahl (2)
public String toString()
▻ Diese Funktion ermöglicht es, dass jedes Objekt jeder beliebigen Klasse in einen String
"umgewandelt" werden kann (z.B. bei der Konkatenation eines Objekts mit einem String)
Sie liefert eine String-Repräsentation des aktuellen Objekts.
▻ Die Default-Implementierung in der Klasse Object erzeugt ein String-Objekt, dessen Inhalt
aus dem – vollqualifizierten – Klassennamen des aktuellen Objekts, dem Zeichen '@' und der
sedezimalen Darstellung des dem Objekt zugeordneten Hash-Codes (s. Methode hashCode())
zusammengesetzt ist.
Bildung des Inhalts des erzeugten String-Objekts :
getClass().getName() + '@' + Integer.toHexString(hashCode())
▻ Soll eine andere das Objekt kennzeichnende String-Darstellung erzeugt werden, muss die Methode
toString() geeignet überschrieben werden
▻ Die Methode toString() wird immer dann implizit aufgerufen, wenn eine Objekt-Referenz in
einem String-Konkatenations-Ausdruck auftritt.
protected Object clone() throws CloneNotSupportedException
▻ Die Funktion liefert ein neues Objekt, das ein Clone (eine Kopie) des aktuellen Objekts ist.
▻ Die tatsächliche Klasse des aktuellen Objekts muß das Interface Cloneable implementieren.
▻ Die Default-Implementierung in der Klasse Object prüft, ob für das aktuelle Objekt das
Interface Cloneable implementiert ist.
Ist das nicht der Fall, wird die Exception CloneNotSupportedException geworfen.
Falls es implementiert ist, wird ein neues Objekt der Klasse erzeugt, dessen Datenkomponenten mit
den Werten der entsprechenden Datenkomponenten des aktuellen Objekts initialisiert werden.
("flache" Kopie, shallow copy)
▻ Soll das neue Objekt als "tiefe" Kopie (deep copy) erzeugt werden, muss clone() geeignet
überschrieben werden.
clone() kann auch überschrieben werden, wenn nur eine "flache" Kopie erzeugt werden soll.
Das ist insbesondere dann notwendig, wenn die Methode von überall her, also öffentlich (public),
- und nicht nur aus dem gleichen Package oder aus abgeleiteten Klassen – aufgerufen werden soll.
Die überschreibende Methode muss dann als public deklariert werden.
▻ Per Konvention sollte eine überschreibende Methode zunächst super.clone() aufrufen.
Notwendige Änderungen an den Datenkomponenten des zurückgegebenen Objekts, die ObjektReferenzen sind (bei "tiefer" Kopie), sind anschließend durchzuführen.
▻ Wenn die überschreibende Methode super.clone() nicht aufruft, sondern das Kopieren der
Datenkomponenten direkt vornimmt, muss weder die Klasse das Interface Cloneable
implementieren noch muss die Exception CloneNotSupportedException beachtetet werden.
▻ Die Klasse Object selbst implementiert das Interface Cloneable nicht.
Das bedeutet, dass der Aufruf von clone() für ein Objekt der Klasse Object zum Werfen der
Exception CloneNotSupportedException führt.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/3 – TH – 07
--------------------------------------------------------------------------------------------------------------------------------------------------------
Die Klasse Object in Java (3)
• Memberfunktionen der Klasse Object, Auswahl (3)
public boolean equals(Object obj)
▻ Die Funktion vergleicht das aktuelle Objekt mit dem durch obj referierten Objekt.
Sie liefert true als Funktionswert, wenn Gleichheit vorliegt, andernfalls false.
▻ Grundsätzlich ist die Funktion für eine Überprüfung auf Wert-Gleichheit der Objekte vorgesehen.
▻ Die Default-Implementierung in der Klasse Object geht davon aus, dass ein Objekt nur zu sich
selbst gleich sein kann, d.h. sie setzt Wert-Gleichheit mit Referenz-Gleichheit (Identität) gleich.
Sie liefert genau dann true, wenn obj das aktuelle Objekt referiert (obj==this).
▻ Für die Implementierung einer echten Wert-Gleichheit, die sich auf den Inhalt (Zustand) der
referierten Objekte bezieht, muss die Methode in abgeleiteten Klassen geeignet überschrieben
werden.
Dies ist für zahlreiche Klassen der Standard-Bibliothek, u.a. auch für die Klasse String, erfolgt.
Allgemein gilt : Ein Überschreiben ist für Value-Typen erforderlich, nicht jedoch für Entity-Typen
public int hashCode()
▻ Die Funktion liefert einen Hash-Code für das aktuelle Objekt.
Jedes Objekt besitzt einen derartigen Hash-Code. Er ermöglicht die Verwaltung von Objekten in
hash-basierten Containern (z.B. java.util.Hashtable).
▻ Der Hash-Code eines Objekts darf sich während der Ausführung einer Java-Applikation nicht ändern.
Bei unterschiedlichen Ausführungen derselben Applikation kann er dagegen unterschiedlich sein.
▻ Die Default-Implementierung in der Klasse Object liefert für unterschiedliche Objekte
unterschiedliche Hash-Codes (Typischerweise ist dieser gleich der in einen int-Wert
umgewandelten Speicheradresse des Objekts).
▻ Wenn für die zwei durch x und y referierten Objekte x.equals(y) den Wert true liefert,
die beiden Objekte also inhaltlich gleich sind, muß der für die beiden Objekte erzeugte Hash-Code
auch gleich sein (x.hashCode()==y.hashCode())
bei Überschreiben der Funktion equals() ist es daher i.a. auch notwendig die Funktion
hashCode() entsprechend zu überschreiben.
▻ Wenn für die zwei durch x und y referierten Objekte x.equals(y) den Wert false liefert,
die beiden Objekte also inhaltlich verschieden sind, darf der für die beiden Objekte erzeugte HashCode durchaus auch gleich sein.
Dies bewirkt aber eine ineffizientere Verwaltung derartiger Objekte in hash-basierten Containern
▻
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/4 – TH – 06
--------------------------------------------------------------------------------------------------------------------------------------------------------
Überschreiben der Object-Methode equals() (1)
• Allgemeines
◇ Die Methode equals() überprüft auf die inhaltliche Gleichheit (Wert-Gleichheit) zweier Objekte
◇ Ein Überschreiben ist nur für für Value-Typen erforderlich,
für Entity-Typen macht eine Überprüfung auf Wert-Gleichheit i.a. keinen Sinn.
• Kontrakt der Methode equals()
Die Methode equals() sollte die folgenden Bedingungen erfüllen :
▻ Reflexivität : Der Vergleich eines Objekts mit sich selbst liefert immer true (Identität)
▻ Symmetrie : x.equals(y) liefert immer dasselbe Ergebnis wie y.equals(x)
▻ Transitivität : Wenn x.equals(y) true liefert und y.equals(z) true liefert, muss auch
x.equals(z) true liefern
▻ Konsistenz : Sofern sich weder x noch y inhaltlich ändern, liefern mehrmalige Aufrufe von
x.equals(y) immer dasselbe Ergebnis
▻ null-Verschiedenheit : Für jedes x!=null liefert x.equals(null) immer false
• Implementierung von equals()
◇ Grundsätzlich ist nur ein Vergleich von Objekten derselben Klasse sinnnvoll (Typgleichheit).
Zu beachten ist, dass das Objekt, mit dem das aktuelle Objekt verglichen werden soll, über eine ObjectReferenz übergeben wird.
◇ Prinzipiell müssen die Datenkomponenten der beiden Objekte auf Gleichheit überprüft werden. Dabei
kann ein Objekt i.a. nur zu den Datenkomponenten, die in seiner Klasse direkt definiert sind, zugreifen.
◇ Ein Vergleich der von der Basisklasse geerbten Komponenten muss an diese delegiert werden
( Aufruf von super.equals())
◇ Ein Aufruf von super.equals() muss in allen abgeleiteten Klassen erfolgen, außer in einer direkten
Subklasse von Object. Unter einer direkten Subklasse von Object wird hier die erste Klasse in einer
Ableitungs-Hierarchie verstanden, die equals() überschreibt. I.a. wird es eine direkt von Object
abgeleitete Klasse sein.
◇ In einer direkten Subklasse von Object wird eine Überprüfung auf die null-Referenz als Parameter
sowie auf Typgleichheit vorgenommen.
◇ Prinzipielle Darstellung der Implementierung :
Überprüfung auf Identität (optional)
direkte Subklasse
indirekte Subklasse
Überprüfung auf null-Referenz
Delegierung an Basisklasse
Überprüfung auf Typgleichheit
Vergleich der Datenkomponenten
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/5 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Überschreiben der Object-Methode equals() (2)
• Sourcecode einer Beispiel-Implementierung
◇ direkte Subklasse von Object
public class MyClass
{
private String s1;
private int i;
// ...
public boolean equals(Object other)
{ boolean bRet = false;
if (this == other)
bRet = true;
else
if (other != null)
if (other.getClass() == getClass())
if (s1.equals(((MyClass)other).s1))
if (i == ((MyClass)other).i)
bRet = true;
return bRet;
}
}
// Überprüfung auf Identität
//
//
//
//
Überprüfung auf null-Referenz
Überprüfung auf Typgleichheit
Vergleich der
Datenkomponenten
◇ indirekte Subklasse von Object
public class MySubClass extends MyClass
{
private String s2;
// ...
public boolean equals(Object other)
{ boolean bRet = false;
if (this == other)
// Überprüfung auf Identität
bRet = true;
else
if (super.equals(other))
// Delegierung an Basisklasse
if (s2.equals(((MySubClass)other).s2)) // Vergleich der Datenkomponente
bRet = true;
return bRet;
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/6 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Überschreiben der Object-Methode hashCode() (1)
• Allgemeines
◇ Ein Hashcode ist ein einem Objekt zugeordneter ganzzahliger Wert, der eine Speicherung und Wiederfinden von Objekten in hash-basierten Containern (z.B. Hash-Tabellen) ermöglicht.
◇ Ein hash-basierter Container besteht aus mehreren Sektionen ("Buckets"), in denen die Objekte
sequentiell abgelegt werden (Für Java gilt : Es werden Objekt-Referenzen statt Objekte abgelegt)
Der Hashcode legt einen Index fest, der zur Auswahl des Buckets dient, in dem das Objekt abgelegt wird.
Die Auswahl des Buckets erfolgt für alle Objekte gleich schnell. Der Zugriff zu einem Objekt innerhalb
des Buckets erfolgt dagegen sequentiell. Damit hängt die Zugriffszeit von der Position des Objekts innerhalb
des Buckets ab.
Daraus folgt, dass ein effizienter hash-basierter Container aus möglichst vielen, möglichst kleinen Buckets
bestehen sollte.
◇ Die Methode hashCode() ermittelt den Hashcode eines Objekts und damit "dessen" Bucket.
Beim Durchsuchen der sequentiell organisierten Buckets müssen Objekte miteinander inhaltlich verglichen
werden. Hierfür wird equals() aufgerufen.
Es wird immer die Kombination von hashCode() und equals() verwendet, um zu Objekten in
hash-basierten Containern zuzugreifen.
Die beiden Methoden müssen konsistent zueinander implementiert sein.
Wenn equals() überschreiben wird, muss auch hashCode() passend hierzu überschrieben
werden
• Kontrakt der Methode hashCode()
Die Methode hashCode() sollte die folgenden Bedingungen erfüllen :
▻ Während eines Programmlaufs muss der für ein Objekt ermittelte Hashcode immer derselbe sein, vorausgesetzt, der Inhalt des Objekts ändert sich nicht.
Bei einem anderen Programmlauf darf der Hashcode ein anderer sein.
▻ Für zwei Objekte, die inhaltlich gleich sind (d.h. für die equals() true liefert), muss hashCode()
denselben Wert erzeugen
▻ Für zwei Objekte, die inhaltlich verschieden sind (d.h. für die equals() false liefert), muss
hashCode() nicht unbedingt verschiedene Werte liefern. (bei gleichen Werten : die Objekte kommen in
denselben Bucket). Für die Performance wären aber unterschiedliche Hashcodes günstiger)
• Implementierung von hashCode()
◇ Zwei wichtige Aspekte :
▻ Konsistenz zu equals()
▻ Performanz : Die Zugriffe zu hash-basierten Containern sollen möglichst effizient erfolgen
Die Hashcode-Berechnung muss schnell und einfach erfolgen
schneller Zugriff zum Bucket
Die berechneten Hashcodes sollten möglichst gleichmäßig verteilt sein viele kleine Buckets
◇ Die Konsistenz zwischen hashCode() und equals() kann erreicht werden, indem zur Berechnung
des Hashcodes nur die Informationen verwendet werden, die auch in der Implementierung von equals()
berücksichtigt sind.
Typischerweise werden in die Berechnung des Hashcodes nur die Datenkomponenten eingehen, die in
equals() miteinander verglichen werden. Die Komponenten, die in equals() nicht vorkommen,
dürfen auch nicht zur Berechnung des Hashcodes herangezogen werden.
Allerdings müssen nicht alle in equals() verwendete Datenkomponenten auch tatsächlich in
hashCode() berücksichtigt werden. Dies wird gegebenenfalls dazu führen, dass ungleiche Objekte den
selben Hashcode haben werden, was ja nach dem Kontrakt zulässig ist.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/7 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Überschreiben der Object-Methode hashCode() (2)
• Implementierung von hashCode(), Forts.
◇ Die Berechnung des Hashcodes darf nicht zu aufwendig sein, sonst dauert die Auswahl eines Buckets zu
lange Der Geschwindigkeitsvorteil durch einen schnellen Bucket-Zugriff geht verloren.
Daher kann es aus Performanz-Gründen durchaus sinnvoll sein, einige von equals() berücksichtigte
Datenkomponenten bei der Hashcode-Berechnung wegzulassen.
Z.B. werden bei einem Array als Datenkomponente zwar alle Array-Elemente zur Ermittlung der ObjektGleichheit beitragen, ihre Berücksichtigung in der Hashcode-Berechnung wäre aber bei größeren Arrays
zu aufwendig. Hier bietet es sich an, nur jedes n-te Array-Element zu berücksichtigen.
Weiterhin kann man Datenelemente weglassen, die bei den meisten Objekten den gleichen Wert haben
werden.
◇ Implementierungsvorschlag :
▻ Identifikation der Datenkomponenten, die in die Hashcode-Berechnung eingehen sollen
▻ Zuordnung eines int-Wertes an jede Datenkomponente ( feldwertN) (s. nachfolgende Tabelle)
▻ Auswahl einer – als Multiplikator verwendeten – nicht zu großen Primzahl (
▻ Festlegung eines von 0 verschiedenen Ausgangswertes für den Hashcode (
hashmult)
hashcode0)
▻ Ausgehend von dem festgelegten Anfangswert Ermittlung des Hashcodes (hashcodeN) durch iterative
Aufaddition gemäß folgender Beziehung :
hashcodeN = hashcodeN-1 * hashmult + feldwertN
▻ Tabelle Zuordnung Datenkomponente (komp) – int-Wert
Typ von komp
boolean
zugeordneter int-Wert
(komp ? 0 : 1)
byte, char, short, (int)komp
int
long
(int)(komp^(komp>>>32))
float
(komp==0.0F) ? 0 : Float.floatToIntBits(komp)
double
Zuordnung eines long-Wertes :
(komp==0.0) ? 0L : Double.doubleToLongBits(komp)
anschließend Behandlung des long-Wertes wie oben
Objekt-Referenz
(komp==null) ? 0 : komp.hashCode()
Arrays
Behandlung der zu berücksichtigen Elemente entsprechend ihres Typs
Gesamtheit der
Basisklassenkomponenten
super.hashCode()
◇ hashCode() in Klassenhierarchien :
▻ in indirekten Subklassen : Berücksichtigung der Basisklassenkomponenten durch
super.hashCode()
▻ in direkten Subklassen : kein Aufruf von super.hashCode() (ein Aufruf wäre ein grober Fehler)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/8 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Überschreiben der Object-Methode hashCode() (3)
• Sourcecode einer Beispiel-Implementierung
◇ direkte Subklasse von Object
public class MyClass
{
private String s1;
private int i;
// ...
public int hashCode()
{ int hc = 13;
// willkürlicher Anfangswert
int hashMult = 37;
// Primzahl als Multiplikator
hc = hc*hashMult + i;
hc = hc*hashMult + s1.hashCode();
return hc;
}
}
◇ indirekte Subklasse von Object
public class MySubClass extends MyClass
{
private String s2;
// ...
public int hashCode()
{ int hc = 13;
// willkürlicher Anfangswert
int hashMult = 37;
// Primzahl als Multiplikator
hc = hc*hashMult + s2.hashCode()
hc = hc*hashMult + super.hashCode();
return hc;
}
}
◇ Klasse mit Array als Datenkomponente
public class MyArrClass
{
private Object arrField[];
// ...
public int hashCode()
{ int hc = 13;
// willkürlicher Anfangswert
int hashMult = 37;
// Primzahl als Multiplikator
// ...
hc = hc*hashMult + arrField.length;
// Beruecksichtigung der Array-Laenge
for (int i=0; i<arrField.length; i*=4)
// Beruecksichtigung nur
hc = hc*hashMult + arrField[i].hashCode() // jedes 4. Array-Elements
return hc;
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/9 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zu Memberfunktionen der Klasse Object (1)
• Quellcode der Klasse ObjDemo
public class ObjDemo implements Cloneable
{
private String name;
public ObjDemo(String str)
{ name=str; }
public String toString()
{ return super.toString()+" ("+name+") "+ super.hashCode(); }
public boolean equals(Object other)
{ boolean bRet = false;
if (this == other)
bRet = true;
else
if (other != null)
if (other.getClass() == getClass())
if (name.equals(((ObjDemo)other).name))
bRet = true;
return bRet;
}
public int hashCode()
{ int hc = 13;
int hashMult = 37;
hc = hc*hashMult + name.hashCode();
return hc;
}
// Überprüfung auf Identität
// Überprüfung auf null-Referenz
// Überprüfung auf Typgleichheit
// Vergleich der Datenkomponente
// willkürlicher Anfangswert
// Primzahl als Multiplikator
public static void main(String[] args) throws Exception
{ ObjDemo o1 = new ObjDemo("Test1");
Object o2 = new ObjDemo("Test2");
Object o3 = new Object();
System.out.println("Klassenname von o1 : " + o1.getClass().getName());
System.out.println("Klassenname von o2 : " + o2.getClass().getName());
System.out.println("Klassenname von o3 : " + o3.getClass().getName());
System.out.println("o1.toString() : " + o1.toString());
System.out.println("o2
: " + o2);
System.out.println("o2.equals(o1) : " + o2.equals(o1));
System.out.println("o1.hashCode() : " + o1.hashCode());
System.out.println("o2.hashCode() : " + o2.hashCode());
o2=o1;
System.out.println("\nnach o2=o1 :");
System.out.println("o1
: " + o1);
System.out.println("o2
: " + o2);
System.out.println("o2.equals(o1) : " + o2.equals(o1));
System.out.println("o1.hashCode() : " + o1.hashCode());
System.out.println("o2.hashCode() : " + o2.hashCode());
o2=o1.clone();
System.out.println("\nnach o2=o1.clone() :");
System.out.println("o1
: " + o1);
System.out.println("o2
: " + o2);
System.out.println("o2.equals(o1) : " + o2.equals(o1));
System.out.println("o1.hashCode() : " + o1.hashCode());
System.out.println("o2.hashCode() : " + o2.hashCode());
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/07/10 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Demonstrationsprogramm zu Memberfunktionen der Klasse Object (2)
• Ausgabe des Programms ohne überschreibende Methoden equals() und hashCode()
(beide Methoden sind im Quellcode der Klasse auskommentiert)
Klassenname von o1 : ObjDemo
Klassenname von o2 : ObjDemo
Klassenname von o3 : java.lang.Object
o1.toString()
o2
o2.equals(o1)
o1.hashCode()
o2.hashCode()
:
:
:
:
:
ObjDemo@173f7175 (Test1) 390033781
ObjDemo@4631c43f (Test2) 1177666623
false
390033781
1177666623
nach o2=o1 :
o1
o2
o2.equals(o1)
o1.hashCode()
o2.hashCode()
:
:
:
:
:
ObjDemo@173f7175 (Test1) 390033781
ObjDemo@173f7175 (Test1) 390033781
true
390033781
390033781
nach o2=o1.clone() :
o1
: ObjDemo@173f7175 (Test1) 390033781
o2
: ObjDemo@6d4b2819 (Test1) 1833642009
o2.equals(o1) : false
o1.hashCode() : 390033781
o2.hashCode() : 1833642009
/* 390033781 == 0x173f7175 */
/* 1177666623 == 0x4631c43f */
/*
Hashcode == Adresse
*/
/* Adressen sind gleich */
/* 1833642009 == 0x6d4b2819 */
/* Inhalte sind gleich, aber */
/* Adressen sind ungleich
*/
• Ausgabe des Programms mit überschreibenden Methoden equals() und hashCode()
Klassenname von o1 : ObjDemo
Klassenname von o2 : ObjDemo
Klassenname von o3 : java.lang.Object
o1.toString()
o2
o2.equals(o1)
o1.hashCode()
o2.hashCode()
:
:
:
:
:
ObjDemo@4cf5fa0 (Test1) 390033781
ObjDemo@4cf5fa1 (Test2) 1177666623
false
80699296
80699297
nach o2=o1 :
o1
o2
o2.equals(o1)
o1.hashCode()
o2.hashCode()
:
:
:
:
:
ObjDemo@4cf5fa0 (Test1) 390033781
ObjDemo@4cf5fa0 (Test1) 390033781
true
80699296
80699296
nach o2=o1.clone() :
o1
: ObjDemo@4cf5fa0 (Test1) 390033781
o2
: ObjDemo@4cf5fa0 (Test1) 1833642009
o2.equals(o1) : true
o1.hashCode() : 80699296
o2.hashCode() : 80699296
/*
/*
/*
/*
4cf5fa0 ist der Hashcode
390033781 ist Adresse
Hashcode inhaltsabhängig
80699296 == 0x4cf5fa0
*/
*/
*/
*/
/* Adressen sind gleich */
/* Inhalte sind gleich, aber */
/* Adressen sind ungleich
*/
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/08/1 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel : Klasse Ratio (1)
• Klasse zur Darstellung rationaler Zahlen (hier nur Implementierung einer Teilfunktionalität)
• Definition der Klasse Ratio :
// Ratio.java
public class Ratio implements Cloneable
{
final private int iZaehl;
final private int iNenn;
public Ratio(int iZ, int iN)
{ if(iN>0)
{ iZaehl = iZ;
iNenn = iN;
}
else if (iN<0)
{ iZaehl = -iZ;
iNenn = -iN;
}
else
{ iNenn = 1;
iZaehl = 0;
}
}
// Konstruktor 1
public Ratio(int iZ)
{ this(iZ,1);
}
// Konstruktor 2 - ganze Zahl
public Ratio()
{ this(0);
}
// Konstruktor 3 - ohne Parameter
// Vorzeichen nur im Zaehler
// Throw exception !! spaeter
public Ratio add(Ratio sP)
{ int iZ = sP.iZaehl*iNenn + sP.iNenn*iZaehl;
int iN = sP.iNenn*iNenn;
return new Ratio(iZ, iN);
}
private int iGgTeil()
{ int iP,iQ,iS;
int iGgt;
int locZ;
if(iZaehl >= 0)
locZ = iZaehl;
else
locZ = -iZaehl;
if(locZ > iNenn)
{ iP = locZ; iQ = iNenn;
}
else
{ iP = iNenn; iQ = locZ;
}
do
{ iS = iP%iQ;
iP = iQ;
iQ = iS;
} while(iS>0);
iGgt = iP;
return iGgt;
}
//
//
//
int iKgViel()
{ return ( iZaehl *
}
// groesster gemeinsamer Teiler
// Zaehler und Nenner positiv - Vorzeichen !
// groessere der beiden Zahlen ermitteln
// Fortgesetzte Division bis Rest gleich 0
// ergibt ggT
// kleinstes gem Vielfaches - nicht benoetigt
iNenn / iGgTeil());
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/08/2 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel : Klasse Ratio (2)
• Definition der Klasse Ratio, Forts.:
public Ratio kuerze()
{ int iZw,iZ,iN;
iZw = iGgTeil();
iZ = iZaehl / iZw;
iN = iNenn / iZw;
return new Ratio(iZ,iN);
}
public String toString()
{ return new String(iZaehl + "/" + iNenn);
}
public String toMixedString()
{ int iGanz = iZaehl/iNenn;
int iBruch = Math.abs(iZaehl)%iNenn;
Ratio lRat = new Ratio(iBruch,iNenn);
return new String (iGanz + " " + lRat);
}
public boolean equals(Object other)
{ boolean bRet = false;
if (this == other)
bRet = true;
else
if (other != null)
if (other.getClass() == getClass())
if ((iZaehl==((Ratio)other).iZaehl)
&& (iNenn == ((Ratio)other).iNenn))
bRet = true;
return bRet;
}
public int hashCode()
{ int hc = 13;
int hashMult = 37;
hc = hc*hashMult + iZaehl;
hc = hc*hashMult + iNenn;
return hc;
}
//Achtung Vorzeichen
// Überprüfung auf Identität
//
//
//
//
Überprüfung auf null-Referenz
Überprüfung auf Typgleichheit
Vergleich der
Datenkomponenten
// willkürlicher Anfangswert
// Primzahl als Multiplikator
public Object clone()
// hier : Erweiterung der Zugriffsberechtigung !!!
{
try {return super.clone();}
catch (CloneNotSupportedException e) {return null;}
}
/*
public Object clone()
// oder : public Ratio clone();
{
return new Ratio(iZaehl, iNenn);
}
*/
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 12/08/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Beispiel : Klasse Ratio (3)
• Definition der Demonstrations- und Test-Klasse RatioDemo :
// RatioDemo.java
// Test der Klasse Ratio
public class RatioDemo
{
public static void main(String[] args)
{ Ratio rFirst = new Ratio(150,60);
Ratio rSecnd = new Ratio(28,84);
Ratio rThird;
System.out.println("First: "+rFirst);
System.out.println("Secnd: "+rSecnd);
rFirst = rFirst.kuerze();
rSecnd = rSecnd.kuerze();
System.out.println("First: "+rFirst);
System.out.println("Secnd: "+rSecnd);
rThird = rFirst.add(rSecnd);
System.out.print(rFirst+" + "+rSecnd+" = "+rThird);
System.out.println(" = " + rThird.toMixedString());
rFirst = new Ratio(27, 99);
rSecnd = (Ratio)rFirst.clone(); // Typecast nicht notwendig, wenn
System.out.println();
if (rFirst.equals(rSecnd))
System.out.println(rFirst + " ist gleich " + rSecnd);
else
System.out.println(rFirst + " ist ungleich " + rSecnd);
System.out.println("Hashcode von " + rFirst + " : "
+ rFirst.hashCode());
System.out.println("Hashcode von " + rSecnd + " : "
+ rSecnd.hashCode());
rSecnd = rFirst.kuerze();
System.out.println();
if (rFirst.equals(rSecnd))
System.out.println(rFirst + " ist gleich " + rSecnd);
else
System.out.println(rFirst + " ist ungleich " + rSecnd);
System.out.println("Hashcode von " + rFirst + " : "
+ rFirst.hashCode());
System.out.println("Hashcode von " + rSecnd + " : "
+ rSecnd.hashCode());
}
}
Programmausgabe :
First: 150/60
Secnd: 28/84
First: 5/2
Secnd: 1/3
5/2 + 1/3 = 17/6 = 2 5/6
27/99 ist gleich 27/99
Hashcode von 27/99 : 18895
Hashcode von 27/99 : 18895
27/99 ist ungleich 3/11
Hashcode von 27/99 : 18895
Hashcode von 3/11 : 17919
public Ratio clone()
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/00/0 – TH – 02
-------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
13. Elementare Programmfunktionalitäten in Java
13.1. Zugriff zu Programmparametern
13.2. Standard-Ein-und-Ausgabe (Konsolen-E/A)
13.3. Exceptions
13.4. Dateibearbeitung
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/01/1 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Zugriff zu Programmparametern in Java
• Programparameter (Kommandozeilenparameter)
◇ Einem Programm können i.a. beim Aufruf Parameter übergeben werden.
Programmparameter
◇ Diese werden beim Aufruf aus der Kommandozeile durch Blanks getrennt an den eigentlichen Programmaufruf angehängt. Kommandozeilenparameter
Bei Java-Programmen werden sie nach dem Namen der Start-Klasse angegeben.
◇ Beispiel :
java Echo Sie tanzte nur einen Sommer
Kommandozeilenparameter sind :
Sie tanzte nur einen Sommer
• Zugriff im Programm
◇ Die Programmparameter (ohne Namen der Startklasse !) werden in einem String-Array zusammengefasst.
Eine Referenz auf dieses Array wird der main()-Methode der Startklasse als Parameter übergeben.
◇ Die main()-Methode der Startklasse muß daher mit einem Parameter (Argument) vom Typ String[]
definiert werden :
public static void main(String[] args)
{
// ...
}
◇ Innerhalb der main()-Methode stehen damit die Programmparameter als Komponenten des StringArrays args zur Verfügung.
◇ Die Anzahl der Programmparameter (= Länge des String-Arrays) ist ermittelbar mittels
args.length.
• Beispiel :
// Echo.java
class Echo
{
public static void main(String[] args)
{
for (int i=0; i<args.length; i++)
System.out.println("Parameter "+ i + " : " + args[i]);
}
}
Beispiel eines Programm-Aufrufs :
E:\java\fhm\ee\vorl>java Echo Sie tanzte nur einen Sommer
Parameter
Parameter
Parameter
Parameter
Parameter
0
1
2
3
4
:
:
:
:
:
Sie
tanzte
nur
einen
Sommer
E:\java\fhm\ee\vorl>
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (1)
• Grundsätzliches zum I/O-Model von Java
◇ Eine Programm-Ein- und Ausgabe findet üblicherweise über Dateien und/oder Geräte statt.
◇ Java betrachtet sowohl Geräte als auch Dateien grundsätzlich als sequentielle Folge von Bytes (Byte-Stream).
Da Java Zeichen und Strings im Unicode darstellt, bietet die Sprache auch die Möglichkeit Textdateien/
Text-Geräte als sequentielle Folge von Unicode-Zeichen (Zeichen-Stream) zu interpretieren.
◇ Programmintern wird eine Datei bzw ein Gerät durch ein Objekt einer Stream-Klasse repräsentiert.
◇ Es gibt zahlreiche verschiedene Stream-Klassen, die jeweils unterschiedliche Eigenschaften modellieren.
Alle Stream-Klassen sind im Package java.io enthalten.
• Standard-Ein- und Ausgabe-Objekte
◇ In den Programmen der meisten Programmiersprachen (z.B. auch in C-Programmen) werden automatisch
Zugriffsmöglichkeiten zu den Standard-Ein- und Ausgabegeräten (üblicherweise Tastatur und Bildschirm
des Konsolengeräts) bereitgestellt. Diese können sofort nach Programmstart ohne weitere Maßnahmen
eingesetzt werden.
◇ Auch in Java ist dies der Fall :
In jedem Java-Programm werden automatisch Stream-Objekte angelegt, die einen Zugriff zur StandardEingabe, Standard-Ausgabe und Standard-Fehlerausgabe ermöglichen.
Die entsprechenden Streams sind implizit geöffnet.
Aus historischen Gründen handelt es sich bei diesen Stream-Objekten um Byte-Stream-Objekte.
◇ Referenzen auf diese Stream-Objekte stehen als öffentliche statische Datenkomponenten der – nicht
instanzierbaren – Klasse System (im Package java.lang) zur Verfügung .
Es handelt sich um Referenz-Konstante.
Sie müssen zusammen mit dem Klassennamen System verwendet werden.
▻ public static final InputStream in
Standard-Eingabe-Objekt
Referierung mittels System.in
▻ public static final PrintStream out
Standard-Ausgabe-Objekt
Referierung mittels System.out
▻ public static final PrintStream err
Standard-Fehlerausgabe-Objekt
Referierung mittels System.err
◇ Ab den JDK 6.0 existiert eine – allerdings nicht immer verfügbare – alternative Möglichkeit zum Zugriff
zur Standard-Ein- und Ausgabe zur Verfügung :
Wenn ein Java-Programm aus der Kommandozeile (ohne Ein-/Ausgabe-Umleitung) gestartet wird, wird
implizit durch die JVM ein Objekt der Klasse Console erzeugt. (nicht jedoch beim Start aus einer IDE).
Eine Referenz auf dieses Objekt kann mit einer statischen Methode der Klasse System ermittelt werden :
public static Console console()
Die Klasse Console (Package java.io) stellt u.a. Methoden zum Schreiben in das sowie Lesen aus
dem Konsolengerät (== Standard-Eingabe + Standard-Ausgabe) zur Verfügung.
Genaueres s. API-Dokumentation.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (2)
• Schreiben in die Standard-Ausgabe und Standard-Fehlerausgabe
◇ Die durch system.out (Standard-Ausgabe) und system.err (Standard-Fehlerausgabe) referierten
Stream-Objekte sind Instanzen der Klasse PrintStream (Package java.io).
Diese Klasse stellt die folgenden Methoden zum Schreiben zur Verfügung :
public void print(...)
public void println(...)
▻ Beide Methoden sind mehrfach überladen und haben jeweils einen Parameter, der dem jeweiligen Typ
des auszugebenden Werts bzw Objekts entspricht.
Sie ermöglichen die – nicht explizit formatierbare – Ausgabe von
-
von boolean-, char-, double-, float-, int- und long-Werten
-
von char-Arrays
-
von Strings (Objekte der Klasse String)
-
sowie der String-Repräsentation von Objekten beliebiger Klassen
(impliziter Aufruf der Objekt-Methode toString())
▻ Die Anwendung der String-Konkatenation in Verbindung mit der automatischen Umwandlung von
beliebigen Datenwerten und Objekten in eine String-Repräsentation bei ihrem Auftritt in KonkatenationsAusdrücken (Methode toString()) erlaubt die Ausgabe mehrerer Werte/Objekte mit einem
Methodenaufruf.
▻ Die Methode println() ergänzt die Ausgabe um ein Zeilenendezeichen.
Sie lässt sich auch ohne Parameter aufrufen. In diesem Fall bewirkt sie lediglich einen Übergang in eine
neue Zeile.
public PrintStream printf(String form, Object... args) (ab dem JDK 5.0)
▻ Diese Methode ermöglicht eine – C-ähnliche – formatierte Ausgabe (s. gesonderte Beschreibung)
▻ Ihr Rückgabewert ist eine Referenz auf das aktuelle PrintStream-Objekt, für das sie aufgerufen wird.
◇ Beispiel zur Anwendung von print(...) und println(...) :
class StdOutDemo1
{
public void demo()
{ boolean b = true;
char
c = 'Z';
int
i = 399127;
long
l = 124L;
float
f = 2.25E-2f;
double d = 0.0/0.0;
String s = "Hallo !";
System.out.print(b); System.out.println();
System.out.println("Zeichen : " +c);
System.out.println(i);
System.out.println(i + " " + l);
System.out.println(f);
System.out.println(d);
System.out.println(s);
System.out.println(this);
}
public static void main(String[] args)
{ new StdOutDemo().demo();
}
}
Ausgabe :
true
Zeichen : Z
399127
399127 124
0.0225
NaN
Hallo !
StdOutDemo1@3e25a5
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (3)
• Formatierte Ausgabe (ab dem JDK 5.0)
◇ Mittels der PrintStream-Methode
public PrintStream printf(String form, Object... args);
◇ Die Methode ermöglicht eine C-ähnliche formatierte Ausgabe
◇ Der erste Parameter form ist der Format-String. Er enthält – analog zum Format-String der C-Funktion
printf() – die einzelnen Formatangaben (format specifier) für die auszugebenden Werte/Objekte.
Durch die Formatangaben wird auch der Typ bzw die Darstellungsart der Ausgabe-Werte festgelegt
Zusätzlich kann der Format-String weiteren Text enthalten, der direkt ausgegeben wird.
◇ Die zweite Parameter-Angabe Object... args bedeutet, dass eine beliebige Anzahl (auch keine)
weiterer Parameter beliebigen Referenz-Typs folgen darf. Diese Parameter (Argumente) legen die Werte
fest, die entsprechend des jeweils spezifizierten Formats auszugeben sind.
Für jede Formatangabe muss ein passendes Argument übergeben werden. Die Typen dieser Argumente
müssen zu den entsprechenden Angaben im Format-String passen. Überflüssige Argumente werden ignoriert
Als Parameter können auch Werte einfacher Datentypen auftreten, da sie mittels Autoboxing automatisch in
Objekte der zugehörigen Wrapper-Klassen umgewandelt werden.
◇ Syntax der Formatangaben (vereinfacht) :
% [flags][width][.precision]conversion
▻ conversion (Konvertierungszeichen)
legt die Formatierungsart und/oder den Typ der Ausgabe fest und schließt eine Formatangabe ab
Die wichtigsten Konvertierungszeichen sind :
b
d
o
x
c
e
f
g
oder B
oder X
oder C
oder E
oder G
s oder S
n
logischer Wert
ganzzahliger Wert in Dezimaldarstellung
ganzzahliger Wert in Oktaldarstellung
ganzzahliger Wert in Sedezimaldarstellung
Unicode-Zeichen
Gleitpunkt-Wert in Exponentialdarstellung
Gleitpunkt-Wert in Dezimalbruchdarstellung
Gleitpunkt-Wert in Dezimalbruchdarstellung oder Exponentialdarstellung
(abhängig von Wert und Genauigkeit)
String (der String, der durch die jeweilige Methode toString() erzeugt wird)
implementierungsabhängige Darstellung des Zeilenendes
Bei einem Großbuchstaben als Konvertierungszeichen : Ausgabe aller Buchstaben als Großbuchstaben
▻ flags (Steuerflags)
modifizieren das Ausgabeformat, sie können gegebenenfalls miteinander kombiniert werden
Die wichtigsten Steuerflags sind :
+
0
' ' (Blank)
linksbündige Ausgabe
auch positive Werte werden mit Vorzeichen ausgegeben (nur für Zahlen anwendbar)
Ausgabe führender Nullen (nur für Zahlen anwendbar)
Ausgabe positiver Werte mit führendem Blank (nur für Zahlen anwendbar)
▻ width (Ausgabefeldbreite)
legt die minimale Ausgabefeldbreite fest
▻ precision (Genauigkeit)
legt i.a. die maximale Ausgabefeldbreite fest, bei Gleitpunktzahlen jedoch die Anzahl der Nachpunktstellen
(Ausnahme : beim Konvertierungszeichen g bzw G wird die Gesamtzahl der Ziffern festgelegt),
bei Strings : Anzahl der auszugebenden Zeichen des Strings
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (4)
• Formatierte Ausgabe (ab dem JDK 5.0), Forts.
◇ Nichtzulässige Zeichen in einer Formatangabe sowie fehlende oder zu einer Formatangabe nichtkompatible Argumente führen zum Werfen einer IllegalFormatException.
• Demonstrationsprogramm zur formatierten Ausgabe :
// FormOutpDemo.java
// Demonstrationsprogramm zur formatierten Ausgabe
public class FormOutpDemo
{
void show()
{ int anz = 10;
double summe = 99.98765;
float anteil = 0.00005432f;
String type = "Airbus A-380";
System.out.println("0123456789012345678901234567890123456789");
System.out.printf("Anzahl : %5d Summe : %8.3e\n", anz, summe);
System.out.printf("Anzahl : %-5d Summe : %8.3f\n", anz, summe);
System.out.printf("Anzahl : %0+5d Summe : %08.3f\n", anz, summe);
System.out.printf("Anteil : % .4g\n", anteil);
System.out.printf("Anteil : %.4E\n", anteil);
System.out.printf("Anteil : %.4f\n", anteil);
System.out.printf("Typ : %s\n", type);
System.out.printf("Typ : %15s\n", type);
System.out.printf("Typ : %5s\n", type);
System.out.printf("Typ : %5.6S\n", type);
System.out.printf("Typ : %8.6s\n", type);
System.out.printf("%B\n", true);
}
public static void main(String[] args)
{ FormOutpDemo1 demo = new FormOutpDemo();
demo.show();
}
Ausgabe des Programms :
0123456789012345678901234567890123456789
Anzahl :
10 Summe : 9.999e+01
Anzahl : 10
Summe :
99,988
Anzahl : +0010 Summe : 0099,988
Anteil : 5.432e-05
Anteil : 5.4320E-05
Anteil : 0,0001
Typ : Airbus A-380
Typ :
Airbus A-380
Typ : Airbus A-380
Typ : AIRBUS
Typ :
Airbus
TRUE
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/5 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (5)
• Alternativen zur formatierten Ausgabe
◇ 1. Erzeugung eines formatierten Strings
mittels einer statischen Memberfunktion der Klasse String (ab JDK 5.0) :
public static String format(String form, Object... args)
▻ Bedeutung der Parameter wie bei der PrintStream-Methode printf(...)
2. Ausgabe dieses Strings mittels der PrintStream-Methoden print(...) bzw println(...)
Beispiel :
int anz = 10;
double summe = 99.98765;
String s;
s = String.format("Anzahl : %5d
System.out.println(s);
Summe : %8.3e\n", anz, summe);
◇ Verwendung der Klasse Formatter (Package java.util).
● Verwendung der Klasse Formatter zur formatierten Ausgabe
◇ Objekte der Klasse Formatter ermöglichen die Ausgabe formatierter Strings in ein mit Ihnen verbundenes Ausgabe-(Ziel-)Objekt.
Das Ziel-Objekt eines Formatter-Objekts ist dessen Konstruktor als Parameter zu übergeben.
◇ Ziel-Objekt eines Formatter-Objekts kann u.a. auch das Standard-Ausgabe-Objekt sein.
Erzeugung eines Formatter-Objekts für die Standard-Ausgabe :
Formatter stdoutform = new Formatter(System.out);
◇ Mittels der Formatter-Objekt-Methode
public Formatter format(String form, Object... args);
lässt sich ein formatierter String erzeugen und in das Ziel-Objekt, hier also in das Standard-Ausgabe-Objekt,
ausgeben.
▻ Bedeutung der Parameter wie bei der PrintStream-Methode printf(...)
◇ Beispiel :
int anz = 10;
double summe = 99.98765;
Formatter stdoutform = new Formatter(System.out);
stdoutform.format("Anzahl : %5d Summe : %8.3e\n", anz, summe);
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/6 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (6)
• Lesen aus der Standard-Eingabe
◇ Das durch system.in referierte Stream-Objekt für die Standard-Eingabe steht als Instanz der Klasse
InputStream (Package java.io) zur Verfügung.
Diese Klasse besitzt zum Einlesen lediglich die mehrfach überladene Methode read(), mit der nur
einzelne Bytes oder eine Gruppe von Bytes (Byte-Array) eingelesen werden können.
public int read() throws IOEception
▻ Einlesen eines Bytes
▻ Rückgabewert : gelesenes Byte zu int ergänzt, bzw -1 falls Eingabeende erreicht ist
public int read(byte[] b) throws IOEception
▻ Einlesen einer Bytefolge in das als Parameter übergebene Array b
Es werden maximal b.length Bytes gelesen
▻ Rückgabewert : Anzahl der gelesenen Bytes, bzw -1 falls Eingabeende erreicht ist
◇ Sofern die über die Standard-Eingabe (Tastatur) eingegebenen Zeichen jeweils nur aus einem Byte bestehen
– was bei dem bei uns üblichen Zeichensatz i.a. der Fall ist – können mit diesen Methoden einzelne Zeichen
eingelesen werden.
◇ Üblicherweise soll von der Standard-Eingabe aber aus mehreren Zeichen bestehender Text eingelesen werden,
der vom Programm entweder als Wert eines einfachen Datentyps oder direkt als String interpretiert und
verwendet werden soll.
D.h. also, es müssen Zeichenfolgen (Datentyp char) und nicht Bytefolgen (Datentyp Byte) eingelesen
und in interne Wertdarstellungen umgewandelt werden.
◇ Um dies effektiv zu realisieren, muß das Objekt System.in mit Objekten anderer Klassen zusammenarbeiten. Hierfür existieren mehrere Möglichkeiten.
Die einfachste – allerdings erst ab dem JDK 5.0 vorhandene – Möglichkeit besteht im Einsatz eines Objekts
der Klasse Scanner (Package java.util).
• Allgemeines zur Klasse Scanner
◇ Objekte der Klasse Scanner dienen zum Zergliedern und Interpretieren von Zeichenfolgen.
◇ Die von Scanner-Objekten bearbeitbaren Zeichenfolgen können aus unterschiedlichen Eingabe-Quellen
– auch aus Bytestreams – stammen. (Dateien, sonstige Eingabe-Streams wie z.B. die Standard-Eingabe,
Strings). Das jeweilige Quell-Objekt muss beim Erzeugen eines Scanner-Objekts festgelegt werden
( Konstruktor-Parameter).
◇ Scanner-Objekte zerlegen ihre Eingabe-Zeichenfolge in Teil-Abschnitte (token). Die Zeichen(-muster),
die als Trennzeichen zwischen den Abschnitten interpretiert werden (delimiter pattern), lassen sich
konfigurieren. Defaultmässig werden Whitespace-Character verwendet.
◇ Die einzelnen Teil-Abschnitte stehen als Strings zur Verfügung und können als Werte eines einfachen
Datentyps interpretiert und in die dem jeweiligen Typ entsprechende interne Darstellung umgewandelt
werden.
Damit lassen sich Scanner-Objekte sehr elegant zum Einlesen von Werten der einfachen Datentypen aus
der Standard-Eingabe einsetzen, insbesondere auch dann, wenn in einer Eingabezeile mehrere einzulesende
Werte enthalten sind.
◇ Scanner-Objekte ermöglichen auch das zeilenweise Lesen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/7 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (7)
• Konstruktoren der Klasse Scanner (Auswahl)
public Scanner(InputStream source) Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle das
durch source spezifizierte InputStream-Objekt ist
public Scanner(File source)
Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle die
durch source spezifizierte Datei ist
public Scanner(String source)
Erzeugung eines Scanner-Objekts, dessen Eingabe-Quelle der
durch source spezifizierte String ist.
• Erzeugung eines Scanner-Objekts für die Standard-Eingabe
Scanner stdinscan = new Scanner(System.in);
• Memberfunktionen der Klasse Scanner (Auswahl)
public String nextLine()
Rückgabe des Rests der aktuellen Eingabe-Zeile
Wenn die Bearbeitungsposition sich unmittelbar vor dem Anfang der nächsten
Zeile befindet, wird die gesamte nächste Zeile zurückgegeben
public boolean hasNextLine()
Überprüfung, ob eine weitere Eingabe-Zeile vorhanden ist
Rückgabe von true wenn ja, sonst von false
public String next()
Rückgabe des nächsten Teil-Abschnitts (token) der Eingabe-Zeichenfolge
public boolean hasNext()
Überprüfung ob ein weiterer Teil-Abschnitt vorhanden ist
Rückgabe von true wenn ja, sonst von false
public boolean hasNextByte()
public boolean hasNextShort()
public boolean hasNextInt()
public boolean hasNextLong()
public boolean hasNextFloat()
public boolean hasNextDouble()
Überprüfung, ob der nächste Teilabschnitt sich als Wert des jeweiligen
Typs interpretieren lässt. (boolean-Werten können mit Groß- und/oder
Kleinbuchstaben dargestellt werden)
Rückgabe von true wenn ja, sonst von false
Die Methoden geben auch false zurück, wenn kein weiterer Teil-Abschnitt
mehr vorhanden ist (d.h. das Eingabe-Ende erreicht ist)
Achtung : Der Dezimal-Punkt bei Float- und Double-Werten muss
für das in Deutschland standardmäßig verwendete Default-Locale-Objekt
bei der Eingabe durch ein Dezimal-Komma ersetzt werden,
änderbar durch Setzen eines anderen Locale-Objekts (s. unten)
public boolean hasNextBoolean()
public byte nextByte()
public short nextShort()
public int nextInt()
public long nextLong()
public float nextFloat()
public double nextDouble()
Interpretation des nächsten Teil-Abschnitts der Eingabe-Zeichenfolge als
Wert des jeweiligen Typs und Rückgabe der internen Darstellung dieses
Wertes
Jede der Funktionen wirft eine Exception vom Typ
- InputMismatchException,
wenn der Teil-Abschnitt sich nicht als Wert des jeweiligen Typs interpretieren lässt (boolean-Werte können mit Groß- und/oder Kleinbuchstaben dargestellt werden)
- NoSuchElementException,
wenn das Ende der Zeichenfolge, d.h. bei Dateien und Geräten das StreamEnde, erreicht ist (z.B. Eingabe des Dateiendezeichens)
public boolean nextBoolean()
public Scanner useLocale (Locale loc) Setzen des vom Scanner-Objekt verwendeten Locale-Objekts
(Locale-Objekte repräsentieren regionale Besonderheiten/Darstellungen)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/02/8 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Standard-Ein- und Ausgabe in Java (8)
• Demonstrationsbeispiele zur Klasse Scanner
◇ Programm EchoLinesScanDemo
// EchoLinesScanDemo.java
// Echo der von der Standard-Eingabe eingelesenen Zeilen in die Standard-Ausgabe
// Verwendung der Klase Scanner
import java.util.*;
public class EchoLinesScanDemo
{
public static void main(String[] args)
{ Scanner stdinscan = new Scanner(System.in);
while (stdinscan.hasNextLine())
System.out.println(stdinscan.nextLine());
}
}
◇ Programm StdInpScanDemo
// StdInpScanDemo.java
// Demonstrationsprogramm zum Einlesen von der Standard-Eingabe
// unter Verwendung der Klasse Scanner
import java.util.*;
public class StdInpScanDemo
{
public void demo()
{ int a, b;
Scanner stdinscan = new Scanner(System.in);
stdinscan.useLocale(Locale.ROOT);
System.out.print("Zwei Integer-Werte a und b ? ");
a = stdinscan.nextInt();
b = stdinscan.nextInt();
System.out.println("a+b = " + (a + b));
System.out.print("Boolean-Wert ? ");
boolean bw = stdinscan.nextBoolean();
System.out.println("Eingabe war : " + bw);
System.out.print("Double-Wert ? ");
if (stdinscan.hasNextDouble())
{ double d = stdinscan.nextDouble();
System.out.println("Double-Wert : " + d);
}
else
System.out.printf("keine oder falsche Eingabe");
}
public static void main(String[] args)
{ new StdInpScanDemo().demo(); }
}
◇ Beispiel für die Ein- und Ausgabe des Programms StdInpScanDemo
Zwei Integer-Werte a und b ? 12 25
a+b = 37
Boolean-Wert ? FalsE
Eingabe war : false
Double-Wert ? 4.5
Double-Wert : 4.5
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/1 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (1)
• Prinzip des Exception Handlings
◇ Java stellt – wie auch z.B. C++ – einen speziellen Mechanismus zur Behandlung von Ausnahmesituationen
(Fehlerfällen, Exceptions), die während der Programmausführung auftreten können, zur Verfügung
⇒ Exception Handling
◇ Ausnahmesituationen in diesem Sinne sind Fehler oder sonstige unerwünschte Sonderfälle (z.B. Dateizugriffsfehler, Verletzung von Array-Grenzen, Datenformatfehler usw), die im normalen Programmablauf
nicht auftreten sollten, aber auftreten können.
Exception Handling ist kein Mechanismus zur Behandlung von externen oder internen Interrupts.
◇ Exception Handling trennt den Code, der Fehler verursachen kann, von dem Code, der einen aufgetretenen
Fehler behandelt.
Dadurch kann der eigentliche den normalen Programmablauf steuernde (produktive) Code frei gehalten
werden von einer ständigen Überprüfung auf den Auftritt von Fehlern und Sonderfällen sowie der Reaktion
auf diese. Dies trägt erheblich zur Klarheit und Übersichtlichkeit des Codes bei.
◇ Code (z.B. eine Methode einer Bibliotheksklasse), der eine Ausnahme- (Fehler-)situation entdeckt, wirft eine
Exception, die dann von einem anderen Code-Teil, dem Exception-Handler gefangen wird.
Im Exception-Handler findet die Reaktion auf die (Bearbeitung der) Ausnahmesituation statt. Diese kann
der Fehlerart sowie der jeweiligen spezifischen Programmsituation angepasst werden. Z.B. kann eine
Fehlermeldung ausgegeben werden und anschliessend das Programm fortgesetzt oder beendet werden. Oder
es kann versucht werden, die Fehlerursache zu beseitigen und das Programm dann fortzusetzen.
◇ Exceptions werden als Objekte behandelt. Sie enthalten Informationen über die Fehlerursache und stellen
Methoden zum Zugriff zu diesen zur Verfügung.
Werfen einer Exception (throwing) bedeutet das Erzeugen eines Objekts einer Exception-Klasse und die
anschliessende Suche eines passenden Exception-Handlers.
Wird ein derartiger Handler gefunden, wird ihm das Exception-Objekt (genauer eine Referenz darauf)
übergeben ( Fangen der Exception, catching).
Dem Handler stehen damit die im Exception-Objekt enthaltenen Informationen zur Auswertung zur
Verfügung.
produktiver Code
Werfen einer Exception
Fehlersituation
ExceptionObjekt
Fangen der Exception
Exception-Handler
(Fehlerbehandlung)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/2 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (2)
● Fangen einer Exception
◇ Es gibt verschiedene Exception-Klassen. Ein Exeption-Handler kann immer nur Exception-Objekte
ganz bestimmter Klassen fangen.
◇ Nach dem Werfen einer Exception muss ein passender Exception-Handler gesucht werden, der diese
fangen kann.
Der Exception-Handler kann sich befinden :
▻ unmittelbar nach dem produktiven (überwachten) Code-Block (
try-Anweisung)
▻ in einem übergeordneten Block
▻ in einer aufrufenden Funktion
▻ in der JVM
Die Suche erfolgt von "innen nach außen".
◇ Exception-Objekte enthalten i.a. Informationen über die Ursache bzw eine Kurzbeschreibung der
Exception, die in ihrem Konstruktor durch einen String festgelegt werden (direkt codiert oder als Parameter).
Der Exception-Handler kann zu diesen Informationen mittels entsprechende Objekt-Methoden zugreifen.
◇ Nach der Ausführung des Exception-Handlers wird das Programm mit der dem Handler nachfolgenden
Anweisung fortgesetzt.
Keine Rückkehr zum Ort des Fehlerauftritts
• Sehr umfangreiche und strikte Anwendung des Exception Handlings in Java :
◇ In der Standard-Bibliothek sind zahlreiche Exception-Klassen definiert.
Sie bilden eine über mehrere Pakete verteilte Klassenhierarchie, die die Klasse
Throwable (von Object abgeleitet, Paket java.lang)
als Wurzelklasse besitzt.
Die Exception-Ursache bzw eine Kurzbeschreibung der Exception wird i.a. im Konstruktor der
jeweiligen Klasse durch einen String (fest codiert oder als Parameter übergeben) festgelegt.
◇ Methoden vieler Bibliotheksklassen werfen Exceptions.
◇ Java erzwingt, dass geworfene Exceptions gefangen werden müssen. Existiert in einem Benutzerprogramm kein geeigneter Exception-Handler, so werden sie von einem in der JVM angesiedelten
Standard-Handler behandelt, der Informationen über die Exception in die Standard-Fehler-Ausgabe
ausgibt und anschliessend das Programm (genauer den aktuellen Thread) beendet.
◇ Ein Benutzer kann jederzeit eigene Exception-Klassen definieren. Dies kann sinnvoll sein, wenn spezielle
Fehlerfälle gesondert von anderen behandelt werden sollen (z.B. "Division durch Null" als Spezialfall einer
ArithmeticException).
Eine beutzerdefinierte Exception-Klasse muß von einer der Standard-Bibliotheks-Exception-Klassen
abgeleitet sein.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/3 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (3)
• Hierarchie der Bibliotheks-Exception-Klassen
◇ Alle Exception-Klassen sind – direkt oder indirekt – von der Klasse Throwable abgeleitet.
◇ Die Klasse Throwable (Paket java.lang) ist eine direkte Unterklasse von Object.
◇ Überblick über den Anfang der Hierarchie :
Throwable
Error
Exception
ungeprüfte Exceptions
VirtualMachineError
weitere
Error-Klassen
ArithmeticException
geprüfte Exceptions
RuntimeException
IOException
weitere
RuntimeExceptionKlassen
FileNotFoundException
weitere
ExceptionKlassen
weitere
IOExceptionKlassen
◇ Exceptions der Klasse Error sowie der davon abgeleiteten Klassen kennzeichnen ernsthafte
Systemfehler, die normalerweise nicht auftreten sollten, aber prinzipiell jederzeit auftreten können.
Ein normales Benutzerprogramm kann auf derartige Exceptions i.a. nicht sinnvoll reagieren und sollte sie
deswegen auch nicht fangen. Vielmehr sollten sie immer vom Default-Exception-Handler der JVM behandelt
werden.
◇ Exceptions der Klasse RuntimeException sowie der davon abgeleiteten Klassen können ebenfalls
prinzipiell an beliebiger Stelle während des Programmablaufs auftreten. I.a. liegt ihre Ursache in einem
logischen Programmierfehler (z.B. Division durch 0 oder unzulässiger Array-Index). Eine vernünftige
Reaktion zur Laufzeit ist i.a. nicht möglich. Derartige Fehler sollten i.a. im Programm-Code korrigiert werden.
Ein Benutzerprogramm kann diese Exceptions behandeln (d.h. geeignete Exception-Handler bereitstellen),
muß es aber nicht. Im letzteren Fall erfolgt wiederum eine Behandlung durch den Default-Handler der JVM.
◇ Exceptions aller übrigen Klassen (z.B. IOException) repräsentieren Fehler, mit denen man prinzipiell
rechnen muß (z.B. Datei ist nicht vorhanden) und die deswegen auch in irgendeiner Weise behandelt werden
müssen. Für diese Exceptions schreibt Java eine "catch-or-declare"-Erfordernis vor. Sie müssen von der
Funktion, in der sie geworfen werden, entweder gefangen oder von ihr als weiter-werfbar deklariert werden.
Andererseits darf eine Funktion nur solche Exceptions dieser Klassen werfen, die sie auch deklariert hat.
Das Einhalten dieser Erfordernis kann vom Compiler überprüft werden. Sie werden daher als geprüfte
(checked) Exceptions bezeichnet.
◇ Exceptions der Klassen Error und RuntimeException sowie der davon abgeleiteten Klassen sind
dagegen ungeprüfte (unchecked) Exceptions. Sie können jederzeit geworfen werden, ohne dass sie von der
entsprechenden Funktion deklariert werden müssen. Für sie gilt die "catch-or-declare"-Erfordernis nicht, sie
dürfen aber deklariert werden
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (4)
• Die Klasse Throwable
◇ Die direkt von der Klasse Object abgeleitete Klasse Throwable (Paket java.lang) ist Basisklasse
aller Exception-Klassen.
◇ Sie definiert ein allgemeines Interface für den Zugriff zu den Exception-Objekten aller Exception-Klassen
Die vier wichtigsten Methoden dieses Interfaces sind :
public String getMessage()
▻ Diese Methode gibt den bei der Exception-Objekt-Erzeugung gespeicherten Ursachen- bzw KurzbeschreibungsString als Funktionswert zurück, bzw null, falls kein derartiger String gespeichert wurde.
public String toString()
▻ Diese Methode erzeugt einen String, der aus dem vollqualifizierten Namen der tatsächlichen Klasse des ExceptionObjekts besteht, gegebenenfalls (falls getMessage() einen Wert !=null zurückgibt) gefolgt von ": " und
dem Rückgabewert von getMessage().
public void printStackTrace()
▻ Diese Methode gibt den Funktionswert von toString() gefolgt von dem Aufruf-Stack der Funktion, in der die
Exception ursprünglich geworfen wurde, in die Standard-Fehler-Ausgabe (System.err) aus.
public void printStackTrace(PrintStream s)
▻ Diese Methode gibt den Funktionswert von toString() gefolgt von dem Aufruf-Stack der Funktion, in der die
Exception ursprünglich geworfen wurde, in den durch s referierten Print-Stream aus.
• Deklaration werfbarer Exceptions (throws-Klausel)
◇ Die von einer Funktion werfbaren geprüften Exceptions müssen von dieser deklariert werden.
Werfbare ungeprüfte Exceptions können, müssen aber nicht deklariert werden.
◇ Die Deklaration erfolgt durch eine im Funktionskopf nach der Parameterliste anzugebende throwsKlausel. Die throws-Klausel besteht aus dem Schlüsselwort throws, gefolgt von einer durch
Kommata getrennten Auflistung von Exception-Klassen
◇ Syntax :
throws
Exception-Klasse
,
◇ Beispiel :
public String liesEtwas(String dname)
throws FileNotFoundException, EOFException
{
// ...
}
• Werfen einer Exception – throw-Anweisung
throw
Exception-Objekt-Referenz
;
◇ Nach dem Schlüsselwort throw ist eine Referenz auf das zu werfende Exception-Objekt anzugeben.
Diese Referenz kann
▻ der Wert eines new-Ausdrucks sein (neu erzeugtes Exception-Objekt)
▻ die einem Exception-Handler übergebene Objekt-Referenz sein ("Weiterwerfen" der Exception)
◇ Beispiel :
throw new ArithmeticException("Nenner ist Null");
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/5 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (5)
• Die try-Anweisung
◇ Sie ermöglicht das Überwachen und das Fangen von Exceptions.
◇ Eine try-Anweisung besteht i.a. aus drei Teilen :
▻ dem try-Block
▻ einem oder mehreren catch-Blöcken
▻ einem finally-Block
Entweder die catch-Blöcke oder der finally-Block dürfen auch fehlen, aber nicht beides.
◇ Syntax :
try
{
catch
(
finally
Anweisung
Exception-Klasse
}
Name
{
Anweisung
}
{
Anweisung
}
)
◇ Der try-Block enthält den eigentlichen produktiven Code, der auf den Auftritt von Exceptions überwacht wird. Er wird solange ausgeführt, bis entweder durch eine der Anweisungen eine Exception (direkt
durch eine throw-Anweisung oder indirekt durch eine aufgerufene Funktion) geworfen wird oder alle
Anweisungen erfolgreich abgearbeitet worden sind.
Beim Auftritt einer Exception wird der try-Block sofort verlassen und nach einem Exception-Handler
gesucht, der die Exception fangen kann.
◇ Die catch-Blöcke bilden die Exception-Handler.
Jeder catch-Block ist zuständig für das Fangen von Exceptions einer bestimmten Klasse und deren
Unterklassen. Die von einem catch-Block fangbare Exception-Klasse ist in seinem Kopf – analog zur
Parameter-Deklaration von Funktionen – angegeben. Da Objekte (genauer Objekt-Referenzen) einer Klasse
zuweisungskompatibel zu Variablen einer Basisklasse sind, können auch Exceptions aller Klassen, die von der
deklarierten Klasse abgeleitet sind, gefangen werden.
Die Suche nach einem "passenden" Exception-Handler erfolgt in der Reihenfolge seiner Angabe im Quellcode. Wird ein Exception-Handler gefunden, der die geworfene Exception fangen kann, wird das Programm mit dessen Abarbeitung fortgesetzt. Dabei wird die Exception wie ein formaler Funktionsparameter
an den Handler übergeben. Wird kein "passender" Exception-Handler gefunden, wird die Suche in der
jeweils nächsten umfassenden try-Anweisung (gegebenenfalls in der aufrufenden Funktion) fortgesetzt.
Bleibt auch das erfolglos, wird der Default-Exception-Handler der JVM ausgeführt und das Programm
beendet.
◇ Der finally-Block enthält Code, der immer ausgeführt wird, unabhängig davon, wie die try-Anweisung verlassen wird, ob durch reguläre Beendigung des try-Blocks oder durch Werfen einer gefangenen
oder nicht gefangenen Exception.
Die Ausführung des finally-Blocks erfolgt unmittelbar vor Verlassen des try-Blocks bzw – beim
Fangen einer Exception – vor Verlassen des entsprechenden catch-Blocks. Die Ausführung erfolgt auch
dann, wenn try- bzw catch-Block mit einer return-Anweisung verlassen werden.
Der finally-Block wird insbesondere verwendet, um Aufräumarbeiten durchzuführen und sicherzustellen, dass belegte System-Resourcen (z.B. geöffnete Dateien) freigegeben werden
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/6 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (6)
• Demonstrationsprogramm zu Exceptions
// ExceptDemo.java
import java.io.*;
class ExceptDemo
{
public void func1()
{ try
{ System.out.println("in func1() : Aufruf von func2()");
func2();
System.out.println("normales Ende von try in func1()!");
return;
}
finally
{ System.out.println("func1() verlassen !");
}
}
public void func2()
{ try
{ System.out.println("in func2() : Aufruf von func3()");
func3();
System.out.println("normales Ende von try in func2()!");
return;
}
catch (RuntimeException e)
{ System.out.print("Exception-Handler in func2() : ");
System.out.println(e.getMessage());
return;
}
finally
{ System.out.println("func2() verlassen !");
}
}
public void func3()
{ try
{ System.out.println("in func3() : Exception wird geworfen");
throw new RuntimeException("Demo-Exception !");
}
catch (ArithmeticException e)
{ System.out.print("Exception-Handler in func3() : ");
System.out.println(e.getMessage());
}
finally
{ System.out.println("func3() verlassen !");
}
}
public static void main(String[] args)
{ new ExceptDemo().func1();
}
}
in func1() : Aufruf von func2()
in func2() : Aufruf von func3()
in func3() : Exception wird geworfen
func3() verlassen !
Exception-Handler in func2() : Demo-Exception !
func2() verlassen !
normales Ende von try in func1()!
func1() verlassen !
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/03/7 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Exceptions in Java (7)
• Demonstrationsprogramm ExeptDemo : Veranschaulichung des Programmablaufs
func1()
try
func2()
func2()
return
finally
try
func3()
func3()
return
try
catch
(RuntimeException)
throw RuntimeException
return
finally
?
catch
(ArithmeticException)
finally
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java - Überblick
• Überblick
◇ Java behandelt Dateien als sequentielle Folge von Bytes (Byte-Streams).
◇ In der IO-Bibliothek (Package java.io) sind zahlreiche Klassen definiert, die das Arbeiten mit Dateien
unterstützen.
Die wichtigsten dieser Klassen sind :
▻ die File-Stream-Klassen zum sequentiellen Zugriff
• FileInputStream (byteweises Lesen)
• FileOutputStream (byteweises Schreiben)
• FileReader (zeichenweises Lesen)
• FileWriter (zeichenweises Schreiben)
▻ eine Klasse zum wahlfreien Zugriff (Lesen und Schreiben)
• RandomAccessFile
▻ Klassen für den Zugriff zum Dateisystem
• FileDescriptor (Handle zur Datei-Verwaltungsstruktur des Betriebssystems, hier nicht betrachtet)
• File
▻ Zahlreiche weitere Stream-Klassen (meist Filter-Klassen) ermöglichen einen spezialisierten Dateizugriff
◇ Auszugsweises Klassendiagramm :
Anmerkungen :
▻ Das Diagramm beschränkt sich auf die Datei-Eingabe-Stream-Klassen (FileInputStream,
FileReader).
▻ Für die Datei-Ausgabe-Stream-Klassen (FileOutputStream, FileWriter) bestehen analoge
Ableitungs-und Nutzungsbeziehungen.
Deren – direkte bzw indirekte – Basisklassen sind OutputStream bzw Writer.
▻ Die IO-Bibliothek enthält eine Reihe weiterer – im Diagramm nicht aufgeführter – Stream-Klassen, die
zumeist von InputStream oder Reader (bzw OutputStream oder Writer) direkt oder
indirekt abgeleitet sind.
InputStream
Reader
FilterInputStream
BufferedInputStream
DataInputStream
InputStreamReader
FileInputStream
FileReader
BufferedReader
RandomAccessFile
FileDescriptor
File
◇ Eine Bearbeitung von Textdateien ist auch möglich mit den Klassen Formatter und Scanner
(Package java.util)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse File (1)
• Die Klasse File
◇ Objekte dieser Klasse repräsentieren Zugriffspfade (zu Dateien bzw Directories) im Dateisystem.
◇ Wesentliche Funktionalitäten :
▻ Speicherung eines Datei- bzw Directory-Zugriffspfades in einer abstrakten system-unabhängigen
Darstellung, sowie Bereitstellung von Informationen über denselben
▻ Ermittlung diverser Eigenschaften einer repräsentierten Datei (bzw eines repräsentierten Directories)
▻ Manipulation von Dateisystem-Einträgen (Erzeugen, Löschen, Änderung bestimmter Eigenschaften)
◇ Der durch ein File-Objekt repräsentierte Zugriffspfad muss nicht einen tatsächlich existierenden Dateisystem-Eintrag referieren.
◇ Konstruktoren der Klasse File (Auswahl) :
public File(String path)
▻ Erzeugung eines neuen File-Objekts, das den Zugriffspfad path repräsentiert.
▻ Falls path==null ist, wird eine NullPointerException geworfen.
public File(String dirName, String name)
▻ Erzeugung eines neuen File-Objekts, das den aus dirName und name gebildeten Zugriffspfad
repräsentiert. (Eintrag name im Directory dirName).
▻ Äquivalent zu : File(dirName + File.separator + name)
▻ Falls name==null ist, wird eine NullPointerException geworfen.
public File(File fileDir, String name)
▻ Erzeugung eines neuen File-Objekts, das den Zugriffspfad repräsentiert, der aus dem Eintrag
name in dem durch fileDir repräsentierten Directory gebildet wird,
▻ Äquivalent zu : File(fileDir.getPath(), name)
▻ Falls name==null ist, wird eine NullPointerException geworfen.
◇ Methoden zum Erzeugen neuer Dateisystem-Einträge (Auswahl)
public boolean createNewFile() throws IOException
▻ Diese Methode erzeugt eine neue leere Datei mit dem vom aktuellen File-Objekt repräsentierten
Zugriffspfad, falls noch keine Datei mit diesem Zugriffspfad existiert.
▻ Funktionswert : true, falls die Datei erzeugt werden konnte
false,falls eine Datei mit dem Zugriffspfad bereits existiert hat.
▻ Falls ein I/O-Fehler auftritt, wird eine IOException geworfen.
public boolean mkdir()
▻ Diese Methode erzeugt ein neues Directory mit dem vom aktuellen File-Objekt repräsentierten
Zugriffspfad
▻ Funktionswert : true, falls das Directory erzeugt werden konnte, andernfalls false
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/3 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse File (2)
• Die Klasse File, Forts
◇ Methoden zur Veränderung von Dateisystem-Einträgen
public boolean renameTo(File newPath)
▻ Umbenennen / Verschieben des aktuell repräsentierten Dateisystem-Eintrags.
▻ Der Dateisystem-Eintrag wird anschliessend durch das File-Objekt newPath repräsentiert.
Der vom aktuellen Objekt bisher repräsentierte Eintrag existiert anschliessend nicht mehr.
▻ Funktionswert : true, falls Umbenennen / Verschieben erfolgreich war, andernfalls false
▻ Falls newPath==null ist, wird eine NullPointerException geworfen.
public boolean delete()
▻ Löschen des aktuell repräsentierten Dateisystem-Eintrags. Ein zu löschendes Directory muss leer sein.
▻ Funktionswert : true, falls erfolgreich gelöscht werden konnte, andernfalls false
◇ Methoden zur Ermittlung von Informationen über Dateisystem-Einträge (Auswahl)
public boolean exists()
▻ Überprüfung, ob durch das File-Objekt ein tatsächlich existierender Eintrag repräsentiert wird
▻ Funktionswert : true, wenn der Eintrag existiert, sonst false
public boolean isDirectory()
▻ Funktionswert : true, wenn der Eintrag existiert und ein Directory ist, sonst false
public boolean isFile()
▻ Funktionswert : true, wenn der Eintrag existiert und eine normale Datei ist, sonst false
public boolean canWrite()
▻ Funktionswert : true, wenn Eintrag existiert und geändert werden kann, sonst false
public boolean canRead()
▻ Funktionswert : true, wenn Eintrag existiert und gelesen werden kann, sonst false
public String[] list()
▻ Funktionswert : Wenn Eintrag ein Directory ist : Rückgabe einer Auflistung aller in ihm
enthaltenen Einträge als String-Array (ausser "." und "..")
Wenn Eintrag kein Directory ist : null
public long length()
▻ Funktionswert : Dateilänge, wenn Eintrag Datei ist, bzw. undefiniert, wenn Eintrag Directory ist
public String getPath()
public String getAbsolutePath()
▻ Funktionswert : Zugriffspfad des repräsentierten Dateisystem-Eintrags (wie im Konstruktor angegeben)
bzw absoluter Zugriffspfad (gegebenenfalls mittels aktuellem Arbeitsdir. ermittelt)
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/4 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse File (3)
• Demoprogramm zur Klasse File
// ListDir.java
import java.io.*;
public class ListDir
{
public static void main(String[] args)
{
try
{ if (args.length<=0)
throw new IOException("Programmparameter (Directory-Pfad) fehlt !");
File dir = new File(args[0]);
if (!dir.exists())
throw new IOException("\"" + args[0] + "\" existiert nicht !");
if (!dir.isDirectory())
throw new IOException("\"" + dir.getPath() + "\" ist kein Directory !");
String[] entries = dir.list();
System.out.println("Inhalt des Directories \"" + dir.getAbsolutePath() + "\" :");
for (int i=0; i<entries.length; i++)
System.out.println(entries[i]);
}
catch (IOException ex)
{
System.out.println(ex.getMessage());
}
}
}
Beispiele für Programmaufruf und -ausgabe
E:\Java\fhm\ee\vorl\fileacc>java ListDir
Programmparameter (Directory-Pfad) fehlt !
E:\Java\fhm\ee\vorl\fileacc>java ListDir meindir
"meindir" existiert nicht !
E:\Java\fhm\ee\vorl\fileacc>java ListDir ftestdir
"ftestdir" ist kein Directory !
Inhalt des Directories "E:\Java\Vorl_Prog\elemprogfunc\." :
FormOutpDemo.java
Echo.java
StdOutDemo1.java
EchoLinesScanDemo.java
StdInpScanDemo.java
package cache
StdOutDemo1.class
FormOutpDemo.class
Echo.class
formoutpdemo.txt
EchoLinesScanDemo.class
StdInpScanDemo.class
ExceptDemo.java
ExceptDemo.class
StdInpCharDemo.class
ListDir.java
ftestdir
ListDir.class
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/5 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse File (4)
• Verwendung von File-Objekten für den Dateizugriff
◇ File-Objekte können Konstruktor-Parameter der File-Stream-Klassen sein.
Dateizugriff mittels der damit erzeugten File-Stream-Objekte
◇ File-Objekte können auch Konstruktor-Parameter der Klasse RandomAccessFile sein.
Dateizugriff mittels der damit erzeugten RandomAccessFile-Objekte (wahlfreier Dateizugriff)
◇ File-Objekte können auch Konstruktor-Parameter der Klassen
▻ Scanner und
▻ Formatter sein
formatiertes Schreiben (Klasse Formatter) sowie Lesen (Klasse Scanner) in/aus Textdateien
• Formatiertes Schreiben in Textdateien mittels der Klasse Formatter (Package java.util)
Öffnen der Datei :
beim Erzeugen
eines Formatter-Objekts
durch den Konstruktor
Formatter(File fil) throws
FileNotFoundException
z.B. :
Formatter fdat = new Formatter(new File("dat.txt");
formatiertes Schreiben in die Datei :
mit Objekt-Methode format()
public Formatter format(String form, Object... args);
Bedeutung der Parameter :
wie bei der PrintStream-Methode printf()
z.B. :
fdat.format("%4d", 127);
Schließen der Datei :
mit Objekt-Methode close()
public void close()
z.B.
fdat.close();
• Formatiertes Lesen aus Textdateien mittels der Klasse Scanner (Package java.util)
Öffnen der Datei :
beim Erzeugen
eines Scanner-Objekts
durch den Konstruktor
Scanner(File fil) throws
FileNotFoundException
z.B. :
Scanner sdat = new Scanner(new File("dat.txt");
formatiertes Lesen aus der Datei :
mit entsprechenden Objekt-Methoden
der Klasse Scanner
(s. Standard-Eingabe)
z.B.
public int nextInt();
public double nextDouble();
usw.
z.B. :
int zahl = sdat.nextInt();
Schließen der Datei :
mit Objekt-Methode close()
public void close()
z.B.
sdat.close();
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/6 – TH – 10
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Formatiertes Schreiben und Lesen
• Demoprogramm zur Verwendung der Klassen Formatter und Scanner
// FormTextFileDemo1.java
import java.io.*;
import java.util.*;
public class FormTextFileDemo1
{
private String dpfad;
public FormTextFileDemo1(String str)
{ dpfad = str;
}
public void formOutput() throws IOException
{ Formatter fdat = new Formatter(new File(dpfad));
Random rand = new Random();
for (int i=1; i<=8; ++i)
{ int anz = 1 + rand.nextInt(6);
fdat.format("%d : ", anz);
double d ;
for (int j=1; j<=anz; ++j)
{ d = rand.nextDouble();
fdat.format("%.5f ", d);
}
fdat.format("%n");
}
fdat.close();
}
public void formInput() throws IOException
{ Scanner sdat = new Scanner(new File(dpfad));
try
{ while (sdat.hasNext())
{ int anz = sdat.nextInt();
System.out.printf("%d : ", anz);
sdat.next();
// Ueberlesen des ':'
double d;
for (int i = 1; i <= anz; ++i)
{ d = sdat.nextDouble();
System.out.printf("%.5f ", d);
}
System.out.printf("%n");
}
}
catch (InputMismatchException ex)
{ System.err.println("Type Mismatch beim Lesen.");
}
sdat.close();
}
public static void main(String[] args)
{ Locale.setDefault(Locale.ROOT);
try
{ if (args.length <= 0)
throw new IOException("Programmparameter (Datei-Pfad) fehlt !");
FormTextFileDemo1 demo = new FormTextFileDemo1(args[0]);
demo.formOutput();
demo.formInput();
}
catch (IOException ex)
{ System.err.println(ex.getMessage());
}
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/7 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – File-Stream-Klassen (1)
• Sequentieller Dateizugriff mittels der File-Stream-Klassen
◇ Klassen für den byteweisen sequentiellen Dateizugriff :
▻ FileInputStream (byteweises Lesen)
▻ FileOutputStream (byteweises Schreiben)
◇ Klassen für den zeichenweisen sequentiellen Dateizugriff :
▻ FileReader (zeichenweises Lesen)
▻ FileWriter (zeichenweises Schreiben)
◇ Überblick über die Konstruktoren (Auswahl) :
1.Parameter
2. Parameter
String path
Dateizugriffspfad
------File fil
repräsentiert Dateizugriffspfad
String path
Dateizugriffspfad
File fil
repräsentiert Dateizugriffspfad
boolean app
wenn true : Anhängen an Datei (append)
wenn false : Schreiben am Dateianfang
existiert für Klasse
FileInputStream
FileOutputStream
FileReader
FileWriter
FileOutputStream
FileWriter
Beispiele :
public FileInputStream(File fil) throws FileNotFoundException
public FileWriter(String path, boolean app) throws IOException
● Bei der Erzeugung eines File-Stream-Objekts wird die referierte Datei – falls sie vorhanden und die
jeweilige Zugriffsart zulässig ist – geöffnet
● Die Konstruktoren der Datei-Ausgabe-Stream-Klassen versuchen die referierte Datei neu anzulegen,
falls sie nicht existiert.
● Kann eine Datei nicht geöffnet werden (z.B. weil der Zugriffspfad ein Directory referiert), wird eine
FileNotFoundException (bzw bei der Klasse FileWriter eine IOException) geworfen.
◇ Schliessen eines File-Streams
mittels der für alle File-Stream-Klassen existierenden Methode :
public void close() throws IOException
▻ Die Methode schliesst den File-Stream und damit die referierte Datei und gibt alle sonstigen mit dem
Stream assoziierten System-Resourcen frei.
▻ Beim Auftritt eines I/O-Fehlers wird eine IOException geworfen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/8 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – File-Stream-Klassen (2)
• Sequentieller Dateizugriff mittels der File-Stream-Klassen, Forts.
◇ Methoden zum Dateizugriff (Auswahl)
● Alle folgenden Methoden werfen eine IOException falls ein I/O-Fehler auftritt
● Schreiben in Dateien (Klassen FileOutputStream / FileWriter)
public void write(int bc) throws IOException
▻ Schreiben des Bytes bc (FileOutputStream) bzw des Zeichens bc (FileWriter)
public void write(byte[] buff, int pos, int len) throws IOException
public void write(char[] buff, int pos, int len) throws IOException
▻ Schreiben von len Bytes (FileOutputStream) bzw von len Zeichen (FileWriter) aus buff
ab Position pos
public void write(String str) throws IOException
// nur FileWriter
▻ Schreiben des Strings str
public void flush() throws IOException
▻ Herausschreiben aller Schreib-Buffer (Übergabe an das Betriebssystem)
● Lesen aus Dateien (Klassen FileInputStream / FileReader)
public int read() throws IOException
▻ Lesen eines Bytes (FileInputStream) bzw eines Zeichens (FileReader)
▻ Funktionswert : gelesenes Byte bzw gelesenes Zeichen
bzw -1 falls das Dateiende erreicht ist
public int read(byte[] buff) throws IOException
public int read(char[] buff) throws IOException
▻ Lesen einer Byte-Folge (FileInputStream) bzw einer Zeichen-Folge (FileReader)
▻ Ablage der gelesenen Byte- bzw Zeichen-Folge im Buffer buff
▻ Funktionswert : Anzahl gelesener Bytes bzw Anzahl gelesener Zeichen
bzw -1 falls das Dateiende erreicht ist
public long skip(long n) throws IOException
▻ Überlesen der nächsten n Bytes (FileInputStream) bzw der nächsten n Zeichen (FileReader)
● Es existieren noch weitere Methoden, mit denen auch Teile eines Byte- bzw Zeichen-Arrays gelesen
bzw Teile eines Strings geschrieben werden können.
◇ Durch die Zusammenarbeit mit geeigneten Filterklassen lassen sich spezialisiertere Formen der DateiEin-/Ausgabe realisieren.
Beispiel : Die Zusammenarbeit von FileInputStream mit DataInputStream sowie
FileOutputStream mit DataOutputStream ermöglicht das Lesen und Schreiben von
Daten in Binärdarstellung.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/9 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – File-Stream-Klassen (3)
• Demoprogramm zum sequentiellen Dateizugriff (Kopieren einer Datei)
// FileCopy.java
import java.io.*;
public class FileCopy
{
public void copy(String inpath, String outpath)
{
try
{
File ifile = new File(inpath);
FileInputStream in = new FileInputStream(ifile);
FileOutputStream out = new FileOutputStream(outpath);
long fsize = ifile.length();
System.out.print("Kopiert wird \"" + inpath+ "\" (Laenge : ");
System.out.println(fsize + " Bytes) nach \"" + outpath+ "\"");
byte[] buff = new byte[512];
int len;
while ((len=in.read(buff))>=0)
{
out.write(buff, 0, len);
}
out.close();
in.close();
System.out.println("Kopieren der Dateien erfolgreich");
}
catch(IOException ex)
{
System.out.println("Exception : " + ex.getMessage());
}
}
public static void main(String[] args)
{ if (args.length <2)
System.out.println("Aufruf : \"java FileCopy quelldatei zieldatei\"");
else
new FileCopy().copy(args[0],args[1]);
}
}
Beispiel für Programmaufruf und -ausgabe
E:\Java\fhm\ee\vorl\fileacc>java FileCopy ListDir.class ListDir.bin
Kopiert wird "ListDir.class" (Laenge : 1100 Bytes) nach "ListDir.bin"
Kopieren der Dateien erfolgreich
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/10 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (1)
• Wahlfreier Zugriff zu Dateien – Klasse RandomAccessFile
◇ Den wahlfreien Zugriff zu Dateien ermöglicht die Klasse RandomAccessFile.
Diese Klasse ist von keiner der File-Stream-Klassen sondern direkt von Object abgeleitet.
◇ Objekte dieser Klasse ermöglichen einen wahlfreien Lese- bzw Schreibzugriff zu Dateien.
Der wahlfreie Zugriff wird durch die Möglichkeit zur expliziten Veränderung der aktuellen Bearbeitungsposition (File-Pointer, nicht zu verwechseln mit dem File-Pointer in C, der referiert eine Datei) realisiert.
◇ Öffnen der Datei : durch den Konstruktor bei der Erzeugung eines RandomAccessFile-Objekts
Dateibearbeitung : Aufruf von Memberfunktionen für das RandomAccessFile-Objekt.
◇ Hierfür stellt die Klasse zahlreiche Methoden – auch für spezialisierte Dateizugriffe – bereit :
▻ Lesen und Schreiben von Bytes und Bytefolgen (Byte-Arrays)
▻ Schreiben von Strings als Byte-Folge, Zeichenfolge und in UTF-8-Codierung
▻ zeilenweises Lesen (Textdateien !)
▻ Lesen von Strings in modifizierter UTF-8-Codierung
▻ Lesen und Schreiben von Werten der einfachen Datentypen in interner Binärdarstellung (Binärdateien !)
◇ Anmerkung zur modifizierten UTF-8-Codierung :
▻ UTF = Universal Transfer Format : effiziente Codierung von Unicode-Zeichen durch 1, 2 und 3 Bytes
▻ Zeichen zwischen '\u0001' und '\u007F' durch ein Byte :
01 ... 7F
Zeichen zwischen '\u0080' und '\u07FF' durch zwei Bytes : C2 80 ... DF BF
Zeichen zwischen '\u0800' und '\uFFFF' durch drei Bytes : E0 A0 80 ... EF BF BF
der NUL-Character (das Zeichen '\u0000')
durch zwei Bytes : C0 80
▻ Vor der eigentlichen den String codierenden Bytefolge wird deren Länge als short-Wert
(2 Bytes) in den Stream geschrieben.
◇ Konstruktoren der Klasse RandomAccessFile
public RandomAccessFile(String path, String mode) throws
FileNotFoundException
public RandomAccessFile(File fil, String mode) throws
FileNotFoundException
▻ Der erste Parameter referiert die zu bearbeitende Datei (über den Zugriffspfad bzw ein FileObjekt, das diesen repräsentiert)
▻ Der zweite Parameter legt die Zugriffsart fest :
- "r" nur Lesen,
- "rw" Lesen und Schreiben. ("rws" und "rwd" sind auch zulässig)
▻ Die Konstruktoren versuchen die referierte Datei für die angegebene Zugriffsart zu öffnen.
▻ Falls die referierte Datei nicht existiert wird bei der Zugriffsart "rw" versucht, sie zu erzeugen.
▻ Die Konstruktoren können die folgenden Exceptions werfen :
- FileNotFoundException, falls die referierte Datei nicht existiert (Zugriffsart "r") oder ein
Directory ist oder nicht erzeugt werden kann oder aus einem anderen Grund nicht geöffnet werden
kann.
- SecurityException, falls für die vorgesehene Zugriffsart keine Berechtigung besteht
- IllegalArgumentException, falls ein nicht zulässiger String für die Zugriffsart angegeben
wird.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/11 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (2)
• Wahlfreier Zugriff zu Dateien – Klasse RandomAccessFile, Forts
◇ Methoden der Klasse RandomAccessFile zum Lesen u. Schreiben (Auswahl) :
Schreiben
void write(int b)
Lesen
Datentyp
int read()
byte
Rückgabewert : gelesenes Byte
bzw -1 bei Dateiende
void write(byte[] ba)
void write(byte[] ba,
int off, int len)
int read(byte[] ba)
int read(byte[] ba,
int off, int len)
byte-Array
byte-Array (Länge
len ab Offset off)
Rückgabewert : Anzahl gelesener Bytes
bzw -1 bei Dateiende
void writeBytes(String str)
String als Byte-Folge
---
pro Zeichen ein Byte,
das höherwertige Byte jedes
Zeichens geht verloren
void writeChars(String str)
String als Char-Folge
---
pro Zeichen zwei Bytes,
höherwertiges Byte zuerst
(big endian)
void writeUTF(String str)
String readUTF()
*)
String readLine()
String (UTF-8-Cod.)
String (Zeile)
Rückgabewert : nächste Zeile (Bytes zu char
ergänzt, höherwertiges Byte
== NUL-Byte)
bzw null bei Dateiende
void
void
void
void
void
void
void
void
writeBoolean(boolean b)
writeByte(int b)
writeChar(int c)
writeShort(int s)
writeInt(int i)
writeLong(long l)
writeFloat(float f)
writeDouble(double d)
boolean readBoolean()
byte readByte()
char readChar()
short readShort()
int readInt()
long readLong()
float readFloat()
double readDouble()
*)
*)
*)
*)
*)
*)
*)
*)
boolean
byte
char
short
int
long
float
double
Ablage
in Datei
in big
endian
*) diese Lese-Methoden werfen beim vorzeitigen Erreichen des Dateiendes eine EOFException
Alle Methoden zum Lesen/Schreiben werfen eine IOException, wenn ein I/O-Fehler auftritt.
alle Methoden sind mit der throws-Klausel throws IOException definiert.
◇ Die I/O-Methoden der Klasse RandomAccessFile ermöglichen sowohl eine Bearbeitung von
▻ Binärdateien
▪ byteweises Lesen und Schreiben
▪ datenobjektorientiertes Lesen und Schreiben
▻ als auch von Textdateien.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/12 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (3)
• Wahlfreier Zugriff zu Dateien – Klasse RandomAccessFile, Forts
◇ Bearbeitung von Textdateien mittels RandomAccessFile-Objekten
▻ Mit der Methode writeBytes() kann Text in der üblichen Darstellung (pro Zeichen ein Byte)
in Dateien geschrieben werden.
▻ Eine direkte Methode zum Schreiben eines Zeilenendes in eine Textdatei existiert allerdings nicht.
Die im jeweiligen System verwendete – implementierungsabhängige - Zeilenendedarstellung
lässt sich jedoch u.a. mittels der statischen String-Methode format(...) ermitteln :
String.format("%n") erzeugt die Zeilenendedarstellung als String-Objekt
raf.writeBytes(String.format("%n")) schreibt ein Zeilenende in die
vom Objekt raf referierte Datei.
▻ Zum Lesen von Textdateien kann die Methode readLine() eingesetzt werden
▻ Formatiertes Schreiben :
Erzeugung eines String-Objektes, das den entsprechend formatierten String enthält
- mittels der statischen Methode format(...) der Klasse String
- bzw mittels eines Formatter-Objektes
anschliessende Ausgabe dieses Strings mittels der Methode writeBytes()
▻ Formatiertes Lesen :
Lesen des formatierten Textes mittels der Methode readLine(),
anschliessende Interpretation des erhaltenen String-Objektes mittels eines Scanner-Objektes
◇ Weitere Methoden der Klasse RandomAccessFile (Auswahl)
void seek(long pos) throws IOException
▻ Setzen der Bearbeitungsposition auf pos Bytes nach dem Dateianfang
long getFilePointer() throws IOException
▻ Rückgabe der aktuellen Bearbeitungsposition (in Bytes bezogen auf den Dateianfang)
int skipBytes(int cnt) throws IOException
▻ Versuch, die Bearbeitungsposition um cnt Bytes weiterzusetzen
▻ Rückgabe der Anzahl Bytes, um die tatsächlich weitergesetzt wurde
long length() throws IOException
▻ Rückgabe der Dateilänge
void setLength(long newLength) throws IOException
▻ Explizites Verändern der Dateilänge.
Die neue Länge kann größer oder kleiner der aktuellen Dateilänge sein.
newLength < akt. Dateilänge : die Datei wird auf newlength verkürzt,
wenn die akt. Position des File Pointers > newLength ist, wird sie auf
newLength gesetzt
newLength > akt. Dateilänge : die Datei wird auf newlength verlängert,
der Inhalt des verlängerten Dateiteils ist undefiniert
void close()throws IOException
▻ Schliessen der Datei und Freigabe aller belegten Resourcen
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/13 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (4)
• Demoprogramm zum Kopieren von Dateien (mittels RandomAccessFile)
// FileCopy2.java
import java.io.*;
public class FileCopy2
{
public void copy(String inpath, String outpath)
{ try
{
RandomAccessFile in = new RandomAccessFile(inpath, "r");
RandomAccessFile out = new RandomAccessFile(outpath, "rw");
out.setLength(0);
long fsize = in.length();
System.out.print("Kopiert wird \"" + inpath+ "\" (Laenge : ");
System.out.println(fsize + " Bytes) nach \"" + outpath+ "\"");
byte[] buff = new byte[512];
int len;
while ((len=in.read(buff))>=0)
out.write(buff, 0, len);
out.close();
in.close();
System.out.println("Kopieren der Dateien erfolgreich");
}
catch(IOException ex)
{
System.out.println("Exception : " + ex.getMessage());
}
}
public static void main(String[] args)
{ if (args.length <2)
System.out.println("Aufruf : \"java FileCopy quelldatei zieldatei\"");
else
new FileCopy2().copy(args[0],args[1]);
}
}
Beispiel für Programmaufruf und -ausgabe
E:\Java\fhm\ee\vorl\fileacc>java FileCopy2 Echo.class Echo.bin
Kopiert wird "Echo.class" (Laenge : 808 Bytes) nach "Echo.bin"
Kopieren der Dateien erfolgreich
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/14 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (5)
• Demoprogramm zum wahlfreien Dateizugriff
// RandAccDemo.java
// Zufallsgesteuerte Auswahl eines Spruches aus einer Textdatei mit Spruechen.
// Die einzelnen Sprueche sind jeweils durch eine Zeile,
// die nur das Zeichen '*' enthaelt, getrennt.
import java.io.*;
public class RandAccDemo
{
public void demo(String dname)
{ try
{ RandomAccessFile raf = new RandomAccessFile(dname, "r");
long pos = (long)(raf.length()*Math.random());
raf.seek(pos);
while((pos!=0) && ((char)raf.read()!='*'))
raf.seek(--pos);
int ch;
if (pos==0)
System.out.println();
while (((ch=raf.read())!=-1) && ((char)ch!='*'))
System.out.print((char)ch);
System.out.println();
raf.close();
}
catch (IOException ex)
{
System.out.println(ex);
}
}
public static void main(String[] args)
{ String datname ="sprueche.txt";
if (args.length >0)
datname = args[0];
new RandAccDemo.demo(datname);
}
}
Beispiele für Programmaufruf und -ausgabe
E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo
Gut gehaengt ist besser als schlecht verheiratet.
E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo
Es ist nicht genug, dass man redet,
man muss auch richtig reden.
E:\Java\fhm\ee\vorl\fileacc>java RandAccDemo
Ein Tor nur schliesst aus aeusserem Gehaben
getrost auf eines Menschen innere Gaben.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/15 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Dateibearbeitung in Java – Klasse RandomAccessFile (6)
• Demoprogramm zum Schreiben von Daten in Binärdarstellung (mittels RandomAccessFile)
// BinDataDemo.java
// Schreiben von Werten einfacher Datentypen und Strings in interner Binaerdarstellung
// Lesen als Bytefolgen
import java.io.*;
public class BinDataDemo
{
static final String NL = String.format(%n");
public String byteToHex(byte b)
{ String str = Integer.toHexString(b & 0x00FF);
if (str.length() == 1)
str = '0' + str;
return str;
}
public void demo(String dpfad)
{ try
{ RandomAccessFile raf = new RandomAccessFile(dpfad, "rw");
raf.setLength(0);
raf.writeBoolean(true);
raf.writeInt(124);
raf.writeDouble(3.14);
raf.writeUTF("Hallo Java-Freunde");
raf.writeUTF("\u007F\u0080\u07FF\u0800\uFFFF\u0000");
raf.writeChars("Hallo");
raf.writeBytes("Hallo\n");
raf.writeBytes(NL);
raf.seek(0);
System.out.println("Inhalt der Datei \"" + dpfad + "\" :");
int b;
int cnt = 0;
while((b=raf.read())!=-1)
{ cnt++;
String hex = byteToHex((byte)b);
System.out.print(hex + ' ');
if (cnt%25 == 0)
System.out.println();
}
System.out.println();
raf.close();
}
catch(IOException ex)
{ System.out.println("Exception : " + ex.getMessage());
}
}
public static void main(String[] args)
{ if (args.length <1)
System.out.println("Aufruf : \"java BinDataDemo dateipfad\"");
else
{ new BinDataDemo().demo(args[0]);
}
}
}
Programmaufruf und -ausgabe
E:\Java\fhm\ee\vorl\fileacc>java
Inhalt der Datei "demo.dat" :
01 00 00 00 7c 40 09 1e b8 51 eb
2d 46 72 65 75 6e 64 65 00 0d 7f
00 61 00 6c 00 6c 00 6f 48 61 6c
BinDataDemo demo.dat
85 1f 00 12 48 61 6c 6c 6f 20 4a 61 76 61
c2 80 df bf e0 a0 80 ef bf bf c0 80 00 48
6c 6f 0a 0d 0a
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 13/04/16 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Zusammenfassende Gegenüberstellung der Dateibearbeitung in Java und C
• Vorbemerkung
In der Java-Standardbibliothek sind zahlreiche – teilweise alternative – Möglichkeiten zur Dateibearbeitung
implementiert. Nachfolgend werden nur einige – nach Meinung des Verfassers die wichtigsten und einfachsten –
dieser Möglichkeiten aufgeführt
Aktion
C
Java
Dateireferenz
FILE-Pointer (FILE*) Objekt einer geeigneten Klasse
Öffnen einer Datei
fopen(...)
Beim Erzeugen des für den Dateizugriff verwendeten
Objekts durch den jeweiligen Konstruktor
Schließen einer Datei
fclose(...)
Methode close()
(existiert für jede für den Dateizugriff eingesetzte Klasse)
byteweises Schreiben
in Datei
fputc(...)
oder putc(...)
Klasse FileOutputStream
oder Klasse RandomAccessFile
Methode write(...)
byteweises Lesen
aus Datei
fgetc(...)
oder getc(...)
Klasse FileInputStream
oder Klasse RandomAccessFile
Methode read()
formatiertes Schreiben
in Textdatei
fprintf(...)
Klasse Formatter
Methode format(...)
formatiertes Lesen
aus Textdatei
fscanf(...)
Klasse Scanner
zahlreiche Methoden zum Lesen des Werts eines
bestimmten Datentyps
z.B. nextDouble()
stringweises Schreiben
in Textdatei (ein Byte pro Zeichen)
fputs(...)
Klasse RandomAccessFile
Methode writeBytes(...)
Schreiben eines Zeilenendes
in Textdatei
fputc('\n')
writeBytes(String.format("%n"))
zeilenweises Lesen
aus Textdatei
fgets(...)
Klasse RandomAccessFile
Methode readLine()
oder :
Klasse Scanner
Methode nextLine()
Schreiben von Datenwerten
in Binärdatei
(maschineninterne Darstellung)
fwrite(...)
Klasse RandomAccessFile
datentypspezifische Schreib-Methoden
z.B. writeDouble(...)
Lesen von Datenwerten
aus Binärdatei
(maschineninterne Darstellung)
fread(...)
Klasse RandomAccessFile
datentypspezifische Lese-Methoden
z.B. readDouble()
Verändern der aktuellen
Bearbeitungsposition
fseek(...)
Klasse RandomAccessFile
Methode seek(...)
Ermitteln der aktuellen
Bearbeitungsposition
ftell(...)
Klasse RandomAccessFile
Methode getFilePointer()
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/00/0 – TH – 04
-------------------------------------------------------------------------------------------------------------------------------------------------------
Programmieren (4. Sem.)
Kapitelüberblick
14. Vererbung und Polymorphie
14.1. Umfassende Klassendefinition
14.2. Klassenableitung
14.3. Polymorphie
14.4. Interfaces und ihre Implementierung
14.5. Programmbeispiel : Simpson-Integration
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/01/1 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Umfassende Klassendefinition (1)
• Wiederholung
◇ Klassen sind die grundlegenden Programmiereinheiten in Java.
Jedes Java-Programm besteht aus einer oder mehreren Klassen.
Es existiert kein Code ausserhalb einer Klasse.
◇ I.a. ist jede Klasse in einer eigenen Quelldatei definiert (Ausnahme : eingebettete Klassen).
Diese Klassen-Quelldateien bilden die Übersetzungseinheiten (Übersetzungs-Module).
Pro Klasse (auch für jede eingebettete Klasse) wird vom Compiler eine eigene Byte-Code-Datei erzeugt.
◇ Klassen definieren den Aufbau und die Funktionalität (das Verhalten) von Objekten.
Objekte sind Instanzen von Klassen.
Sie bestehen aus Datenkomponenten (Felder, fields), die ihren Zustand beschreiben und besitzen
Funktionen (Memberfunktionen, Methoden, methods), die mit diesen Datenkomponenten arbeiten und ihr
Verhalten festlegen.
Die Datenkomponenten und die (Member-)Funktionen werden in einer Klassendefinition festgelegt und als
Klassenkomponenten bezeichnet.
◇ Neben den instanzspezifischen Datenkomponenten (Instanz-Variable) und Memberfunktionen (InstanzMethoden) kann eine Klasse auch klassenspezifische Datenkomponenten (Klassen-Variable) und Memberfunktionen (Klassen-Methoden) besitzen. Derartige statische Klassenkomponenten beschreiben den
Zustand und das Verhalten der Klasse.
• Klassenkomponenten
◇ Klassenkomponenten (class members) können sein :
▻ Datenkomponenten (Membervariable, Felder, fields)
▻ Funktionskomponenten (Memberfunktionen, Methoden, methods)
▻ Eingebettete Klassen und Interfaces (nested classes and interfaces)
(hier nicht weiter betrachtet)
◇ Zusätzlich kann eine Klassendefinition enthalten :
▻ Konstruktoren (Sie werden in Java nicht zu den Memberfunktionen gerechnet)
▻ Initialisierungsblöcke (Code zur Initialisierung von Datenkomponenten)
(hier nicht weiter betrachtet)
• Syntax der Klassendefinition (vollständig) :
class
Klassen-Name
KlassenModifizierer
Typ-ParamDeklaration
{
KlassenAbleitung
Komponentendefinition
Konstruktordefinition
Initialisierungsblock
;
InterfaceImplementierung
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/01/2 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Umfassende Klassendefinition (2)
• Klassen-Modifizierer
◇ Sie legen bestimmte Eigenschaften der Klasse fest
◇ Die wichtigsten Modifizierer sind :
▻ public
Die Klasse ist öffentlich zugänglich – sie kann überall verwendet werden.
Ohne diesen Modifizierer ist sie nur innerhalb des Packages, in dem sie enthalten ist, verwendbar.
▻ abstract
Die Klasse ist unvollständig definiert oder sie wird als unvollständig definiert betrachtet
abstrakte Klasse.
Eine abstrakte Klasse ist nicht instanzierbar.
I.a. besitzt sie eine oder mehrere abstrakte Methoden.
Eine abstrakte Methode besitzt keine vollständige Definition. Häufig ist sie lediglich deklariert.
Ihre – vollständige – Definition bleibt einer abgeleiteten Klasse überlassen.
▻ final
Von der Klasse können keine weiteren Klassen abgeleitet werden.
Damit können ihre Methoden niemals überschrieben werden.
◇ Die gleichzeitige Verwendung der Modifizierer public und abstract bzw public und final
ist zulässig.
Die Modifizierer abstract und final schließen sich gegenseitig aus.
• Typ-Param-Deklaration
Deklaration von formalen Typ-Parametern (Typ-Variable).
Falls vorhanden, wird dadurch eine generische Klasse definiert.
Generische Klassen werden hier nicht weiter betrachtet.
• Klassen-Ableitung
Festlegung der Basisklasse, falls es sich um eine abgeleitete Klasse handelt.
Eine direkte Ableitung von der Klasse Object wird nicht explizit angegeben.
• Interface-Implementierung
Festlegung der von der Klasse direkt implementierten Interfaces
• Eingebettete Klassen und Interfaces
Als Klassenkomponenten lassen sich auch Klassen und Interfaces definieren.
eingebettete Typen.
Dies wird gegebenenfalls angewendet, um den logischen Zusammenhang eng zusammenarbeitender
Klassen auszudrücken.
Es gibt verschiedene Arten eingebetteter Typen
Sie werden hier nicht weiter betrachtet
• Initialisierungsblöcke
Hierbei handelt es sich um Codeblöcke, die ausserhalb jeder Funktion definiert sind.
Dieser Code dient – als Alternative und/oder Ergänzung zu Konstruktoren und Feld-Initialisierern – zur
Initialisierung von Datenkomponenten. Damit lassen sich – insbesonder auch für statische Datenkomponenten –
komplexere Initialisierungsalgorithmen formulieren.
Auf Initialisierungsblöcke wird hier nicht weiter eingegangen.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/01/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Umfassende Klassendefinition (3)
• Ergänzungen zur Definition von Memberfunktionen :
◇ Methoden-Modifizierer :
Neben den schon bekannten Modifizierern public, private, protected, static und
final existieren noch weitere Methoden-Modifizierer. U.a. sind dies :
▻ abstract
Es handelt sich um eine abstrakte Methode.
Für eine derartige Methode wird nur die Signatur (Name und Parameter), der Rückgabetyp und gegebenenfalls die throws-Klausel nicht jedoch die Implementierung festgelegt. (Methode wird nur deklariert)
Der Methoden-Rumpf wird durch ein ; ersetzt.
Abstrakte Methoden sind nur in abstrakten Klassen zulässig.
Jede von einer abstrakten Klasse abgeleitete Klasse, die selbst nicht wieder abstrakt ist, muß eine
Definition aller abstrakten Methoden bereitstellen.
▻ native
Dieser Modifizierer erlaubt das Einbinden von Methoden, die in nativen Machinencode vorliegen.
Üblicherweise werden derartige Methoden in einer anderen Programmiersprache (z.B. C, C++,
FORTRAN, Assembler) formuliert und dann in Maschinencode übersetzt.
Eine native-Methode wird in der Klassendefinition nur deklariert (statt Methoden-Rumpf nur ; ).
◇ Überladene Funktionen :
In einer Klasse können zwei oder mehr Funktionen mit gleichem Namen, aber unterschiedlicher Parameterliste ( unerschiedliche Signatur) definiert werden.
Derartige Funktionen werden als überladen bezeichnet.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/1 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (1)
• Prinzip und Eigenschaften der Klassenableitung
◇ Eine Klasse kann durch Vererbung erweitert (extended, subclassed) werden
abgeleiteten Klasse.
Definition einer
▻ Die abgeleitete Klasse (subclass, extended class) erbt alle Datenkomponenten und Memberfunktionen
ihrer Basisklasse (superclass), d.h. diese Komponenten stehen auch in der abgeleiteten Klasse zur
Verfügung.
▻ Sie kann neue Datenkomponenten und Methoden hinzufügen. Ergänzung / Änderung der
Eigenschaften und/oder des Verhaltens der Klasse bzw ihrer instanzierter Objekte.
◇ Von einer als final vereinbarten Klasse können keine weiteren Klassen abgeleitet werden.
◇ In Java hat jede Klasse, die nicht explizit von einer anderen Klasse abgeleitet ist, die Klasse Object als
direkte Basisklasse.
◇ Da in Java nur einfache Vererbung möglich ist, besitzt jede Klasse – außer der Klasse Object – genau
eine Basisklasse.
◇ In jedem Objekt einer abgeleiteten Klasse ist ein Teilobjekt seiner Basisklasse enthalten.
◇ Abgeleitete Klassen sind zuweisungs-kompatibel zu ihren Basisklassen.
Eine Basisklassen-Referenzvariable kann auch auf Objekte abgeleiteter Klassen zeigen.
Über diese Referenz können aber nur die Komponenten der Basisklasse (d.h. des jeweiligen BasisklassenTeilobjekts) angesprochen werden, nicht jedoch die in der abgeleiteten Klasse zusätzlich definierten
Komponenten. (Ausnahme : Zugriff zu überschreibenden Methoden, s. Polymorphie)
◇ In jedem Objekt einer abgeleiteten Klasse steht mit dem Schlüsselwort super eine Referenz auf das in
ihm enthaltene Basisklassen-Teilobjekt zur Verfügung.
Mittels der super-Referenz können nur Komponenten der Basisklasse angesprochen sowie BasisklassenKonstruktoren aufgerufen werden. Sie kann nicht – wie this – allein benutzt werden, um das Basisklassen-Teilobjekt insgesamt anzusprechen.
• Syntax zur Angabe der Basisklasse in der Klassendefinition (extends-Deklaration)
Klassen-Ableitung :
extends
Basisklassenname
• Konstruktoren von abgeleiteten Klassen
◇ Ein Konstruktor kann nur die Datenkomponenten der eigenen Klasse initialisieren.
Die Initialisierung der geerbten Datenkomponenten der Basisklasse muß durch einen Konstruktor der
Basisklasse erfolgen.
◇ Jeder Aufruf eines Konstruktors muß daher als erstes den Aufruf eines Konstruktors der Basisklasse
bewirken. Dies kann u.a. erfolgen durch :
▻ den expliziten Aufruf eines Basisklassen-Konstruktors mittels super(...).
Dieser Aufruf muß die erste Anweisung im Konstruktor der abgeleiteten Klasse sein
▻ den impliziten Aufruf des no-arg-Konstruktors der Basisklasse, wenn die erste Anweisung im
Konstruktor der abgeleiteten Klasse weder super(...) noch this(...) ist.
◇ Anmerkung : In den Parameter-Ausdrücken eines expliziten Konstruktorsaufrufs (mittels
super(...)) darf keine Komponente des aktuellen Objekts referiert werden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (2)
• Demonstrationsbeispiel zur Vererbung
◇ Definition einer Basisklasse Punkt
// Punkt.java
public class Punkt
{
private double x;
private double y;
public Punkt()
{ this(0.0, 0.0); }
// no-arg-Konstruktor
// nur zur Demo, ist nicht notwendig
public Punkt(double wx, double wy)
{ x = wx; y = wy; }
// Konstruktor
public Punkt move(double dx, double dy)
{ x += dx; y += dy;
return this;
}
// Die this-Referenz referiert das aktuelle Objekt
// innerhalb einer Memberfunktion
public void printPunkt()
{ System.out.print(this); }
public String toString()
{ return "(" + x + " , " +y + ")"; }
public void printPos()
{ System.out.print("x/y : " + x + " / " + y); }
}
◇ Definition einer abgeleiteten Klasse FarbPunkt
// Farbpunkt.java
public class Farbpunkt extends Punkt
{
private String col;
public Farbpunkt()
{ col = "black";
}
// no-arg-Konstruktor
// impliziter Aufruf von super()
public Farbpunkt(String wcol)
{ col = wcol;
}
// Konstruktor
// impliziter Aufruf von super()
public Farbpunkt(double wx, double wy, String wcol)
// Konstruktor
{ super(wx, wy);
// expliziter Aufruf von super()
col = wcol;
}
public void printFarbe()
{
System.out.print(" Farbe : " + col);
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/3 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (3)
• Demonstrationsbeispiel zur Vererbung, Forts.
◇ Definition einer Testklasse APunktTest
// APunktTest.java
// Testklasse fuer die Klassen Punkt und Farbpunkt
public class APunktTest
{
public static void main(String[] args)
{
Punkt p1
= new Punkt(4.2, 6.3);
Farbpunkt p2 = new Farbpunkt("rot");
Punkt p3
= new Farbpunkt(2.5, 3.7, "blau");
System.out.print("p1 :
System.out.println("p1
System.out.print("p2 :
System.out.println("p2
System.out.print("p2 :
System.out.println("p3
//System.out.print("p3
System.out.print("p3 :
"); p1.printPunkt(); System.out.println();
: " + p1);
"); p2.printPunkt(); System.out.println();
: " + p2);
"); p2.printPos();p2.printFarbe(); System.out.println();
: " + p3);
: "); p3.printPos(); p3.printFarbe(); System.out.println();
"); p3.printPos(); ((Farbpunkt)p3).printFarbe();
System.out.println();
System.out.println("nach \"move(1,1)\": ");
p1.move(1.0, 1.0);
p2.move(1.0, 1.0);
p3.move(1.0, 1.0);
System.out.println("p1 : " + p1);
System.out.println("p2 : " + p2);
System.out.println("p3 : " + p3);
}
}
◇ Ausgabe des Demonstrations-Programms
p1 :
p1 :
p2 :
p2 :
p2 :
p3 :
p3 :
nach
p1 :
p2 :
p3 :
(4.2 , 6.3)
(4.2 , 6.3)
(0.0 , 0.0)
(0.0 , 0.0)
x/y : 0.0 / 0.0
(2.5 , 3.7)
x/y : 2.5 / 3.7
"move(1,1)":
(5.2 , 7.3)
(1.0 , 1.0)
(3.5 , 4.7)
Farbe : rot
Farbe : blau
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/4 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (4)
• Namensgleichheit bei Komponenten
Die in der abgeleiteten Klasse neu definierten Komponenten können den gleichen Namen wie Komponenten
der Basisklasse tragen. In einem derartigen Fall werden die gleichnamigen Komponenten der Basisklasse
entweder überschrieben oder überdeckt oder überladen.
• Überschreiben von Methoden (Overriding)
◇ Eine in der abgeleiteten Klasse definierte Methode mit gleichem Namen und gleicher Parameterliste
( == gleicher Signatur) sowie gleichem Rückgabetyp wie in der Basisklasse, überschreibt die Methode
der Basisklasse. Hierdurch wird die Basisklassen-Implementierung der Methode für die abgeleitete Klasse
durch eine neue Implementierung ersetzt.
◇ Eine abstrakte Methode der Basisklasse (Deklaration als abstract), wird durch die überschreibende
Methode in der abgeleiteten Klasse überhaupt erst implementiert.
◇ Die Definition einer Methode in der abgeleiteten Klasse, die sich nur im Rückgabetyp von einer Methode
gleichen Namens in der Basisklasse unterscheidet, ist ein Fehler.
Ausnahme : Wenn der Rückgabetyp ein Referenztyp ist, darf die überschreibende Methode eine Referenz
auf eine davon abgeleitete Klasse zurückgeben
◇ Es können nur Methoden überschrieben werden, die weder als static noch als private noch als
final vereinbart sind. Methoden, die überschrieben werden können, werden als virtuell bezeichnet.
In Java ist jede nichtstatische Methode der Basisklasse, die weder private noch final ist,
grundsätzlich virtuell.
◇ Eine in der abgeleiteten Klasse definierte überschreibende Funktion darf die in der Basisklasse festgelegte
Zugriffsberechtigung nicht einschränken.
Eine Erweiterung der Zugriffsberechtigung ist zulässig.
Beispiel : Wenn in der Basisklasse für eine Methode protected festgelegt ist, darf eine überschreibende
Methode in der abgeleiteten Klasse protected oder public sein, jedoch nicht private oder
package.
◇ Bezüglich der Exception-Deklaration (throws-Klausel) gelten folgende Regeln :
▻ Die Exception-Deklarationen von überschreibender und überschriebener Methode können
unterschiedlich sein.
▻ Eine überschreibende Methode darf in ihrer Exception-Deklaration nur Exception-Typen enthalten,
die zu einem in der Exception-Deklaration der überschriebenen Methode festgelegten Typen polymorph
kompatibel sind (d.h. von der gleichen Klasse oder einer von ihr abgeleiteten Klasse sind).
▻ Eine überschreibende Methode muss nicht alle von der überschriebenen Methode deklarierten ExceptionTypen ebenfalls deklarieren.
Auch wenn die überschriebene Methode eine Exception-Deklaration hat, darf diese bei der
überschreibenden Methode fehlen.
▻ Eine überschreibende Methode darf nichtgeprüfte Exceptions werfen, die in der überschriebenen
Methode nicht auftreten können. Diese dürfen aber nicht in die Exception-Deklaration aufgenommen
werden.
◇ Zu der überschriebenen Methode kann – innerhalb der überschreibenden Methode oder in einer anderen
Instanz-Methode der gleichen Klasse – über die Basisklassen-Teilobjekt-Referenz super zugegriffen
werden (nicht jedoch über ihren vollqualifizierten Namen).
super.method(...) ruft immer die Implementierung der Methode method(...) auf, die in der
direkten Basisklasse verwendet wird.
Es kann sich dabei durchaus um eine Methode handeln, die in der Ableitungshierarchie "weiter oben" definiert
und in der direkten Basisklasse selbst nicht neu definiert worden ist.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/5 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (5)
• Überladen von Methoden (Overloading)
◇ Eine in der abgeleiteten Klasse definierte Methode mit gleichem Namen aber anderer Parameterliste,
überlädt die Methode der Basisklasse.
Der Rückgabetyp bleibt unberücksichtigt, er kann gleich oder unterschiedlich sein.
Das gleiche gilt für die Exception-Deklaration (throws-Klausel).
◇ In der abgeleiteten Klasse kann sowohl zu der geerbten Methode der Basisklasse als auch zu der neu
definierten Methode allein über ihren Namen zugegriffen werden.
in Java ist ein Überladen von Methoden auch über Klassengrenzen hinweg möglich.
◇ Auch statische Methoden können im obigen Sinn überladen werden.
◇ Ferner können statische Methoden nicht-statische Methoden und umgekehrt überladen.
• Demonstrationsbeispiel zum Überschreiben und Überladen : siehe nächste Seite
• Überdecken von Klassenkomponenten (Hiding)
◇ Statische Methoden können überdeckt statt überschrieben werden :
Eine in der abgeleiteten Klasse definierte statische Methode mit gleicher Signatur sowie gleichen
Rückgabetyps wie eine statische Methode der Basisklasse, überdeckt die Methode der Basisklasse.
◇ Eine statische Methode darf keine Instanz-Methode überdecken (
Compiler-Fehler)
◇ Überdecken von Datenkomponenten ist zulässig
▻ zwischen Instanz-Variablen (nicht-statischen Datenkomponenten)
▻ zwischen Klassen-Variablen (statischen Datenkomponenten)
▻ gemischt zwischen Instanz- und Klassen-Variablen.
◇ Das Überdecken zwischen Datenkomponenten unterschiedlichen Typs ist zulässig.
◇ Überdeckt werden genaugenommen Namen.
Überdecken eines Namens bedeutet, dass in der abgeleiteten Klasse der Name allein nur in der in dieser
Klasse definierten Bedeutung verwendet werden kann.
Um den Namen in der Bedeutung, die in der Basisklasse definiert ist, zu verwenden, muss
- bei statischen Komponenten der entsprechende vollqualifizierte Name und
- bei nicht-statischen (objektspezifischen) Komponenten der Name zusammen mit einer BasisklassenReferenz (in Instanzmethoden z.B. super.kompo) angegeben werden.
◇ Beim Zugriff zu überdeckten Komponenten wird die tatsächlich ausgewählte Komponente durch die
deklarierte Klasse der verwendeten Referenz (und nicht durch die Klasse des referierten Objekts) festgelegt.
(Festlegung zur Compile-Zeit, statisches Binden, early binding)
◇ Zwischen einer Variablen und einer Funktion gleichen Namens findet kein Überdecken statt.
Beide können gleichzeitig allein mit ihrem Namen verwendet werden.
Das gilt auch, wenn beide in derselben Klasse definiert sind.
◇ Eine als private deklarierte Komponente kann weder überschrieben noch überdeckt werden
(Sie ist ja in der abgeleiteten Klasse überhaupt nicht zugänglich).
Ihr Name kann deshalb in der abgeleiteten Klasse für beliebige Definitionen verwendet werden.
Beispielsweise ist es bei einer Memberfunktion zulässig, in der abgeleiteten Klasse eine Methode mit
gleicher Signatur aber unterschiedlichem Rückgabetyp zudefinieren.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/02/6 – TH – 05
--------------------------------------------------------------------------------------------------------------------------------------------------------
Klassenableitung (6)
• Demonstrationsbeispiel zum Überschreiben und Überladen von Methoden
◇ Definition einer Basisklasse Punkt (ergänzt)
public class Punkt
{
private double x;
private double y;
public Punkt()
{ this(0.0, 0.0); }
// no-arg-Konstruktor
// nur zur Demo, ist nicht notwendig
public Punkt(double wx, double wy)
{ x = wx; y = wy; }
// Konstruktor
public Punkt move(double dx, double dy)
{ x += dx; y += dy;
return this;
}
// Die this-Referenz referiert das aktuelle Objekt
// innerhalb einer Memberfunktion
public void printPunkt()
{ System.out.print(this); }
public String toString()
{ return "(" + x + " , " +y + ")"; }
public void printPos()
{ System.out.print("x/y : " + x + " / " + y); }
}
// ------------------------------ Ergänzung ------------------------------------public Punkt change(Punkt p)
{ x = p.x; y = p.y; return this;}
}
◇ Definition einer abgeleiteten Klasse Farbpunkt (ergänzt)
public class Farbpunkt extends Punkt
{
private String col;
public Farbpunkt()
{ col = "black"; }
// impliziter Aufruf von super()
public Farbpunkt(String wcol)
{ col = wcol; }
// impliziter Aufruf von super()
public Farbpunkt(double wx, double wy, String wcol)
{ super(wx, wy);
// expliziter Aufruf von super()
col = wcol;
}
public void printFarbe()
{ System.out.print(" Farbe : " + col); }
// ------------------------------ Ergänzung ------------------------------------public String toString()
{ return super.toString() + " & " +
}
}
col;
// ueberschreibt Basisklassenmethode
// Aufruf Basisklassenmethode
public Farbpunkt change(String ncol)
{ col = ncol; return this; }
// ueberlaedt Basisklassenmethode
public Farbpunkt change(Farbpunkt fp)
{ change((Punkt)fp); change(fp.col);
return this;
}
// ueberlaedt Basisklassenmethode
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/03/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Polymorphie (1)
• Wesen der Polymorphie
◇ Poymorphie (griech. : Vielgestaltigkeit) bedeutet, dass mit dem gleichen Namen unterschiedliche Dinge
bezeichnet werden können.
D.h. der gleiche Name/Begriff kann unterschiedliche Bedeutungen haben.
• Realisierungsarten der Polymorphie in Java
◇ Eine simple Form der Polymorphie liegt bei – auch innerhalb einer Klasse möglichen – überladenen
Funktionen vor.
Funktionen mit unterschiedlicher Funktionalität werden über den gleichen Namen aufgerufen.
Die Unterscheidung, welche Funktion aufzurufen ist, wird durch die Parameterliste festgelegt.
Dies kann der Compiler vornehmen Compilezeit-Polymorphie, statisches Binden, early binding.
◇ Compilezeit-Poymorphie ist auch bei der Auswahl von überdeckten Komponenten zwischen
Basisklassen und abgeleiteten Klassen gegeben.
◇ Wesentlich interessanter und mächtiger sind die Referenz-Polymorphie und die darauf aufbauende
Laufzeit-Polymorphie durch virtuelle Methoden.
Beide haben das Konzept der Vererbung (Ableitung) als Voraussetzung.
• Referenz-Polymorphie (polymorphic references)
◇ Abgeleitete Klassen sind zuweisungs-kompatibel zu ihren Basisklassen.
D.h. einer Basisklassen-Referenzvariablen können auch Referenzen auf abgeleitete Objekte zugewiesen
werden.
Eine Referenz-Variable kann somit auf Objekte der eigenen Klasse sowie auf Objekte von ihr
abgeleiteter Klassen zeigen
Über den Namen einer Referenz-Variablen können somit Objekte unterschiedlicher – wenn auch
in Ableitungsbeziehungen zueinander stehender – Klassen angesprochen werden.
◇ Andererseits kann ein Objekt einer abgeleiteten Klasse immer auch als Objekt ihrer Basisklasse betrachtet
werden.
Ein Objekt einer abgeleiteten Klasse kann überall verwendet werden, wo ein Objekt der Basisklasse
benötigt wird.
• Laufzeit-Polymorphie durch virtuelle Methoden
◇ Folgt aus der Referenz-Polymorphie :
Virtuelle Methode == (in Java jede) überschriebene/überschreibende Methode
Virtuelle Methoden bewirken, dass Objekte einer abgeleiteten Klasse und Objekte der jeweiligen Basisklasse
für den gleichen Methodenaufruf unterschiedliches Verhalten zeigen.
◇ Beim Aufruf einer virtuellen Methode über eine Basisklassen-Referenz wird die in der tatsächlichen Klasse
des aktuell referierten Objekts definierte Methode ausgeführt.
Über eine Basisklassenreferenz aufgerufene virtuelle Methoden können also – je nach der tatsächlichen
Klasse des referierten Objekts – zur Abarbeitung unterschiedlicher Funktionen führen.
Da eine Basisklassen-Referenz während der Laufzeit i.a. auf unterschiedliche Objekte zeigen kann, kann
der Compiler einem Methodenaufruf keine eindeutige Funktion zuordnen. Dies kann erst die JVM.
Dynamisches Binden, late binding, Laufzeit-Polymorphie !
◇ Ein Cast einer Referenz auf ein Objekt der abgeleiteten Klasse in die Basisklassen-Referenz ändert nicht den
tatsächlichen Typ des referierten Objekts. Über einen derartigen Cast kann daher nicht zur überschriebenen
sondern nur zur überschreibenden Methode zugegriffen werden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/03/2 – TH – 04
--------------------------------------------------------------------------------------------------------------------------------------------------------
Polymorphie (2)
• Demonstrationsbeispiel zur Polymorphie
◇ Verwendung der Klassen Punkt und Farbpunkt
◇ Definition einer weiteren Test-Klasse ErwPunktTest
// ErwPunktTest.java
// Testklasse fuer die erweiterten Versionen der Klassen Punkt und Farbpunkt
public class ErwPunktTest
{
public static void main(String[] args)
{
Punkt p1
= new Punkt(4.2, 6.3);
Punkt p2
= new Farbpunkt();
Farbpunkt p3 = new Farbpunkt("magenta");
Farbpunkt p4 = new Farbpunkt(2.5, 3.7, "blue");
Punkt pa[]
= {p1, p2, p3, p4};
for (int i=0; i< pa.length; i++)
{ System.out.printf("p%d : ", i+1); pa[i].printPunkt();
System.out.println();
}
System.out.println();
for (int i=0; i< pa.length; i++)
System.out.printf("p%d : %s\n", i+1, pa[i].toString());
System.out.println("\nnach einigen Aenderungen : ");
p4.move(1.0, 1.0);
p4.change("yellow");
p3.change(p4);
p1.change(p4);
p2.change(p1);
//p2.change("red");
// Fehler : p2 ist Punkt-Referenz !!!
((Farbpunkt)p2).change("red");
for (int i=0; i< pa.length; i++)
System.out.printf("p%d : %s\n", i+1, pa[i]);
System.out.println("\nCast der Farbpunkt-Ref. p4 in Punkt-Ref. :");
System.out.println("p4 : " + ((Punkt)p4).toString());
}
}
◇ Ausgabe des Demonstrations-Programms
p1
p2
p3
p4
:
:
:
:
(4.2
(0.0
(0.0
(2.5
,
,
,
,
6.3)
0.0) & black
0.0) & magenta
3.7) & blue
p1
p2
p3
p4
:
:
:
:
(4.2
(0.0
(0.0
(2.5
,
,
,
,
6.3)
0.0) & black
0.0) & magenta
3.7) & blue
nach
p1 :
p2 :
p3 :
p4 :
einigen Aenderungen :
(3.5 , 4.7)
(3.5 , 4.7) & red
(3.5 , 4.7) & yellow
(3.5 , 4.7) & yellow
Cast der Farbpunkt-Ref. p4 in Punkt-Ref. :
p4 : (3.5 , 4.7) & yellow
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/03/3 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Polymorphie (3)
• Typkompatibilität und -umwandlung
◇ Einer Basisklassen-Variablen kann jederzeit eine Referenz auf ein Objekt einer von der Basisklasse
abgeleiteten Klasse zugewiesen werden.
Das durch eine Basisklassen-Variable referierte Objekt einer abgeleiteten Klasse wird dadurch als Objekt
der Basisklasse behandelbar.
Implizite Typumwandlung.
Da von einem spezialisierteren Typ in einen allgemeineren Typ umgewandelt wird, spricht man auch von
einer erweiternden Typumwandlung (widening conversion). Weil diese Umwandlung in der KlassenHierarchie aufwärts erfolgt nennt man sie auch Aufwärts-Cast (upcast).
◇ Die Zuweisung einer Basisklassen-Referenz, die aber tatsächlich auf ein Objekt einer abgeleiteten Klasse
zeigt, an eine Variable des tatsächlich referierten Typs ist implizit nicht möglich.
Sie erfordert eine explizite Typumwandlung mittels des Cast-Operators. Hierbei findet eine Umwandlung
von einem allgemeineren Typ in einen spezialisierteren Typ statt. Man nennt dies eine einengende Typumwandlung (narrowing conversion) oder einen Abwärts-Cast (down cast).
◇ Ein Abwärts-Cast ist prinzipiell unsicher.
Er darf nur dann durchgeführt werden, wenn durch die Basisklassen-Referenz tatsächlich ein Objekt des
angegebenen Zieltyps referiert wird.
Der Versuch einer Typumwandlung, bei der dies nicht erfüllt ist, führt zum Werfen einer Exception vom Typ
ClassCastException.
• Der Operator instanceof
◇ Dieser binäre Operator ermöglicht die Überprüfung, ob ein Ausdruck zuweisungskompatibel zu einem
bestimmten Typ ist.
◇ Syntax :
Ausdruck
instanceof
Typangabe
◇ Wert eines instanceof-Ausdrucks :
▻ true, wenn der Ausdruck der linken Seite zuweisungs-kompatibel zum Typ der rechten Seite ist
▻ false, wenn der Ausdruck der linken Seite nicht zuweisungs-kompatibel zum Typ der rechten Seite ist
◇ Der instanceof-Operator kann eingesetzt werden, um einen sicheren Abwärts-Cast zu ermöglichen.
◇ Beispiel :
class That
{ // ...
}
class More extends That
{ // ...
}
{ // ...
That sref = new More();
More mref;
// ...
if (sref instanceof More)
mref=(More)sref;
// ...
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (1)
• Prinzip des Interfaces
◇ Interfaces definieren – ähnlich wie Klassen – Typen.
Allerdings erfolgt die Definition in einer abstrakten Form, die keinerlei Implementierung bereitstellt.
Damit lassen sich Interfaces nicht instanziieren.
Sie legen – wie der Name bereits ausdrückt – lediglich eine Schnittstelle (Kontrakt) fest, die dann von
Klassen implementiert werden kann.
◇ Ein Interface besteht – ähnlich wie eine Klasse – aus Komponenten.
Diese Komponenten können aber lediglich sein :
▻ abstrakte Methoden
▻ Konstante
▻ eingebettete Klassen und Interfaces (hier nicht betrachtet)
◇ Im Prinzip ähnelt ein Interface einer abstrakten Klasse.
Im Unterschied zu einem Interface kann eine abstrakte Klasse aber eine (Teil-) Implementierung enthalten.
(Instanz- und Klassen-Variable, Klassen-Methoden, definierte Instanz-Methoden).
Ein Interface kann als abstrakte Klasse, die nur Konstante und abstrakte Methoden (und gegebenenfalls
eingebettete Typen) vereinbart, aufgefasst werden ("rein abstrakte" Klasse).
◇ Interfaces müssen wie Klassen in jeweils einer eigenen Quelldatei (Ausnahme eingebettete Interfaces)
definiert werden, deren Hauptname gleich dem Interface-Namen sein muß.
Interfaces werden vom Compiler auch getrennt übersetzt.
• Interface-Definition
◇ Syntax :
Interface-Name
interface
InterfaceModifizierer
Typ-ParamDeklaration
{
Konstantendefinition
InterfaceAbleitung
}
Abstrakte-Methoden-Deklaration
Klassen-/Interface-Definition
◇ Beispiel :
public interface Fahrbar
{
int MOTOR_UND_REIFEN = 3;
//
int MOTOR_UND_SCHRAUBE = 5;
//
int MUSKEL_UND_REIFEN = 2;
int MUSKEL_UND_SCHRAUBE = 4;
int LEHRLAUF = 0;
void vorwFahren(double dist);
void rueckwFahren(double dist);
int getAntrieb();
}
;
implizit geltende Modifizierer static und final (Konstante)
sowie public können weggelassen werden
// implizit geltende Modifizierer abstract und public
// können weggelassen werden
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/2 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (2)
• Interface-Modifizierer
◇ Zulässig sind u.a. :
▻ public
Ohne diesen Zugriffs-Spezifizierer besitzt jedes Interface die Zugrifssberechtigung package.
▻ abstract
Jedes Interface ist implizit abstract.
Eine explizite Angabe dieses Modifizierers ist daher überflüssig.
• Definition von Memberkonstanten (Interface-Konstante)
◇ Ein Interface darf als Datenkomponenten nur Konstante enthalten.
◇ Jede Datenkomponente ist daher implizit static und final. Zusätzlich ist sie implizit public.
Eine explizite Angabe dieser Feld-Modifizierer ist zwar zulässig, aber überflüssig.
◇ Jede Datenkomponente muß durch einen Initialisierer initialisiert werden.
blank finals sind nicht zulässig. Der Initialisierer muß ein konstanter Ausdruck sein.
• Deklaration abstrakter Memberfunktionen (Interface-Methoden)
◇ Jede in einer Interface-Definition enthaltene Memberfunktion ist implizit public und abstract.
Ihre Vereinbarung muß an Stelle eines Funktionsrumpfes ein ; enthalten.
Nichtöffentliche Memberfunktionen machen keinen Sinn.
◇ Eine explizite Verwendung der Modifizierer abstract und public ist zwar zulässig, aber überflüssig
◇ Andere Modifizierer sind unzulässig.
• Abgeleitete Interfaces
◇ Interfaces können auch voneinander abgeleitet werden
◇ Es gibt aber kein allgemeines Wurzel-Basis-Interface (etwa wie die Wurzel-Basisklasse Object)
◇ Im Unterschied zu Klassen ist bei Interfaces Mehrfachvererbung zulässig.
Ein Interface kann von mehreren anderen Interfaces abgeleitet sein.
◇ Syntax :
Interface-Ableitung :
extends
Basis-Interface-Name
,
◇ Beispiel :
Bibliotheks-Interfaces
Serializable (Package java.io)
Runnable
(Package java.lang)
public Interface SerializeAndRunnable
extends java.io.Serializable, Runnable
{
// ...
}
Das Interface SerializeAndRunnable enthält – durch Vererbung – alle Komponenten der Interfaces
Serializable und Runnable. Zusätzlich kann es weitere Komponenten definieren.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (3)
• Implementierung von Interfaces
◇ Der Sinn von Interfaces liegt in der Definition von Schnittstellen, die zur Realisierung einer Funktionalität
durch Klassen implementiert werden müssen.
◇ Eine Klasse, die ein Interface implementiert, muß sämtliche Methoden des Interfaces (einschliesslich der
von Basis-Interfaces geerbten) definieren (d.h. für sie eine Implementierung bereitstellen) oder selbst als
abstract deklariert sein. Im letzteren Fall können – müssen aber nicht – die nicht implementierten
Methoden des Interfaces als abstrakt deklariert werden
Prinzipiell entspricht die Implementierung eines Interfaces damit der Ableitung von einer (rein) abstrakten
Klasse.
◇ Eine Klasse kann – gegebenenfalls zusätzlich zur Ableitung von einer Klasse – mehrere Interfaces
implementieren. Realisierung einer Art eingeschränkter Mehrfachableitung in Java.
◇ Syntax :
Interface-Implementierung :
implements
Interface-Name
,
Die implements-Klausel muss nach einer – gegebenenfalls vorhandenen – extends-Klausel
aufgeführt werden.
◇ Beispiel :
public class Auto implements Fahrbar
{
private double kmZaehler;
private double kraftstoff;
private double verbrauch;
public Auto(double verbr, double km)
{ kmZaehler = km;
verbrauch = verbr;
kraftstoff = 0.0;
}
public int getAntrieb()
{
return MOTOR_UND_REIFEN;
}
public void vorwFahren(double dist)
{
// Funktionsrumpf
}
// Definition der im Interface
// deklarierten Methoden
public void rueckwFahren(double dist)
{
// Funktionsrumpf
}
// weitere Memberfunktionen
}
◇ Die Implementierung eines Interfaces wird auch an abgeleitete Klassen vererbt.
In einem derartigen Fall ist die Angabe einer implements-Klausel bei der abgeleiteten Klasse nicht
explizit notwendig, aber zulässig.
Dem Kopf einer Klassendefinition können nicht in jedem Fall alle implementierten Interfaces entnommen
werden.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/4 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (4)
• Demonstationsbeispiel zur Implementierung eines Interfaces
▻ Definition der das Interface Fahrbar implementierenden Klasse Auto
// Auto.java
// Definition der Klasse Auto
// Klasse implementiert das Interface Fahrbar
import java.util.*;
public class Auto implements Fahrbar
{
public static String TANKLEER = "Tanken notwendig !";
private
private
private
private
final double verbrauch;
double kmZaehler;
double kraftstoff;
double restkm;
// liter/(100 km)
public Auto(double verbr, double km)
{ kmZaehler = km;
verbrauch = verbr;
kraftstoff = 0.0;
restkm = 0.0;
}
public int getAntrieb()
{ return MOTOR_UND_REIFEN;
}
public void vorwFahren(double dist)
{ double maxEntf = kraftstoff/verbrauch*100;
if (dist>maxEntf)
{ restkm = dist-maxEntf;
dist=maxEntf;
}
else
restkm = 0.0;
kmZaehler+=dist;
kraftstoff-=dist/100*verbrauch;
if (dist==maxEntf)
throw new RuntimeException(TANKLEER);
}
public void rueckwFahren(double dist)
{
}
public void tanken(double lit)
{ kraftstoff += lit;
}
public double getKmStand()
{ return kmZaehler;
}
public double getKraftstoff()
{ return kraftstoff;
}
public double getRestKm()
{ return restkm;
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/5 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (5)
• Demonstationsbeispiel zur Implementierung eines Interfaces, Forts.
▻ Definition einer Start- und Testklasse AutoTest
// AutoTest.java
// Klasse AutoTest zum Testen der Klasse Auto
import java.util.Scanner;
public class AutoTest
{
public static void main(String[] args)
{ Locale.setDefault(Locale.ROOT);
Auto myCar = new Auto(6.0, 500);
Scanner stdinp = new Scanner(System.in);
double entf;
double liter;
double altkm = myCar.getKmStand();
double neukm;
boolean mussTanken = false;
boolean endeFahrt = false;
do
{ try
{ altkm = myCar.getKmStand();
entf = myCar.getRestKm();
if (entf == 0.0)
{ System.out.print("Fahrstrecke ? ");
entf=stdinp.nextDouble();
}
if (entf==0.0)
endeFahrt = true;
else
myCar.vorwFahren(entf);
}
catch (RuntimeException ex)
{ if (ex.getMessage().equals(Auto.TANKLEER));
{
mussTanken = true;
}
}
finally
{ neukm = myCar.getKmStand();
if (neukm > altkm)
{ System.out.println("gefahrene km : " + (neukm-altkm));
System.out.println("km-Stand : " + neukm);
altkm = neukm;
}
if (mussTanken)
{ System.out.print("Tanken notwendig, wieviel Liter ? ");
liter = stdinp.nextDouble();
myCar.tanken(liter);
if (myCar.getKraftstoff() > 0)
mussTanken = false;
else
endeFahrt = true;
}
}
} while (!endeFahrt);
System.out.println("Ende der Fahrt !!!");
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/6 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (6)
• Demonstationsbeispiel zur Implementierung eines Interfaces, Forts. (2)
▻ Beispielhafte Programmdialoge
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/04/7 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Interfaces und ihre Implementierung (6)
• Interfaces als Objekt-Referenzen
◇ Objekte einer Klasse, die ein Interface implementiert, lassen sich auch als Instanzen des implementierten
Interfaces behandeln.
Explizite oder implizite Typkonvertierung (cast) eines Objekts in den Typ eines implementierten
Interfaces.
◇ Dies ermöglicht es auch, mit dem instanceof-Operator zu überprüfen, ob die Klasse eines Objekts
ein bestimmtes Interface implementiert.
Beispiel :
Auto car = new Auto(7.0, 10000);
boolean isfb = car instanceof Fahrbar;
//
true
◇ Man kann Variable eines Interface-Typs definieren und ihnen Referenzen auf Objekte einer implementierenden Klasse zuweisen.
Natürlich lassen sich über eine Interface-Variable auch nur die Komponenten eines Objekts ansprechen, die in
dem implementierten Interface definiert sind.
Beispiel :
// ...
Fahrbar fb = new Auto(10, 150000);
// ...
fb.getAntrieb();
// ok
fb.tanken(50);
// unzulässig !
// ...
◇ Jede Objekt-Referenz eines Interface-Typs lässt sich allerdings als Referenz auf die Klasse Object
verwenden. Damit lassen sich über eine Interface-Variable auch die in der Klasse Object definierten
Methoden aufrufen.
Dies ist deswegen möglich, da ja das referierte Objekt von irgendeiner Klasse sein muss und jede Klasse von
Object abgeleitet ist.
Beispiel :
// ...
Fahrbar fb = new Auto(5, 1500);
// ...
String str = fb.toString(); // keine Methode von Auto,
// ...
// aber von Object
• Marker Interfaces
◇ Es kann auch sinnvoll sein, ein Interface leer, d.h. ohne jegliche Komponenten, zu definieren.
◇ Ein derartige Interface dient dazu, anzuzeigen, dass eine implementierende Klasse über eine bestimmte
Eigenschaft verfügt. Es markiert die Klasse hinsichtlich des Besitzes dieser Eigenschaft Marker Interface
◇ In der Java-Standardbibliothek sind mehrere derartige Marker Interfaces enthalten.
Beispiel :
▻ Cloneable (Package java.lang)
Es legt fest, dass Objekte einer implementierenden Klasse mit der clone()-Methode geclont werden
können.
Die clone()-Methode ist selbst nicht Komponente des Interfaces, sondern der Klasse Object.
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/05/1 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Simpson-Integration(1)
● Aufgabenstellung
◇ Im C-Teil der Vorlesung ist als Beispiel für die Verwendung von Funktions-Pointern ein Programm zur
Anwendung der Simpson-Integration auf verschiedene zu integrierende Funktionen y=f(x) vorgestellt
worden.
In dem Beispiel wird eine allgemeine Simpson-Integrations-Funktion formuliert, der die jeweils zu
integrierende Funktion mittels eines Funktions-Pointers als Parameter übergeben werden muss.
◇ Dieses Beispiel soll nun sinnvoll nach Java portiert werden.
Die Implementierung soll dabei so erfolgen, dass auch eine einfache Erweiterung auf andere numerische
Integrationsverfahren ermöglicht wird.
● Lösung :
◇ Definition einer abstrakten Basisklasse Integrator.
Sie ist Basisklasse für konkrete Klassen, die jeweils ein spezielles numerisches Integrationsverfahren
implementieren. Für die Simpson-Integration soll das die Klasse SimpsonIntegrator sein.
Die Klasse Integrator definiert eine Instanzvariable zur Speicherung der gewünschten Genauigkeit
(die ihr im Konstruktor zu übergeben ist) und deklariert eine abstrakte Methode integrate(...).
Diese ist von den abgeleiteten Klassen zur Realisierung des jeweiligen Integrations-Algorithmus geeignet
zu implementieren.
◇ Jede zu integrierende Funktion wird durch eine Klasse beschrieben, die das Interface FunctionYvonX
implementieren muss ( Funktionsklassen).
Dieses Interface deklariert die von den Funktionsklassen zu definierenden Methode
double funcVal(double x), die die jeweilige zu integrierende Funktion realisiert.
Beispielhaft werden hier die Funktionsklassen Halbkreis, Dreieck und Polynom2 (Polynom 2. Ord.)
definiert.
◇ Jedes Objekt einer Funktionsklasse lässt sich als Instanz des Interfaces FunctionYvonX behandeln.
Als solche wird es (mittels einer entsprechenden Referenz) der Integrator-Funktion integrate(...)
als Parameter übergeben. Innerhalb dieser Funktion ruft das Integrator-Objekt die Methode
funcVal(...) für dieses Objekt auf.
( "Das Integrator-Objekt schickt die Botschaft funcVal an das FunctionYvonX-Objekt")
◇ Als Startklasse dient eine Klasse SimpIntDemo. In ihrer Methode main() werden ein
SimpsonIntegrator-Objekt sowie die verschiedenen Funktionsklassen-Objekte erzeugt.
● Klassendiagramm der Lösung (ohne Startklasse)
Integrator
FunctionYvonX
eps
integrate()
funcVal()
SimpsonIntegrator
Halbkreis
Dreieck
Polynom2
integrate()
funcVal()
funcVal()
funcVal()
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/05/2 – TH – 02
--------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Simpson-Integration(2)
● Definition der abstrakten Klasse Integrator
// Integrator.java
// abstrakte Basisklasse fuer Integrator-Klassen
package integration;
public abstract class Integrator
{
protected double eps;
protected Integrator(double gen)
{ eps = gen;
}
public abstract double integrate(FunctionYvonX func, double x1, double x2);
}
● Definition der Klasse SimpsonIntegrator
// SimpsonIntegrator.java
// Integrator-Klasse, implementiert die Simpson-Integration
package integration;
public class SimpsonIntegrator extends Integrator
{
public SimpsonIntegrator(double gen)
{ super(gen);
}
public double integrate(FunctionYvonX func, double dXu, double dXo)
{ long i;
double
dFl = 0.;
/* letzter Integralwert
*/
double
dFa = 0.;
/* aktueller Integralwert
*/
double
dH;
/* Schrittweite
*/
double
dX;
/* X-Wert
*/
double
dY;
/* Funktionswert
*/
double
dg;
/* Gewicht
*/
double
dErr;
/* Fehler
*/
long n = 2;
/* Intervallanzahl
*/
do /* Schleife ueber jeweils verdoppelte Intervallanzahl */
{ dH = (dXo - dXu)/n;
/* Schrittweite bestimmen
*/
dFa = func.funcVal(dXu) + func.funcVal(dXo);
for (i=1; i < n; i++)
/* Flaeche der akt. Intervallanzahl */
{ dX
= dXu + dH*i;
dY
= func.funcVal(dX);
if (i%2 == 1)
dg = 4.;
else
dg = 2.;
dFa += dg*dY;
}
dFa *= dH/3;
dErr = Math.abs((dFa -dFl)/dFa);
n*=2;
/* Verdoppeln der Intervallanzahl */
dFl = dFa;
} while ( dErr > eps);
return dFa;
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/05/3 – TH – 03
--------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Simpson-Integration(3)
● Definition des Interfaces FunctionYvonX
// FunctionYvonX.java
// Interface fuer Funktions-Klassen, Funktionen der Form y = f(x)
package integration;
public interface FunctionYvonX
{
double funcVal(double x);
}
● Definition der Klasse Halbkreis
// Halbkreis.java
// Funktions-Klasse, die einen Halbkreis implementiert
// Der Radius wird dem Konstruktor uebergeben
package integration;
public class Halbkreis implements FunctionYvonX
{
private double radius;
public Halbkreis(double r)
{ radius = r;
}
public double funcVal(double dX)
{ double dY = 0;
if (dX <= radius && dX >= -radius)
dY = Math.sqrt (radius*radius - dX*dX);
return dY;
}
}
● Definition der Klasse Polynom2
//
//
//
//
Polynom2.java
Funktions-Klasse, die ein Polynom 2.Grades implementiert
y = a*x*x + b*x +c
Die Polynom-Koeffizienten werden dem Konstruktor uebergeben
package integration;
public class Polynom2 implements FunctionYvonX
{
private double a, b, c;
public Polynom2(double ak, double bk, double ck)
{ a = ak;
b = bk;
c = ck;
}
public double funcVal(double dX)
{ return a*dX*dX + b*dX + c;
}
}
HOCHSCHULE MUENCHEN
FAKULTÄT FÜR ELEKTROTECHNIK UND INFORMATIONSTECHNIK
FG TECHNISCHE INFORMATIK
V – PR–JV – 14/05/4 – TH – 01
--------------------------------------------------------------------------------------------------------------------------------------------------------
Programmbeispiel : Simpson-Integration(4)
● Definition der Klasse Dreieck
//
//
//
//
//
Dreieck.java
Funktions-Klasse, die ein Dreieck implementiert
Koordinaten der Eckpunkte : (xl,0), (xr,0), (0,yh), mit xl < 0 und xr > 0
xl, xr und yh werden dem Konstruktor uebergeben
Die Flaeche des Dreiecks betraegt (xr-xl)*yh/2
package integration;
public class Dreieck implements FunctionYvonX
{
private double xl;
private double xr;
private double h;
public Dreieck(double xlw, double xrw, double yhw)
{ xl=xlw;
xr=xrw;
h=yhw;
}
public double funcVal(double dX)
{ double dRet;
if (dX > xr || dX < xl)
dRet = 0.;
else if(dX >= 0) dRet = (h-h/xr*dX);
else dRet = (h-h/xl*dX);
return dRet;
}
}
● Definition der Start-Klasse SimpIntDemo
// SimpIntDemo.java
// Startklasse fuer ein Demo-Programm zur Simpson-Integration
package integration;
import java.io.*;
import java.util.*;
public class SimpIntDemo
{
public static void main(String[] args)
{ double dXo = 1.;
double dXu = -1.;
double dF;
FunctionYvonX funcArr[] ={ new Halbkreis(1.0),
new Dreieck(-1, 1, 1),
int i;
boolean bBed;
Scanner scan = new Scanner(System.in);
Integrator sig = new SimpsonIntegrator(1.e-8);
new Polynom2(1, -6, 1),
new Dreieck(-0.5, 1, 3) };
do
{ System.out.printf(" Auswahl: ");
i=scan.nextInt();
bBed = i>0 && i <= funcArr.length;
/* Abbruchbedingung formulieren */
if(bBed)
{ dF = sig.integrate(funcArr[i-1],dXu,dXo);
System.out.printf (" Xu: %8.4f, Xo: %8.4f, F: %12.8f \n", dXu,dXo,dF);
}
} while(bBed);
}
}
Herunterladen