Software Engineering Inhaltsverzeichnis - FSMB

Werbung
SE
Software Engineering
Inhaltsverzeichnis
1. Vom Problem zum Programm.............................................................................................4
1.1. Einführung Software Engineering................................................................................4
1.2. Phasen der Software Entwicklung...............................................................................6
1.2.1. Analysephase........................................................................................................7
1.2.2. Designphase.........................................................................................................7
1.2.3. Implementierungsphase........................................................................................9
2. Die Programmiersprache C...............................................................................................10
2.1. Globale Deklarationen und Funktionen......................................................................10
2.2. Ablauf der Programmerstellung..................................................................................11
2.3. „Hello World“...............................................................................................................12
3. Elementare Datentypen – Initialisierung, Deklaration und Verknüpfung...........................13
3.1. Variablen....................................................................................................................13
3.2. Identifier (Bezeichner)................................................................................................13
3.3. Syntax ........................................................................................................................13
3.4. Die 5 atomaren Typen................................................................................................14
3.4.1. Modifizierung der atomaren Typen.....................................................................14
3.5. enum und typedef.......................................................................................................15
3.6. Variablen Definition, Zuweisung und Verknüpfung....................................................16
3.7. char in ASCII = int .....................................................................................................17
3.8. Ausführen eines Programms .....................................................................................19
3.9. Die Ausgabefunktion printf.........................................................................................19
3.9.1. Format Elemente.................................................................................................20
3.10. Die Einlesefunktion scanf.........................................................................................21
3.11. die Darstellung von Umlauten..................................................................................21
3.12. Die Modulo-Operation..............................................................................................21
4. Operatoren, Ausdrücke & Funktionsaufrufe......................................................................22
4.1. Operanden & Operatoren...........................................................................................22
4.1.1. Klassifikation der Operatoren nach Anzahl der Operanden...............................22
4.1.2. Inhaltliche Klassifikation von Operatoren............................................................22
4.2. Ausdrücke...................................................................................................................26
4.2.1. Prioritäten von Ausdrücken.................................................................................27
4.2.2. Typen von Ausdrücken.......................................................................................27
4.3. math.h.........................................................................................................................29
4.3.1. Trigonometrische und hyperbolische Funktionen...............................................29
4.3.2. Exp, Log und andere...........................................................................................30
5. Kontrollstrukturen in C.......................................................................................................30
5.1. Sequenz ....................................................................................................................31
5.1.1.Deklarations-Liste: Variablen und Konstanten.....................................................31
5.1.2. Verschachtelte Sequenzen.................................................................................32
5.2. Bedingte Verzweigung – if-Anweisung.......................................................................32
5.2.1. logische Operatoren............................................................................................33
5.2.2. Verschachtelte If-Anweisung..............................................................................33
5.3. Bedingte Verzweigung – Mehrfachauswahl mit switch..............................................34
5.4. Bedingte Iteration.......................................................................................................34
5.4.1. Prüfen am Anfang – while...................................................................................34
5.4.2. Prüfen am Ende – do ... while.............................................................................36
1
SE
5.5. gesteuerte Iteration – for ...........................................................................................38
5.5.1. geschachtelte Schleifen mit for ..........................................................................39
5.6. Kontrollstrukturen im Nassi-Schneidermann-Diagramm............................................40
6. Arrays, Strings und Pointer................................................................................................41
6.1. Arrays.........................................................................................................................41
6.1.1. Grenzüberschreitung...........................................................................................42
6.1.2. Sortieralgorithmus...............................................................................................43
6.1.3. Zählen der Elemente eines Arrays......................................................................44
6.1.4. Vergleichen von Arrays.......................................................................................44
6.2. Strings.........................................................................................................................45
6.2.1. Zeichenzahl eines Arrays auslesen....................................................................45
6.2.2. Beispiel suchen und Ersetzten eines Worts in einem String..............................46
6.2.3. Beispiel Grenzüberschreitung.............................................................................47
6.2.4. Einlesen von Strings durch gets.........................................................................47
6.2.5. Einlesen von Strings durch fgets........................................................................47
6.3. String.h.......................................................................................................................48
6.4. Mehrdimensionale Felder...........................................................................................57
6.4.1. Beispiel Matrix Quadratwerte..............................................................................58
7. Funktionen.........................................................................................................................59
7.1. Definition von Funktionen...............................................................................................59
7.2. Funktionsaufruf/-deklaration/-definiton.......................................................................60
7.3. Lokale Variablen.........................................................................................................61
7.4 Globale Variablen........................................................................................................62
7.5. Statische Variablen ....................................................................................................63
7.6. Datenaustausch zwischen Funktionen.......................................................................64
7.6.1 Funktion mit Wertübergabe – call by value..........................................................64
7.6.2. Funktion mit Wertrückgabe.................................................................................65
7.7. Die Hauptfunktion main()............................................................................................65
7.8. Rekursive Funktionen.................................................................................................66
7.9. Übergabe von Arrays an Funktionen.........................................................................70
7.10. Arrays aus Funktionen zurückgeben........................................................................70
8. Präprozessoranweisungen................................................................................................75
8.1. Einkopieren von Dateien mittels #include..................................................................75
8.2. Markos und Konstanten.............................................................................................76
8.2.1. symbolische Konstanten.....................................................................................76
8.2.2 Makros......................................................................................................................77
8. Zeiger.................................................................................................................................78
8.1. Zeigerarithmetik.....................................................................................................81
8.2. Zeiger auf Arrays........................................................................................................83
8.2.1. Zugriff auf einen Array mittels Zeiger..................................................................83
8.2.2. Funktionsaufrufe von Arraynamen......................................................................85
8.2.3 Gemeinsamkeinten Array und Zeiger..................................................................86
8.3. Zeiger auf Strings.......................................................................................................87
8.3.1. Zeiger auf konstante Objekte - Read-only-Zeiger...............................................88
8.3.4 Zeiger auf Zeiger und Stringtabellen....................................................................89
8.3.5. Zeiger erhöhen....................................................................................................90
8.3.6. Beispiel für Zeiger...............................................................................................91
9. Kommandozeilenargumente..............................................................................................92
9.1. Optionen/Schalter.......................................................................................................94
10. Dynamische Speicherverwaltung....................................................................................96
10.1. Speicherallokation mit malloc()................................................................................96
2
SE
10.2. Speicherbereich wieder freigeben - free()................................................................98
10.3. dynamische Arrays.................................................................................................100
10.4. realloc und calloc....................................................................................................100
11. Strukturen......................................................................................................................105
11.1. Strukturen deklarieren............................................................................................105
11.2. Initialisierung und Zugriff auf Strukturen................................................................106
11.3. Strukturen als Werteübergabe an Funktionen.......................................................108
11.4. Strukturen als Rückgabewerte einer Funktion ......................................................109
11.5. Strukturen vergleichen...........................................................................................110
11.6. Arrays von Strukturen.............................................................................................111
11.7. Verschachtelte Strukturen......................................................................................115
11.8. Aufzählungstyp enum.............................................................................................124
11.9. Typendefinition mit typdef......................................................................................125
12. Dynamische Datenstrukturen........................................................................................128
12.1. Lineare Listen – einfache verkettete Listen...........................................................128
12.1.1. Erstes Element in der Liste löschen...............................................................132
12.1.2. beliebiges Element der Liste löschen.............................................................132
12.1.3. Elemente der Liste ausgeben.........................................................................133
12.1.4. Alle Elemente löschen....................................................................................134
12.1.5. Elemente in die Liste einfügen........................................................................134
13. Dateien...........................................................................................................................137
13.1 Schema für Dateioperationen..................................................................................138
13.2. fopen()....................................................................................................................138
13.2.1. Modus der Dateiöffnung..................................................................................138
14. Headerfiles.....................................................................................................................141
14.1. One-Definition-Rule....................................................................................................141
14.2 Inhalt Header ..........................................................................................................142
14.2. Inhalt Implementierungsdatei.................................................................................142
14.3. Einbinden von Headern..........................................................................................143
14.4. Objekt Files.............................................................................................................143
15. Objektorientierung.........................................................................................................144
15.1. Stärken der Strukturierten Programmierung..........................................................144
15.2. Schwächen der Strukturierten Programmierung ...................................................144
15.3. Imperativ / prozedurales Programmierparadigma..................................................144
15.4. objektorientiertes Programmierparadigma.............................................................145
15.3. Objekte...................................................................................................................145
15.4. Information Hidding................................................................................................145
15.5. Zugriffsmechanismen.............................................................................................145
15.6. Klassen...................................................................................................................146
15.7. Beziehung zwischen Klassen.................................................................................148
15.7.1. Vererbung.......................................................................................................148
15.7.2. Aggregation.....................................................................................................149
15.2.3. Assoziation......................................................................................................150
15.2.4. Unified Modeling Language - UML.................................................................150
3
SE
1. Vom Problem zum Programm
1.1. Einführung Software Engineering
●Verwendung im Maschinenbau
In Produkten – Steuerung von Sensoren und Aktoren
in Produktionsanlagen – CNC-Steuerung
in der Entwicklung – CAD, Officeanwendungen
●Fakten
Fünf von sechs Software-Projekten sind erfolgreich
ein drittel wird abgebrochen
restlichen oft finanziell und zeitlich stark verkalkuliert
Softwarekriese ist ein immer aktuelles Thema
●Fehler
Rate 1977: 7 – 20 Fehler pro 1000 LOC
Rate 1994: 0,05 – 0,2 Fehler pro 1000 LOC
Steigerung um den Faktor 100 in 13 Jahren
Komplexitätssteigerung Faktor 10 in 5 Jahren
0,1 Fehler bedeuten
•300 versagende Herzschrittmacher pro Jahr
•18 Flugzeugabstürze am Tag
•22.000 falsch gebuchte Schecks pro Stunde
●Definition Software Engineering
nach IEEE
•The application of a systematic, disciplined, quantifiable approach to
the development, operation, and maintenance of software; that is, the
application of engineering to software
•The study of approaches as in (1)
4
SE
●Teilgebiete
Software Requirements
Software Design
Software Construction
Software Testing
Software Maintenance
Software Configuration Management
Software Engineering Management
Software Engineering Process
Software Engineering Tolls & Methods
Software Quality
Knowledge Areas of Related Disciplines
5
SE
1.2. Phasen der Software Entwicklung
●Analysephase oder Spezifikation
Abstrahieren des Problems
auf das wesentliche beschränkte, präzise und eindeutige Form
Ergebnis ist die Problemspezifikation (Pflichtenheft)
●Designphase
Überführen des Problems in Rechenverfahren
kann vom Computer in einzelnen Schritten ausgeführt werden
Ergebnis ist ein Algorithmus
●Implementierung
Algorithmus wird in Form eines Programms oder mehrerer
Programmmodule in einer Programmiersprache formuliert
das ist die sogenannte Codierung
●Integration, Test & Inbetriebnahme
Programmmodule werden zusammengeführt, in Betrieb genommen und
auf Erfüllung des Pflichtenhefts geprüft
Ergebnis ist fertiges Softwaresystem (Installiert, Abgenommen durch
Auftraggeber, inklusive Dokumentation)
●tritt ein Fehler in einer der letzten beiden Phasen auf, muss in der linken
Phase auf gleicher Höher der Fehler gesucht werden
6
SE
1.2.1. Analysephase
●Ergebnis ist Problembeschreibung/-spezifikation mit Anforderungen
●vollständig, eindeutig, lösungsneutral und widerspruchsfrei
●Spezifikation in umgangssprachlicher, mathematischer oder formaler Form
Formale Spezifikation beschreibt
•Menge aller Eingabegrößen
•Menge aller Ausgabegrößen mit allen für die Lösung wichtigen
Eigenschaften
•funktionalen Zusammenhang zwischen ihnen
•z.B. mathematisch
Semi-formale Spezifikation
•Anwendungsfälle umgangssprachlich
•deren Verknüpfung mit formalen Mitteln
•z.B. UseCase-Diagramm
Informale Spezifikation
•textuelle Beschreibung
•meist nur für den Autor eindeutig und widerspruchsfrei
•z.B. umgangssprachlicher Text
1.2.2. Designphase
●Spezifikation des Lösungswegs
●Architekturbeschreibung
●Kreativphase
●Algorithmus
in Beschreibung und Ausführung endlich, deterministisch und
effektive Vorschrift zur Lösung eines Problems, die effizient sein soll
•Endlich:
Der Algorithmus endet nach einer endlichen Zeit
•Deterministisch: Eindeutige Beschreibung des nächsten Schritts
•Effektiv:
Eindeutige Ausführbarkeit der Einzelschritte
•Effizient:
Möglichst geringer Ressourcenverbrauch
●es gibt viele unterschiedliche Lösungswege für das gleiche Problem
●sinnvoll vorhandene Programmmodule einzubinden
●bei Komplexen Problemstellungen müssen Programmmodule entwickelt
werden
●allgemeiner Weg, noch nicht an eine Programmiersprache gebunden
7
SE
●Darstellung durch ein Flussdiagramm
●sehr anschaulich
●werden bei komplexeren Problemstellungen schnell unübersichtlich
●viel Querverweise, sogenannte Gotos
●Darstellung durch Struktogramm oder Nassi-ShneidermannDiagramm
●Schleife mit Prüfung vor dem Rumpf
Test der Schleifenbedingung
falsch → überspringen der Schleife
wahr → durchlaufen der Schleife
●Schleife mit Prüfung nach dem Rumpf
Rumpf wird erst durchlaufen
Test der Schleifenbedingung
falsch → Fortsetzung bei nächstem Schritt
wahr → erneutes Durchlaufen der Schleife
●allgemein gilt für Schleifen:
durch den Rumpf muss irgendwann die Bedingung erfüllt werden können
ansonsten kann sich ein Programm aufhängen – Endlosschleife
8
SE
●Beispiel:
1: Gehe zur ersten Person.
2: Erfrage das Alter.
3: Merke dieses Alter.
4: Solange noch nicht alle Personen gefragt wiederhole Schritte 4.1 – 4.3:
•4.1: Gehe zur nächsten Person.
•4.2: Erfrage das Alter.
•4.3: Wenn das Alter kleiner als das gemerkte Alter, dann merke das
neue Alter.
5 Jüngste Person ist ‘gemerktes Alter‘ Jahre alt.
●Alternatives Programm mit zusätzlichen nicht gefragten Funktionen
1.2.3. Implementierungsphase
● Umsetzung in einer Programmiersprache
9
SE
2. Die Programmiersprache C
Objektorientiert
weiterentwickelt zu C++ und C#
UNIX basiert auf C
hohe Portabilität weil auf alle Plattformen verfügbar
wird häufig im technischen Bereich eingesetzt
kleiner,überschaubarer Sprachumfang
ermöglicht kompakte, effiziente Codes
Sehr mächtig
verlangt hohe Disziplin
schon vorhandene Bausteine werden oft eingebunden – Standartelemente
heißen Header, Dateiendung .h
● case sensitive!!!
●
●
●
●
●
●
●
●
●
●
2.1. Globale Deklarationen und Funktionen
● ganz oben sind globale Deklarationen
 #include <stdio.h> - als Präpozessoranweißung zum einbinded des
Standard Ein-/Ausgabe Headers
● besteht aus einer beliebigen Menge an Funktionen
● zwingend ist die Main-Funktion
 ist Hauptfunktion
 kann als Alleinige Funktion vorhanden sein
 wird nach Programmstart als erstes Aufgerufen
 Übergabe von Parametern in runden Klammern
10
SE
2.2. Ablauf der Programmerstellung
● editieren, also tippen, des Programms in einem Editor
● das ist Quelltext oder source, wird mit der Endung .c gespeichert
● geschrieben in höherer Programmiersprache
● übersetzten des Programms durch Compiler in die Prozessorsprache, heißt
auch Maschinensprache
● Compiler überprüft auf Syntaxfehler
● Linker oder Binder bindet Bibliotheken/Header eindeutige
● Laden des Programms vom Loader oder Lader -> Programmstart
● Debugger hilft bei Fehlersuche
11
SE
2.3. „Hello World“
● Einstiegsprogramm
1. #include <stdio.h>
2.
3. int main (void)
4. {
5.
/*Hello World Programm*/
6.
printf („\n Hello World!! \n“);
7.
retung 0;
8. }
1. Anweisung an Präprozessor Standartbibliothek stdio.h einzufügen
 Standard Ein-/Ausgabefunktion
 Kopfzeile heißt „Globaler Deklarationsblock“
 Quellcode wird vor dem Compilieren vom Präprozessor bearbeitet
3. main ist Hauptfunktion - Beginn eigentliches C-Programm
 wird immer sofort nach Programmstart aufgerufen
4. { Beginnn des ersten Blocks Blockklammer
5. Kommentarblock mit /* */ - Kommentar für eine Zeile //
6. printf ist Ausgabefunktion aus stdio.h
 Text in ()
 \n neue Zeile
 „ zeigen, dass es sich um reinen Text handelt und nicht um
Programmanweisungen
 Zeile Endet mit ;
7. return 0 gibt OS den Integer-Wert 0 zurück -> OS weiß, dass Programm
wurde erfolgreich beendet
8. } Ende des ersten Blocks
12
SE
3. Elementare Datentypen – Initialisierung,
Deklaration und Verknüpfung
3.1. Variablen
● verarbeitende Daten und Befehle werden im Hauptspeicher abgelegt
● Speicherbereich muss auf der untersten Maschinenebene reserviert
werden
● geschieht durch Label
● Varaible ist Platzhalter für solchen Speicherbereich
● wird in höherer Programmiersprache komfortabel definiert und beliebig
benannt
● Durch Datentyp wird angegeben wieviel Speicherplatz die Variable
brauchen wird
● Format:
Datentyp, Name
● jetzt kann der Variablen beliebige Werte mit dem gleichen Datentyp
zugeordnet werden
3.2. Identifier (Bezeichner)
●
●
●
●
●
●
Name für Vriable
Name besteht aus Buchstaben, Ziffern und Unterstrich
keine Ziffer am Anfang
case sensitive
Standard-Identifier wie printf dürfen nicht überschrieben werden
in KamelSchreibWeise
3.3. Syntax
● Folge von Basiselementen
● Leerzeichen, Tabs und Zeilenumbrüche werden überlesen
● Syntax ist korrekte Aneinanderreihung von Befehlen, also prinzipiell
ausführbar
● Samentisch korrekt heißt, dass das Programm sein Pflichtenheft erfüllt,
also keine Endlosschleifen, Erfüllung aller Voraussetzungen
13
SE
3.4. Die 5 atomaren Typen
●
●
●
●
●
Buchstaben
character
char
Ganze Zahlen
integer
int
Gleitkommazahlen
floating-point
float
doppelte genaue float double float.-p. double
ohne Wert
valueless
void
● void ist eigentlich kein eigener Datentyp
● char sind einzelne Buchstaben, Ziffern oder Sonderzeichen – ASCII-Code
3.4.1. Modifizierung der atomaren Typen
● alle ausschließlich void
● signed, unsigned, long, short
● Platzsparen für Computer bei kleinen Programmen sinnlos
● bei Steuerungen essentiell
● signed ist standardwert für alle Datentypen
● unsigned schränkt Wertebereich auf positive Zahlen ein
14
SE
● long
 Ganzzahlvaraible wie int
 auf einem 32-Bit Rechner gleicher Datenbereich wie int
 nicht aber auf einem 16-Bit Rechner
 long double
• 80 Bit Gelitkommazahl
● short
 zum Platzsparen
 verkleinert int um die Hälfte
3.5. enum und typedef
●
●
●
●
●
●
●
●
definieren eigener Datentypen durch typedef
Aufzählung einer konstanten Reihe festlegen durch enum (enumeration)
Elemente werden von 0 an durchnummeriert
Konstanten und neu eingeführte Datentypen in GROSSBUCHSTABEN
in geschweiften Klammern und mit Komma getrennt
nach der geschlossenen Klammer den Namen des Datentyps
erhöht Lesbarkeit des Programms
es kann dem bestimmten Datentyp kein Wert zugewiesen werden, der
nicht der Aufzählung entspricht
● Beispiel
 typedef enum {ROT, GRUEN, GELD, GELB-ROT, GEL_BLINK} AMPEL;
 AMPEL AmpelSafranberg, AmpelAlbeckerSteige;
 AmpelSafranberg = GRUEN;
 AmpelAlbeckerSteige = GELB;
 vorheriges ist das gleiche wie: AmpelAlbeckerSteige = 3;
15
SE
3.6. Variablen Definition, Zuweisung und Verknüpfung
● Definition
 kann beliebige Werte des Datentyps annehmen
 Identifier mit Präfix in Abhängigkeit von Datentyp
 Definition mehrerer Variablen des gleichen Typs mit Komma getrennt
hintereinander
• i
int
• c
char
• f
float
• d
double
● Initialisierung
 erfolgt durch Gleichheitszeichen
 nicht mathematische Überprüfung auf Gleichheit
 nur Werte, die dem Datentyp entsprechen
 es können schon mathematische Oprationen angewendet werden
 nicht initialisierte Variablen verwenden den zufälligen Wert, der an dem
Bereich im Hauptspeicher im Moment gespeichert ist auf den die
Varaible abgebildet wird
 sicherheitshalber alle Variablen vor erstem gebraucht zu initialisieren
 am besten direkt nach Definition
● Verknüpfung
 ausführen von Funktionen
16
SE
● Beispiel Definition
 int iSummand1;
 int iSummand2;
 int iSummand3 = 3, iSummand4 = 4;
 float fPi, fExpo_Test;
 double dPiQuadrat;
 char cZeichen1, cZeichen2;
● Beispiel Initialisierung
 iSummand1 = 38;
 iSummand2 = iSummand1;
 fPi = 3,141;
 fExpo_Test = 7,1E-5;
 cZiffer1 = 'c';
• einfacher Buchstabe in ' ' längerer Ausdruck in „ „
 cZiffer2 = cZiffer1;
 Definition und Initialisierung
• char cZeichen = 0;
• int iIndex = 0'
• float fTemp = 0.0f;
• double dFaktor = 0.0;
● Beispiel Zuweisung
 int iSummand3, iFaktor;
 iFaktor = 5;
 iSummand3 = (iSummand1 + iSummand2) * iFaktor;
• Punkt vor Strich!
 dPiQuadrat = fPi * fPi
3.7. char in ASCII = int
Zeichen werden im ASCII-Code codiert und als Zahl gespeichert
char können mit und ohne Vorzeichen gespeichert werden
macht kein Sinn für Buchstaben
spart aber Speicherplatz, wenn sie als Zahlen betrachtet werden
ein char braucht 1 Byte Speicherplatz, ein short int schon 2 Byte
Zeichen als char werden internt als Zahlen nach der ASCII-Tabelle
gespeichert
● bei int kann auch mit Buchstaben operiert werden
● Beispiele
 char cZeichen1;
 cZeichen1 = 'A' ist das gleiche wie cZeichen1 = 65
 int iZahl;
 iZahl = 'A' + 'B';
 Ergebnis wäre 131 (65 + 66)
 iZahl = 'A' + 32
 Ergebnis wäre 'a' oder 97
●
●
●
●
●
●
17
SE
18
SE
3.8. Ausführen eines Programms
● Definition und Initialisierung
 im Speicher werden die Speicherplätze für die Varaiblen belegt
 int iX = 1, iY = 2, iZ = 3, iSum=0;
● Zuweisung
 iSum = iX + iY – iZ;
● Im Speicher
 itemp1 = iX;
 itemp 1 += iY;
 itemp1 -= iZ
 i += a ist i = i + a
3.9. Die Ausgabefunktion printf
● printf („Das Ergebnis von %d + %d - %d = %d \n“, iX, iY, iZ, iSum)
●
●
●
●
%d ist Format-Element/Format-String
wird durch Argumente hinter Komma ersetzt
Zeichenkette heißt String
\n für neue Zeile, \t für Tab
19
SE
3.9.1. Format Elemente
● %[Flags][Feldbreite][.Genauigkeit]Konvertierungszeichen
● Flags
 +
 ''
 -
mit Vorzeichen ausgeben
mit führendem Leerzeichen ausgeben
im Feld linksbündig ausgeben
● Feldbreite
 Länge des Ausgabefeldes
● Genauigkeit
 Anzahl der Nachkommastellen
 mit Punkt davor!
● Konvertierungszeichen
 d,i
Dezimalzahl
 u
Dezimalzahl ohne Vorzeichen
 o
Oktalzahl
 x,X
Hexadezimalzahl
 f
Gleitkommazahl
 e,E
Exponentialzahl
 c
einzelnes Zeichen
 s
Ausgabe eines Strings bis '\0'
 p
Adresse in Hexadezimalform (pointer)
● Elemente in [] sind optional
● im Bereich der Argumentenliste kann auch gerechnet werden
20
SE
3.10. Die Einlesefunktion scanf
● scan formatted
● Bedienschnittstelle ist Interaktion zwischen Benutzer und Programm durch
Ein- und Ausgabe
● Vor der Eingabe erfolgt eine Eingabeaufforderung mit printf
scanf („Format-String“ [, Argumentenliste]);
scanf („ %d“, &iX);
scanf („%f %1f, &fFloat1, &dDouble);
Das Leehrzeichen vor %d bewirkt, dass scanf Leerzeichen, Tabs und
Returns (sogenannte White-space-Zeichen) überliest
● & ist Adressoperatort
● 1 vor f gibt spezielle Größenangabe an
●
●
●
●
3.11. die Darstellung von Umlauten
●
●
●
●
●
●
●
●
●
Umlaute werden nicht richtig dargestellt
codierung durch Hex-Code
ä
\204
Ä
\216
ö
\224
Ö
\231
ü
\201
Ü
\232
ß
\341
3.12. Die Modulo-Operation
●
●
●
●
„Rechnen mit Rest“
es wird bei einer Division als Ergebnis der Rest ausgegeben
Operator ist das %-Zeichen
5%2=1
21
SE
4. Operatoren, Ausdrücke & Funktionsaufrufe
4.1. Operanden & Operatoren
● Operanden:
 Konstanten
 Variabeln
 Funktionsaufrufe
 andere Ausdrücke
● Operatoren:
 +, -, *, /
 %
 =
 +=, -=, *=, /=, %=
 ++, - &&, ||
 !, &, |, ~
 <, >, <=, >0
 *, ?
 uvm
4.1.1. Klassifikation der Operatoren nach Anzahl der Operanden
● unäre Operatoren
 z.B. ++
erhöht Wert um 1
Inkrementfunktion
● binäre Operatoren
 z.B. a + b
● drei Operanden
 (a>b) ? c=a : c=-a; Ordne a c zu, wenn a >0, sonst -a Betragsfunktion
4.1.2. Inhaltliche Klassifikation von Operatoren
● Stärke von C ist die Vielfalt an Operatoren
● Arithmetische Operatoren
unär




Minus
postfix Inkrement ++
prefix Inkrement
postfix Dekrement --
-x
x negiert
x++
eval. dann um 1 erhöhen
++ ++x
um 1 erhöhen, dann eval
x-eval. dann um 1 reduzieren
22
SE
 prefix Dekrement
---x
um 1 reduzieren, dann eval
(Evaluation bedeutet ausführen eines Befehls, z.B. printf)
binär
 Addition
+
x+y
x plus y
 Subtraktion
x–y
x minus y
 Multiplikation
*
x*y
x mal y
 Division
/
x/y
x durch y
 Modulo-Fkt.
%
x%y
x modulo y
 Stellung des Operators wichtig, z.B. Minus für Negation und Subtraktion
 In-/Dekrement nur für Zähler-Variable, meist int -> indexgesteuerte
Schleifen
 Beispiel mit Ausgabe im Kommentar
int iZahl1 = 2, iZahl2 = 23;
printf(„iZahl1:%d\t iZahl2:%d\n“, iZahl1, iZahl2 );
/*iZahl1:2
iZahl2:23*/
printf(„iZahl1:%d\t iZahl2:%d\n“, iZahl1++, iZahl2-- ); /*iZahl1:2
iZahl2:23*/
printf(„iZahl1:%d\t iZahl2:%d\n“, iZahl1, iZahl2 );
/*iZahl1:3
iZahl2:22*/
printf(„iZahl1:%d\t iZahl2:%d\n“, ++iZahl1, --iZahl2 ); /*iZahl1:4
iZahl2:21*/
printf(„iZahl1:%d\t iZahl2:%d\n“, iZahl1, iZahl2 );
/*iZahl1:4
iZahl2:21*/
23
SE
 Seiteneffekt wenn nicht klar, ob In-/Dekrement-Befehl zuerst evaluiert
wird
 von Compiler zu Compiler verschieden
 trennen in einzelnen Ausdrücken sicherer
 Beispiel:
int iZahl = 5, iErgebnis;
...
iErgebnis = iZahl * iZahl++;
entweder:
oder
iErgebnis = 5 * 5;
also 25
iErgebnis = 6 * 5;
also 30
sicherheitshalber:
iErgebnis = iZahl * iZahl;
iZahl++;
iZahl++;
iErgebnis = iZahl * iZahl;
oder:
● Zuweisungs-Operatoren
 Zuweisung





Additive Zuw.
Subtrak. Zuw.
Multik. Zuw.
Divid.. Zuw.
Modulo Zuw.
=
+=
-=
*=
/=
%=
Standard
a=b
Wert von b nach a
arithmetisch
a += b
Wert von a + b nach a
a -= b
Wert von a – b nach a
a *= b
Wert von a * b nach a
a /= b
Wert von a / b nach a
a %= b
Wert von a % b nach a
 Verkürzt Gesamtfunktion a = a + b
 schneller und kompakter weil nur einmal Operatoren aufgerufen
werden müssen
● Logische und Vergleichs-Operatoren (auch relationale)
Vergleich
 größer
>
a>b
1 wenn a größer b, sonst 0
 kleiner
<
a<b
1 wenn a kleiner b, sonst 0
 größer gleich >= a >= b
1 wenn a größer oder gleich b,
sonst 0
 kleiner gleich <= a <= b
1 wenn a kleiner oder gleich b,
sonst 0
 gleich
== a == b
1 wenn a gleich b, sonst 0
 ungleich
!=
a != b
1 wenn a ungleich b, sonst
0
24
SE
 logisches Und &&
 logisches Oder
 logische Negat.
Logisch
a && b
1 wenn a und b nicht 0, sonst 0
||
a || b
1 wenn a oder b nicht 0, sonst 0
!
!a
1 wenn a 0, sonst 0
 Vergleichsoperatoren liefern 1 wenn Vergleichsbedinung erfüllt ist,
sonst 0
 weitere logische Operatoren müssen aus den 3 eifnachen Operatoren
zusammengesetzt werden
 Beispiel:
#include<stdio.h>
int main(void)
{
int iNote = 1, iPunkte = 100;
printf („Bitte Punkte eingeben: „)
scanf („%i“, &iPunkte);
iNote = 1 + (iPunkte < 90) + (iPunkte < 80) + (iPunkte < 65) +
(iPunkte < 50) + (iPunkte < 26);
printf )“\n Die Note ist: %i“, iNote);
return0;
}
 jeder Vergleich leifert entweder 0 oder 1, die Summe ist dann die
Entnote nach dem Notenschlüssel
● Bitweise Operatoren
 wichtig für hardwarenahe Programmierung
 &
bsp.: x = 7 & 20 7: 00111 z.B. Kontrollfunktion bei
Sicherheits20: 10100 Netzwerktechnik
12: 01100 also x = 12
 |, ~, <<, >> wird nicht weiter drauf eingeangen
25
SE
● Sonstige Operatoren
 Adressoperator
 Dereferenzierungsoperator
 Pfeil-Operator
->
 Punkt-Operator
 Konditionale Operator
 Kommaoperator
&
*
.
?:
,
bsp.: (a>b0) ? c=a : c=-a;
bsp.: int iZahl1 = 1, iZahl2 = 2;
4.2. Ausdrücke
● Ausdruck ist Verknüpfung von Operatoren mit Operanden
● jeder Ausdruck hat einen Typ und endet in einem Wert
● Reihenfolge der Auswertung nicht immer definiert
 Bsp.: x = fkt_1() + fkt_2();unklar ob fkt_1 oder fkt_2 zuerst ausgeführt
wird
 wichtig bei der Abarbeitung von Steuerbefehlen
● Typ eines Ausdrucks ist von den Typen seiner Operanden abhängig
● Anweisung ist Aneinanderreihung von Ausdrücken, die mit einem
Strichpunkt abgeschlossen ist
26
SE
4.2.1. Prioritäten von Ausdrücken
● Festlegung welcher Oprator welchen Operanden wann zu einem
Zwischenergebnis verknüpft
● d.i. Reihenfolge der Evaluation
● Assoziativität gibt an wie Operatoren innerhalb einer Klassenebene
ausgewertet werden
● Vorrangregel gibt an welcher Operator innerhalb eines Ausdrucks einer
höheren Ebene zugeordnet wird
Klasse
primär
unär
multiplikativ
Operator
() [] -> .
! ~ ++ -- + - (type) * &
Assoziativität
links nach rechts
links nach rechts
+-
links nach rechts
<< >>
links nach rechts
< <= > >=
links nach rechts
== !=
links nach rechts
bitweises UND
&
links nach rechts
bitw. exklus. ODER
^
links nach rechts
bitw. inkls. ODER
|
links nach rechts
&&
links nach rechts
logisches ODER
||
links nach rechts
Kondition
?:
rechts nach links
= += -= *= /= %= &= ^=
|= <<=
rechts nach links
shift
relational
Geleichheit
logisches UND
Zuweisung
Komma
,
hoch
rechts nach links
*/%
additiv
Vorran
g
links nach rechts
niedrig
● Punkt vor Strich wird automatisch eingehalten
● boolsches UND vor boolschem ODER -> (A&&B) || (C&&D) auch ohne
Klammern das gleiche
● A – B % C entspricht A – (B % C)
● !A || !B entspricht (!A) || (!B)
4.2.2. Typen von Ausdrücken
● Ausdrücke representieren konstante, ganzzahligen oder gebrochen
rationale Werte
● bestimmter Typ wird durch Cast-Operator erwirkt
● egal, ob gecasteter Wert von höherem oder niedererem Typ ist
27
SE
● meisten Operanden geben Ergebnis mit dem selben Typ wie ihre
Operanden zurück
 double + double ergibt double
 float * float ergibt float
 int / int ergibt int (!!!)





1.0 / 2.0 ergibt 0.5
1 / 2 ergibt 0
bei downcast werden Kommastellen einfach abgeschnitten
Ergebnis bei / und % unklar, wenn Operand negativ ist
mögliche Lösung: einen der beiden Operanden als float deklarieren
Implizite Typkonversion bei Typen-Mix
● werden mehrere unterschiedliche Typen an Operanden in einem Ausdruck
verwendet, wird höchster Typ für Ergebnis ausgewählt
● hochcasten erfolgt nur für die Zeit der Berechnung, deklarierter Datentyp
bleibt erhalten
● in Absteigender Reihenfolge:
 long double, double, floar, unsigned long, long unsigned int
● Beispiel:
char c; int i; float f; double d;
(c / i) + (f * d) - (f + i); ergibt ein double als Ergebnis
int iZahl1 = 2, iZahl2 = 3;
float fZahl3 = 1.0;
fZahl3 = iZahl2 / iZahl1;
fZahl3 ist 1.0 weil Ergebnis ein int ist
28
SE
Explizite Typenkonversion – der Cast-Operator
● gesteuerte Konvertierung eines Ausdrucks
● Cast-Operator besteht aus: (Typ) Ausdruck
● Beispiel:
int iZahl1 = 1, iZahl2 = 2;
float fZahl3;
fZahl3 = (float) iZahl2 / iZahl1;
fZahl3 erhält den Wert 0.5 als float
4.3. math.h
● enthält mathematische Funktionen
● #include <math.h>
4.3.1. Trigonometrische und hyperbolische Funktionen
● Typenangabe links neben Funktionsnamen sagt aus von welchem Typ das
Ergebnis der Funktion sein wird
● in den runden Klammern steht welches Argument die Funktion zur
Bearbeitung erwartet
● double sin (double x);
● double cos (double x);
● double tan (double x);
●
●
●
●
double
double
double
double
sin (x) Argumen im Bogenmaß
cos (x) Argumen im Bogenmaß
tan (x) Argumen im Bogenmaß
asin (double x);
arcsin (x)
acos (double x);
arccos (x)
atan (double x);
arctan (x)
atan2 (double y, double x); arctan (y/x)
● double cosh (double x);
● double sinh (double x);
● double tanh (double x);
Hyperbelfunktion
Hyperbelfunktion
Hyperbelfunktion
29
SE
4.3.2. Exp, Log und andere
● double exp (double x);
● double log (double x);
● double log10 (double x);
x>0
● double sqrt (double x);
● double ldexp (double x, int n);
● double pow (double x, double y);
Exponentialfunktion e x
natürlicher Logarithmus ln (x), x > 0
dekadischer Logarithmus lg (x) ,
● double ceil (double x);
Ceiling-Funktion, nächst höherer
Ganzzahlwert
Floor-Fkt., nächst niederer
● double floor (double x);
Ganzzahlwert
● double fabs (double x);
Quadratwurzel aus x, x >= 0
n
x ∗2
x y x>0, y ganzzahlig
Absolutbetrag |a|
5. Kontrollstrukturen in C
● Programmteile abhängig von Bedingungen oder aktuellen Zuständen zu
durchlaufen
● Anweisungen wiederholt ausführen
● wesentliche Fähigkeit höherer Programmiersprachen
● 3 Klassen:
 Sequenz
• einfache sequenzielle Aneinanderreihung
• einmaliges Durchlaufen des ganzen Programms
 bedingte Verzweigung
• abhängig von Bedingung
• wird Programmteil durchlaufen oder nicht
• Sprünge im Programm
 Iteration
• gesteuerte Wiederholung
• Schleifen im Programm
30
SE
5.1. Sequenz
● wird mit öffnender geschweifter Klammer begonnen und mit schließender
abgeschlossen
● Im Block kann vor Programmanweisung eine eine Deklarationsliste folgen
 Syntax:
{
[Delarations-Liste]
[Anweisungs-Liste]
}
5.1.1.Deklarations-Liste: Variablen und Konstanten
● Benannte Konstanten
 const double dPi = 3,1415;
● Literal-Konstanten
 int iIndex = 0;
 double dTest = 0.0;
 iIndex = iIndex + 1;
● Kostante Ausdrücke
 dTest = 3,1415 * 3,1415;
 dTest = dPi * dPi;
● Symbolische Konstanten
 #define PI 3,14
int main()
{
double dFlaeche = 0.0, dRadius = 1.0;
dFlaeche = dRadius * dRadius * PI;
return0;
}
31
SE
● #define ist Präprozessoranweisung
● eignet sich gut um am Anfang eines Programms Parameter zu deklarieren
● können einfach geändert oder angepasst werden, ohne dass im Programm
was geändert werden muss
●
●
●
●
Präprozessor wird vom Compiler gestartet
erster Arbeitsgang beim compilieren
durchsucht Quelltext auf #
Präprozessoranweisungen enden nicht mit ;
5.1.2. Verschachtelte Sequenzen
● Variablendefinitionen gelten nur innerhalb eines Struckturblocks -> logale
Variablen
● und im Struckturblock enthaltenen Struckturblöcken
● Beispiel zur Kreisberechnung
#include <stdio.h>
#define PI 3,14
int main()
{
//Deklarationsliste
double dRadius = 1.0;
//Anweisungsliste
{
//Deklarationsliste
double dUmfang = 1.0;
//Anweisungsliste
dUmfang = 2 * dRadius * PI;
printf (\n Der Umfang betraegt %1f“, dUmfang);
}
{
//Deklarationsliste – dUmfang ist hier nicht mehr Verfügbar
double dFlaeche = 1.0;
//Anweisungsliste
dFlaeche = dRadius * dRadius * PI;
printf /“\n Die Fläche betraegt %1f“, dFlacehe);
}
return 0;
}
5.2. Bedingte Verzweigung – if-Anweisung
● einfachster Fall der bedingten Verzweigung
● Syntax:
if (Ausdruck)
Strukturblock if
else
Struckturblock else
32
SE
● Beispiel:
if (...)
// Einzelanweisung
printf („test);
else
printf (“something else“);
if (...)
//Anweisungsliste
{
//Anweisungen
}
● Wahrheitswert 0 für falsche, 1 und alle anderen Werte für wahr
● Überprüfen auf Gleichheit z.B. eines Indexes mit doppeltem
Gleichheitszeichen == (einfaches ist Variablenzuweisung)
● wenn auf else zwei Zeilen folgen, aber diese nicht mit {} in einem Block
zusammengefasst sind, wird die zweite Zeile immer ausgeführt
5.2.1. logische Operatoren
●
●
●
●
●
●
==
<=
>=
<
>
!=
gleich
kleiner gleich
größer gleich
kleiner
größer
ungleich
iIndex == 2
'B' > 'A'
5.2.2. Verschachtelte If-Anweisung
●
●
●
●
überprüfen weiterer Bedingungen innerhalb einer Bedingung
jedes else wird vom Compiler dem nächstgelegenen if zugeordnet
andere Zuordnung durch Blockklammern
übersichtilcher wenn if immer auf einer Ebene
● Beispiel: Suchen kleinster Wert aus drei Werten
{
if (a < b)
if (a < c)
printf („\n der kleineste Wert ist a \n“)
else
printf („\n der kleineste Wert ist c \n“)
else
if (b < c)
printf („\n der kleineste Wert ist b \n“)
else
33
SE
printf („\n der kleineste Wert ist c \n“)
}
5.3. Bedingte Verzweigung – Mehrfachauswahl mit switch
● Überprüfen eines Ausdrucks auf mehrere Fälle
● entsprechende unterschiedliche Anweisungen
● trifft keine der Bedingungen ein wird default-Zweig durchlaufen
● zu überprüfender Ausdruck in runden Klammern nach switch
● Block mit geschweiften Klammern
● jeder Fall begint mit case, dann in die Bedingung und nach Doppelpunkt
die Anweisung
● am Ende breake, so dass Switch-Block nach erfolgreichem durchlaufen
einer Anweisung verlassen wird
● nach default geschlossenen geschweifte Klammer
● Beispiel:
 einfacher Taschenrechner
 Variablen schon eigegeben
 char in einfachen Gänsefüßchen!
char cRechenzeiche;
...
swtich (cRechenzeiche)
{
case '+' : fErgebnis = fOperand1 + fOperand2;
break;
case '-' : fErgebnis = fOperand1 - fOperand2;
break;
case '*' : fErgebnis = fOperand1 * fOperand2;
break;
case '/' : fErgebnis = fOperand1 / fOperand2;
break;
default : //unzulässiger Operator
printf („falsche Operation!“);
break;
5.4. Bedingte Iteration
5.4.1. Prüfen am Anfang – while
● auch Wiederholungsanweisung oder Schleife genannt
● wird so lange ausgeführt bis Bedingung nicht mehr erfüllt ist
34
SE
● wenn Bedingung von Anfang an nicht erfüllt ist, wird Schleife
übersprungen
● Einzelanweisung oder Anweisungsliste in {}
● Kriterium, das bei der Bedingung geprüft wird, muss im Rumpf irgendwie
verändert werden, sonst Endlosschleife
35
SE
● Syntax:
while (Bedingung)
Strukturblock
● Beispiel Älteste Person im Raum:
#include <stdio.h>
int main(void)
{
//Variablendeklaration
int iAlter = 0, iMaxAlter = 0, iAnzahlPersonen = 0, iIndex = 1;
//Anweisungsliste
printf („\n Geben Sie die Anzahl der Personen an:“);
scanf (%d“, &iAnzahlPersonen);
while (iIndex <= iAnzahlPersonen)
{
printf („Alter der %d-ten Person eingeben:“, iIndex);
scanf („%d“, %iAlter);
if (iAlter > iMaxAlter)
iMaxAlter = iAlter;
iIndex++;
//Änderung des Bedingungskriteriums
}
printf („\n Die aelteste Person ist %d Jahre alt!“, iMaxAlter);
return 0;
}
5.4.2. Prüfen am Ende – do ... while
● zuerst Rumpf ausführen
● dann überprüfen
● und evt. nochmal durchlaufen wenn Bedingung wahr
● Schleife wird unabhängig von Bedingung einmal ausgeführt
36
SE
● Syntax:
do
Strukturblock
while (Bedingung)
● Beispiel Älteste Person im Raum:
//siehe oben
do
{
printf („Alter der %d-ten Person eingeben:“, iIndex);
scanf („%d“, %iAlter);
if (iAlter > iMaxAlter)
iMaxAlter = iAlter;
iIndex++;
//Änderung des Bedingungskriteriums
}
while (iIndex <= iAnzahlPersonen);
 Wenn der Index von Anfang an größer ist als die Anzahl der Personen
wird ein falsches Ergebnis ausgegeben
 Schleifenart muss abhängig vom Problem ausgesucht werden
37
SE
5.5. gesteuerte Iteration – for
● Sonderform der Iteration mit Test vor Schleifeneintritt
● werden verwendet wenn einen Schleife durch einen Zählindex gesteuert
wird
● Einzelanweisung oder Anweisungsblock in {}
● kann durch while-Schleife dargestellt werden
● Vorteil ist zentrale Festlegung der Initialisierung, der Bedingung und der
Weiterschaltung
● nach for folgt dreiteilige Schleifensteuerung
 Initialisierung
Setzen des Schleifenzählers auf Startwert
 Bedingung
muss erfüllt sein, dass Schleife durchlaufen
wird
 Veränderungsanweisung wird nach jedem Schleifendurchlauf
durchgeführt
 durch Semikolon ; getrennt
● Syntax:
for ([Initialisierung]; [Bedingung]; [Veränderung])
Struckturblock
● Beispiel Älteste Person im Raum:
//siehe oben
for (iIndex = 1; iIdnex <= iAnzahlPersonen; iIndex++)
{
printf („Alter der %d-ten Person eingeben:“, iIdnex);
scanf („%d“, &iAlter);
if (iAlter > iMaxAlter)
iMaxAlter = iAlter;
}
38
SE
5.5.1. geschachtelte Schleifen mit for
● weitere Schleifen in einer Schleife
● können auch gemischte Formen der Iteration sein
● Beispiel Tabelle aller Produkten aus 1 – 10 x 1 – 10
#include <stdio.h>
int main(void)
{
int j = 0, k = 0; //Indexvariabeln
printf ("
1 2 3 4 5 6 7 8 9 10\n");
printf ("---------------------------------\n");
}
for (j=1; j<=10; j++) //Zeilenindex j
{
printf ("%2d|",j); //aktuelle Zeile
for (k=1; k<=10; k++)
printf ("%3d", j*k); //Produkte
printf("\n");
}
printf("\n");
return 0;
39
SE
5.6. Kontrollstrukturen im Nassi-Schneidermann-Diagramm
● Sequenz / Folge:
 Variablendeklaration:
index := 0, index ∈ ℤ
 Anweisung:
index := index + 1
 Einzelanweisungen und Anweisungsblöcke in quadratischen Kästchen
● Symbole
 == Vergleich

:= Zuweisung
 <a> Variable a
 ≠ / != Ungleich
 >/< Größer / Kleiner
40
SE
6. Arrays, Strings und Pointer
6.1. Arrays
●
●
●
●
●
Reihung, Felder oder Vektor
vereinfacht Verwendung vieler Varaiblen/Bezeichner
ansprechbar über Indexwert → Schleifen
einfacherer Programmänderung durch Änderung der Länge des Arrays
Länge oft als #define Länge
● Syntax:
Datentyp Bezeichner [Länge] = {Initialisierungsliste};
 Datentyp gilt für alle Element
 Bezeichner mit A für Array, dann Kleinbuchstabe für Datentyp, dann
mit Großbuchstabe beginnend Bezeichner
 Länge ganze Zahl größer 0 in eckigen Klammern – hier können auch
Ausrücke stehen
 Initialisierungsliste nicht notwendig direkt nach Deklaration – Werte
durch Komma getrennt und in geschweiften Klammern
 werden in der Initialisierungsliste nicht alle Elemente angegeben,
werden diese mit 0 belegt
 Länge muss bei Deklaration entweder angegeben werden oder durch
Initialisierung festgelegt werden → Klammer kann leer gelassen werden
 die Elemente von mit static deklarierte Arrays werden auf 0 gesetzt
 globale Arrays werden ebenfalls auf 0 gesetzt
#include <stdio.h>
int AiArray1[5];
int main()
{
static int AiArray2[5];
int AiArray3[5]={1};
int AiArray4[5];
int i=0;
printf("Index \tArray 1\tArray 2\tArray 3\tArray 4\n");
for (i=0; i<5; i++)
printf ("%d\t%d\t%d\t%d\t%d\n", i, AiArray1[i], AiArray2[i],
AiArray3[i], AiArray4[i]);
return 0;
}
Index
0
1
2
3
4
Array 1
0
0
0
0
0
Array 2
0
0
0
0
0
Array 3
1
0
0
0
0
Array 4
0
-1074865512
-1209377266
-1208661639
134520820
● Länge wird von 0 an gezählt, das ist Basisadresse, dann je nach Datentyp
41
SE
Anzahl an Speicherplätzen weiter gezählt → Index
● n-Dimensionaler Array läuft von 0 bis n-1
● Länge kann auch durch #define Varaible... am Programmanfang definiert
werden
● Feldlänge nicht überschreiten -> sonst Absturz., überschreiben anderer
Daten, Endlosschleife...
● Feldüberschreitung wird vom Compiler nicht überprüft
● Operationen können nur für jedes Element einzeln durchgeführt werden
6.1.1. Grenzüberschreitung
#include <stdio.h>
int main()
{
int AiTest[10];
int i;
for(i=0; i<=10; i++)
AiTest[i]=i;
}
/* !!Bereichsüberschreitung!! */
for(i=0; i<=10; i++)
printf("%d, ",AiTest[i]);
printf("\n");
return 0;
● es wird ein Array mit 10 Feldern deklariert
● in der for-Schleife wird von 0 bis 10 hochgezählt, sprich in 11 Schritten
●
for(i=0; i<10; i++)
// ohne '='-Zeichen richtig
42
SE
6.1.2. Sortieralgorithmus
#include<stdio.h>
#define DIM 6
int main(void)
{
float AfZahl[DIM], fTemp=0;
int i=0, j=0;
for (i=0; i<DIM; i++)
{
printf ("\n Bitte Zahl eingeben: ");
scanf ("%f", &AfZahl[i]);
}
//DIM -1 weil unten zu j eins dazugezählt wird [j+1]
//Zähle von DIM-1 runter auf 0
for (i=DIM-1; i>0; i--)
{
//zähle von 0 bis i hoch, 2. Schleife verlgeiche die Zahl j und die
darauf folgende Zahl
for (j=0; j<i; j++)
{
//wenn j+1 größer als j, tausche die beiden Zahlen Zwischenspeichern in fTemp
if (AfZahl[j+1] < AfZahl[j])
{
fTemp = AfZahl[j];
AfZahl [j] = AfZahl [j+1];
AfZahl [j+1] = fTemp;
}
}
}
printf ("\n\n Ausgabe Zahlen sortiert: \n");
for (i=0; i<DIM; i++)
printf ("%.2f ", AfZahl[i]);
return 0;
}
● sortiert 6 eingegebene Zahlen nach der Größe und gibt diese aus
● Bubble-Sort-Algorithmus, weil die Zahlen sortiert werden, wie eine Blase
im Wasser nach oben schwimmen
● Anzahl der Zahlen kann durch DIM am Anfang festgelegt werden
● typisches EVA-Prinzip – Eingabe, Verarbeitung, Ausgabe
43
SE
● Nassi Schneridermann Diagamm für Buble-Sort-Algorithmus
6.1.3. Zählen der Elemente eines Arrays
#include <stdio.h>
#define SIZE 10
int main()
{
int AiZahlen[SIZE] = { 0 };
}
printf("Anz. Elemente : %d\n",sizeof(AiZahlen)/sizeof(int));
return 0;
6.1.4. Vergleichen von Arrays
for(i=0; i<10; i++)
{
if( AiArray1[i] == AiArray2[i] )
continue;
else
{
printf("Unterschied an Position %d\n",i);
break;
}
}
44
SE
6.2. Strings
● Zeichenketten
● wird mit \0 abgeschlossen
● nutzbare Bereich um ein Zeichen kleiner ( wegen \0)
● Syntax:
char szBezeichner [Länge] = „String“;
 Länge definiert Anzahl der Elemente
 Länge kann bei direkter Initialisierung offen gelassen werden
● \0 wird automatisch angehängt → immer ein Zeichen mehr
Deklarieren, als für Buchstaben benötigt wird
● Operationen und Zuweisungen nur für einzelne Elemente
● Einhaltung der Grenzen!
● scanf und printf mit %s
 scanf interpretiert Leerzeichen, Tabs und Return als Trennzeichen
zwischen Strings
 scanf fügt automatisch \0 ein
 Adressoperator bei scanf nicht notwendig (scanf(„%s“, szString);)
char hallo[]={'H','a','l','l','o', ' ',
'W','e','l','t','\n','\0'};
char hallo[] = {"Hallo Welt\n"};
 beide Deklarationen sind identisch
6.2.1. Zeichenzahl eines Arrays auslesen
for(i=0; hello1[i] != '\0'; i++);
printf("Länge von '%s' = %d Zeichen\n",hello1,i);
45
SE
6.2.2. Beispiel suchen und Ersetzten eines Worts in einem String
#include <stdio.h>
char undbig[] = {
"Hund und Katze sind nicht ohne "
"Grund des Menschens bester Freund\n"
};
int main()
{
int i;
for(i=0; undbig[i] != '\0'; i++)
{
if(undbig[i-1]==' '&& (undbig[i]=='u'||undbig[i]=='U'))
{
if(undbig[i+1]=='n'&&
undbig[i+2]=='d' && undbig[i+3]==' ')
{
undbig[i]='U';
/* n zu Grossbuchstabe konvertieren (N) */
undbig[i+1]-=32;
/* d zu Grossbuchstabe konvertieren (D) */
undbig[i+2]-=32;
}
}
}
printf("%s",undbig);
return 0;
}
● Stringkonstante über zwei Zeilen, wenn mit „ am Ende der einen und „ am
Anfang der nächsten
● oder \ am Ende der ersten
char array[] = {"Eine Zeichenkette über"
"2 Zeilen\n"};
/* Alternative */
char array[] = {"Eine Zeichenkette über \
2 Zeilen"};
46
SE
6.2.3. Beispiel Grenzüberschreitung
● Source:
char szString[]=“Affe“;
printf(„Kontrollausgabe: %s \n“,szString);
szString[4] = 'x'; //-> \0 überschrieben
printf („Kontrollausgabe. %s \n“, szString);
● Ausgabe:
Kontrollausgabe: Affe
Kontrollausgabe. Affex� ԩ�8թ�P�����8թ�P��
Speicherschrott!!!
● Verhinderung der Grenzüberschreitung bei scanf durch Angabe der
Feldbreite
● scanf(„%20s“, szString);
● Feldbreite auf 20 festgelegt, muss direkt dezimal angegeben werden,
keine Variabeln möglich
6.2.4. Einlesen von Strings durch gets
● Syntax:
gets (Bezeichner_String);
gets (szText);
● keine Feldbreite einstellbar -> Gefahr des Überschreitens groß
● deutet Leerzeichen als Leerzeichen... -> einlesen von ganzen Sätzen
6.2.5. Einlesen von Strings durch fgets
char fgets(char string,int anzahl_zeichen,FILE stream);
fgets(str, 100, stdin);
● in der 2. Zeile werden bis zu 100 Zeichen aus dem Tastaturbuffer in den
String str geschrieben
● hängt am Ende eines Strings immer ein \n an
47
SE
6.3. String.h
strcat
char *strcat(char *s1, const char *s2);
●
●
●
●
Pointer!
Hängt s2 an das Ende von s1, wenn s1 genug Platz hat
die Größe des Zielstrings kann nicht überprüft werden!!!
die \0 am Ende des Zielstrings wird überschrieben
strchr
char *strchr(const char *s, int ch);
● suchen nach einem Zeichen im String
● gibt die Position des Zeichens ch zurück, wenn es nicht vorhanden ist gibt
sie 0 zurück
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Ein String mit Worten";
printf("%s\n",strchr(str, (int)'W'));
return 0;
}
Worten
● gibt alle Zeichen ab W aus
48
SE
strcmp
int strcmp(const char *s1, const char *s2);
●
●
●
●
Strings vergleichen
gibt 0 zurück für gleichgroße Strings
einen Wert kleiner als 0 wenn s1 kleiner als s2
einen Wert größer als 0 wenn s1 größer als s2
#include <stdio.h>
#include <string.h>
void String_Vergleich(char s1[], char s2[])
{
int ret;
ret = strcmp (s1, s2);
if(ret == 0)
printf("%s == %s\n",s1, s2);
else
printf("%s %c %s\n",s1,( (ret < 0)?'<':'>'), s2);
}
int main()
{
char str1[] = "aaa";
char str2[] = "aab";
char str3[] = "abb";
}
aaa
aaa
abb
aaa
String_Vergleich(str1,
String_Vergleich(str1,
String_Vergleich(str3,
String_Vergleich(str1,
return 0;
str2);
str3);
str2);
str1);
< aab
< abb
> aab
== aaa
49
SE
strcpy
char *strcpy(char *s1, const char *s2);
● Kopieren eines Strings in einen adressierten Char-Vektor
● Speicherbereich wird nicht auf Größe überprüft!!!
#include <stdio.h>
#include <string.h>
int main()
{
char ziel_str[50];
char str1[] = "Das ist ";
char str2[] = "ein ";
char str3[] = "Teststring";
strcpy(ziel_str, str1);
/* Ein umständliches Negativbeispiel */
strcpy(&ziel_str[8], str2);
/* So ist es einfacher und sicherer */
strcat(ziel_str, str3);
printf("%s\n",ziel_str);
return 0;
}
● mit strcpy(&ziel_str[8], str2); können auch Strings aneinander gehangen
werden, ist aber wesentlich umständlicher als mit strcat
strcspn
int strcspn(const char *s1, const char *s2);
● einen Teilstring ermitteln
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "Das ist ein Teststring";
int pos;
pos = strcspn( string, "Ttg" );
printf("Erstes Auftreten von T, t oder g an Pos.: %d\n",pos);
return 0;
}
Erstes Auftreten von T, t oder g an Pos.: 6
50
SE
strlen
size_t strlen(const char *s1);
● Länge eines Strings ermitteln
● gibt die Länge des Strings ohne das \0 zurück
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "Das ist ein Teststring";
size_t laenge;
laenge = strlen(string);
printf("Der String \"%s\" hat %d Zeichen\n",string, laenge);
return 0;
}
strncat
char *strncat(char *s1, const char *s2, size_t n);
● hängt s2 an das Ende von s1
● übergibt einen Integer, der die Anzahl der anzuhängenden Zeichen
festlegt
#include <stdio.h>
#include <string.h>
#define MAX 15
int main()
{
char string[MAX] = "Hallo ";
char puffer[20];
/* Vorhandenen Platz in string ermitteln*/
size_t len = MAX - strlen(string)+1;
printf("Ihr Name: ");
fgets(puffer, 20, stdin);
strncat(string, puffer, len);
printf("%s",string);
return 0;
}
51
SE
strncmp
int strncmp(const char *s1, const char *s2, size_t n);
● vergleichen der ersten n Zeichen der beiden Strings
● Rückgabewert wie bei strcmp
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "aaaa";
char str2[] = "aabb";
int i;
for(i = strlen(str1); i > 0; i--)
{
if(strncmp( str1, str2, i) != 0)
printf("Die ersten %d Zeichen der Strings "\
"sind nicht gleich\n",i);
else
{
printf("Ab Zeichen %d sind "\
"beide Strings gleich\n",i);
/* Weiter vergleich sind nicht mehr nötig */
break;
}
}
return 0;
}
Die ersten 4 Zeichen der Strings sind nicht gleich
Die ersten 3 Zeichen der Strings sind nicht gleich
Ab Zeichen 2 sind beide Strings gleich
52
SE
strncpy
char *strncpy(char *s1, const char *s2, size_t n);
● Kopiert n Zeichen aus s2 nach s1
#include <stdio.h>
#include <string.h>
#define MAX 20
int main()
{
char str1[MAX];
char str2[] = "Ein Teststring welcher laenger"\
" als 20 Zeichen ist";
/* MAX-Zeichen in str1 kopieren */
strncpy(str1, str2, MAX);
/* Wichtig, String am Ende terminieren !! */
str1[MAX] = '\0';
printf("%s\n",str1);
return 0;
}
Ein Teststring welch
strpbrk
char *strpbrk( const char *s1, const char *s2);
● gibt alle Zeichen aus s1 wieder, die hinter einem der Zeichen folgen, die
in s2 angegeben sind
#include <stdio.h>
#include <string.h>
int main()
{
char str1[]="Das ist ein Teststring";
char str2[]="ie";
}
printf("%s\n",strpbrk(str1, str2));
return 0;
ist ein Teststring
53
SE
strrchr
char *strrchr(const char *s, int ch);
● ermitteln des letzten Zeichens, das in ch angegeben wird und in s gesucht
wird
#include <stdio.h>
#include <string.h>
int main()
{
char string[20];
char *ptr;
}
printf("Eingabe machen: ");
fgets(string, 20 , stdin);
/* Zeiger auf die Adresse des Zeichens \n */
ptr = strrchr(string, '\n');
/* Zeichen mit \0 überschreiben */
*ptr = '\0';
printf("%s",string);
return 0;
● Überschreiben des \n, das fgets automatisch am Ende eines Strings setzt
mit \0
strspn
int strspn(const char *s1, const char *s2);
● gibt erste Position an, an der ein Zeichen aus s1 nicht in s1 zu finden ist
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "75301234-2123";
int pos = strspn(string, "0123456789");
printf("Position, welche keine Ziffer ist:");
printf(" %d\n",pos); /* 8 */
return 0;
}
Position, welche keine Ziffer ist: 8
54
SE
strstr
char *strstr(const char *s1, const char *s2);
● durchsuchen von s1 nach dem Teilstring, der in s2 angegeben ist
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "Das ist ein Teststring";
char suchstring[] = "ein";
if( strstr(string, suchstring) != NULL)
printf("Suchstring \"%s\" gefunden\n",suchstring);
return 0;
}
Suchstring "ein" gefunden
55
SE
strtok
char *strtok(char *s1, const char *s2);
● Zertrennen von Strings
● s1 wird von Token aus s2 getrennt
● ein Token ist ein String, der keine Zeichen von s2 enthält
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "Ein Teststring mit mehreren Worten\n"
"und mehreren Zeilen.\t Ende\n";
int i=1;
char *ptr;
ptr = strtok(string, "\n\t ");
while(ptr != NULL)
{
printf("% d. Wort: %s\n",i++,ptr);
ptr = strtok(NULL, "\n\t ");
}
return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
Wort:
Wort:
Wort:
Wort:
Wort:
Wort:
Wort:
Wort:
Wort:
Ein
Teststring
mit
mehreren
Worten
und
mehreren
Zeilen.
Ende
56
SE
6.4. Mehrdimensionale Felder
● definiert als Feld von Feld von Feld...
Datentyp iMatrix [Zeilen][Spalten] = {
double dMatrix [3][4];
{Initialisierungsliste 1},
{Initialisierungsliste 2},
{Initialisierungsliste 3}
};
 Feld mit 3 Elementen, wobei jedem der 3 Elemente 4 weitere
untergeordnet sind
 Zeilen kann bei direkter Initialisierung offen gelassen werden
int Matrix[4][4] = { {0},
{1},
{0,1},
{0,0,1} };
● hier wird eine 4x4 Matrix initialisiert und gleich deklariert
● alle Werte, die nicht auf 1 gesetzt sind 0
● für jede weitere Dimension müssen neue geschweifte Klammern eingefügt
werden
int dreid[][][]= {1.Feldindex{2.Feldindex{3.Feldindex}}};
57
SE
6.4.1. Beispiel Matrix Quadratwerte
#include<stdio.h>
int main(void)
{
float AdMatrix [] [2] = {
{1.6, 2.43},
{5.634, 6.2345},
{4.123, 7.735}
};
float AdQMatrix [3] [2];
int i=0, j=0;
//AdMatrix ausgeben
printf ("Ausgangsmatrix:\n");
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
printf("%8.2f", AdMatrix[i][j]);
printf("\n");
}
//Quadrtrat der Matrix
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
AdQMatrix [i][j]= AdMatrix[i][j] * AdMatrix[i][j];
}
//AdQMatrix ausgeben
printf ("Quadrierte Matrix:\n");
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
printf("%8.2f", AdQMatrix[i][j]);
printf("\n");
}
return 0;
}
 %8.2f bei printf ist Feldbreite.Genauigkeit
● Ausgabe:
Ausgangsmatrix:
1.60 2.43
5.63 6.23
4.12 7.74
Quadrierte Matrix:
2.56 5.90
31.74 38.87
17.00 59.83
58
SE
7. Funktionen
●
●
●
●
Ablauf eines Programms beginnt immer mit der main Funktione!!!
Unterprogramme
lösen von Teilprobleme
keine parallele Ausführung von Funktionen
● Vorteile:
 Übersichtlicherer Code
 wiederverwenden durch Erstellen eines Headers
 Zusammenfassen von sich wiederholenden Aufgaben
 einfacheres Verändern des Codes
7.1. Definition von Funktionen
● Syntax:
Rückgabetyp Funktionsname (Parameter)
{
/*Anweisungsblock*/
}
● Hautpfunktion int main (void)
Rückgabetyp
● Datentyp des Rückgabewertes
● ohne Rückgabewert → void
Funktionsname
● eindeutiger Name
● gleiche Benennung wie Variablen
● zum Aufruf innerhalb einer Funktion
Parameter
● optionale
● durch Datentyp und Name spezifiziert
● durch Komma getrennt
● kein Parameter → void oder garnichts
Anweisungsblock
● wie bei normaler Funktion
59
SE
7.2. Funktionsaufruf/-deklaration/-definiton
● In Anweisungsblock Funktionsname und in Klammern Parameter
#include <stdio.h>
void hilfe(void)
{
printf("Ich bin die Hilfsfunktion\n");
}
int main()
{
hilfe();
return 0;
}
● Deklaration vor main-Funktion → Vorwärtsdeklaration
● Deklaration mit ; abschließen!
● Definition kann an beliebiger Stelle stattfinden
● nach Deklaration
● kein ; wegen Anweisungsblock
#include <stdio.h>
void func1();
void func2();
void func3();
void func1()
{
printf("Ich bin func1 \n");
func3();
}
void func2(void)
{
printf("Ich bin func2 \n");
}
void func3()
{
printf("Ich bin func3 \n");
func2();
}
int main()
{
func1();
return 0;
}
● Vorwärtsdeklaration hier unbedingt notwendig
● auch notwendig für die Verwendung von Funktionen über Dateigrenzen hinweg
60
SE
7.3. Lokale Variablen
● Lokalste Variable is die im Anweisungsblock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main()
{
int i=333;
if(i == 333)
{
int i = 111;
printf("%d\n",i); /* 111 */
}
printf("%d\n",i);
/* 333 */
return 0;
}
● gibt 333 \n 111 aus
● weil das i im ersten Anweisungsblock eine andere Variable ist, wie das i,
das im zweiten Anweisungsblock deklariert wird
● eine Variable ist nur für den Anweisungsblock in dem sie steht und für
Anweisungsblöcke, die in dem Anweisungsblocke enthalten sind in der sie
deklariert wurde, gültig
● wenn in Zeile 9 nur i=111 steht ist die Ausgabe 111 \n 111
● bei gleichnamigen Variablen ist immer die lokalste Variable gültig
● also die die dem Anweisungsblock am nächsten steht
● lokale Variablen in Funktionen verhalten sich genauso wie in
Anweisungsblöcke
#include <stdio.h>
void aendern()
{
int i = 111;
printf("In der Funktion aendern: %d\n",i);
}
int main()
{
int i=333;
printf("%d\n",i);
aendern();
printf("%d\n",i);
return 0;
}
● gibt 333 \n In der Funktion aendern: 111 \n 333 aus
● Varialbe muss aber in der neuen Funktion definiert werden
61
SE
7.4 Globale Variablen
● Sind in allen Funktionen gültig
● müssen im Voraus deklariert werden
● werden vor der ersten Funktion deklariert
#include <stdio.h>
int i=333; /* Globale Variable */
void aendern()
{
i = 111;
printf("In der Funktion aendern: %d\n",i); /* 111 */
}
int main()
{
printf("%d\n",i);
aendern();
printf("%d\n",i);
return 0;
}
/* 333 */
/* 111 */
● gibt 333 \n In der Funktion aendern: 111 \n 111 aus
#include <stdio.h>
int i=333;
/* Globale Variable i */
void aendern()
{
i = 111;
/* Ändert die globale Variable */
printf("In der Funktion aendern: %d\n",i); /* 111 */
}
int main()
{
int i = 444;
printf("%d\n",i);
aendern();
printf("%d\n",i);
return 0;
}
/* 444 */
/* 444 */
● hier gilt aber das gleiche für lokale Variablen
● gibt 444 \n In der Funktion aendern:111 \n 444 aus
● Varaiblen so lokal wir möglich und so global wie nötig
62
SE
7.5. Statische Variablen
#include <stdio.h>
void inkrement()
{
int i = 1;
printf("Wert von i : %d\n",i);
i++;
}
int main()
{
inkrement();
inkrement();
inkrement();
return 0;
}
● gibt 1 \n 1 \n 1 aus
● Variable wird bei jedem Durchlauf der Fkt erneut mit 1 initialisiert
#include <stdio.h>
void inkrement()
{
static int i = 1;
printf("Wert von i : %d\n",i);
i++;
}
int main()
{
inkrement();
inkrement();
inkrement();
return 0;
}
● gibt 1 \n 2 \n 3 aus
● statische Varialben verlieren nach Beendigung der Funktion nicht ihren
Wert bzw. ihre Speicheradresse
● werden wo anders in der Hardware gespeichert
● statische Varaiblen müssen bei Deklaration initialisiert werden
63
SE
7.6. Datenaustausch zwischen Funktionen
● Funktion als Schnittstelle
● dadurch können globale Variablen gespart werden
 durch Werteübergabe
Übertragen von Daten in die Funktion mittels
Parameter
 durch Werterückgabe
Daten aus der Funktion
7.6.1 Funktion mit Wertübergabe – call by value
●
●
●
●
Werte in Klammern
werden kopiert
hier von z nach zahl
können auch mehrere Werte mit Komma getrennt übergeben werden
#include <stdio.h>
void verdoppeln(int);
void halbieren(int);
ind dann
void halbieren(int zahl)
{
zahl/=2;
printf("Halbiert: %d\n",zahl);
}
void verdoppeln(int zahl)
{
zahl*=2;
printf("Verdoppelt: %d\n",zahl);
}
int main()
{
int wahl, z;
printf("Bitte geben Sie eine Zahl ein : ");
scanf("%d",&z);
printf("Wollen Sie diese Zahl\n");
printf("\t1.)verdoppeln\n\t2.)halbieren\n\nIhre Wahl : ");
scanf("%d",&wahl);
}
switch(wahl)
{
case 1 : verdoppeln(z);
break;
case 2 : halbieren(z);
break;
default: printf("Unbekannte Eingabe\n");
}
return 0;
64
SE
7.6.2. Funktion mit Wertrückgabe
● Ende der Funktion mit return
● Wert wird an aufrufende Funktion zurückgeliefert
●
#include <stdio.h>
int bignum(int a, int b)
{
if(a > b)
return a;
else if(a < b)
return b;
else
return 0; /* beide Zahlen sind gleich groß */
}
int main()
{
int wert1, wert2, big;
printf("Bitte einen Wert eingeben: ");
scanf("%d",&wert1);
printf("Bitte noch einen Wert eingeben: ");
scanf("%d",&wert2);
}
big = bignum(wert1, wert2);
if(big != 0)
printf("%d ist die größere der beiden Zahlen\n",big);
else
printf("Beide Zahlen haben denselben Wert\n");
return 0;
7.7. Die Hauptfunktion main()
●
●
●
●
●
Funktion des Typs int
Startup-Code wird beim Beginn des Prozesses erzeugt
damit wird auch der Prozess wieder beendet
zurückspringen zum Startup-Code mit return 0
er führt dann die Funktion exit aus und räumt auf, z.B. Freigabe der
Speicherplätze...
int main()
{
return 0;
}
● Prozess ist Programm während seiner Ausführung
65
SE
7.8. Rekursive Funktionen
● Rufen sich selbst immer wieder auf
● ohne Abbruchbedingung gibt es einen Stack-Overflow
int divide(int x, int y)
{
if(x>=y)
return (1 + divide(x-y, y));
if(x)
printf("Zahl nicht teilbar -> Rest: %d -> ", x);
return 0;
}
● z.B. Teilen mit Rest ohne / oder % zu verwenden
Beispiele Fakultät
#include <stdio.h>
long fakul(long n)
{
if(n)
return n * fakul(n-1);
return 1;
}
int main()
{
printf("Fakultät von 5 = %ld\n",fakul(5));
printf("Fakultät von 9 = %ld\n",fakul(9));
return 0;
}
Beispiele Text invertieren
#include <stdio.h>
void reverse(char *s)
{
if(*s)
reverse(s+1);
putchar(*s);
/* putchar gibt ein Zeichen aus */
}
int main()
{
reverse("\nSome men interpret nine memos");
reverse("\nHALLO");
return 0;
}
66
SE
Beispiel Fibonacci
#include <stdio.h>
long fibo(long n)
{
if(n)
return (n<=2) ?n:fibo(n-2)+fibo(n-1);
}
int main()
{
long f;
long i=0;
printf("Wie viele Fibonacci-Zahlen wollen Sie ausgeben:");
scanf("%ld",&f);
while(i++ < f)
printf("F(%ld) = %ld\n", i, fibo(i));
return 0;
}
Beispiele Größter gemeinsamer Teiler
#include <stdio.h>
unsigned long ggt(unsigned long a, unsigned long b)
{
if(a==b)
return a;
else if(a<b)
return ggt(a,b-a);
else
return ggt(a-b,b);
}
int main()
{
unsigned long a,b;
printf("ggt = größter gemeinsamer Teiler\n");
printf("Zahl 1: ");
scanf("%lu",&a);
printf("Zahl 2: ");
scanf("%lu",&b);
printf("Der ggT von %lu und %lu ist %lu\n", a, b, ggt(a,b));
return 0;
}
67
SE
Alternative GGT
#include <stdio.h>
unsigned long ggt(unsigned long a, unsigned long b)
{
unsigned long count;
if(a==b)
return a;
else if((a%b)==0)
return b;
else
for(count=b; count>0; count--)
{
if(((a%count) + (b%count)) == 0)
return count;
}
}
int main()
{
unsigned long a,b,c;
printf("ggt = größter gemeinsamer Teiler\n");
printf("Zahl 1: ");
scanf("%lu",&a);
printf("Zahl 2: ");
scanf("%lu",&b);
if(a<b)
{ /*a und b vertauschen*/
c=a; a=b; b=c;
}
printf("Der ggT von %lu und %lu ist %lu\n", a, b, ggt(a,b));
return 0;
}
68
SE
Beispiel GGT für beliebig viele Zahlen
#include <stdio.h>
unsigned long ggt(unsigned long a, unsigned long b)
{
if(b==0)
return a;
return ggt(b, a%b);
}
int main()
{
unsigned long a,b;
printf("ggt = größter gemeinsamer Teiler(mit 0 beenden)\n");
printf("Zahl> ");
scanf("%lu", &a);
printf("Zahl> ");
scanf("%lu", &b);
a=ggt(a, b);
while(1)
{
printf("Zahl> ");
scanf("%lu", &b);
if(b==0)
break;
a=ggt(a, b);
}
printf("-------->ggt = %lu\n", a);
return 0;
}
Beispiel Dezimalzahl zu Dualzahl
#include <stdio.h>
void dez2bin(unsigned long dez)
{
if(dez)
{
dez2bin(dez/2);
printf("%lu", dez%2);
}
}
int main()
{
unsigned long dezimal;
printf("Dezimalzahl in Dualzahl konvertieren \n");
printf("Welche Zahl soll konvertiert werden: ");
scanf("%lu", &dezimal);
printf("Dezimal = %lu \nDual = ",dezimal);
dez2bin(dezimal);
printf("\n");
return 0;
}
69
SE
7.9. Übergabe von Arrays an Funktionen
void function(int feld[], int MAX)
● Indexwert für Größe wird nicht angegeben, weil der Funktion nicht bekannt ist,
wieviele Werte der Array hat
● empfehlenswert die Angabe als Argument mitzugeben – oben MAX
● Arrays werden über Zeiger weitergegeben → keine Kopie der ganzen Werte,
sondern lediglich der Anfangsadresse
● nicht call-by-value, sondern call-by-reference
●
function(&feld[0], MAX); ist identisch zu dem oben
#include <stdio.h>
struct array{ int wert[3]; };
void output_array(struct array z)
{
int i;
for(i=0; i < sizeof(struct array)/sizeof(int); i++)
printf("%d\t",z.wert[i]);
printf("\n");
}
int main()
{
struct array new_array;
new_array.wert[0] = 10;
new_array.wert[1] = 20;
new_array.wert[2] = 30;
/* call-by-value */
output_array(new_array);
return 0;
}
7.10. Arrays aus Funktionen zurückgeben
● Können nicht als Rückgabetyp von Funktionen definiert werden
#include <stdio.h>
struct array{ int wert[3]; };
struct array init_array()
{
int i;
struct array z;
for(i=0; i < sizeof(struct array)/sizeof(int); i++)
{
printf("Wert %d eingeben: ",i);
scanf("%d",&z.wert[i]);
}
return z;
}
70
SE
void output_array(struct array z)
{
int i;
for(i=0; i < sizeof(struct array)/sizeof(int); i++)
printf("%d\t",z.wert[i]);
printf("\n");
}
int main()
{
struct array new_array;
/* Array als Rückgabewert in einer Struktur verschachtelt */
new_array=init_array();
/* call-by-value */
output_array(new_array);
return 0;
}
71
SE
72
SE
73
SE
●
74
SE
8. Präprozessoranweisungen
● Teil dds Compilers
● nimmt Änderungen am Quelltext vor dessen Übersetzung vor
● Aufgaben
 entfernen Zeilenumbrüche mit Backslash am Anfang
 entfernen von Kommentaren
 entfernen von Whitespacecharakters
 Header- und Quelldateien kopieren
 Konstanten einbinden
 Bedingte Kompilierung
8.1. Einkopieren von Dateien mittels #include
● Meinstens Einfügen von Headern
● Direktiv wird einfach durch Quellcode ersetzt
● Dateipfad in Anführungszeichen
● im implementierungsdefinierten Pfad dann in Dreiecksklammern
● im aktuellen Arbeitsverzeichniss dann in Anführungszeichen
● Standardheader
 assert.h Fehlersuche und Debugging
 ctype.h Zeichentest und Konvertierung
 errno.h Fehlercodes
 float.h Limits/Eigenschaften für Gleitpunkttypen
 limits.h Implementierungskonstanten
 locale.h Länderspezifische Eigenschaften
 math.h Mathematische Funktionen
 setjmp.h
Unbedingte Sprünge
 signal.h Signale
 stdarg.h Variable Parameterübergabe
 stddef.h Standard-Datentyp
 stdio.h Standard-I/O
 stdlib.h Nützliche Funktionen
 string.h Zeichenkettenoperationen
 time.h Datum und Uhrzeit
75
SE
8.2. Markos und Konstanten
8.2.1. symbolische Konstanten
#include <stdio.h>
#define EINS 1
int main()
{
printf("%d\n",EINS);
return 0;
}
● Präprozessor ersetzt jedesmal die Konstante bei ihrem Aufruf mit dem
Wert, der am Anfang des Programms definiert wurde
● Konstanten können im Verlauf des Programms nicht verändert werden
● es können auch Strings definiert werden, z.B. #define SCHREIB printf(
● einfacher zu Ändern
● weniger Tippaufwand für längere Konstanten, die öfter verwendet werden
● wenn mit define eine Konstante festgelegt wird, die aus einer Berechnung
hervorgeht, wird diese jedesmal erneut berechnet, z.B. #define PI
atan(1)*4
● besser ist es eine konstante Variable zu deklarieren, z.B. const double PI =
atan(1)*4;
76
SE
8.2.2 Makros
#include <stdio.h>
#define KLEINER_100(x) ((x) < 100)
void klHundert(int zahl)
{
if(KLEINER_100(zahl))
printf("Yep! Die Zahl ist kleiner als 100!\n");
else
printf("Die Zahl ist größer als 100!\n");
}
int main()
{
int b=100;
klHundert(b);
return 0;
}
● parametrisierte Makros mit (*Parameter*) am Ende
● mehrere Parameter mit Komma getrennt
● bei Definition über mehrere Zeilen muss jede Zeile mit \ abgeschlossen
werden
● lange Makros sind nicht sinnvoll → Funktionen
● Parameter muss auf beiden Seiten in Klammern stehen
● kein ; am Ende der Zeile
● durch #undef kann ein Makro oder eine Konstante aufgehoben werden
● ansonsten gilt sie bis zum Dateiende
#define MAX(x,y) ( (x)<=(y) ?(y) :(x) )
#define TAUSCHE(x,y)
int j; \
j=x; x=y; y=j; \
}
{ \
● häufige Verwendung
77
SE
8. Zeiger
●
●
●
●
●
●
●
●
Stärkstes und gefährlichstes Feature von C
Syntaxcrash bei unsachgemäßer Verwendung
Art Link
entscheidend ist die Variable, die in der
angegebenen Adresse gespeichert ist
mehrere Zeiger können auf eine Adresse
zeigen
Arrays werden über Zeiger realisiert
Stern für jeden Zeiger extra, z.B. bei Aufzählung mit Komma...
kann im Verlauf des Programms erneut zugewiesen werden
● * ist Dereferenzierungsoperator oder Indirektionsoperator
 ohne * steht für die Adresse
 mit * steht für den Wert, der in der Adresse steht
Verwendung:
● Speicherbereiche können dynamisch reserviert, verwaltet und wieder gelöscht
werden.
● Mit Zeigern können Sie Datenobjekte direkt (call-by-reference) an Funktionen
übergeben.
● Es lassen sich Funktionen als Argumente an andere Funktionen übergeben.
● Rekursive Datenstrukturen wie Listen und Bäume lassen sich fast nur mit Zeigern
bewerkstelligen.
● Es lässt sich ein typenloser Zeiger (void *) definieren, womit Datenobjekte beliebigen
Typs verarbeitet werden können.
● Rückgabe eines Wertes aus einer Funktion, auf den z.B: eine Schleife reagieren
könnte
Deklaration:
Datentyp *zeigervariable;
● Datentyp des Zeigers muss mit dem Datentyp übereinstimmen auf den er
zeigt
●
78
SE
Initialisierung:
● ohne Initialisierung zeigt der Zeiger auf irgendeine Adresse
● kann zu Fehlern, Überschreibung... führen
● Speicherort einer Variablen wird beim kompilieren festgelegt
● kann durch printf("%p", &VariabLe); ausgegeben werden
Zeiger=&Variable;
● Zeiger ohne * und Variable mit & stehen für Adressen
● Zeiger mit * und Variable ohne & stehen für gespeicherten Wert
● Zeiger mit & davor steht für die Adresse in der der Zeiger gespeichert ist
Beispiel Menu
#include <stdio.h>
int main()
{
int abfrage;
int Kapitel1 = 5;
int Kapitel2 = 60;
int Kapitel3 = 166;
int Nachtrag = 233;
int *Verzeichnis; /* Zeiger */
do{
printf("\tINDEXREGISTER VOM BUCH\n");
printf("\t*******************************\n\n");
printf("\t-1- Kapitel 1\n");
printf("\t-2- Kapitel 2\n");1
printf("\t-3- Kapitel 3\n");
printf("\t-4- Nachtrag\n");
printf("\t-5- Ende\n");
printf("\n");
printf("\tAuswahl : ");
scanf("%d",&abfrage);
printf("\tKapitel %d finden Sie auf ",abfrage);
switch(abfrage)
{
case 1 : Verzeichnis=&Kapitel1;
printf("Seite %d\n",*Verzeichnis);
break;
case 2 : Verzeichnis=&Kapitel2;
printf("Seite %d\n",*Verzeichnis);
break;
case 3 : Verzeichnis=&Kapitel3;
printf("Seite %d\n",*Verzeichnis);
break;
case 4 : Verzeichnis=&Nachtrag;
printf("Seite %d\n",*Verzeichnis);
break;
default: printf("Seite ???\n");
break;
}
}while(abfrage<5);
return 0;
}
79
SE
Dereferenzieren:
● schreiben auf Variable mit Zeigern
*Zeiger=Wert
● Dereferenzierung eines Zeigers, der auf eine zufällige Adresse Zeigt, weil
er nicht Initialisiert wurde kann zu schweren Fehlern führen
● Wenn noch keine Adresse für Zeiger feststeht mit NULL initialisieren
int *Zeiger=NULL;
● Überprüfung ob der Zeiger auf eine Adresse zeigt
if(Zeiger == NULL)
● Wert eines Zeigers kann auch in einer Variablen gespeichert werden
Variable = *Zeiger;
● gleichsetzten zweier Zeiger – ohne *!!!
piA = piB
● Scanf ohne *, weil Zeiger schon auf Adresse zeigt
scanf („%f“, pfZeiger);
● printf mit *, damit der Wert aus der Adresse ausgegeben wird
printf („%f“, *pfZeiger);
● einen String ausgeben – ohne*!!!
● mit + und %c wird nur der erste Buchstabe ausgegeben
printf("%s",ptr1);
Beispiel schreiben auf Zeiger
#include <stdio.h>
int main()
{
int x=5;
int *y;
y=&x;
printf("%d==%d\n", x, *y);
printf("%p==%p\n", &x, y);
*y=10;
printf("%d==%d!=5\n", x, *y);
printf("%p==%p\n", &x, y);
return 0;
}
80
SE
8.1. Zeigerarithmetik
Operationen mit Pointern
● Ganzzahlwerte erhöhen
● Ganzzahlwerte verringern
● Inkrementieren
● Dekrementieren
● Vergleichsoperatoren
● immer um ein Vielfaches der Größe des Datentyps
● Zeiger+=10 ist 40 Bytes weiter wie Anfangsadresse
Verweis auf andere Zeiger
● können auch auf andere Zeiger verweisen
● kopieren der Adresse
Zeiger1=Zeiger2
8.2. Zeiger als Funktionsparameter - call-by-reference
Zeiger zur Wertübergabe
● Nachteil von call-by-value ist kopieren der zu übergebenden Werte
#include <stdio.h>
#define PI 3.141592f
float kreisflaeche(float wert)
{
return (wert = wert * wert * PI);
}
int main()
{
float radius, flaeche;
printf("Berechnung einer Kreisfläche!!\n\n");
printf("Bitte den Radius eingeben : ");
scanf("%f",&radius);
flaeche = kreisflaeche(radius);
printf("\nDie Kreisfläche beträgt : %f\n",flaeche);
return 0;
}
81
SE
#include <stdio.h>
#define PI 3.141592f
void kreisflaeche(float *wert)
{
*wert = ( (*wert) * (*wert) * PI );
}
int main()
{
float radius;
printf("Berechnung einer Kreisfläche!!\n\n");
printf("Bitte den Radius eingeben : ");
scanf("%f",&radius);
/*Adresse von radius als Argument an kreisflaeche() */
kreisflaeche(&radius);
printf("\nDie Kreisfläche beträgt : %f\n",radius);
return 0;
}
● keine Rückgabe mit return
● Funktionstyp void
● keine Rückgabe notwendig, da der Wert, der in der Übergebenden Adresse
geändert wird und die Adresse für das ganz Programm gleich bleibt
● die Varaible Wert wird nur für die Funktion Kreisflaeche verwendet
● das Ergebnis wird in radius geschrieben
Zeiger zur Wertrückgabe
● geben Anfangsadresse des Rückgabewerts zurück
● geben nur Werte mit dem gleichen Datentyp zurück
● Rückgabe bei Strings oder Strukturen
#include <stdio.h>
#include <string.h>
#define MAX 255
char *eingabe(char *str)
{
char input[MAX];
printf("Bitte \"%s\" eingeben: ",str);
fgets(input, MAX, stdin);
return strtok(input, "\n");
}
int main()
{
char *ptr;
ptr = eingabe("Vorname");
printf("Hallo %s\n",ptr);
ptr = eingabe("Nachname");
printf("%s, interssanter Nachname\n",ptr);
return 0;
}
● strtok untberbricht string bei \n und gibt Anfangsadresse zurück
82
SE
8.2. Zeiger auf Arrays
● Arrays werden über Zeiger realisiert
 int AiFeld[3]
 Adresse ist 4 Byte lang wegen int
 erster Wert steht in Adresse, mit der Array beginnt → 0x4+Basisadresse
 zweiter Wert in Basisadresse+ 1x4
 0, 1... ist Offset
8.2.1. Zugriff auf einen Array mittels Zeiger
#include <stdio.h>
int main()
{
int element[8]= {1,2,4,8,16,32,64,128};
int *ptr;
int i;
ptr=element;
printf("Der Wert auf den *ptr zeigt ist %d\n",*ptr);
printf("Durch *ptr+1 zeigt er jetzt auf %d\n",*(ptr+1));
printf("*(ptr+3) = %d\n",*(ptr+3));
printf("\nJetzt alle zusammen : \n");
for(i=0; i<8; i++)
printf("element[%d]=%d \n",i,*(ptr+i));
return 0;
}
Der Wert auf den *ptr zeigt ist 1
Durch *ptr+1 zeigt er jetzt auf 2
*(ptr+3) = 8
Jetzt alle zusammen :
element[0]=1
element[1]=2
element[2]=4
element[3]=8
element[4]=16
element[5]=32
element[6]=64
element[7]=128
● Zuweisen der Basisadresse des Arrays auf den Zeiger erfolgt ohne
eckige Klammern und ohne Adressoperator
● der Arrayname ohne Adressoperator ist die Basisadresse
● identisch zu ptr=element wäre ptr=&element[0]
● Array ist Zeiger sehr ähnlich
 in der Adresse des Arrays steht der erste Werte, d.i. die Basisadresse
 in der Adresse des Zeiger steht eine weitere Adresse
83
SE
#include <stdio.h>
int main()
{
int element[8] = {1,2,4,8,16,32,64,128};
int i;
printf("*element
= %d\n",*element);
printf("*(element+1) = %d\n",*(element+1));
printf("*(element+3) = %d\n",*(element+3));
}
printf("\nJetzt alle zusammen : \n");
for(i=0; i<8; i++)
printf("*(element+%d) = %d \n",i,*(element+i));
return 0;
*element
= 1
*(element+1) = 2
*(element+3) = 8
Jetzt alle zusammen :
*(element+0) = 1
*(element+1) = 2
*(element+2) = 4
*(element+3) = 8
*(element+4) = 16
*(element+5) = 32
*(element+6) = 64
*(element+7) = 128
● mit Arrays kann so umgegangen werden wie mit Zeigern
● vgl. die letzten beiden Beispiele
int array[10];
/* Deklaration */
int *pointer1, *pointer2;
pointer1=array;
/*pointer1 auf Anfangsadresse von array */
pointer2=array+3;
/* pointer2 auf 4.Element von array */
array[0]
*(array+1)
pointer1[1]
*(pointer1+2)
*pointer2
=
=
=
=
=
99;
99;
88;
77;
66;
/*
/*
/*
/*
/*
array[0]
array[1]
array[1]
array[2]
array[3]
*/
*/
*/
*/
*/
● Dereferenzierungsoperator wenn der Offset dazuaddiert wird
84
SE
8.2.2. Funktionsaufrufe von Arraynamen
int funktion(int elemente[])
/* Gleichwertig mit ... */
int funktion(int *elemente)
Möglichkeiten eine Funktion aufzurufen
int werte[] = { 1,2,3,5,8 };
int *pointer;
pointer = werte;
funktion(werte);
funktion(&werte[0]);
funktion(pointer);
/* 1. Möglichkeit */
/* 2. Möglichkeit */
/* 3. Möglichkeit */
Übergabe der Anzahl der Elemente an eine Funktion
#include <stdio.h>
void funktion(int *array, int n_array)
{
int i;
for(i=0; i < n_array; i++)
printf("%d ",array[i]);
printf("\n");
}
int main()
{
int werte[] = { 1,2,3,5,8,13,21 };
funktion(werte, sizeof(werte)/sizeof(int));
return 0;
}
85
SE
8.2.3 Gemeinsamkeinten Array und Zeiger
#include <stdio.h>
int main()
{
int n=3;
/* eindim. Array mit Platz für 5 Werte*/
int array[5]={ 1,2,3,4,5 };
/* int-Zeiger verweist jetzt auf array[0] */
int *ptr = array;
/* 4 Möglichkeiten, um auf das erste Element zuzugreifen */
printf("%d " ,*ptr);
printf("%d ",ptr[0]);
printf("%d ",*array);
printf("%d\n",array[0]);
/* 4 Möglichkeiten, um auf das n-te Element zuzugreifen */
printf("%d " ,*(ptr+n));
printf("%d ",ptr[n]);
printf("%d ",*(array+n));
printf("%d\n",array[n]);
/* 4 Möglichkeiten, um auf die Anfangsadresse zuzugreifen */
printf("%p " ,ptr);
printf("%p ",&ptr[0]);
printf("%p ",array);
printf("%p\n",&array[0]);
/* 4 Möglichkeiten, um auf die Adresse des n-ten Elements
zuzugreifen */
printf("%p " ,ptr+n);
printf("%p ",&ptr[n]);
printf("%p ",array+n);
printf("%p\n",&array[n]);
return 0;
}
86
SE
8.3. Zeiger auf Strings
● Für Strings gilt das gleiche wie für Arrays
#include <stdio.h>
void funktion(char *str)
{
printf("%s\n",str);
}
int main()
{
char *string = "Hallo Welt";
funktion(string);
printf("Anfangsadresse auf die *string zeigt = %p\n",*string);
printf("Der Inhalt dieser Anfangsadresse
= %c\n",*string);
return 0;
}
Hallo Welt
Anfangsadresse auf die *string zeigt = 0x48
Der Inhalt dieser Anfangsadresse
= H
● str beinhaltet die Adresse in der das H gespeichert wird
87
SE
8.3.1. Zeiger auf konstante Objekte - Read-only-Zeiger
● Variablen muss const vorangestellt sein
● kann nicht verändert, nur gelesen werden
#include <stdio.h>
void funktion1(char *str)
{
char *ptr;
ptr = str+5;
*ptr = '-';
}
int main()
{
char string1[] = "Hallo Welt\n";
funktion1(string1);
printf("%s\n",string1);
return 0;
}
Hallo-Welt
● die Basisadresse von ptr entspricht der Adresse des 6. Elements von str
● welches im Verlauf von funktion1 verändert wird
● Überschreiben wird verhindert durch:
void funktion1(const char *str)
88
SE
8.3.4 Zeiger auf Zeiger und Stringtabellen
● Syntax
datentyp **bezeichner;
● d.i. ein Zeiger, der auf einen Zeiger zeigt, der wiederum auf eine Variable
zeigt
● mehrfache Indirektion
● für gewöhnlich nur zweidimensional
● Verwendbar zur dynamischen Erzegung von mehrdimensionalen Arrays,
z.B. Matrizenberechnung
●
#include <stdio.h>
int main()
{
int wert = 10;
/* ptr ist ein Zeiger auf int wert */
int *ptr=&wert;
/* ptr_ptr ist ein Zeiger auf den Zeiger int *ptr */
int **ptr_ptr=&ptr;
printf("*ptr
= %d\n",*ptr);
printf("**ptr_ptr = %d\n", **ptr_ptr);
/* Verändert den Wert, auf den int *ptr zeigt */
**ptr_ptr = 100;
printf("*ptr
= %d\n",*ptr);
printf("**ptr_ptr = %d\n", **ptr_ptr);
/* Verändert nochmals den Wert */
*ptr = 200;
printf("*ptr
= %d\n",*ptr);
printf("**ptr_ptr = %d\n", **ptr_ptr);
return 0;
}
*ptr
**ptr_ptr
*ptr
**ptr_ptr
*ptr
**ptr_ptr
=
=
=
=
=
=
10
10
100
100
200
200
● *ptr und *ptr_ptr zeigen letztlich auf die gleiche Adresse
● *ptr_ptr=100 würde auf die Speicheradresse 100 verweisen → **!!!
89
SE
8.3.5. Zeiger erhöhen
● Zeiger können wie Indizes erhöht werden
#include <stdio.h>
#define GROESSE 3
int main()
{
int Matrix [GROESSE]=
{ 3, 2, 1 };
int *Zeiger;
Zeiger=Matrix;
printf("Wert X=%d\n", *(Zeiger+1));
Zeiger++;
printf("Wert Y=%d\n", (*Zeiger+1));
return 0;
}
Wert X=2
Wert Y=3
90
SE
8.3.6. Beispiel für Zeiger
●
●
●
●
Sucht Leerzeichen
ersetzt Leerzeichen mit \0
gibt String bis \0 aus
gibt den Rest des Strings aus
#include <stdio.h>
int main()
{
int Ausgabe = 0;
char Satz[] = "ARRAYS STRINGS UND ZEIGER";
char *Temp_z;
for (Temp_z = Satz; Temp_z <= Satz + 24; Temp_z++)
{
if (*Temp_z == 32) /* ASCII 32 ist ein Leerzeichen */
{
*Temp_z = 0; /* ASCII 0 ist ein '\0' */
printf("\n Ausgabe Nr. %d", ++Ausgabe);
printf("\n Satz: %s", Satz);
printf("\n Temp_z: %s", Temp_z+1);
printf("\n");
}
}
return 0;
}
Ausgabe Nr. 1
Satz: ARRAYS
Temp_z: STRINGS UND ZEIGER
Ausgabe Nr. 2
Satz: ARRAYS
Temp_z: UND ZEIGER
Ausgabe Nr. 3
Satz: ARRAYS
Temp_z: ZEIGER
91
SE
9. Kommandozeilenargumente
● Übergabe von Argumenten an die Main
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int i;
for (i=0; i < argc; i++)
{
printf("argv[%d] = %s ", i, argv[i]);
printf("\n");
}
return EXIT_SUCCESS;
}
argv[0] = /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung
●
●
●
●
●
●
Übergabe 2 Argumente an die Main von der Konsole
ein Integer, eine Stringtabelle
Ausgabe ist Stringtabelle
stdlib enthält EXIT_SUCCESS ist definiert als 1
int argc zählt automatisch Anzahl der übergebenen Argumente
**argv ist ein Array aus Zeigern, die auf Adressen zeigen, die Variablen
des gleichen Datentyps enthalten
weltmachtsubzentrum% /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung Hallo
Welt
argv[0] = /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung
argv[1] = Hallo
argv[2] = Welt
● in argv[0] steht für gewöhnlich der Programmname
● Argumente müssen mit Leerzeichen getrennt werden
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
printf("Insgesamt %d Argumente\n", argc-1);
printf("Letztes Argument: %s\n", argv[argc-1]);
return EXIT_SUCCESS;
}
weltmachtsubzentrum% /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung Hallo
Welt
Insgesamt 2 Argumente
Letztes Argument: Welt
92
SE
Beispiel Taschenrechner
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
int i, j;
long y, erg;
if (argc < 4)
{
printf("Benötige mindestens 4 Argumente!\n");
printf("Aufruf: %s <zahl> <op> <zahl> ...\n", *argv);
return EXIT_FAILURE;
}
/* 1.Zahl in einen Integer konvertieren*/
erg = strtol(argv[1], NULL, 10);
if (erg == 0)
{
printf("Keine gültige Ganzzahl ... \n");
return EXIT_FAILURE;
}
for (i = 1; i < argc-1; i += 2)
{
for (j=i+1; j < i+2; j++)
{
y = strtol(argv[i+2], NULL, 10);
if (y == 0)
{
printf("Keine gültig Ganzzahl ... \n");
printf("argc: %d (%s)?!\n", i+2, argv[i+2]);
return EXIT_FAILURE;
}
if (strcmp(argv[j], "+") == 0)
erg += y;
else if (strcmp(argv[j], "-") == 0)
erg -= y;
else if (strcmp(argv[j], "x") == 0)
erg *= y;
else if (strcmp(argv[j], "/") == 0)
erg/=y;
else
{
printf("Ungültiger Operand: %s\n", argv[j]);
return EXIT_FAILURE;
}
}
}
printf("Ergebnis: %d\n", erg);
return EXIT_SUCCESS;
}
weltmachtsubzentrum% /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung 2 + 7 5/2
Ergebnis: 2
93
SE
●
●
●
●
●
●
●
●
●
●
Argumenten unbedingt mit Leerzeichen trennen
kein Punkt vor Stricht
nur Integer als Ergebnis
Übergabe von 7 Argumenten
jedes Argument hat ein \0 am Ende
müssen mindestens 4 Argumente eingegeben werden, sonst
Fehlermeldung
 Prog.name, Zahl, Operand, Zahl
strtol um String in float umzuwandeln
1. For-Schlaufe durchläuft die ungeraden Feldindizes, die Integer enthält
2. For-Schlaufe durchläuft die geraden Feldindizes, die Operanden
strcmp vergleicht string
9.1. Optionen/Schalter
● Schalter mit -- davor
Beispiel Verändert Eingegebenen Text in Abhängigkeit des Schlaters
#include <stdio.h>
#include <string.h>
#include <ctype.h> /* tolower(), toupper(), isalpha() */
#include <stdlib.h>
#define FALSE 0
#define TRUE 1
#define BUF 4096
void show_help(void) {
printf("\nProgrammaufruf: myecho [OPTION] STRING\n"\
"Programm gibt den Text in gewünschter Form auf"\
"dem Bildschirm aus\n\nFolgende Optionen stehen"\
"Ihnen zur Verfügung:\n\n"\
"\t-r Text wird spiegelverkehrt ausgegeben\n"\
"\t-g Text wird in Grossbuchstaben ausgegeben\n"\
"\t-s Text wird in Kleinbuchstaben ausgegeben\n "\
"\t-h Dieser Text\n"
"\t-v Versionsnummer\n\n");
}
int getopt(char *argument, char *option) {
if( argument[0]=='-' && argument[1]==option[0] )
return TRUE;
return FALSE;
}
void spiegeln(char *string) {
char *reverse = string;
while(*reverse++);
while(reverse-- != string)
printf("%c",*reverse);
printf("\n");
}
void larger(char *string) {
char *large=string;
while(*large)
printf("%c",(isalpha(*large))?toupper(*large++):*large++);
printf("\n");
}
94
SE
void smaller(char *string) {
char *small=string;
while(*small)
printf("%c",(isalpha(*small))?tolower(*small++):*small++);
printf("\n");
}
int main(int argc, char **argv) {
int counter=3;
char buffer[BUF];
size_t len=0;
if(argc == 1 || getopt(argv[1],"h") == TRUE ) {
show_help();
return EXIT_FAILURE;
}
else if(getopt(argv[1],"v") == TRUE) {
printf("Version 1.0\n");
return EXIT_SUCCESS;
}
else if(argc < 3) {
show_help();
return EXIT_FAILURE;
}
len=strlen(argv[2])+1;
/* Ab argv[2] bis argv[n] alle Elemente in buffer */
if(len > BUF) {
printf("Der String enthält zu viele Zeichen\n");
return EXIT_FAILURE;
}
strcpy(buffer,argv[2]);
while(argv[counter] != NULL) {
len += strlen(argv[counter])+2;
if(len > BUF) {
printf("Der Puffer ist bereits voll\n");
break;
}
strcat(buffer, " ");
strcat(buffer, argv[counter++]);
}
if(getopt(argv[1],"r") == TRUE)
spiegeln(buffer);
else if(getopt(argv[1],"g") == TRUE)
larger(buffer);
else if(getopt(argv[1],"s") == TRUE)
smaller(buffer);
else
show_help();
return EXIT_SUCCESS;
}
95
SE
weltmachtsubzentrum% /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung
Programmaufruf: myecho [OPTION] STRING
Programm gibt den Text in gewünschter Form aufdem Bildschirm aus
Folgende Optionen stehenIhnen zur Verfügung:
-r Text wird spiegelverkehrt ausgegeben
-g Text wird in Grossbuchstaben ausgegeben
-s Text wird in Kleinbuchstaben ausgegeben
-h Dieser Text
-v Versionsnummer
weltmachtsubzentrum% /home/weltmachtsubzentrum/workspace/Uebung/Debug/Uebung -r abc
cba
10. Dynamische Speicherverwaltung
10.1. Speicherallokation mit malloc()
#include <stdlib.h>
void *malloc(size_t size);
● gibt die Anfangsadresse eines Speicherbereichs in der angeforderten
Größe zurück
● die Größe des zu reservierenden Bereichs muss in Byte angegeben
werden
● Problem ist unterschiedliche Größe der Datentypen auf verschiedenen
Systemen, besser der Compiler berechnet die Größe selbst
● gibt bei Fehler Null zurück – wenn nicht mehr genügend
zusammenhängender Speicher gefunden werden kann
p=(int *)malloc(sizeof(int));
● weißt p die Anfangsadresse eines Bereichs zu, in dem ein int gespeichert
werden kann
● (int *) ist Typenkonfertierung der voidfunktion
96
SE
Beispiel
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;
p=(int *)malloc(2*sizeof(int));
if(p != NULL)
{
*p=99;
/* Alternativ auch p[0] = 99 */
*(p+1) = 100; /* Alternativ auch p[1] = 100 */
printf("Allozierung erfolgreich ... \n");
}
else
printf("Kein Speicherplatz vorhanden!!!\n");
}
printf("%d %d\n",p[0],p[1]);
/* Sie können die Werte auch so ausgeben lassen */
printf("%d %d\n",*p, *(p+1));
return 0;
● reserviert Speicher für 2 int
● Zugriff durch p[0],p[1] oder *p, *(p+1)
3 Möglichkeiten zur Übergabe der Größe:
● Als numerische Konstante:
 p=(int *)malloc(sizeof(2));
 Problem der verschiendenen Größen der Datentypen auf verschiedenen
Systeme
● Die Angabe des Datentyps mithilfe des sizeof-Operators:
 p=(int *)malloc(sizeof(int));
 Problem, dass wenn plötzklich statt int double benötigt werden
● Den dereferenzierten Zeiger selbst für den sizeof-Operator verwenden:
 p=(double *)malloc(sizeof(*p));
 wichtig ist Dereferenzierungsoperator
 ohne wird Größe des Zeigers übergeben, nicht des zugewisenen
Datentyps → Überlappung des Speicherbereichs
Fehler bei Reservierung
● #define check4error(x) if(NULL == x) { printf(„Es ist ein Fehler in der
Funktion malloc aufgetreten, das Programm wird beendet.“): exit(0);}
97
SE
10.2. Speicherbereich wieder freigeben - free()
#include <stdlib.h>
void free (void *p);
Beispiel
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;
p=(int *)malloc(sizeof(int));
if(p != NULL)
{
*p=99;
printf("Allozierung erfolgreich ... \n");
}
else
printf("Kein Speicherplatz vorhanden!!!\n");
}
if(p != NULL)
free(p);
return 0;
● Speicherbereich wird nur freigegeben, wenn p auf eine Adresse verweist
● p zeigt nach der Freigabe immer noch auf den Bereich, auch wenn er zum
überschreiben wieder freigegeben ist
 entweder nach freigabe auf NULL setzten
 oder Makro definieren, z.B. #define myfree (x) free(x); *x=NULL
98
SE
Beispiel reserviert dynamischen String, Länge abhängig von Eingabe
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF 80
int main()
{
char puffer[BUF];
char *dyn_string;
printf("Ein Text mit max. 80 Zeichen: ");
fgets(puffer, BUF, stdin);
dyn_string = (char *)malloc(strlen(puffer)+1);
if(dyn_string != NULL)
strncpy(dyn_string, puffer, strlen(puffer)+1);
else
printf("Konnte keinen Speicherplatz reservieren\n");
printf("%s",dyn_string);
free(dyn_string);
return 0;
}
99
SE
10.3. dynamische Arrays
#include <stdio.h>
#include <stdib.h>
#include <string.h>
int main()
{
int *value;
int size;
int i=0;
printf("Wie viele Werte benötigen Sie : ");
scanf("%d",&size);
value=(int *)malloc(size*sizeof(int));
if( NULL == value )
{
printf("Fehler bei malloc....\n");
exit(0);
}
while( i<size )
{
printf("Wert für value[%d] eingeben : ",i);
scanf("%d",&value[i]);
i++;
}
printf("Hier Ihre Werte\n");
for(i=0; i<size; i++)
printf("value[%d] = %d\n",i,value[i]);
return 0;
}
● liest vom Benutzer die Anzahl der zu speichernden Werte ein
● reserviert entsprechend die notwendige Menge an Speicherplatz
10.4. realloc und calloc
void *calloc(size_t anzahl, size_t groesse);
void *realloc(void *zgr, size_t neuegroesse);
int *zahlen;
zahlen=(int *)calloc(100,sizeof(int));
● reserviert Speicher für 100 Werte des Typs int
● werden auf 0 initialisiert, wohingegen malloc die werte nicht intialisieren
würde
100
SE
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n=0,
max=2,
z,i,
*zahlen=NULL;
/*Wir reservieren Speicher für 10 int-Werte mit calloc*/
zahlen = (int *)calloc(max, sizeof(int));
if(NULL == zahlen)
{
printf(".....Speicherplatzmangel!!!!!\n");
exit(0);
}
printf("Zahlen eingeben --- Beenden mit 0\n");
/* Endlossschleife */
while(1)
{
printf("Zahl (%d) eingeben : ",n+1);
scanf("%d",&z);
if(z==0)
break;
/*Reservierung von Speicher während der Laufzeit
des Programms mit realloc*/
if(n>=max)
{
max+=max;
zahlen = (int *)realloc(zahlen,max*sizeof(int));
if(NULL == zahlen)
{
printf("Speicherplatzmangel!!!\n");
exit(1);
}
printf("Speicherplatz reserviert "
" (%d Bytes)\n",sizeof(int)*max);
}
zahlen[n++]=z;
}
printf("Folgende Zahlen wurden eingegeben ->\n\n");
for(i=0;i<n;i++)
printf("%d ",zahlen[i]);
printf("\n");
free(zahlen);
return 0;
}
● der Speicherbereich kann mit realloc während des laufenden Programms
verändert werden
 gespeicherter werte werden zwischengespeichert
 neuer Speicherbereich wird gesucht
 Werte werden wieder zurückkopiert
101
SE
Beispiel dynamischer String
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF 5
int main()
{
size_t len;
char *str=NULL;
char puffer[BUF];
}
printf("Ein dynamisches char-Array für Strings\n");
printf("Eingabe machen : ");
fgets(puffer, BUF, stdin);
str = (char *)malloc(strlen(puffer)+1);
if(NULL == str)
{
printf("Konnte keinen Speicher bereitstellen...\n");
exit(0);
}
strcpy(str, puffer);
printf("Weitere Eingabe oder beenden mit \"END\"\n>");
/* Endlossschleife */
while(1)
{
fgets(puffer, BUF, stdin);
/* Abbruchbedingung */
if(strcmp(puffer,"end\n")==0||strcmp(puffer,"END\n")==0)
break;
/* Aktuelle Länge von str zählen für realloc */
len = strlen(str);
/* Neuen Speicher für str anfordern */
str = (char *)realloc(str,strlen(puffer)+len+1);
if(NULL == str)
{
printf("Konnte keinen Speicher bereitstellen...\n");
exit(0);
}
/* Hinten Anhängen */
strcat(str, puffer);
}
printf("Ihre Eingabe lautete: \n");
printf("%s",str);
free(str);
return 0;
● liest Buchstaben aus Tastaturbuffer ein bis end oder END eingegeben wird
● reserviert neuen Speicherbereich, je nach dem wieviele Buchstaben mehr
eingegeben werden
● Speicherbereich muss für String immer um ein Zeichen größer
sein als der Text!
102
SE
10.5. Mehrdimensionale dynamische Arrays
#include <stdio.h>
#include <stdlib.h>
#define BUF 255
int main()
{
int i, j, zeile, spalte;
/* Matrix ist Zeiger auf int-Zeiger */
int ** matrix;
printf("Wie viele Zeilen : ");
scanf("%d",&zeile);
printf("Wie viele Spalten: ");
scanf("%d",&spalte);
/* Speicher reservieren für die int-Zeiger (=zeile) */
matrix = (int **)malloc(zeile*sizeof(int *));
if(NULL == matrix)
{
printf("Kein Speicher fuer die Zeilen...\n");
exit(0);
}
/* Jetzt noch Speicher reservieren für die einzelnen Spalten
der i-ten Zeile
*/
for(i=0; i < zeile; i++)
{
matrix[i] = (int *)malloc(spalte*sizeof(int));
if(NULL == matrix[i])
{
printf("Kein Speicher fuer Zeile %d\n",i);
exit(0);
}
}
/* Mit beliebigen Werten initialisieren */
for (i = 0; i < zeile; i++)
for (j = 0; j < spalte; j++)
matrix[i][j] = i+j; /* matrix[zeile][spalte] */
/* Inhalt der Matrix entsprechend ausgeben */
for (i = 0; i < zeile; i++)
{
for (j = 0; j < spalte; j++)
printf("%d ",matrix[i][j]);
printf("\n");
}
}
/* Speicherplatz wieder freigeben
* Wichtig! In umgekehrter Reihenfolge
*/
/* Spalten der i-ten Zeile freigeben */
for(i=0; i< zeile; i++)
free(matrix[i]);
/* Jetzt können die leeren Zeilen freigegeben werden */
free(matrix);
return 0;
103
SE
● Zeiger auf Zeiger ergibt mehrdimensionale Arrays
● matrix = (int **)malloc(zeile*sizeof(int *)); reserviert Speicherbereich
für die Zeilen, in denen int-Zeiger gespeichert werden sollen
● matrix[i] = (int *)malloc(spalte*sizeof(int)); in einer for-Schleife, die die
einzelnen Zeilen durchzählt wird der Bereich für die tatsächlich Werte
reserviert
● Zeilen müssen wieder in einer for-Schleife freigegeben werden
Zugriff auf …
Möglichkeit 1
Möglichkeit 2
Möglichkeit 3
1.Zeile, 1.Spalte
**matrix
*matrix[0]
matrix[0][0]
i.Zeile, 1.Spalte
**(matrix+i)
*matrix[i]
matrix[i][0]
1.Zeile, i.Spalte
*(*matrix+i)
*(matrix[0]+i)
matrix[0][i]
i.Zeile, j.Spalte
*(*(matrix+i)+j)
*(matrix[i]+j)
matrix[i][j]
104
SE
11. Strukturen
● Zusammenfassen von verschiedenen Variablen verschiedener Datentypen
zu einer Struktur
11.1. Strukturen deklarieren
struct adres {
char vname[20];
char nname[20];
long PLZ;
char ort[20];
int geburtsjahr;
}adressen;
●
●
●
●
deklariert eine Struktur zum speichern von Adressen
mit 5 Variablen
adres ist der Name der Struktur
mit adresse kann auf die Struktur zugegriffen werden, das ist der
Variablen-Bezeichner
Allgemeiner Syntax
struct typNAME {
Datentyp1;
Datentyp2;
.........
/* Liste der Strukturelemente */
Datentyp_n;
}Variablen_Bezeichner;
struct index {
int seite;
char titel[30];
};
● bräuchte 4 Byte für int und 30 Byte für char, also 34 Byte
● wird vom Betriebssystem häufig in 36 Byte gespeichert wegen dem
sogenannten Vier-Byte-Alignment
105
SE
11.2. Initialisierung und Zugriff auf Strukturen
#include <stdio.h>
#include <string.h>
struct index {
};
int seite;
char titel[30];
int main()
{
struct index lib;
lib.seite = 23;
strcpy(lib.titel, "C-Programmieren");
}
printf("%d, %s\n",lib.seite, lib.titel);
return 0;
● Zugriff durch den Punktoperator
● Deklaration wie Variable durch struct index lib;
● auch umständlicher möglich
struct index {
int seite;
char titel[30];
}lib;
● es können auch mehrere Strukturen auf einmal deklariert werden
struct index {
int seite;
char titel[30];
}lib1, lib2, lib3;
● können auch gleich initialisiert werden
struct index {
int seite;
char titel[30];
}lib = { 308, "Strukturen" };
● oder bei der Deklaration in der Mainfunktion Initialisieren
struct index lib = { 55, "Einführung in C" };
● Zugriff auf Elemente durch lib.seite = 23; oder strcpy(lib.titel, "CProgrammieren");
● Kopieren einer Struktur in einer andere
memcpy(&werte2,&wert1, sizeof(werte1));
● auch komponentenweise möglich als sogenannte flache Kopie
● eine Änderung des Inhaltes des Bezeichners1 würde automatisch auch den Inhalt
des Bezeichners2 ändern, da beide auf die gleiche Adresse verweisen
Bezeichner1 = Bezeichner2;
106
SE
Beispiel:
#include <stdio.h>
#define MAX 30
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen;
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres x)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",
x.vname);
printf("Nachname........:%s",
x.nname);
printf("Postleitzahl....:%ld\n",x.PLZ);
printf("Ort.............:%s",
x.ort);
printf("Geburtsjahr.....:%d\n", x.geburtsjahr);
}
int main()
{
printf("Vorname
: ");
fgets(adressen.vname, MAX, stdin);
printf("Nachname
: ");
fgets(adressen.nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%5ld",&adressen.PLZ);
} while(getchar()!= '\n');
printf("Wohnort
: ");
fgets(adressen.ort, MAX, stdin);
printf("Geburtsjahr : ");
do {scanf("%4ld",&adressen.geburtsjahr);
}while(getchar()!='\n' );
}
ausgabe(adressen);
return 0;
● Übergabe einer Struktur an einen Funktion durch ausgabe(adressen);
● einlesen einer Zahl
●
do {scanf("%5ld",&adressen.PLZ);
} while(getchar()!= '\n');
Parameter für Funktion sind void ausgabe(struct adres x)
Bezeichner
● Zugriff mit x printf("Vorname.........:%s",
Typ und
x.vname);
107
SE
11.3. Strukturen als Werteübergabe an Funktionen
● Struktur wird komplett kopiert ausgabe(adressen); und
printf("Vorname.........:%s",struct_ptr.vname);
● sehr zeitaufwendig bei größeren Strukturen
● besser mit Zeigern → call-by-reference ausgabe(&adressen); und
printf("Vorname.........:%s",(*struct_ptr).vname); bzw. struct_ptr->vname
#include <stdio.h>
#define MAX 30
struct adres { char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen;
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres *struct_ptr)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",(*struct_ptr).vname);
printf("Nachname........:%s",(*struct_ptr).nname);
printf("Postleitzahl....:%ld\n",(*struct_ptr).PLZ);
printf("Ort.............:%s",(*struct_ptr).ort);
printf("Geburtsjahr.....:%d\n",(*struct_ptr).geburtsjahr);
}
int main()
{
printf("Vorname
: ");
fgets(adressen.vname, MAX, stdin);
printf("Nachname
: ");
fgets(adressen.nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%5ld",&adressen.PLZ);
} while(getchar()!= '\n');
printf("Wohnort
: ");
fgets(adressen.ort, MAX, stdin);
printf("Geburtsjahr : ");
do {scanf("%4ld",&adressen.geburtsjahr);
}while(getchar()!='\n' );
ausgabe(&adressen);
return 0;
}
● Übergabe der Adresse ausgabe(&adressen);
● Parameter ist Typ und Adresse des Bezeichners void ausgabe(struct adres
*struct_ptr)
● Zugriff über Zeiger printf("Vorname.........:%s",(*struct_ptr).vname);
● Alternativ mit dem Elementkennzeichnungsoperator ->
printf("Vorname.........:%s",(struct_ptr->vname);
● -> nimmt automatisch eine Dereferenzierung vor
108
SE
11.4. Strukturen als Rückgabewerte einer Funktion
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
};
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres *struct_ptr)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",struct_ptr->vname);
printf("Nachname........:%s",struct_ptr->nname);
printf("Postleitzahl....:%ld\n",struct_ptr->PLZ);
printf("Ort.............:%s",struct_ptr->ort);
printf("Geburtsjahr.....:%d\n",struct_ptr->geburtsjahr);
}
struct adres *eingabe(void)
{
struct adres *adressen;
adressen=(struct adres *)malloc(sizeof(struct adres));
printf("Vorname : ");
fgets(adressen->vname, MAX, stdin);
printf("Nachname : ");
fgets(adressen->nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%ld",&adressen->PLZ);} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(adressen->ort, MAX, stdin);
printf("Geburtsjahr : ");
do {
scanf("%ld",&adressen->geburtsjahr);
}while(getchar()!='\n' );
return adressen;
}
int main()
{
struct adres *adresse1, *adresse2;
adresse1=eingabe();
adresse2=eingabe();
ausgabe(adresse1);
ausgabe(adresse2);
return 0;
}
109
SE
●
●
●
●
Übergabe der Adresse eines Zeigers durch adresse1=eingabe();
Deklaration der Funktion zur Eingabe durch struct adres *eingabe(void)
Deklarieren einer neuen Struktur struct adres *adressen;
zuweisen von Speicher mit der notwendigen Größe adressen=(struct adres
*)malloc(sizeof(struct adres));
● Schreiben der einzelnen Elemente fgets(adressen->vname, MAX, stdin);
oder do {scanf("%ld",&adressen->PLZ);} while(getchar()!= '\n');
● Rückgabe der Adresse return adressen;, die in adresse1 gespeichert wird
11.5. Strukturen vergleichen
● Gibt keine Funktion, die das übernimmt
Beispiel:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30
struct adres { char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
};
int cmp_structs(struct adres *str1, struct adres *str2)
{
/* Vorname gleich und */
if(strcmp(str1->vname, str2->vname) == 0 &&
/* Nachname gleich und */
strcmp(str1->nname, str2->nname) == 0 &&
/* Postleitzahl gleich und */
(str1->PLZ-str2->PLZ) == 0 &&
/* Wohnort gleich und */
strcmp(str1->ort, str2->ort) == 0 &&
/* geburtsjahr gleich */
(str1->geburtsjahr-str2->geburtsjahr) == 0)
return 0; /* Beide Strukturen gleich */
else
return 1; /* Strukturen nicht gleich */
}
int main()
{
struct adres adresse1={"John","Leroy",1234,"New York",1980 };
struct adres adresse2={"John","Leroy",1234,"New York",1980 };
if(cmp_structs(&adresse1, &adresse2) == 0)
printf("Beide Strukturen sind gleich?!?!\n");
else
printf("Die Strukturen weisen Unterschiede auf\n");
return 0;
}
110
SE
11.6. Arrays von Strukturen
#include <stdio.h>
#include <string.h>
struct index {
};
int seite;
char titel[30];
int main()
{
int i;
struct index lib[3];
lib[0].seite=312;
strcpy(lib[0].titel, "Arrays von Strukturen");
lib[1].seite=320;
strcpy(lib[1].titel, "Strukturen in Strukturen");
lib[2].seite=900;
strcpy(lib[2].titel, "Anhang");
for(i=0; i<3; i++)
printf("Seite %3d\t %-30s\n",lib[i].seite, lib[i].titel);
return 0;
}
● Deklarieren des Arrays durch struct index lib[3];
● Zugriff über lib[0].seite=312; bzw strcpy(lib[1].titel, "Strukturen in
Strukturen");
111
SE
Beispiel Adressbuch
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30
static int x=0;
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen[100];
void Eingabe(int nr, struct adres neu[])
{
printf("Vorname : ");
fgets(neu[nr].vname, MAX, stdin);
printf("Nachname : ");
fgets(neu[nr].nname, MAX, stdin);
printf("Postleitzahl: ");
do {scanf("%5ld",&neu[nr].PLZ);} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(neu[nr].ort, MAX, stdin);
printf("Geburtsjahr : ");
do {
scanf("%4d",&neu[nr].geburtsjahr);
}while(getchar()!= '\n');
}
void Suche(struct adres search[],char buchstabe,int nr)
{
int i;
for(i=0; i<=nr; i++)
{
if(search[i].nname[0] == buchstabe)
{
printf("\n\nGefunden unter Buchstabe "
":\"%c\"\n\n",buchstabe);
printf("Vorname......:%s",search[i].vname);
printf("Nachname.....:%s",search[i].nname);
printf("Postleitzahl.:%ld\n",search[i].PLZ);
printf("Ort..........:%s",search[i].ort);
printf("Geburtsjahr..:%d\n",
search[i].geburtsjahr);
printf("\n\tWeiter mit <ENTER>\n");
getchar();
}
}
}
112
SE
void Ausgabe(struct adres all[],int nr)
{
int i;
for(i=0; i<nr; i++)
{
printf("Vorname.........:%s",all[i].vname);
printf("Nachname........:%s",all[i].nname);
printf("Postleitzahl....:%ld\n",all[i].PLZ);
printf("Ort.............:%s",all[i].ort);
printf("Geburtsjahr.....:%d\n\n",all[i].geburtsjahr);
}
if((!(i%2))&& i!=0)
{
printf("\n\tWeiter mit <Enter>\n\n");
getchar();
}
}
void Sort(struct adres sort[],int nr)
{
int i,j;
struct adres *temp;
temp=(struct adres *)malloc(sizeof(struct adres *));
if(NULL == temp)
{
printf("Konnte keinen Speicher reservieren...\n");
return;
}
for(i=0; i<nr; i++)
{
for(j=i+1;j<nr;j++)
{
if(strcmp(sort[i].nname, sort[j].nname)>0)
{
*temp=sort[j];
sort[j]=sort[i];
sort[i]=*temp;
}
}
}
printf(".....Sortiert!!\n\n");
}
113
SE
int main()
{
int auswahl;
char c;
do {
printf("-1- Neue Adresse eingeben\n");
printf("-2- Bestimmte Adresse ausgeben\n");
printf("-3- Alle Adressen ausgeben\n");
printf("-4- Adressen sortieren\n");
printf("-5- Programm beenden\n");
printf("\nIhre Auswahl : ");
scanf("%d",&auswahl);
/* fflush(stdin); */
getchar();
switch(auswahl)
{
case 1 : Eingabe(x++,adressen);
break;
case 2 : printf("Anfangsbuchstabe d. Nachnamen :");
do {
scanf("%c",&c);
} while(getchar()!= '\n');
Suche(adressen,c,x);
break;
case 3 : Ausgabe(adressen,x);
break;
case 4 : Sort(adressen,x);
break;
case 5 : printf("Ende....\n");
break;
default: printf("Falsche Eingabe\n");
}
}
}while(auswahl <5);
return 0;
● liest Adressen ein
● Sortiert sie
● Duchrsucht alle Adressen nach einem Nachname mit gleichen Buchstaben
wie Eingabe
● Gibt alle Adressen aus
● Eingabe(x++,adressen); zählt mit wieviele Adressen engegeben wurden
● Zum sortieren wird ein Buffer benötitg
struct adres *temp;
temp=(struct adres *)malloc(sizeof(struct adres *));
114
SE
11.7. Verschachtelte Strukturen
● Verwendung von Strukturen in Strukturen
struct uhrzeit {
unsigned int stunde;
unsigned int minute;
unsigned int sekunde;
};
struct datum
{
unsigned int tag;
unsigned int monat;
int jahr;
};
struct termin {
struct datum d;
struct uhrzeit z;
}t;
● struct uhrzeit und struct datum werden in struct termin verwendet
● Zugriff auf einzelne Strukturen wird komplizierter
#include <stdio.h>
struct uhrzeit {
unsigned int stunde;
unsigned int minute;
unsigned int sekunde;
};
struct datum
{
unsigned int tag;
unsigned int monat;
int jahr;
};
struct termin
{
struct datum d;
struct uhrzeit z;
}t;
115
SE
int main()
{
struct termin t = {{19,8,2003},{20,15,0}};
printf("Termin am ");
printf("%u.%u.%d um ",t.d.tag,t.d.monat,t.d.jahr);
printf("%u.%u.%u0 Uhr \n\n",t.z.stunde,
t.z.minute,t.z.sekunde);
}
printf("Neuen Termin eingeben !!\n\n");
printf("Tag.............: ");
scanf("%u",&t.d.tag);
printf("Monat...........: ");
scanf("%u",&t.d.monat);
printf("Jahr............: ");
scanf("%d",&t.d.jahr);
printf("\n");
printf("Stunde..........: ");
scanf("%u",&t.z.stunde);
printf("Minuten.........: ");
scanf("%u",&t.z.minute);
printf("Sekunden........: ");
scanf("%u",&t.z.sekunde);
printf("\n");
printf("Neuer Termin am ");
printf("%02u.%02u.%04d um ",t.d.tag,t.d.monat,t.d.jahr);
printf("%02u.%02u.%02u Uhr \n",t.z.stunde,
t.z.minute,t.z.sekunde);
return 0;
● Deklaration und Initialisierung struct termin t = {{19,8,2003},{20,15,0}};
● zugriff über zwei Strukturen durch z.B. scanf("%u",&t.d.tag);
struct termin {
struct datum d;
struct uhrzeit z;
struct adressen a;
}t[20];
● würde eine Struktur mit 20 Feldern definieren, in der Datum, Uhrzeit und
Adresse gespeichert werden können
116
SE
Beispielprogramm zu Termin mit Datum, Uhrzeit und Adresse
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30
static int dates=0;
static int nr=0;
struct uhrzeit {
unsigned int stunde;
unsigned int minute;
unsigned int sekunde;
};
struct datum
{
unsigned int tag;
unsigned int monat;
int jahr;
};
struct adressen {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}xyz[100];
struct termin {
struct datum d;
struct uhrzeit z;
struct adressen a;
}t[20];
void newdate(struct termin *);
int suche(char *);
void listdate(struct termin *,int);
void replacedate(struct termin *,int);
void sortdate(struct termin *,int);
void Eingabe(struct adressen *);
void Ausgabe(struct adressen *);
void Sortadress(struct adressen *);
117
SE
void newdate(struct termin t[])
{
int auswahl,ret;
char such_name[MAX];
printf("Tag.......: ");
scanf("%u",&t[dates].d.tag);
printf("Monat.....: ");
scanf("%u",&t[dates].d.monat);
printf("Jahr......: ");
scanf("%d",&t[dates].d.jahr);
printf("---------------------\n");
printf("Stunde....: ");
scanf("%u",&t[dates].z.stunde);
printf("Minute(n).: ");
scanf("%u",&t[dates].z.minute);
printf("---------------------\n");
printf("\nTermin mit :\n -1- Neuer Adresse\n");
printf(" -2- Vorhandener Adresse\n");
printf("Ihre Auswahl : ");
do {
scanf("%d",&auswahl);
} while(getchar()!= '\n');
if(auswahl==1)
{
printf("Vorname.....: ");
fgets(t[dates].a.vname, MAX, stdin);
printf("Nachname....: ");
fgets(t[dates].a.nname, MAX, stdin);
printf("Postleitzahl: ");
do {
scanf("%ld",&t[dates].a.PLZ);
} while(getchar()!= '\n');
printf("ORT.........: ");
fgets(t[dates].a.ort, MAX, stdin);
printf("Geburtsjahr..: ");
do {
scanf("%ld",&t[dates].a.geburtsjahr);
} while(getchar()!= '\n');
/* Neue Adresse kommt auch zum neuen Adresssatz */
strcpy(xyz[nr].vname, strtok(t[dates].a.vname, "\n"));
strcpy(xyz[nr].nname, strtok(t[dates].a.nname, "\n"));
xyz[nr].PLZ = t[dates].a.PLZ;
strcpy(xyz[nr].ort, t[dates].a.ort);
xyz[nr].geburtsjahr=t[dates].a.geburtsjahr;
dates++;
nr++;
}
118
SE
else
{
}
printf("Bitte geben Sie den Nachnamen ein : ");
fgets(such_name, MAX, stdin);
ret=suche(strtok(such_name,"\n"));
strcpy(t[dates].a.vname,xyz[ret].vname);
strcpy(t[dates].a.nname,xyz[ret].nname);
t[dates].a.PLZ=xyz[ret].PLZ;
strcpy(t[dates].a.ort,xyz[ret].ort);
t[dates].a.geburtsjahr=xyz[ret].geburtsjahr;
dates++;
}
int suche(char suchname[])
{
int n,found=0;
for(n=0; n<=nr; n++)
{
if(strcmp(xyz[n].nname,suchname)==0)
{
found=1;
break;
}
}
if(found==1)
return n;
else
{
printf("Keine Eintrage mit dem Namen %s "
"gefunden\n",suchname);
return 0;
}
}
void listdate(struct termin list[],int dates)
{
int i;
for(i=0;i<dates;i++)
{
printf("Termin am %02u.%02u.%04d ",
list[i].d.tag,list[i].d.monat,list[i].d.jahr);
printf("um %02u.%02u Uhr\n",
list[i].z.stunde,list[i].z.minute);
printf("mit %s %s\n\n",list[i].a.vname,list[i].a.nname);
}
}
119
SE
void replacedate(struct termin aendern[],int nt)
{
if(nt < 20)
{
printf("Bitte neue Terminzeit eingeben!!\n");
printf("Tag..........: ");
scanf("%u",&aendern[nt].d.tag);
printf("Monat........: ");
scanf("%u",&aendern[nt].d.monat);
printf("Jahr.........: ");
scanf("%d",&aendern[nt].d.jahr);
printf("------------------------\n");
printf("Stunden......: ");
scanf("%u",&aendern[nt].z.stunde);
printf("Minuten......: ");
scanf("%u",&aendern[nt].z.minute);
}
else
printf("Falsche Eingabe\n");
}
void sortdate(struct termin sort[],int dates)
{
struct termin *temp;
int i,j;
temp=(struct termin *)malloc(sizeof(struct termin *));
if(NULL == temp)
{
printf("Konnte keinen Speicher reservieren...\n");
return;
}
for(i=0; i<dates; i++)
{
for(j=i+1;j<dates;j++)
{
if(sort[i].d.jahr>sort[j].d.jahr)
{
*temp=sort[j];
sort[j]=sort[i];
sort[i]=*temp;
}
}
}
printf(".....Sortiert!!\n");
}
120
SE
void Eingabe(struct adressen neu[])
{
unsigned int size;
printf("Vorname : ");
fgets(neu[nr].vname, MAX, stdin);
/* newline-Zeichen entfernen */
size = strlen(neu[nr].vname);
neu[nr].vname[size-1] = '\0';
printf("Nachname : ");
fgets(neu[nr].nname, MAX, stdin);
/* newline-Zeichen entfernen */
size = strlen(neu[nr].nname);
neu[nr].nname[size-1] = '\0';
printf("Postleitzahl: ");
do {
scanf("%ld",&neu[nr].PLZ);
} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(neu[nr].ort, MAX, stdin);
printf("Geburtsjahr : ");
do {
scanf("%d",&neu[nr].geburtsjahr);
} while(getchar()!= '\n');
nr++;
}
void Ausgabe(struct adressen all[])
{
int i;
for(i=0; i<nr; i++)
{
printf("Vorname.........:%s\n",all[i].vname);
printf("Nachname........:%s\n",all[i].nname);
printf("Postleitzahl....:%ld\n",all[i].PLZ);
printf("Ort.............:%s",all[i].ort);
printf("Geburtsjahr.....:%d\n\n",all[i].geburtsjahr);
if((!(i%2))&& i!=0)
{
// fflush(stdin);
printf("\n\tWeiter mit <Enter>\n\n");
getchar();
}
}
}
121
SE
void Sortadress(struct adressen sort[])
{
struct adressen *temp;
int i,j;
temp=(struct adressen *)malloc(sizeof(struct adressen *));
if(NULL == temp)
{
printf("Konnte keinen Speicher reservieren...\n");
return;
}
for(i=0; i<nr; i++)
{
for(j=i+1;j<nr;j++)
{
if(strcmp(sort[i].nname, sort[j].nname)>0)
{
*temp=sort[j];
sort[j]=sort[i];
sort[i]=*temp;
}
}
}
printf(".....Sortiert!!\n");
}
122
SE
int main()
{
int eingabe,aendern;
do {
}
printf("\tTerminverwaltung\n");
printf("\t----------------\n\n");
printf("\t-1- Neuer Termin\n");
printf("\t-2- Termine auflisten\n");
printf("\t-3- Termin ändern\n");
printf("\t-4- Termine sortieren\n");
printf("\t-5- Neue Adresse eingeben\n");
printf("\t-6- Adressen ausgeben\n");
printf("\t-7- Adressen sortieren\n");
printf("\t-8- Programm beenden\n");
printf("\n\tIhre Auswahl : ");
scanf("%d",&eingabe);
/* fflush(stdin); */
getchar();
switch(eingabe)
{
case 1 : newdate(t);
break;
case 2 : listdate(t,dates);
break;
case 3 : listdate(t,dates);
printf("Welchen Termin ändern(Nr.?):");
scanf("%d",&aendern);
replacedate(t,--aendern);
break;
case 4 : sortdate(t,dates);
break;
case 5 : Eingabe(xyz);
break;
case 6 : Ausgabe(xyz);
break;
case 7 : Sortadress(xyz);
break;
default : break;
}
}while(eingabe<8);
printf("Bye\n");
return 0;
123
SE
11.8. Aufzählungstyp enum
#include <stdio.h>
enum zahl {NU_LL,EINS,ZWEI,DREI,VIER};
int main()
{
enum zahl x;
x=NU_LL;
printf("%d\n",x);
x=EINS;
printf("%d\n",x);
x=ZWEI;
printf("%d\n",x);
x=DREI;
printf("%d\n",x);
}
x=VIER;
printf("%d\n",x);
return 0;
● Festlegen von Konstanten
● gibt die Zahlen 0-4 aus
● entspricht dem Index der Aufzählung
enum farben {rot, gelb=6, blau, gruen};
● würde 0, 6, 7, 8 ausgeben
124
SE
11.9. Typendefinition mit typdef
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30
static int x;
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen[100];
typedef struct adres ADRESSE;
void Eingabe(int nr, ADRESSE *neu)
{
printf("Vorname : ");
fgets(neu[nr].vname, MAX, stdin);
printf("Nachname : ");
fgets(neu[nr].nname, MAX, stdin);
printf("Postleitzahl: ");
do {
scanf("%5ld",&neu[nr].PLZ);
} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(neu[nr].ort, MAX, stdin);
printf("Geburtsjahr : ");
do {
scanf("%4d",&neu[nr].geburtsjahr);
} while(getchar()!= '\n');
}
void Suche(ADRESSE *search,char buchstabe,int nr)
{
int i;
for(i=0; i<=nr; i++)
{
if(search[i].nname[0] == buchstabe)
{
printf("\n\nGefunden unter Buchstabe :\"%c\"\n\n",
buchstabe);
printf("Vorname.......:%s",search[i].vname);
printf("Nachname......:%s",search[i].nname);
printf("Postleitzahl..:%ld\n",search[i].PLZ);
printf("Ort...........:%s",search[i].ort);
printf("Geburtsjahr...:%d\n",search[i].geburtsjahr);
printf("\n\tWeiter mit <ENTER>\n");
getchar();
}
}
}
125
SE
void Ausgabe(ADRESSE *all,int nr)
{
int i;
for(i=0; i<nr; i++)
{
printf("Vorname.........:%s",all[i].vname);
printf("Nachname........:%s",all[i].nname);
printf("Postleitzahl....:%ld\n",all[i].PLZ);
printf("Ort.............:%s",all[i].ort);
printf("Geburtsjahr.....:%d\n\n",all[i].geburtsjahr);
if((!(i%2))&& i!=0)
{
//fflush(stdin);
printf("\n\tWeiter mit <Enter>\n\n");
getchar();
}
}
}
void Sort(ADRESSE *sort,int nr)
{
ADRESSE *temp;
int i,j;
temp = (ADRESSE *)malloc(sizeof(ADRESSE *));
if(NULL == temp)
{
printf("Konnte keinen Speicher reservieren...\n");
return;
}
}
for(i=0; i<nr; i++)
{
for(j=i+1;j<nr;j++)
{
if(strcmp(sort[i].nname, sort[j].nname)>0)
{
*temp=sort[j];
sort[j]=sort[i];
sort[i]=*temp;
}
}
}
printf(".....Sortiert!!\n");
126
SE
int main()
{
int auswahl;
char c;
do {
}
printf("-1- Neue Adresse eingeben\n");
printf("-2- Bestimmte Adresse ausgeben\n");
printf("-3- Alle Adressen ausgeben\n");
printf("-4- Adressen sortieren\n");
printf("-5- Programm beenden\n");
printf("\nIhre Auswahl : ");
scanf("%d",&auswahl);
/* fflush(stdin); */
getchar();
switch(auswahl)
{
case 1 : Eingabe(x++,adressen);
break;
case 2 : printf("Anfangsbuchstabe Nachnamen :");
do {
scanf("%c",&c);
} while(getchar()!= '\n');
Suche(adressen,c,x);
break;
case 3 : Ausgabe(adressen,x);
break;
case 4 : Sort(adressen,x);
break;
default: break;
}
}while(auswahl <5);
return 0;
● typedef struct adres ADRESSE; neuen Typendefinition
● auf die Struktur kann jetzt mit z.B. ADRESSE *temp; zugegriffen werden
typedef Typendefinition Bezeichner;
● sinnvoll, wenn mehrere Strukturen mit ähnliche Aufbau verwendet werden
typedef struct adres {
char vname[20];
char nname[20];
long PLZ;
char ort[20];
int geburtsjahr;
}ADRESSE;
ADRESSE adressen[100];
● andere Möglichkeit der Definition des neuen Typs
127
SE
● andere Anwendung
typedef
typedef
typedef
typedef
typedef
typedef
●
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
char BYTE;
int WORD;
long DWORD;
double QWORD;
int uint;
char uchar;
uint wert1, wert2;
/*1
/*1
/*1
/*1
Byte = 8 BIT*/
WORD = 16 BIT*/
DOUBLE WORD = 32 BIT*/
QUAD WORD = 64 BIT */
wäre dann äquivaltent zu unsigned int wert1,wert2;
12. Dynamische Datenstrukturen
● Vermischung von Zeigern, dynamischer Speicherverwaltung und
Strukturen
12.1. Lineare Listen – einfache verkettete Listen
● Es wird eine Struktur mit einem Zeiger vom Typ der Struktur selbst
definiert
struct datum {
int tag;
int monat;
int jahr;
};
struct angestellt {
char name[20];
char vorname[20];
struct datum alter;
struct datum eingest;
long gehalt;
struct angestellt *next;
};
Zeiger *next mit dem selben Typ, wie die
Struktur selbst → Verkettung
● *next verweist auf die nächste Struktur, die wiederum einen Zeiger
beinhaltet
● eine Art Array von Strukturen, jedoch ohne Index, sondern mit Zeiger
● Ende durch den Nullzeiger festlegen struct angestellt *next = NULL;
●
struct angestellt *next;
● struct angestellt *structzeiger; Deklarieren einer Strucktur über Zeiger
● (*structzeiger).name oder structzeiger->name Zugriff auf einzelne Elemente
● es wird ein Anfang benötigt struct angestellt *anfang=NULL;
128
SE
struct datum {
int tag;
int monat;
int jahr;
};
struct angestellt {
};
char name[20];
char vorname[20];
struct datum alter;
struct datum eingest;
long gehalt;
struct angestellt *next;
struct angestellt *next=NULL;
struct angestellt *anfang=NULL;
Beispiel Verwaltung Mitarbeiter
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 20
struct datum {
int tag;
int monat;
int jahr;
};
struct angestellt {
char name[MAX];
char vorname[MAX];
struct datum alter;
struct datum eingest;
long gehalt;
struct angestellt *next;
};
struct angestellt *next = NULL;
struct angestellt *anfang=NULL;
/*Wir hängen einen Datensatz an oder geben einen neuen ein
n=name,v=vornam,at=alter.tage,am=alter.monat,aj=alter.jahr
eint=eigestellt tag,einm=eingestellt monat,einj=eingest.
Jahr g=gehalt*/
129
SE
void anhaengen(char n[],char v[],int at,int am,int aj,
int eint,int einm,int einj,long g)
{
/*Zeiger zum Zugriff auf die einzelnen Elemente
der Struktur*/
struct angestellt *zeiger;
/* Wir fragen ab, ob es schon ein Element in der Liste
gibt. Wir suchen das Element, auf das unser Zeiger
*anfang zeigt. Falls *anfang immer noch auf NULL zeigt,
bekommt *anfang die Adresse unseres 1. Elements und ist
somit der Kopf (Anfang) unserer Liste*/
if(anfang == NULL)
{
/* Wir reservieren Speicherplatz für unsere Struktur
für das erste Element der Liste*/
if((anfang =(struct angestellt *)
malloc(sizeof(struct angestellt))) == NULL)
fprintf(stderr,"Kein Speicherplatz vorhanden "
"für anfang\n");
strcpy(anfang->name,n);
strcpy(anfang->vorname,v);
anfang->alter.tag=at;
anfang->alter.monat=am;
anfang->alter.jahr=aj;
anfang->eingest.tag=eint;
anfang->eingest.monat=einm;
anfang->eingest.jahr=einj;
anfang->gehalt=g;
/* Somit haben wir unseren Anfang der Liste. Von nun an
zeigt der Zeiger anfang immer auf das Element vor ihm.
Da dies aber jetzt das 1. Element der Liste war, zeigt
der Zeiger anfang auf den Zeiger next. next zeigt am
Ende immer wieder NULL*/
anfang->next=NULL;
}
/* Es scheint schon mindestens ein Element in der Liste
vorhanden zu sein, da der Anfang nicht == NULL ist.
Jetzt suchen wir so lange nach dem nächsten Element,
bis der *next-Zeiger auf NULL zeigt. Somit haben wir
das Ende der Liste gefunden und können einen neuen
Datensatz anhängen*/
130
SE
else
{
zeiger=anfang; /* Wir zeigen auf das 1. Element */
while(zeiger->next != NULL)
zeiger=zeiger->next;
/* Wir reservieren einen Speicherplatz für das letzte
Element der Liste und hängen es an.*/
if((zeiger->next =(struct angestellt *)
malloc(sizeof(struct angestellt))) == NULL)
fprintf(stderr,"Kein Speicherplatz für das "
"letzte Element\n");
zeiger=zeiger->next; /*zeiger auf neuen Speicherplatz*/
strcpy(zeiger->name,n);
strcpy(zeiger->vorname,v);
zeiger->alter.tag=at;
zeiger->alter.monat=am;
zeiger->alter.jahr=aj;
zeiger->eingest.tag=eint;
zeiger->eingest.monat=einm;
zeiger->eingest.jahr=einj;
/*Wir terminieren wieder unsere Datenstruktur*/
zeiger->gehalt=g;
zeiger->next=NULL;
}
}
/*Funktion zur Eingabe der Daten*/
void eingabe()
{
char nam[MAX],vorn[MAX];
int atag,amon,ajahr,eintag,einmon,einjahr;
long gehalt;
printf("Name........................: ");
fgets(nam, MAX, stdin);
printf("Vorname.....................: ");
fgets(vorn, MAX, stdin);
printf("Alter...........(tt.mm.jjjj): ");
scanf("%2d.%2d.%4d",&atag,&amon,&ajahr);
printf("Eingestellt am..(tt.mm.jjjj): ");
scanf("%2d.%2d.%4d",&eintag,&einmon,&einjahr);
printf("Monatsgehalt................: ");
scanf("%ld",&gehalt);
getchar();
/* Eingegebenen Datensatz hinten anhängen */
anhaengen(nam,vorn,atag,amon,ajahr,eintag,
einmon,einjahr,gehalt);
}
int main()
{
while(1)
eingabe();
return 0;
}
● keine Abbruchbedingung!!!
131
SE
12.1.1. Erstes Element in der Liste löschen
● Anfang muss auf 2. Element zeigen
● über temporären Zeiger und Freigabe des Speicherbereichs des ersten
Eintrags
/*Funktion zum Löschen */
void loesche(char wen[])
{
struct angestellt *zeiger ,*zeiger1;
/*Ist überhaupt ein Element vorhanden?*/
if(anfang != NULL)
{
/*Ist unser 1. Element das von uns gesuchte (wen[])?*/
if(strcmp(anfang->name,wen) == 0)
{
zeiger=anfang->next;
free(anfang);
anfang=zeiger;
}
12.1.2. beliebiges Element der Liste löschen
● Zuerst Überprüfen, ob das gesuchte Element nicht das erste ist
● wenn nicht, löschen des beliebigen Elements
/*Funktion zum Löschen einer Datei*/
void loesche(char wen[])
{
struct angestellt *zeiger ,*zeiger1;
/*Ist überhaupt ein Element vorhanden?*/
if(anfang != NULL)
{
/*Ist unser 1. Element das von uns gesuchte (wen[])?*/
if(strcmp(anfang->name,wen) == 0)
{
zeiger=anfang->next;
free(anfang);
anfang=zeiger;
}
else
{
/*Es ist nicht das 1. Element zu löschen.
Wir suchen in der weiteren Kette, ob das zu
löschende Element vorhanden ist*/
zeiger=anfang;
while(zeiger->next != NULL)
{
zeiger1=zeiger->next;
/*Ist die Adresse von zeiger1
132
SE
der gesuchte Name?*/
if(strcmp(zeiger1->name,wen) == 0)
{
/*Falls ja dann.....*/
zeiger->next=zeiger1->next;
free(zeiger1);
break;
}
zeiger=zeiger1;
}/*Ende while*/
} /*Ende else*/
/*Ende if(anfang != NULL)*/
}
else
printf("Es sind keine Daten zum Löschen vorhanden!!!\n");
}
12.1.3. Elemente der Liste ausgeben
● Liste wird durchlaufen, bis der letzte Zeiger auf Null verweist
void ausgabe()
{
struct angestellt *zeiger;
zeiger=anfang;
printf("||====================================="
"==================||\n");
printf("|%10cName%10c |Geburtsdatum|"
"Eingestellt|Gehalt|\n",' ',' ');
printf("||====================================="
"==================||\n");
while(zeiger != NULL)
{
printf("|%12s,%-12s| %02d.%02d.%04d|"
"%02d.%02d.%04d|%06ld|\n",
zeiger->name,zeiger->vorname,zeiger->alter.tag,
zeiger->alter.monat,zeiger->alter.jahr,
zeiger->eingest.tag,zeiger->eingest.monat,
zeiger->eingest.jahr,zeiger->gehalt);
printf("|-----------------------------------"
"----------------------|\n");
zeiger=zeiger->next;
}
}
133
SE
12.1.4. Alle Elemente löschen
void loesche_alles()
{
struct angestellt *zeiger, *zeiger1;
/*Ist überhaupt eine Liste zum Löschen vorhanden*/
if(anfang != NULL)
{
/*Es ist eine vorhanden....*/
zeiger=anfang->next;
while(zeiger != NULL)
{
zeiger1=anfang->next->next;
anfang->next=zeiger1;
free(zeiger->next);
free(zeiger);
zeiger=zeiger1;
}
/*Jetzt löschen wir erst den Anfang der Liste*/
free(anfang->next);
free(anfang);
anfang=NULL;
printf("Liste erfolgreich gelöscht!!\n");
}
else
fprintf(stderr,"Keine Liste zum Löschen vorhanden!!\n");
}
12.1.5. Elemente in die Liste einfügen
● Sortiertes einfügen
● Liste soll nach Nachnamen geordnet werden
● 4 Möglichkeiten
 Es ist noch kein Element in der Liste vorhanden, und das eingegebene
ist das erste Element.
 Das eingegebene Element ist das größte und wird somit hinten
angehängt.
 Das eingegebene Element ist das kleinste und wird ganz an den Anfang
eingefügt.
 Die letzte Möglichkeit ist gleichzeitig auch die schwierigste. Das
Element muss irgendwo in der Mitte eingefügt werden.
134
SE
void sortiert_eingeben(char n[],char v[],int at,int am,int aj,
int et,int em,int ej,long geh)
{
struct angestellt *zeiger, *zeiger1;
/*Ist es das 1. Element der Liste? */
if(anfang==NULL)
anhaengen(n,v,at,am,aj,et,em,ej,geh);
/*Es ist nicht das 1. Element. Wir suchen so lange, bis das
gesuchte Element gefunden wird oder wir auf NULL stoßen*/
else
{
zeiger=anfang;
while(zeiger != NULL && (strcmp(zeiger->name,n)<0))
zeiger=zeiger->next;
/*Falls der Zeiger auf NULL zeigt, können wir unser
Element hinten anhängen, da unser neues Element das
"grösste" zu sein scheint */
if(zeiger==NULL)
anhaengen(n,v,at,am,aj,et,em,ej,geh);
/*Ist unser neues Element das kleinste und somit
kleiner als das 1. Element, so müssen wir es an
den Anfang hängen */
else if(zeiger==anfang)
{
anfang=(struct angestellt *)
malloc(sizeof(struct angestellt));
if(NULL == anfang)
{
fprintf(stderr, "Kein Speicher\n");
return;
}
strcpy(anfang->name,strtok(n, "\n"));
strcpy(anfang->vorname,strtok(v, "\n"));
anfang->alter.tag=at;
anfang->alter.monat=am;
anfang->alter.jahr=aj;
anfang->eingest.tag=et;
anfang->eingest.monat=em;
anfang->eingest.jahr=ej;
anfang->gehalt=geh;
anfang->next=zeiger;
}
/*Die letzte Möglichkeit ist, dass wir das Element
irgendwo in der Mitte einfügen müssen*/
135
SE
else
{
zeiger1=anfang;
/*Wir suchen das Element, das vor dem
Zeiger zeiger steht*/
while(zeiger1->next != zeiger)
zeiger1=zeiger1->next;
zeiger=(struct angestellt *)
malloc(sizeof(struct angestellt));
if(NULL == zeiger)
{
fprintf(stderr, "Kein Speicher");
return;
}
strcpy(zeiger->name,strtok(n, "\n"));
strcpy(zeiger->vorname,strtok(v, "\n"));
zeiger->alter.tag=at;
zeiger->alter.monat=am;
zeiger->alter.jahr=aj;
zeiger->eingest.tag=et;
zeiger->eingest.monat=em;
zeiger->eingest.jahr=ej;
zeiger->gehalt=geh;
/*Wir fügen das neue Element ein*/
zeiger->next=zeiger1->next;
zeiger1->next=zeiger;
}//Ende else
}//Ende else
}
136
SE
13. Dateien
Hauptspeicher ist flüchtig
speichern von Daten auf Festplatte nach Programmende → nicht flüchtig
Zugriff auf Daten bei Programmstart
Organisationseinheit ist Datei
Betriebssystem beitet Schnittstelle zum Zugriff auf verschiedene
Speichermedien → Stream – Datenstrom
● Bibliotheksfunktionen zur Kommunikation mit dem Betriebssystem
●
●
●
●
●
● Ende einer Datei durch EOF – End Of File markiert
● Zugriff auf Elemente einer Datei durch File Position Pointer
● Funktion feof gibt einen Wert ungleich 0 zurück, wenn man sich am
Dateiende befindet – while(!feof(pDatei))
● Standardstreams:
 (File*) stdin → Tastatur
 (File*) stdout → Bildschirm
 (File*) stderr → Fehlerausgabe auf dem Bildschirm
 Das sind Pointer aus speziellen Datentyp namens File – in stdio.h
definiert
● Binärdateien
 werden „im Block“ geschrieben und gelesen
 ähnlich der Laufzeitdaten eines Prozesses
 gut zur Speicherung von Programmdaten
Äquivalente Programme
#include <stdio.h>
#include <stdio.h>
int main()
int main()
{
{
int iZahl=0;
int iZahl=0;
printf("Zahl eingeben: ");
FILE *pAusgabe=0, *pEingabe=0;
scanf(" %d", &iZahl);
pAusgabe = stdout;
printf("Zahl: %d", iZahl);
pEingabe = stdin;
return 0;
fprintf(pAusgabe, "Zahl eingeben:");
}
fscanf(pEingabe, "%d", &iZahl);
fprintf(pAusgabe, "Zahl: %d", iZahl);
return 0;
}
137
SE
13.1 Schema für Dateioperationen
1. Schritt: Definition eines Zeigers des Typs FILE
2. Schritt: Öffnen einer Datei und gleichzeitige Initialisierung des File-Zeigers mit
Hilfe der Funktion fopen()
3. Schritt: Dateioperationen mittels spezieller Funktionen
Zeichenweise Ein-/Ausgabe: fputc(), fgetc()
Zeilenweise Ein-/Ausgabe: fputs(), fgets()
Formatierte Ein-/Ausgabe: fprintf(), fscanf()
Blockweise Ein-/Ausgabe: fwrite(), fread() - Binär
4. Schritt: Schließen der Datei mit fclose()
13.2. fopen()
●
●
●
●
●
●
Definiert in stdio.h
initialisiert Pointer
FILE* fopen(const char *szFilename, const char *szMode)
Liefert Zeiger auf geöffnete Datei oder NULL zurück
szFilename zeigt auf String
szMode zeigt auf String, der die Art des Zugriffs enthällt
● Fehlerursachen:
 Pfadangabe nicht korrekt
 Schreiben ohne Schreibrechte
 Schreiben auf volle Festplatte
 Lesen einer nicht existenten Datei
 Lesen ohne Leseberechtigung
13.2.1. Modus der Dateiöffnung
r
Lesen – Zugriff startet am Anfang
r+ Lesen und schreiben – Zugriff startet
am Anfang
w
Legt eine neue Datei an oder schrumpft w+ Legt eine neue Datei an oder schrumpft
eine existierende auf 0 um sie zu
eine existierende auf 0 um sie zu lesen
bearbeiten – Zugriff startet am Anfang
und zu beschreiben – Zugriff startet am
Anfang
a
Schreiben – Zugriff am Ende der Datei - a+ Öffnet eine existierende Datei oder
append
erzeugt eine neue – Lesezugriff
uneingeschränkt, Schreibzugriff nur am
Dateiende
138
SE
● Zusatz t (default) für translate mode → rt, at+
 Konvertierung für ASCII-Daten - \n umwandeln
 praktisch um die Datei danach mit einem Texteditor zu bearbeiten oder
zu öffnen
● Zusatz b für binary mode → rb, ab+
 keinerlei Konvertierung – Daten werden so geschrieben, wie sie
vorliegen
 praktisch für interne Darstellung → relativ einfaches speichern und
lesen von großen Datenmengen
Beispiel: Zeichenweise Ein- und Ausgabe
#include <stdio.h>
int main()
{
FILE *pDatei = fopen("Textdatei.txt", "wt");
char cZeichen;
printf("Testdatei bereit, bitte Eingabe, Abbruch mit '$' \n");
do
{
cZeichen = getc(stdin);
putc (cZeichen, pDatei);
} while (cZeichen!='$');
fclose(pDatei);
pDatei=fopen("Textdatei.txt", "rt");
printf("\nDie Eingabe war:");
do
{
printf("%c", cZeichen = getc(pDatei));
} while (cZeichen!='$');
fclose(pDatei);
return 0;
}
● überprüfung ob fopen nicht NULL zurückgibt notwendig!!!
● fputs schreibt Text auf einen Rutsch in eine Datei
fputs("So kann Text auf einen Rutsch \nin die Datei nach dem Komma geschrieben
werden", pDatei);
● fgets gibt eine Zeile, jedoch max 5 Zeichen aus einer Datei aus
● zweite Zeile wird ausgegeben, wenn gleicher Syntax nocheinmal
verwendet wird
fgets(szString, 5, pDatei);
139
SE
● formatiertes schreiben von Text in eine Datei mit fprintf
#include <stdio.h>
int main()
{
FILE *pDatei = fopen("Textdatei.txt", "wt");
int iFaktor1=0, iFaktor2=0;
fprintf(pDatei, "\nMultiplikationstabelle");
fprintf(pDatei, "\n----------------------");
for(iFaktor1=1; iFaktor1<=10; iFaktor1++)
for(iFaktor2=1; iFaktor2<=10; iFaktor2++)
fprintf(pDatei, "\n %3i * %3i = %4i", iFaktor1, iFaktor2,
iFaktor1*iFaktor2);
fclose(pDatei);
}
return 0;
● blockweise Ein- & Ausgabe mit fwrite und fread
#include <stdio.h>
typedef struct
{
int iX, iY;
} KOORDINATEN;
int main()
{
KOORDINATEN APunkte[5]=
{
{ 0, 0 },
{ -1, -1 },
{ 1, 1 },
{ -1, 1 },
{ 1, -1 } };
KOORDINATEN AKontrolle[5];
int i=0;
FILE *pDatei = fopen("Textdatei.bin", "wb");
fwrite(APunkte, sizeof(KOORDINATEN), 5, pDatei);
fclose(pDatei);
pDatei=fopen("Textdatei.bin", "rb");
fread(AKontrolle, sizeof(KOORDINATEN), 5, pDatei);
fclose(pDatei);
for (i=0; i<5; i++)
printf("x: %i\t y: %i\n", AKontrolle[i].iX, AKontrolle[i].iY);
return 0;
}
140
SE
●
●
●
●
nur binäres schreiben und lesen
unformatiert aber mit fester Satzstruktur
Plattformabhängig
Übergebene Parameter
 Zeiger auf Datenquelle
 Größe des Elements
 Anzahl der Elemente
 zeigt auf den Strom, der benutzt werden soll
14. Headerfiles
●
●
●
●
●
●
Aufteilung des Programmcodes → abgeschlossene Einheit
Komplexitätsbeherrschung
getrenntes Kompilieren und Debuggen von Programmteilen
Schnittstellten zwischen Programmteilen
Vereinfacht Wiederverwendung von Funktionen
Vereinfachte Wartung und Änderunge
● Strukturierung in der Implementierung
 Aufteilen in mehrere Funktionen
 Aufteilen in Funktionspakete und mehrere Dateien
 Seperates kompilierne von Teilsystemen
 Kapselung der Implementierung
● Einbinden von Quellcode
#include "Funktion.c"
 wird von Präprozessor reinkopiert
● Probleme
 Namenskonflikte – globale Variablen, Datentypen, Funktionen
 Mehrfachdefinition, wenn eine Datei mehrfach eingebunden wird – verschiedene
Funktionen benötigen eine untergeordnete Funktion und werden von einer
übergeordneten Funktion eingebunden
 bei Ände rung muss das komplette Programm neu kompiliert werden
14.1. One-Definition-Rule
● Deklarationen dürfen mehrfach erfolgen, Definitionen nur einmal
 bei Deklaration nur einführung von Namen, keine Speicherreservierung
 Deklaration ist Beschreibung der Schnittstellen
 Definition ist Implementierung
//Deklaration
int addieren(int); /*Einführung des Funktionsnamens mit
Schnittstellenbeschreibung, keine Speicherreservierung*/
extern const float Pi; /* Schlüsselwort extern, Einführung des Namens, keine
Speicherreservierung*/
//Definition
int i=5; /* Name und gleichzeitige Speicherreservierung*/
141
SE
14.2 Inhalt Header
●
●
●
●
Funktionsprototypen
reine Deklaration von externen globalen Variablen und Konstanten
Definition von nach außen sichtbaren Konstanten und Typendefinitionen
nach außen sichtbare includes anderer Header
//Funktionsprototypen
int addieren (int, int);
void zeig(int a);
ELEMENT eingelsen void;
//includes
/*Enthält Definition von LISTE, ELEMENT*/
#include "Liste.h"
void insert (LISTE*, ELEMENT*);
//Deklaration Variablen und Konstanten
extern int iMax;
extern const float Pi;
//Definition von Konstanen und Typendefinitionen
#define MAX 81
typedef struct
{
char szVorname [MAX];
char szNachname [MAX];
} NAME;
14.2. Inhalt Implementierungsdatei
●
●
●
●
Implementierung der Funktionen
includes eigener Header-Files – nach außen nicht sichtbar
Definition globler Variablen und Konstanten
Definition von nach außen nicht sichtbare Konstanten und Typendefinitionen
//Funktionsimplementierung
int addieren (int i, int j)
{
return i+j;
}
//Einbinden von Headern - nicht nach aussen sichtbar
#include "xxx.h"
#include <stdio.h>
void zeig(int i)
{
printf("%i", a);
}
//Definition globale Konstanten und Variablen
int iMax=100;
const float fPi=3,14;
//Definition nach aussen nicht sichtbare Konstanten und Typendefinitionen
#define MAX 81
typedef struct
{
char szVorname[MAX];
char szNachname[MAX];
}NAME;
142
SE
14.3. Einbinden von Headern
● Problem, wenn ein Header mehrfach eingebunden wird → bedingtes Einbinden
#ifndef XXX_H
#define XXX_H XXX_H
#endif
●
●
●
●
ifndef überprüft ob ein Header schoneinmal eingebunden wurde
wenn nicht wird die Strinkonstante definiert
Konvention: erstezten des Punktes durch einen Unterstrich
Klammer durch endif beenden
//Liste.h
#ifndef LISTE_H
#define LISTE_H LISTE_H
typedef struct
{
...}DATEN;
typedef struct LISTENELEMENT
{
...}ELEMENTE;
typedef struct
{
...}LISTE;
void deletList(LISTE*);
void insert(LISTE*, DATEN*);
...
#endif
//Liste.c
#include "Liste.h"
void deletList(LISTE *pEineListe)
{
if (pEineLIste->pFirst==0)
return;
else
{
...
}
}
void insert (LISTE* pEineListe, DATEN
*pEinmalDaten)
{
...
}
//main.c
#include "Liste.h"
int main (void)
{
LISTE ListeGeschaeftsdaten;
...
}
14.4. Objekt Files
Kompilieren eines Teils eines Programms
speicher in .obj Files
können vom Linker zusammengefügt werden
einzelne .obj-Files konnen neu kompiliert werden, wenn deren Programmcode
erzeugt wurde ohne alle Teile eines Programms kompilieren zu müssen → erneutes
linken
● Schnittstellen müssen unbedingt erhalten bleiben oder angepasst werden
●
●
●
●
143
SE
● dynamisches Binden
 ermöglicht updates
 Windows: ddl – dynamic link Libraries
 Unix: schared libraries
 mehrere Prozesse können gleiche dlls verwenden → nur einmal im Speicher,
leicht austauschbar
15. Objektorientierung
15.1. Stärken der Strukturierten Programmierung
Zielorientiertes Vorgehen
Gute Umsetzbarkeit in Programmcode
Schlanker effizienter Code
Wiederverwendung und Komplexitätsbeherrschung durch Funktionen und mehrere
Dateien
● höheres Abstraktionsniveau wie Assembler
●
●
●
●
15.2. Schwächen der Strukturierten Programmierung
● Starke Auxsrichtung an von Neumann Architektur → große semantische Lücke
zwischen Realität und Programm
● Geringe Abstraktionstiefe
● relativ geringer Wiederverwendungswert
● schlechte Möglichkeiten für Veränderungen und Updates
● Schwierigkeit zur Bewälltigung der Komplexität bei großen Programmen
15.3. Imperativ / prozedurales Programmierparadigma
●
●
●
●
●
Zerlegung des Anwendungsproblems
im Mittelpunkt stehen Algorithmen und Daten
prodzedural ist Aufteilung in Untereinheiten
imerpativ ist Art der Befehlsform
Trennung in unabhängige Programmdaten, die im Speicher liegen und darauf warten
von Programmschirtten verarbeitet zu werden
● a=2+3
● der Variablen a wird die Summe aus 2 und 3 zugewiesen
● Zuweisungs-, +-Operator und Ergebnisvariable notwendig
144
SE
15.4. objektorientiertes Programmierparadigma
●
●
●
●
●
Zusammenspeil vieler selbstständiger Software-Objekte
erledigen gemeinsam die Gesammtaufgabe
jedes Objekt stellt Dienstleistungen zur Verfügung
Dienstleistungen können von anderne Objekten genutzt werden
Software-Objekte repräsenteiren reale Objekte
● Obejekt 2 bekommt Objekt 3 übergeben und die Aufgabe beide durch die
Additionsfunktion zu addieren
● Summenobjekt 5 wird neu geschaffen und enthällt die gleichen Funktionen wie das
Objekt 2
15.3. Objekte
●
●
●
●
●
●
●
SW-Einheiten
besitzen und verwalten eigenen Daten
Daten sind Attribute, deren Werte bestimmten den aktuellen Zustand des Objekts
bieten Dienstleistungen an, stellen dazu Methoden bereit
aktive Funktionsträger einers Programms
Kooperaieren mit einander
tragen Verantwortung für die eigene Funktionalität und die eigenen Attribute
15.4. Information Hidding
● Attribute können nur vom Objekt selbst geändert werden
● Objekte kapseln ihre Daten
15.5. Zugriffsmechanismen
● Privat
 Zugriff nur durch Objekt selbst
 kein Zugriff von außen
 Information Hidding
● protected
 Zugriff für alle Objekte abgeleiteter Klassen möglich
 Vererbung
● package
 Zugriff freigegeben für alle Objekte des nächsten umschließenden Packetes
● public
 Uneingeschränkter Zugriff
145
SE
15.6. Klassen
● Art Schablone für die Erzeugung von Objekten
● definiert Attribute, Methoden und Zugriffsmechanismen
● Objekte entstehen durch Instanzierung einer Klasse
Header
//Telefonnummer.h
#ifndef TELEFONNUMMER_H
#define TELEFONNUMMER_H
class CTel
{
public:
//Konstruktor-Methode
CTel();
//Ãœbrige Methoden
void stzteNummer(int, int, int);
void zeigeNummer();
private:
int m_laenderkennziffer;
int m_Ortsvorwahl;
int m_Durchwahl;
};
#endif
● Klassenname Ctel – Konvention: C für Class
● Zugriffsmechanismen privat und public
● Methodendeklaration
 Methoden, die wie die Klassen heißen sind Konstruktoren und werden
bei der Instanziierung automatisch aufgerufen
● Attributdeklaration
146
SE
Implementierungsfile
//Telefonnummer.cpp
#include "Telefonnummer.h"
#include <iostream>
using namespace std;
CTel::CTel()
{
m_iLaenderkennziffer=49;
m_iOrtsvorwahl=0;
m_iDurchwahl=0;
}
void CTel::setzteNummer(int L, int O, int D)
{
m_iLaenderkennziffer=L;
m_iOrtsvorwahl=O;
m_iDurchwahl=D;
}
void CTel::zeigeNummer()
{
cout << "+" << m_iLaenderkennziffer << "(" << m_iOrtsvorwahl << ")"
<< m_iDurchwahl << end1;
}
● :: ist Bereichsoperator – legt die Zugehörigkeit einer Methode zu einer
Klasse fest
● cout gibt Inhalt auf Standardausgabe aus
● Zuweisung an die Klassenvariablen durch Methoden
Main
//main.cpp
#include "Telefonnummer.h"
int main(void)
{
//Objektinstanziierung und automatischer Aufruf der Konstruktoren
CTel eineTel;
eineTel.zeigeNummer();
//Wertzuweisung ueber Methode
eineTel.setzteNummer (49, 89, 2345435);
eineTel.zeigeNummer();
}
//Zugriff auf privaten Bereich wäre:
//eineTel.m_iLaenderkennziffer=43;
//kein direkter Zugriff auf die Variablen!
return 0;
● definierte Klasse wird bei Instanziierung wie ein Typ benutzt
● Zugriff auf öffentliche Methoden durch den Punktoperator
● Zugriffsverletzung bei direktem Zugriff auf die Variablen
147
SE
15.7. Beziehung zwischen Klassen
15.7.1. Vererbung
●
●
●
●
●
●
●
●
Verschiedene Arten, wie Klassen unterinander in Beziehung stehen
grafische Beschreibung durch UML – Unified Modeling Language
bekanntestes Prinzip der Codewiederverwendung
Unterklassen – Teilklassen – ableitete Klasse – Subklassen erben Attribute und
Methoden ihrer Elternklasse
Attribute und Methoden können ergänzt und geändert werden → Spezalisierung der
generalisierten Elternklasse
Problem bei Mehrfachvererbung → Namenskonflikte
Polymorphismus ist verschiedene Art und Weise der Ausführung einer
von einer Elternklasse vererbten Methode an eine Unterklasse
Zugriff der Unterklassen auf die Attribute und Methoden der Oberklasse
nur dann gewährt, wenn Zugriffsmechanismus als protected definiert ist –
nicht private Attribute können nur über öffentliche Schnittstelle verändert
werden
//FestTel.h
#ifndef FESTTEL_H
#define FESTTEL_H FESTTEL_H
#include "Telefonnummer.h"
class CFestTEl:public CTel
{
};
● Klasse CFestTel wird von der Klasse CTel abgeleitet – Doppelpkt. und public
● public vererbt alle Attribute und Methoden mit den gleichen
Zugriffsmechanismen, wie die Elternklasse
#endif
//FestTel.cpp
#include "FestTel.h"
//main.cpp
#include "FestTel.h"
int main(void)
{
//Die gesammte Funktionalität wurde von CTel vererbt
CFestTel eineTel;
eineTel.zeigeNummer();
eineTel.setzteNummer(49, 93, 234455);
eineTel.zeigeNummer();
}
return 0;
148
SE
//HandyTel.h
#ifndef HANDYTEL_H
#define HANDYTEL_H
#include "Telefonnummer.h"
class CHandyTel : public CTel
{
public:
//Konstruktro-Methode
CHandyTel();
//Uebrige Methoden
void setzteNetz(char*);
char* gibNetz();
void zeigNummer();
private:
char m_szNetzbetreiber[10];
};
#endif
● Hinzufügen von Attributen und Methoden ChandyTel();, void
●
setzteNetz(char*); und char* gibNetz();
Überschreiben der Methode void zeigNummer();
→ Ausgabe des
Netzbetreibers
15.7.2. Aggregation
● Klassen gehören anderen Klassen als Datenelement an
● aggredierende Klasse enthällt aggregierte Klassen
● eine Telefonbuch kann Fax-, Handy- und Festnetznummern einer Person
enthalten, müssen aber nicht
● Zugriff auf privat und protected Elemente der aggregierten Klasse
innerhalb der aggregierenden Klasse verwehrt – nur über public
//TelBuch.h
#ifndef TELBUCH_H
#define TELBUCH_H
#include "FestTel.h"
● jedes Objekt der Klasse CtelBuch
aggregiert ein Array von
Festnetznummern als interne
Attribute - CFestTel m_ATels[MAX];
class CTelBuch
{
public:
CTelBuch();
void insertNummer(CFestTel);
...
private:
CFestTel m_ATels[MAX];
...
};
#endif
149
SE
15.2.3. Assoziation
● Lose Beziehung von Klassen unterinander
● eine Person kann eine oder mehrere Telefonnummber haben, muss aber
nicht
● Verbindung von Klasse Telefonnummer und Klasse Person
15.2.4. Unified Modeling Language - UML
● Grafische Beschreibung und Darstellung
● zur Spezifikation, Visualisierung, Konstruktion und Dokumentation
● statische Beziehungen zwischen Klassen werden im Klassendiagramm
dargestellt
● implementierungsunabhängig
Grundelemente
● + für public, - für private
● Verbindung der Klassen durch Pfeile → Vererbungsbeziehungen
● Raute für Aggregationsbeziehung
● hat ein über Pfeil für Assoziationsbeziehung
150
Zugehörige Unterlagen
Herunterladen