Dirk Louis
C/C++ new reference
Markt+Technik Verlag
Die Deutsche Bibliothek CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich.
Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Software-Bezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie zum Schutz vor Verschmutzung ist aus umweltverträglichem und recyclingfähigem PE-Material.
10 9 8 7 6 5 4 3 2 1 04 03 02 01
ISBN 3-8272-6121-X © 2001 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH. Martin-Kollar-Straße 1012, D81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Helfer Grafik Design, München Lektorat: Erik Franz,
[email protected] Herstellung: Anja Zygalakis,
[email protected] Satz: reemers publishing services gmbh, Krefeld (www.reemers.de) Druck und Verarbeitung: Media-Print, Paderborn Printed in Germany
Inhaltsverzeichnis
Inhaltsverzeichnis Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
Die Programmiersprachen C und C++. . . . . . . . . . . . . . . . . . . . . . . . . . . . Leistungsmerkmale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der ANSI-Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schreibkonventionen in C/C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufbau eines C/C++-Programms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typische Komponenten von C/C++-Programmen . . . . . . . . . . . . . . . . . . Größere Programme und Modularisierung . . . . . . . . . . . . . . . . . . . . . . . . Objektorientiertes Programmieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Standardbibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmfluß . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmerstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14 15 15 17 17 20 26 28 29 31 33 34
Sprachkonzepte und Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
Elemente der Sprache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichensatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Trigraphsequenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schlüsselwörter und Symbole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigene Bezeichner: Definition und Deklaration . . . . . . . . . . . . . . . . . . . . Variablen und Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablen und Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die elementaren Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherbelegung der Integer-Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherbelegung der Gleitkommatypen . . . . . . . . . . . . . . . . . . . . . . . . . Vorzeichen integraler Datenypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41 41 43 43 46 48 48 50 51 52 53
5
Inhaltsverzeichnis
Variablendeklaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gültigkeitsbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Namensbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sichtbarkeit und Verdeckung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lebensdauer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammengesetzte Datentypen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammengesetzte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrdimensionale Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten (Strings) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bitfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeiger und Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spezielle Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Referenzen (nur C++) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typdefinitonen und Typumwandlungen . . . . . . . . . . . . . . . . . . . . . . . . . . Typdefinitionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standardkonvertierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typumwandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Literale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #define – symbolische Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const – konstante Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorzeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arithmetische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In- und Dekrementoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergleichende Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Logische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenzugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bitweise Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streamoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Operatoren new und delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typumwandlung und -identifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . Sonstige Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Synonyme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Priorität und Assoziativität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmsteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmsteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
54 58 59 63 64 65 65 66 67 70 72 74 77 79 81 81 84 87 89 89 90 92 94 94 94 96 98 99 99 101 102 103 104 106 106 109 113 116 117 119 125 128 128 129 132 132
Inhaltsverzeichnis
Die if-Bedingung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die if-else-Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . if-else-Ketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . switch-Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die for-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die do-while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abbruchbefehle für Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsdeklaration und -definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Funktion main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenaustausch zwischen Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Rückgabewert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsargumente und Vorgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen und der Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen mit beliebig vielen Argumenten . . . . . . . . . . . . . . . . . . . . . . . Inline-Funktionen (nur C++) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spezifizierer für Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladen, Überschreiben, Verdecken . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen und OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kapselung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statische Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstante Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klasseninstanzen als Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lokale und verschachtelte Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statische Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstante Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsspezifizierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff innerhalb der Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff von außerhalb der Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf Basisklassenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Kopierkonstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standardmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der this-Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
132 133 134 135 137 138 139 141 142 143 143 145 147 149 150 150 153 154 157 158 159 160 162 163 163 164 164 166 167 168 169 170 170 172 173 174 175 176 178 179 181 183 184 186 187 187
7
Inhaltsverzeichnis
Instanzbildung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung und Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung versus Einbettung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsbeschränkung bei der Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsrechte für einzelne Elemente auflockern . . . . . . . . . . . . . . . . . . . . Vererbung und Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nicht vererbbare Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überschreibung und virtuelle Methoden . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung und Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abstrakte Methoden und Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basisklassenzeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RTTI – Laufzeittypidentifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrfachvererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Virtuelle Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden in Klassentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instanziierung und Spezialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implizite Instanziierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Explizite Instanziierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Explizite Spezialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Präprozessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quellcode einfügen und ersetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Makros definieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Argumente in Makros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Direktiven zur bedingten Kompilierung . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenkettenbildung und Grundsymbolverbindung . . . . . . . . . . . . . . . . Sonstige Direktiven und Symbole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exceptions auslösen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exceptions abfangen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Handler-Bereich festlegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwandte Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C-Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wide characters und Multibyte-Zeichen . . . . . . . . . . . . . . . . . . . . . . . . . Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatierte und unformatierte E/A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streams in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streams in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pufferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
188 189 189 191 192 194 194 197 197 199 201 201 202 203 204 205 207 207 209 210 211 212 213 215 216 217 219 220 221 222 224 225 226 226 230 232 233 233 235 235 237 238 239 239 240 242 246
Inhaltsverzeichnis
Die C-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Die Header-Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . assert.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ctype.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . errno.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios646.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . limits.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . math.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . setjmp.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . signal.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdarg.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stddef.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdio.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdlib.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . string.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . time.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . wchar.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . wctype.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Funktionen der C-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . .
247 247 248 248 249 250 251 251 252 253 254 255 255 255 257 259 260 262 263 264
Die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 Übersicht über die Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Header-Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . complex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . fstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . functional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iomanip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iosfwd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . istream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
329 330 330 332 333 334 335 335 336 338 338 340 340 341 342 343 344 344 346 347 347 348 349
9
Inhaltsverzeichnis
queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . sstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdexcept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . streambuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . typeinfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . valarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Container-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Allgemeine Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . map und multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . set und multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Iteratoren-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . reverse_iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Insert-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Stream-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unterstützung eigener Implementierungen . . . . . . . . . . . . . . . . . . . . . . . . Die Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Stream-Klassen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios_base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_streambuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_istream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_stringbuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_istringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ostringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_stringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_filebuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ifstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ofstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_fstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die String-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
349 350 351 351 352 352 353 354 354 354 356 357 357 360 362 365 367 372 373 375 378 379 382 382 384 385 387 390 392 400 400 402 404 405 407 409 411 411 413 414 415 415 417 418 419 420
Inhaltsverzeichnis
Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . char_traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klassen zur lokalen Einstellung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Hilfsklassen für die einzelnen Kategorien . . . . . . . . . . . . . . . . . . . . . . Die numerischen Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . complex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . valarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Teilmengen von valarray-Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . numeric_limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen und Funktionen zur Exception-Behandlung . . . . . . . . . . . . . . . . . exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abgeleitete Exception-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen zur Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . Laufzeittypidentifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . type_info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Struktur pair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherallokation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . allocator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . auto_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen und Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
420 420 421 425 425 427 428 443 443 444 447 452 453 453 454 456 458 458 459 459 459 464 464 465 466
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
11
C/C++ new reference Übersicht Grundlagen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
Sprachkonzepte und Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
Die C-Standardbibliothek. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
247
Die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
329
13
Grundlagen
Grundlagen Die Programmiersprachen C und C++ Im Gegensatz zu anderen Hochsprachen wie Pascal oder Basic, die ursprünglich als reine Lehrsprachen konzipiert wurden, verdankt C seine Entstehung dem Umstand, daß man bei Bell Laboratories eine höhere Programmiersprache für die Implementierung eines neu entwickelten Betriebssystems suchte. Bis dato programmierte man das gesamte Betriebssystem, das später als UNIX bekannt wurde, nämlich in Assembler. Um sich die Arbeit zu erleichtern, bastelte man an einer eigenen Programmiersprache, die möglichst effizient, gut zu portieren und leicht zu compilieren sein sollte. Brian W. Kernighan und Dennis M. Ritchie waren es, die diese Bemühungen zum Erfolg führten und gleichzeitig eine der leistungsfähigsten Programmiersprachen überhaupt schufen: die Programmiersprache C. In der Folgezeit fand C schnell weite Verbreitung und wurde aufgrund seiner Qualitäten und trotz der Schwierigkeiten, denen sich gerade Anfänger bei der Erlernung dieser Sprache gegenüber sehen, immer beliebter. Mit der Zeit wandelten sich die Anforderungen an eine professionelle Programmiersprache. Neben der Erzeugung schneller und kompakter Codes trat die Wartbarkeit und Wiederverwertung bestehender Codes immer weiter in den Vordergrund, und damit auch das Interesse an objektorientierten Konzepten. Mitte der Achtziger begann Bjarne Stroustrup C um objektorientierte Konzepte zu erweitern. Die Betonung liegt dabei auf dem Wort »Erweiterung«, denn die Kompatibilität zu C war bei der Entwicklung der neuen Programmiersprache ein vordringliches Designmerkmal. Eine der ersten Stationen auf dem Weg zur neuen Programmiersprache wurde daher noch als »C mit Klassen« bezeichnet. 1985 kam dann eine Version heraus, die bereits zurecht als objektorientierte Programmiersprache gelten kann, und die unter dem Namen C++ bekannt wurde. Mittlerweile ist C++ voll ausgereift und ebenso erfolgreich wie C. Ein Grund hierfür ist sicherlich die (nahezu) vollständige Abwärtskompatibilität zu C, die dem C-Programmierer den Einstieg in die objektorientierte Programmierung wesentlich erleichtert und die Verwertbarkeit bereits existierenden C-Codes garantiert.
14
Leistungsmerkmale
Im Gegensatz zu rein objektorientierten Sprachen wie Modula oder Smalltalk besteht damit allerdings auch die Gefahr, C++ lediglich als weniger strikte C-Version einzusetzen. Programmieren in C++ ist daher nicht automatisch mit objektorientierter Programmierung gleichzusetzen.
Leistungsmerkmale C wurde ursprünglich als High-Level-Sprache zur Systemprogrammierung, namentlich der Entwicklung des Betriebssystems UNIX, konzipiert. Insofern war C nur ein untergeordnetes Hilfsmittel, und C-Programme sollten einfach zu kompilieren und schnell sein. Das Ergebnis war eine Sprache, die sich durch ●
ihren geringen Umfang an Sprachelementen,
●
die schnelle Laufzeit ihrer Programme,
●
die Unterstützung modularer Programmierung und
●
ihrer guten Portabilität bei gleichzeitiger Systemnähe
auszeichnete. Heutzutage sieht die Situation so aus, daß Rechenzeit immer billiger und der Programmieraufwand immer kostspieliger wird. Ziel der objektorientierten Programmierung mit C++ ist es daher, Quelltext zu schaffen, der sich durch ●
einfache und sichere Verwendung,
●
hohe Wiederverwertbarkeit,
●
einfache Wartbarkeit,
●
gute Verständlichkeit und Lesbarkeit
auszeichnet.
Der ANSI-Standard Der Erfolg einer Programmiersprache hängt nicht nur von der Sprache selbst, sondern auch von der Verfügbarkeit passender Compiler (oder Interpreter) und unterstützender Bibliotheken ab. Letztere waren für die Verbreitung von C sogar von besonderer Bedeutung, da es in C für viele zentralen Aufgaben (Ein- und Ausgabe, Stringbearbeitung, dynamische Speicherverwaltung) keine in der Sprache verankerte Elemente gibt. Die Abhängigkeit von Standardbibliotheken bedeutet aber auch zusätzliche Abhängigkeit von den Compiler-Entwicklern. Um diesem Einfluß entgegenzuwirken und zu verhindern, daß jeder C-Compiler-Entwickler seinen eigenen C-Dialekt definiert, trat im Jahre 1983 im American National Standard Institute (ANSI) ein Komitee aus
15
Grundlagen
Compiler-Herstellern, Software-Entwicklern und Hardware-Produzenten zusammen, das einen Standard für die Programmiersprache C erarbeiten sollte. Der 1989 ratifizierte Standard (Referenznummer ISO/IEC 9899:1990) definiert sowohl Syntax und Semantik der Sprache wie auch die Zusammensetzung der Standardbibliothek. Programmierer, die sich an die vom Standard vorgegebenen Regeln halten und keine plattformspezifischen Funktionen verwenden (beispielsweise Programmierung mit DOS-Interrupts), können dank des ANSI-Standards sicher sein, daß sie ihr Programm von jedem ANSI-kompatiblen Compiler erstellen lassen können. Dies bedeutet aber auch, daß man ein Programm auf jedes beliebige System (Computer/ Betriebssystem) portieren kann, sofern es nur einen passenden ANSI-kompatiblen Compiler für dieses System gibt. 1995 wurde die C-Standardbibliothek um eine Sammlung von Funktionen zur Programmierung mit sogenannten Wide Characters – Zeichen, die durch 16- und 32Bit-Zeichencodes codiert werden – ergänzt. Bei den meisten dieser Funktionen, die in den neuen Headern wchar.h und wctype.h deklariert sind, handelt es sich um Adaptionen bestehender C-Funktionen an den Zeichentyp wchar_t. Die Erweiterungen sind im Amendment ISO/IEC 9899:1990/Amd.1:1995(E) zusammengefaßt. Auch für C++ gibt es mittlerweile einen ANSI-Standard. Die wichtigsten Neuerungen gegenüber dem bis dato geltenden Quasi-Standard von Bjarne Stroustrup dürften in der Einführung von Namensbereichen als frei definierbare globale Gültigkeitsbereiche und der Standardisierung der C++-Laufzeitbibliothek zu sehen sein. Die Ausführungen und Beschreibungen in diesem Buch richten sich nach dem neuen C++-Standard. Im Praxisteil dieses Buches finden Sie viele Abschnitte, die zeigen, wie man die Klassen der C++-Laufzeitbibliothek bei der täglichen Programmierarbeit nutzen kann. Obwohl die Abwärtskompatibilität zu C bei der Entwicklung von C++ höchste Priorität hatte, wurden einige wenige der von C übernommenen Konzepte und Elemente in ihrer Semantik geändert (siehe Anhang). Sollte sich ein C-Programm nach der Kompilation mit einem C++-Compiler anders als zuvor verhalten, sollte man prüfen, ob das Programm von diesen Änderungen betroffen ist.
Bezugsquellen C- und C++-Standard können Sie online beim ANSI-Institut oder IHS (Information Handling Service) anfordern: www.ansi.org www.ihs.de
Kopien der Standards können auch aus der Schweiz ISO/IEC Copyright Office Case Postale 56 CH-1211 Genève 20 Schweiz
16
Schreibkonventionen in C/C++
oder aus München bezogen werden: IHS Information Handling Services Tel. 089 / 89526999 Den abschließenden C++-Draft kann man sich derzeit noch aus dem Internet herunterladen: ftp://ftp.maths.warwick.ac.uk/pub/c++/std/cd2/
Schreibkonventionen in C/C++ Bei der Festlegung der Namen von Bezeichnern, das sind Variablen-, Funktions-, Klassen-, Typen- und Makronamen, dürfen folgende Zeichen benutzt werden: ●
alle Buchstaben des englischen Alphabets (a bis z, A bis Z)
●
die Ziffern 0 bis 9
●
der Unterstrich »_«
●
●
●
Der Name eines Bezeichners muß mit einem Buchstaben oder dem Unterstrich beginnen. Die Verwendung einer Ziffer als ersten Buchstaben eines Bezeichners ist nicht erlaubt. Die deutschen Umlaute und andere Sonderzeichen dürfen also nur in Kommentaren und in Zeichenketten vorkommen. Die maximale Länge der Bezeichner ist nicht vorgeschrieben, jedoch müssen nach ANSI C mindestens die ersten 31 Zeichen zur Unterscheidung herangezogen werden.
Warnung Die Sprache C unterscheidet zwischen Groß- und Kleinschreibung. Die Bezeichner var1 und Var1 können also für zwei verschiedene Variablen benutzt werden.
Aufbau eines C/C++-Programms #include <stdio.h> int main() { /* Anweisungen */ return 0; }
17
Grundlagen
Beschreibung C/C++-Programme bestehen aus einer Ansammlung von Deklarationen und Definitionen von Datentypen, Variablen, Funktionen und Klassen (sowie einigen speziellen Direktiven an den Präprozessor). Anweisungen, die festlegen, was ein Programm macht, findet man nur innerhalb von Funktionsdefinitionen (in C++ auch in den Methoden (Elementfunktionen) der Klassen). Jedes C/C++-Programm muß eine main()-Funktion definieren, mit deren Ausführung das Programm beginnt.
Anwendung Im einfachsten Fall besteht ein C/C++-Programm aus einer oder mehreren include-Direktiven, die den Zugriff auf bestimmte Funktionen und Klassen der C/C++-Standardbibliotheken ermöglichen, sowie
●
der obligatorischen Funktion main(), in der alle Anweisungen zur Ausführung des Programms stehen,
●
aus Variablen, die deklariert werden, um Werte zu repräsentieren und zwischenzuspeichern,
●
aus Anweisungen, in denen die Werte in den Variablen bearbeitet werden.
●
Beispiel Um ein einfaches Programm aufzusetzen, das zwei Zahlen multipliziert und das Produkt der beiden Zahlen ausgibt, könnte man folgendermaßen vorgehen: Um zwei ganzzahlige Werte im Programm verwalten zu können, deklariert man zwei Variablen des Datentyps int (Datentyp für ganzzahlige Werte). Für das Ergebnis kann man eine dritte Variable definieren. int main() { int zahl1, zahl2; int ergebnis;
Mit Hilfe des Zuweisungsoperators kann man den Variablen Werte zuweisen: int main() { ... zahl1 = 120; zahl2 = 4;
/* Werte zuweisen */
Zur Multiplikation von int-Werten verwendet man den *-Operator. Das Ergebnis der Berechnung weist man direkt der dritten Variablen zu. ergebnis = zahl1 * zahl2;
18
Aufbau eines C/C++-Programms
Um Werte auszugeben, kann man sich einer speziellen Funktion namens printf() bedienen, die Teil der C-Laufzeitbibliothek ist. printf("Das Produkt der Zahlen ist : %d\n", ergebnis);
Bevor man eine Funktion aufruft, muß die Funktion allerdings dem Compiler per Deklaration bekanntgemacht worden sein. Die Deklarationen für die Funktionen der Laufzeitbibliothek sind in einer Reihe von Header-Dateien zusammengefaßt. Nachdem man sich erkundigt hat, in welcher Header-Datei die gewünschte Funktion deklariert ist, bindet man die Header-Datei mittels einer #include-Anweisung am Anfang des Programms ein. #include <stdio.h>
Warnung Leser, die mit C/C++ für Windows programmieren wollen, seien darauf hingewiesen, daß Windows-Programme statt der main()-Funktion die Funktion WinMain() verwenden. Je nach verwendetem Compiler und Klassenbibliothek kann WinMain() selbst wieder durch eine andere Funktion (beispielsweise OwlMain() für Borland C++ mit OWL) oder eine Klasseninstanz (beispielsweise ein globales Objekt der MFCKlasse CWinApp in Visual C++) ersetzt sein. Konsolenprogramme, die unter Windows ausgeführt werden (sprich im Fenster der MS-DOS-Eingabeaufforderung ablaufen und über keine eigene Windows-Oberfläche verfügen), verwenden aber wie reine DOS- oder UNIX-Programme die main()Funktion.
Beispiel Das folgende Programm kann nach der Kompilation von der Konsole (unter Windows die MS-DOS-Eingabeaufforderung) Ihres Betriebssystems aus aufgerufen und ausgeführt werden. #include <stdio.h> int main() { int zahl1, zahl2; int ergebnis;
/* Faktoren */ /* Produkt */
zahl1 = 120; zahl2 = 4; ergebnis = zahl1 * zahl2;
/* Werte zuweisen */ /* Wert berechnen */
printf("Das Produkt der Zahlen ist : %d\n", ergebnis); return 0; }
19
Grundlagen
Verweise Siehe nachfolgende Abschnitte für weitere einführende Informationen zum Aufbau von C/C++-Programmen. Siehe Kapitel zur Sprache für detaillierte Informationen zu Variablen, Datentypen, Operatoren, Funktionen.
Typische Komponenten von C/C++-Programmen /* Das erste C-Programm */ #include <stdio.h>
Kommentar Präprozessor-Direktive
Funktion
Anweisungs -block
int main() { printf("Hello World\n"); return 0; }
Anweisungen
Typische Komponenten eines C/C++-Programms
Beschreibung Die Funktionalität eines Programmes wird bestimmt durch die Daten, die es verarbeitet, und die Operationen, die es auf den Daten ausführt. Insofern könnte man meinen, ein Programm bestünde nur aus Variablendefinitionen und Anweisungen. Dem ist aber nicht so. Der Compiler unterscheidet noch ganz andere Elemente.
20
Typische Komponenten von C/C++-Programmen
Aus Sicht des Compilers besteht ein Programm aus: Definitionen Deklarationen Anweisungen Präprozessor-Direktiven Kommentaren
Definitionen und Deklarationen Bei Ausführung des Programms müssen der Code und die Daten des Programms in den Arbeitsspeicher des Computers geladen werden. Welcher Code und welche Daten zu einem Programm gehören, legen Sie beim Aufsetzen des Quelltextes selbst fest. Der Code des Programms besteht aus den in Maschinencode übersetzten Anweisungen aus dem Quelltext. In C/C++ dürfen Anweisungen nur in Funktionsdefinitionen stehen. Indem Sie eine Funktion definieren, beispielsweise die main()-Funktion des Programms, teilen Sie dem Compiler mit, daß er für die Funktion Speicher reservieren und dafür Sorge tragen soll, daß der Code der Funktion bei Aufruf des Programms an die entsprechende Stelle im Speicher geladen wird. Für die Verwaltung der Daten definieren Sie Variablen. Hinter den Variablen stehen Speicherbereiche im RAM, die der Compiler für Sie reserviert. In den Variablen können Sie die Daten Ihres Programms speichern. Durch eine Definition führt man neue Elemente (Funktionen, Variablen, Datentypen) in ein Programm ein. Gleichzeitig wird das Element im Zuge der Definition mit einem Bezeichner verbunden (Variablenname, Funktionsname, etc.), der im Programmquelltext verwendet wird, um auf das Element zuzugreifen. Da mit der Definition von Funktionen und Variablen die Bereitstellung von Speicherbereichen einhergeht, müssen diese Elemente eindeutig sein, d. h., sie dürfen nur einmal im ganzen Programm definiert sein (sonst gäbe es Bezeichner, die mit mehreren Speicherbereichen assoziert wären). Andererseits fordert der Compiler, daß alle Elemente, die man verwendet, ihm vorab bekanntgemacht werden müssen. Bevor man eine Variable verwendet, muß man dem Compiler mitteilen, welchen Datentyp die Variable hat, bevor man eine Funktion aufrufen kann, muß man dem Compiler mitteilen, welche Parameter die Funktion übernimmt und was für einen Rückgabetyp sie zurückliefert. Die Bekanntmachung der Elemente bezeichnet man als Deklaration. Grundsätzlich ist jede Definition auch eine Deklaration. Es gibt aber auch die Möglichkeit, Elemente zu deklarieren, ohne daß man die Elemente neu deklariert. Für Variablen stellt man dabei der »Definition« das Schlüsselwort extern voran, für Funktionen gibt man nur den Funktionskopf (Rückgabetyp, Name, Parameterliste) ohne Anweisungsteil an.
21
Grundlagen
Verweise Siehe Elemente der Sprache.
Variablen und Konstanten Feste Werte, die sich im Laufe des Programms nicht ändern oder nur einmal benötigt werden, kann man direkt in Form von Literalen in den Quelltext schreiben. Für veränderliche Werte definiert man Variablen. Der Compiler weist der Variablen einen Speicherbereich zu, in den man im Laufe des Programms verschiedene Werte abspeichern kann. Eine Variable kann aber immer nur Werte eines Datentyps aufnehmen (mit Ausnahme der unions). Dies liegt daran, daß unterschiedliche Daten (Buchstabe, ganzzahliger Wert, Gleitkommawert) unterschiedlich viel Speicher benötigen. Auch die Codierung der Werte in Binärdarstellung für die Ablage im RAM ist für die einzelnen Datentypen unterschiedlich. Aus diesem Grund gehört jede Variable einem Datentyp an, der bei der Definition der Variablen vom Programmierer angegeben wird. int var1; double var2; char var3; var1 = 3; var2 = 3.14; var3 = 'c';
// // // // // //
Deklaration einer Variablen für ganzzahlige Werte Deklaration einer Variablen für Gleitkommawerte Deklaration einer Variablen für Zeichen Zuweisung einer ganzzahligen Konstanten Zuweisung einer Gleitkommakonstanten Zuweisung einer Zeichenkonstanten
Verweise Siehe Abschnitt, Kategorie Variablen und Datentypen.
Anweisungen
;
Der eigentliche Code des Programms. Die Anweisungen werden vom Compiler in Maschinenbefehle umgesetzt, die vom Prozessor des Computers bei Ausführung des Programms nacheinander abgearbeitet werden. Anweisungen enden stets mit einem Semikolon. ●
● ●
●
22
Anweisungen dürfen nur innerhalb der Anweisungsblöcke von Funktionen stehen. Jede Programmanweisung muß mit einem Semikolon abgeschlossen werden. Mehrere Anweisungen können – durch Semikolons getrennt – in einer Zeile stehen. Außer Anweisungen werden auch Deklarationen mit einem Semikolon abgeschlossen.
Typische Komponenten von C/C++-Programmen var1 = 3 * var2; printf("Hallo"); return 0;
// // // // //
Zuweisung eines Wertes an eine Variable Aufruf einer Funktion Schlüsselwort zur Beendigung einer Funktion
Anweisungsblöcke
{}
In C/C++ werden Anweisungen in Anweisungsblöcken zusammengefaßt. Ein Anweisungsblock beginnt mit einer öffnenden geschweiften Klammer und endet mit einer schließenden geschweiften Klammer. Anweisungsblöcke können ineinander verschachtelt werden. Der äußerste Block ist dabei stets der Definitionsblock einer Funktion. Innerhalb des Anweisungsblocks der Funktion kann man weitere Blöcke definieren, beispielsweise zur Implementierung einer Schleife oder zur Verzweigung des Programmablaufs in Abhängigkeit von einer if-Bedingung . void func() { long loop = 1;
// Funktion // Anweisungsblock zu Schleife
while (loop <= 10) // Schleife und Schleifenbedingung { // Anweisungsblock zu Schleife // Potenzen berechnen printf("%d ^ %d = %f\n", loop, loop, pow(loop,loop)); loop++; } }
Funktionen Funktionen unterstützen die Modularisierung des Quellcodes. Statt alle Anweisungen in die main()-Funktion zu schreiben, kann man zusammengehörende Anweisungen, die eine gemeinsame Aufgabe erfüllen, als Funktionen auslagern. Eine Funktion verfügt über ●
einen Anweisungsblock,
●
eine Schnittstelle zu den anderen Teilen des Programms und
●
einen Namen, über den die Funktion aufgerufen werden kann.
Die folgende Funktion vergleich() definiert zwei Parameter, denen beim Aufruf der Funktion Werte übergeben werden können. Als Ergebnis liefert die Funktion mittels des Schlüsselworts return einen ganzzahligen Wert zurück, der anzeigt, ob die beiden übergebenen Werte gleich oder unterschiedlich sind. Funktionsdefinition: int vergleich (int parameter1, int parameter2) {
23
Grundlagen if (parameter1 == parameter2) return 1; else return 0; }
Funktionsaufruf: int ergebnis; int var = 25; ergebnis = vergleich(23, var);
// weist ergebnis den Wert 0 zu
Verweise Siehe Kapitel Funktionen.
Präprozessor-Direktiven
#
Präprozessor-Direktiven sind spezielle Anweisungen an den Compiler (genauer gesagt, den Präprozessor), die vor der eigentlichen Übersetzung des Programms in Maschinencode ausgeführt werden. Präprozessor-Direktiven beginnen stets mit einem »#« und stehen immer allein in einer Zeile. Die wichtigste Präprozessor-Direktive lautet: #include. Sie dient dazu, den Inhalt einer Textdatei in den Quelltext der aktuellen Datei einkopieren zu lassen. In C/C++ werden selbst Anfänger gleich mit diesem Konzept konfrontiert, da man nur auf diese Weise auf die Funktionen/Klassen der Standardbibliothek zugreifen kann. Die Standardbibliotheken von C und C++ liegen als kompilierte LIB-Dateien vor und enthalten die Definitionen einer Reihe von nützlichen Funktionen (und Klassen). Um eine dieser Funktion in einem Programm aufrufen zu können, muß man die Funktion zuvor beim Compiler bekanntmachen. Dies geschieht durch eine Deklaration. Um dem Programmierer die Tipparbeit für die Deklaration der Bibliothekselemente zu ersparen, sieht der ANSI-Standard eine Reihe von Header-Dateien vor, in denen die Deklarationen der Bibliothekselemente stehen. Jede Header-Datei ist einem speziellen Themengebiet gewidmet. Die Funktion printf() zur Textausgabe fällt beispielsweise in den Bereich Ein- und Ausgabe, englisch Input/Output (IO), ihre Deklaration ist in der Header-Datei stdio.h enthalten. Statt printf() und weitere Funktionen zur Ein- und Ausgabe extra im Programm vorab zu deklarieren, genügt es daher die Header-Datei stdio.h einzubinden: #include <stdio.h>
24
Typische Komponenten von C/C++-Programmen
Kommentare
/* */ und //
Kommentare sind Textpassagen, die vom Compiler ignoriert werden. Sie erlauben dem Programmierer, Anmerkungen in den Quelltext aufzunehmen – meist zur Erklärung des nebenstehenden oder nachfolgenden C/C++-Codes. C
Kommentare beginnen in C mit der Zeichenkombination Schrägstrich – Stern »/*« und enden mit der Zeichenkombination Stern – Schrägstrich »*/«. Die dazwischenstehenden Zeichen werden vom Compiler ignoriert. Dies gilt auch für das Zeilenendezeichen, so daß sich Kommentare auch über mehrere Zeilen erstrecken dürfen. Sie dürfen jedoch nach dem ANSI-Standard nicht verschachtelt werden. (Einige Compiler erlauben optional die Verschachtelung von Kommentaren.) /* Dies ist ein Kommentar, der sich über mehrere – genauer gesagt zwei Zeilen erstreckt */ /* Das folgende Programm gibt einen Gruß auf den */ /* Bildschirm Ihres Computers aus */ #include <stdio.h> int main() { printf("Hallo!\n"); /* Aufruf einer Funktion */ return 0; } C++
In C++ kann die Auskommentierung auch durch doppelte Schrägstriche »//« erfolgen. Der nachfolgende Kommentar reicht bis zum Zeilenende. In C++ können beide Konzepte zur Kommentierung unabhängig voneinander verwendet werden. /* Das folgende Programm gibt einen Gruß auf den */ /* Bildschirm Ihres Computers aus */ #include <stdio.h> int main() { printf("Hallo!\n"); // Aufruf einer Funktion return 0; }
Tip In C++ bietet es sich an, innerhalb von Anweisungsblöcken nur //-Kommentare zu verwenden und die Klammern /* und */ zum Auskommentieren größerer Blöcke beim Debuggen einzusetzen.
25
Grundlagen
Größere Programme und Modularisierung Beschreibung Beim Aufsetzen größerer Programme empfiehlt es sich, den Code zu modularisieren, statt diesen Anweisung für Anweisung untereinander in die main()-Funktion zu schreiben. Die Modularisierung und Strukturierung des Codes sieht so aus, daß man einerseits Anweisungen und Code in Form von Funktionen und Klassen (nur C++) organisiert, andererseits den Quelltext selbst auf mehrere Dateien verteilt.
Aufteilung in Funktionen und Klassen Größere Programmieraufgaben löst man üblicherweise, indem man Teilaufgaben formuliert und diese in Form eigener Funktionen löst. Ein Programm, das zwei Daten einliest, auf der Grundlage der Daten eine Berechnung ausführt und dann das Ergebnis ausgibt, könnte drei Teilaufgaben definieren: ●
Daten einlesen
●
Berechnung durchführen
●
Daten ausgeben
Für jede dieser Teilaufgaben könnte man eine eigene Funktion definieren (wobei man für die Ein- und Ausgabe in einfachen Fällen auch direkt die entsprechenden Funktionen der Laufzeitbibliothek verwenden kann). Die Aufteilung in Funktionen hat den Vorteil, daß ●
der Quelltext übersichtlicher ist,
●
der Quelltext einfacher zu debuggen und zu warten ist.
Letzeres gilt insbesondere, wenn eine der Teilaufgaben mehrfach auszuführen ist. Statt jedes Mal die zugehörigen Anweisungen einzutippen, ruft man nur die entsprechende Funktion auf. Der Code wird nur einmal – in der Funktionsdefinition – aufgesetzt. Stellt sich heraus, daß die Funktion fehlerhaft ist oder erweitert werden muß, kann dies zentral in der Funktionsdefinition geschehen. In C++ kann Code nicht nur in Funktionen, sondern auch in Klassen (Datentypen, in denen Variablen und Funktionen zusammengefaßt werden) organisiert werden.
Aufteilung in mehrere Quelltextdateien Umfangreichen Code kann man auf mehrere Quelltextdateien verteilen. Dies ist beispielsweise angebracht, wenn man eine Sammlung von nützlichen Funktionen in einer eigenen Datei verwahren will (meist der erste Schritt zu einer eigenen Bibliothek), oder wenn man zu mehreren an einem größeren Programmprojekt arbeitet. Aufgabe des Compilers und Linkers ist es dabei, die einzelnen Quelltextdateien zu einem Programm zusammenzufassen.
26
Größere Programme und Modularisierung
Alle Quelltextdateien, die zusammen in einem Schritt vom Compiler übersetzt und zu einer Objektdatei kompiliert werden, bezeichnet man als Modul oder Übersetzungseinheit. Eine Übersetzungseinheit besteht aus einer Implementierungsdatei (Extension .c oder .cpp) und allen Quelltextdateien, deren Inhalt direkt oder indirekt per #include-Anweisung eingebunden werden.
Beispiel Für ein Programm, das zwei Zahlen von der Tastatur einliest und sowohl das Produkt als auch den Mittelwert der beiden Zahlen berechnet und ausgibt, könnte man beispielsweise zwei spezielle Funktionen zur Berechnung des Produkts und des Mittelwerts aufsetzen und diese dann in der main()-Funktion aufrufen. (Für zwei so simple Berechnungen wie das Produkt und den Mittelwert zweier Zahlen, würde man in der Praxis allerdings keine eigene Funktion aufsetzen. Doch geht es hier nur darum, das Prinzip der Modularisierung zu verdeutlichen.) #include <stdio.h> int produkt_berechnen(int z1, int z2) { int ergebnis; ergebnis = z1 * z2; return ergebnis;
/* Produkt berechnen */ /* Ergebnis zurückliefern */
} double mittelwert_berechnen(int z1, int z2) { double ergebnis; ergebnis = (z1 + z2)/2.0; return ergebnis;
/* Mittelwert berechnen */ /* Ergebnis zurückliefern */
} int main(int argc, char **argv) { /* Variablen deklarieren */ int zahl1, zahl2; int produkt; double mittelwert; /* Zahlenwerte einlesen */ printf("Geben Sie zwei Zahlen zwischen 0 und 100 ein: \n\n"); scanf("%d %d", &zahl1, &zahl2); fflush(stdin); /* Ergebnisse berechnen */ produkt = produkt_berechnen(zahl1, zahl2); mittelwert = mittelwert_berechnen(zahl1, zahl2);
27
Grundlagen /* Ergebnisse ausgeben */ puts("\n"); printf("Produkt = %d\n", produkt); printf("Mittelwert = %f\n", mittelwert); return 0; }
Verweise Siehe Abschnitt zur Sprache für detailliertere Informationen zu Funktionen und Klassen.
Objektorientiertes Programmieren Objektorientiert zu programmieren bedeutet nicht einfach, sich die Annehmlichkeiten von C++ gegenüber C zunutze zu machen oder die Klassenbibliotheken statt der C-Bibliotheken zu benutzen. In diesem strengeren Sinne kann man das folgende Programmbeispiel, in dem statt der C-Funktion printf() die Klasseninstanz cout und der überladene <<-Operator verwendet werden, nicht als objektorientiert programmiert bezeichnen.
Beispiel /* Das erste C++-Programm. */ #include
using namespace std; int main() { cout << "Hello world!" << endl; return 0; }
Objektorientierte Programmierung beginnt eigentlich erst da, wo Klassen definiert und die wichtigen Konzepte der Kapselung und des Polymorphismus sinnvoll eingesetzt werden, und sie rechtfertigt sich vor allem dort, wo Klassenbibliotheken entstehen, die sich leicht und sicher verwenden lassen. Letztendlich laufen alle wesentlichen Konzepte der objektorientierten Programmierung darauf hinaus, den Prozeß der Programmentwicklung zu beschleunigen, sei es durch ● ●
28
die Wiederverwertbarkeit bereits implementierter Codes, schnellere Entwicklung von Datentypen durch Vererbung und Polymorphismus,
Verwendung der Standardbibliotheken
●
geringere Fehleranfälligkeit durch Kapselung oder
●
eine bessere Lesbarkeit der Programme, dank der objektorientierten Sichtweise.
Wo diese Kriterien nicht greifen, spricht nichts gegen eine Implementierung in C, wobei der Verzicht auf die objektorientierten Konzepte eine geringere Codegröße und bessere Laufzeiten mit sich bringt.
Verweise Siehe Kapitel über Klassen.
Verwendung der Standardbibliotheken #include
Beschreibung Zu C und C++ gehört eine umfangreiche Sammlung von Funktionen und Klassen, die der Bewältigung der wichtigsten Programmieraufgaben dienen etwa der Ein- und Ausgabe von Daten, der Programmierung mit Zeichenketten (Text), der Verwendung mathematischer Funktionen wie Sinus, Kosinus, Potenz, etc.
Anwendung C/C++ verfügt über einen recht kleinen Satz von Schlüsselwörtern, etlichen Operatoren und einer äußerst umfangreichen Sammlung von Syntaxregeln, die vorschreiben, wie man Schlüsselwörter, Operatoren und selbst definierte Bezeichner zu korrekten C/C++-Programmen verknüpft. Das Potential dieser Sprachelemente und -regeln ist enorm, doch wäre die Entwicklung selbst einfachster Programme allein mit diesen Sprachelementen eine mühsame Angelegenheit, denn selbst für elementarste Programmieraufgaben bieten diese Sprachelemente keine Unterstützung. So müßte man ●
●
●
zum Kopieren von Zeichenketten (sprich Textpassagen) Schleifen programmieren, in denen die Zeichenfolgen Zeichen für Zeichen kopiert werden, zur Berechnung des Sinus einer Zahl sich zuerst in einem Lehrbuch der Mathematik darüber informieren, wie man den Sinus einer Zahl durch Berechnung einer trigonometrischen Reihe annähern kann, für Ein- und Ausgaben auf Betriebssystemebene programmieren.
29
Grundlagen
Voraussehende Programmierer werden diese häufig wiederkehrenden Programmieraufgaben in Form von eigenen Funktionen implementieren, damit sie – etwa bei der Berechnung des nächsten Sinus – nicht mehr die ganze Berechnung ausführen müssen, sondern nur noch die entsprechende Funktion aufrufen müssen. Klevere Programmierer wissen, daß genau dies für die wichtigsten Programmieraufgaben bereits geschehen ist. Bevor Sie jedoch eine dieser Funktionen (für C++ auch Klassen) aufrufen und verwenden können, müssen Sie ●
●
●
sich informieren, welche Funktion / Klasse Ihr Problem lösen könnte (siehe Referenz der Laufzeitbibliotheken) sich informieren, wie die Funktion / Klasse verwendet wird (siehe Referenz der Laufzeitbibliotheken). Für Funktionen ist es beispielsweise wichtig, welche Argumente man der Funktion beim Aufruf übergibt und welchen Ergebniswert die Funktion zurückliefert. dem Compiler, der Ihren Programmquelltext übersetzt, die Funktion (Klasse) bekanntmachen.
Die Präprozessor-Direktive #include dient dazu, den Inhalt einer Datei in den Quelltext einkopieren zu lassen. In C/C++ werden selbst Anfänger gleich mit diesem Konzept konfrontiert, da man nur auf diese Weise auf die Funktionen/Klassen der Standardbibliothek zugreifen kann. Die Standardbibliotheken von C und C++ liegen als kompilierte LIB-Dateien vor und enthalten die Definitionen einer Reihe von nützlichen Funktionen (und Klassen). Um eine dieser Funktion in einem Programm aufrufen zu können, muß man die Funktion zuvor per Deklaration beim Compiler bekanntmachen. Um dem Programmierer die Tipparbeit zu ersparen, sieht der ANSI-Standard eine Reihe von Header-Dateien vor, in denen die Deklarationen der Bibliothekselemente stehen. Jede Header-Datei ist einem speziellen Themengebiet gewidmet. Die Funktion printf() zur Textausgabe fällt beispielsweise in den Bereich Ein- und Ausgabe, englisch Input/Output (IO), ihre Deklaration ist in der Header-Datei stdio.h enthalten. Statt printf() und weitere Funktionen zur Ein- und Ausgabe extra im Programm vorab zu deklarieren, genügt es daher die Header-Datei stdio.h einzubinden: #include <stdio.h>
Warnung Gemäß ANSI-C lauten alle Header-Dateien für die C-Laufzeitbibliothek auf .h aus. Gemäß ANSI-C++ haben die Header-Dateien dagegen keine Extension und den alten C-Headern wird der Buchstabe c vorangestellt (cstdio statt stdio.h). Zudem sind alle Bibliothekselemente in dem Namensbereich std deklariert. Die korrekte Einbindung der Header-Dateien in C++, beispielsweise der Header-Datei iostream mit den Stream-Klassen zur Ein- und Ausgabe, sähe damit wie folgt aus:
30
Ein- und Ausgabe #include using namespace std;
Einige Compiler unterstützen derzeit aber auch noch ältere Versionen von iostream oder haben die Stream-Klassen nicht im Namensbereich std deklariert. In solchen Fällen muß man sich notgedrungen nach der vom Compiler vorgegebenen Syntax richten, meist: #include
oder #include
Ein- und Ausgabe printf() / scanf() cout / cin
Beschreibung Um mit dem Anwender interagieren zu können, muß ein Programm in der Lage sein, Informationen und Daten vom Anwender entgegenzunehmen und selbst Daten und Informationen über den Bildschirm auszugeben.
Anwendung Im Unterschied zu Sprachen wie Fortran oder Basic besitzt die Sprache C keine eigenen Sprachelemente zur Ein- oder Ausgabe. Dies bedeutet nicht, daß mit Coder C++-Programmen keine E/A-Operationen durchgeführt werden können. Man muß nur statt eines Schlüsselworts der Sprache eine passende Funktion der Laufzeitbibliothek aufrufen. In C sind dies vor allem die Funktionen ●
printf() für die Ausgabe auf die Konsole (Bildschirm)
●
scanf() für das Einlesen von Tastatur.
In C++ bedient man sich der Klassen ●
cout (Ausgabe auf Konsole) und
●
cin (Einlesen von Tastatur)
und der Stream-Operatoren << und >>.
31
Grundlagen
Warnung Die oben beschriebenen Funktionen und Klassen sind gemäß ANSI Teil der Standardbibliotheken von C/C++ und daher stets verfügbar. Der Austausch von Informationen zwischen Anwender und Programm über diese Funktionen und Klassen ist allerdings auf Konsolenprogramme beschränkt. Programme mit grafischer Benutzeroberfläche (GUI), die unter Windows, X-Windows oder OSF Motif laufen, verwenden andere Möglichkeiten der Ein- und Ausgabe, die durch spezielle APIs (Sammlungen von Funktionen und Klassen für bestimmte Programmieraufgaben) unterstützt und implementiert werden.
Beispiele Ein- und Ausgabe unter C: #include <stdio.h> int main() { int zahl1, zahl2; /* Daten einlesen */ printf("Geben Sie zwei Zahlen zwischen 0 und 100 ein: \n\n"); scanf("%d %d", &zahl1, &zahl2); fflush(stdin); /* Daten ausgeben puts("\n"); printf("1. Zahl = printf("2. Zahl = printf("Produkt = return 0;
*/ %d\n", zahl1); %d\n", zahl2); %d\n", zahl1 * zahl2);
}
Ein- und Ausgabe unter C++: #include using namespace std; int main() { int zahl1, zahl2; /* Daten einlesen */ cout << "Geben Sie zwei Zahlen zwischen 0 und 100 ein: \n\n"; cin >> zahl1 >> zahl2; /* Daten ausgeben */ cout << endl; cout << "1. Zahl = " << zahl1 << endl; cout << "2. Zahl = " << zahl2 << endl;
32
Programmfluß cout << "Produkt = " << zahl1 * zahl2 << endl; return 0; }
Programmfluß C/C++-Programme bestehen aus einer Ansammlung von Deklarationen und Definitionen. Anweisungen findet man nur in Funktionen (in C++ auch in den Methoden (Elementfunktionen) von Klassen). Damit stellt sich die Frage, mit welcher Anweisung die Ausführung eines Programmes beginnt. ●
● ●
●
Jedes C/C++-Programm beginnt mit der Hauptfunktion main(), die im Quelltext des Programms definiert sein muß. Die Anweisungen in der main()-Funktion werden nacheinander ausgeführt. Durch Aufruf einer Funktion aus einer anderen Funktion heraus, beispielsweise auch aus main(), kann der Programmfluß gesteuert und der Quellcode durch Aufteilung auf mehrere Funktionen modularisiert werden. Innerhalb einer Funktion kann der Programmfluß durch Verzweigungen (if, switch) und Schleifen (for, while) gesteuert werden.
Warnung Leser, die mit C/C++ für Windows programmieren wollen, seien darauf hingewiesen, daß Windows-Programme statt der main()-Funktion die Funktion WinMain() verwenden. Je nach verwendetem Compiler und Klassenbibliothek kann WinMain() selbst wieder durch eine andere Funktion (beispielsweise OwlMain() für Borland C++ mit OWL) oder eine Klasseninstanz (beispielsweise ein globales Objekt der MFCKlasse CWinApp in Visual C++) ersetzt sein. Konsolenprogramme, die unter Windows ausgeführt werden (sprich im Fenster der MS-DOS-Eingabeaufforderung ablaufen und über keine eigene Windows-Oberfläche verfügen), verwenden aber wie reine DOS- oder UNIX-Programme die main()Funktion.
Verweise Siehe die Anschnitte zu Programmsteuerung, Funktionen, Exception-Behandlung.
33
Grundlagen
Programmerstellung Beschreibung Um aus einem in C/C++ formulierten Programmquelltext ein ausführbares Programm zu machen, muß man es kompilieren. Bei der Kompilation wird der Programmquelltext vom Compiler in Maschinencode umgewandelt. In Zusammenarbeit mit dem Linker wird aus diesem Maschinencode eine ausführbare Datei (unter Windows mit der Extension .exe) erzeugt, die allerdings nur auf bestimmten Plattformen (Kombination aus Prozessor und Betriebssystem, beispielsweise Intel-kompatibler Prozessor und Windows 95-Betriebssystem) ausgeführt werden kann.
Anwendung Die Erstellung eines Programmes ist natürlich nicht mit der Eingabe des Quelltextes in einen Editor abgeschlossen. Bevor ein Programm ausgeführt werden kann, muß es in maschinenlesbaren Code umgewandelt werden. Besteht ein Programm aus mehreren Modulen, müssen diese zusammengebunden werden. Diese Aufgaben übernehmen der Compiler und der Linker. 1. Quelltext in Editor eingeben. Dies kann prinzipiell in jedem ASCII-Texteditor geschehen. PC-Compiler verfügen meist über eine integrierte Entwicklungsumgebung (IDE) mit eigenem Editor, der mehr Unterstützung bei der Programmerstellung bietet. 2. Header-Dateien einkopieren. Zu Beginn der Kompilation wird der Quelltext mittels der #include-Direktive aufgeführter Header-Dateien in den Quelltext der Programmdatei kopiert. 3. Quelltextdateien kompilieren. Der Compiler übersetzt dann den Quelltext in maschinenlesbaren Objektcode. Der Objektcode ist meist an eine bestimmte Umgebung (Computerkonfiguration) angepaßt und nicht zwischen unterschiedlichen Umgebungen portierbar. Syntaktische Fehler im Programm werden vom Compiler angezeigt und müssen korrigiert werden. 4. Objektcode-Dateien (.obj) und Bibliotheken (.lib) zusammenbinden. Besteht ein Programm aus mehreren Quelltextdateien, werden diese vom Compiler einzeln in Objektcode übersetzt. Dem Linker obliegt dann die Aufgabe, diese ObjektcodeDateien sowie den Objektcode der benutzten Bibliotheksfunktionen in eine Programmdatei (.exe unter Windows) zusammenzubinden. 5. Debuggen. Nach dem Kompilieren und Linken liegt ein ausführbares, aber selten ein korrektes Programm vor. Bezüglich lauffähigen Programmen unterscheidet man Laufzeitfehler, die sich meist durch Programmabstürze oder Endlosschleifen äußern, und logische Fehler, wenn das Programm zwar läuft, aber nicht seinen Zweck erfüllt. Zum Aufspüren dieser Fehler verwendet man üblicherweise einen Debugger, mit dessen Hilfe man das Programm schrittweise ausführen und kontrollieren kann.
34
Programmerstellung
Ablauf der Programmerstellung
Die gängigsten Compiler Der GNU-Compiler Der GNU-Compiler ist ein frei verfügbarer Compiler, den man kostenlos aus dem Internet beziehen kann (http://Sourceware.cygnus.com). Den GNU-Compiler gibt es für UNIX wie für Windows. Etwas unbequem für Windows-Anwender ist, daß der GNU-Compiler über keine integrierte Entwicklungsumgebung verfügt, und man daher den Compiler explizit aus der MS-DOS-Eingabeaufforderung heraus aufrufen muß. 1. Setzen Sie Ihren Quelltext mit einem beliebigen ASCII-Editor auf, und speichern Sie die Datei. 2. Sofern Sie das /bin-Verzeichnis des Compilers in Ihren Pfad eingetragen haben, so daß der Compiler gcc oder g++ von überall aufrufbar ist, wechseln Sie in das Verzeichnis, in dem die Quelltextdatei steht. 3. Rufen Sie den C++-Compiler mit dem Dateinamen der Quelltextdatei auf: g++ quelltext.cpp
oder gcc quelltext.c
für den Aufruf des reinen C-Compilers. Das fertige Programm lautet a.exe (a.out unter Unix) und kann direkt ausgeführt werden.
35
Grundlagen
Wenn Sie möchten, können Sie auch einen expliziten Namen für die ausführbare Datei angeben. g++ quelltext.cpp -o progname
Programme, die aus mehreren Modulen bestehen, erstellen Sie durch Auflistung aller beteiligten Quelldateien (#include-Dateien werden nicht aufgeführt). gcc quelltext1.cpp quelltext2.cpp -o progname
Wenn Sie dem Compiler irgendwelche Optionen übergeben wollen (beispielsweise ansi für strikt ANSI-kompatiblen Code oder -c für Kompilieren ohne zu Linken), setzen Sie diese hinter den Dateinamen. gcc quelltext.cpp -ansi -c
Für die Erstellung der Windows-Programme müssen verschiedene Bibliotheken explizit mit eingebunden werden: gcc winprog.cpp -lkernel32 -luser32 -lgdi32
Welche weiteren Bibliotheken unter Umständen aufgenommen werden müssen, entnehmen Sie der Compiler-Dokumentation.
Warnung Die neueste Version des GNU-Compilers heißt jetzt egcs.
Der Borland C++-Compiler In der IDE (integrierten Entwicklungsumgebung) des Borland C++-Compilers werden Programme in Form von Projekten verwaltet. Konsolenanwendungen, die nur aus einer Datei bestehen, können zwar ohne Projektverwaltung erstellt werden, doch warum sollte man die angebotenen Möglichkeiten nicht nutzen. 1. Legen Sie ein neues Projekt an (Befehl Datei/Neu/Projekt für BorlandC++ 5.0, unter 4.5 befand sich der Befehl noch im Menü Projekt). Es erscheint der Target Expert, in dem Sie angeben können, welche Art von Anwendung Sie erstellen wollen. 2. Geben Sie das Projektverzeichnis und einen Namen für die ausführbare Datei an. Wählen Sie als Zieltyp Anwendung, als Umgebung Win32 und als Zielmodell Konsole (oder als Umgebung DOS, wenn Sie noch mit einem Win16-Betriebssystem arbeiten). Die weiteren Einstellungen werden für Sie angepaßt, achten Sie aber darauf, daß die OWL nicht eingebunden wird. 3. Öffnen Sie eine neue Quelltextdatei. Klicken Sie im erscheinenden Projektfenster auf den Knoten der .cpp-Datei. (Wurden weitere Knoten angelegt (.rc, .def) können Sie diese mit den Befehlen des Kontextmenüs löschen). Es erscheint ein Editorfenster für die Quelltextdatei. 4. Geben Sie Ihren Quelltext ein und sichern Sie.
36
Programmerstellung
5. Rufen Sie den Compiler auf (Befehl Projekt/Projekt neu kompilieren für BorlandC++ 5.0). 6. Führen Sie das Programm aus (Befehl Debug/Ausführen für BorlandC++ 5.0). Unter Win32 läuft das Programm in einem MS-DOS-Fenster ab, das sofort wieder verschwindet, nachdem das Programm beendet wurde. Um dies zu verhindern, können Sie die Beispielprogramme um einen Aufruf der Funktion getchar() vor der return-Anweisung von main() erweitern. Das Programm wartet dann auf eine Tastatureingabe, und das DOS-Fenster bleibt sichtbar. Die Einstellungen für den Compiler und Linker werden in entsprechenden Dialogfeldern gesetzt (Befehl Optionen/Projekt für BorlandC++ 5.0). Zur Erstellung von Windows-Programmen wählen Sie im Target Expert als Zieltyp Anwendung, als Umgebung Win32 und als Zielmodell GUI (statt Konsole). Achten Sie darauf, daß keine zusätzliche Klassenbibliothek (OWL, OCF, MFC, etc.) eingebunden wird, da in diesem Buch nur reine API-Programme beschrieben sind.
Der C++-Builder Der C++Builder von Borland arbeitet ebenfalls mit einer Projektverwaltung. Während der C++Builder 1.0 die Projekte jedoch noch einzeln verwaltete, gibt es in der Version 3.0 nun auch die Möglichkeit, mehrere zusammengehörende Projekte zusammen in einer Projektgruppe zu verwalten (analog den Arbeitsbereichen in Visual C++). 1. Legen Sie ein neues Konsolen-Projekt an (Befehl Datei/Neu für C++Builder 3.0). Es erscheint ein mehrseitiges Dialogfenster, in dem Sie zur Seite Neu wechseln. 2. Doppelklicken Sie auf das Symbol Konsolen-Experte (Textbildschirm-Anwendung für 1.0). 3. Es erscheint direkt der Quelltexteditor des C++Builders mit einer extra Seite für Ihren Quelltext. Speichern Sie Projekt und Quelltext (Befehl Datei/Alles speichern). 4. Geben Sie Ihren Quelltext ein – entweder indem Sie ihn in das vorgegebene Gerüst einpassen oder indem Sie das vorgegebene Gerüst zuvor löschen und dann Ihren Text eintippen. 5. Führen Sie das Programm aus (Befehl Start/Start). Unter Win32 läuft das Programm in einem MS-DOS-Fenster ab, das sofort wieder verschwindet, nachdem das Programm beendet wurde. Um dies zu verhindern, können Sie die Beispielprogramme um einen Aufruf der Funktion getchar() vor der return-Anweisung von main() erweitern. Das Programm wartet dann auf eine Tastatureingabe, und das DOS-Fenster bleibt sichtbar.
37
Grundlagen
Die Einstellungen für den Compiler und Linker werden in entsprechenden Dialogfeldern gesetzt (Befehl Optionen/Projekt für C++Builder 1.0 oder Projekt/Optionen für 3.0). Zur Erstellung von Windows-Programmen doppelklicken Sie im Schritt 1 auf das Symbol Anwendung statt auf Konsolen-Experte.
Warnung Wenn Sie mit dem C++Builder 3 arbeiten, müssen Sie in Ihre Konsolenanwendungen unter Umständen auch die Header-Datei aufnehmen.
Tip Der große Vorzug des C++-Builders liegt in der RAD-Umgebung zur Entwicklung von Windows-Programmen. RAD bedeutet Rapid Application Development und steht für die visuelle, komponentenbasierte Erstellung von Programmen.
Der Visual C++-Compiler Auch der Visual C++-Compiler von Microsoft arbeitet mit einer Projektverwaltung. Zusätzlich werden in Visual C++ Projekte in Arbeitsbereichen verwaltet (auf diese Weise können beispielsweise die Projekte für eine Windows-Anwendung und etwaige unterstützende DLLs und LIBs zusammen in einem Arbeitsbereich verwaltet werden). 1. Legen Sie ein neues Konsolen-Projekt an (Befehl Datei/Neu ab Visual C++ 5.0). Es erscheint ein mehrseitiges Dialogfenster, in dem Sie zur Seite Projekte wechseln. 2. Geben Sie einen Projektnamen ein, wählen Sie den Pfad aus, unter dem das Verzeichnis für die Quelldateien des Projekts eingerichtet wird, aktivieren Sie die Option Neuen Arbeitsbereich erstellen und doppelklicken Sie dann auf das Symbol Win32-Konsolenanwendung. (In Visual C++ 6 erscheint daraufhin ein Dialogfenster, in dem Sie neben einem leeren Projekt auch Projekte mit Datei und Grundgerüst erstellen lassen können. Die nachfolgenden Schritte 3 und 4 erübrigen sich dann.) 3. Öffnen Sie eine neue Quelltextdatei (Befehl Datei/Neu, Seite Dateien ab Visual C++ 5) Geben Sie einen Dateinamen ein, und doppelklicken Sie dann auf das Symbol C++-Quellcodedatei. 4. Nachdem die Datei erstellt wurde, doppelklicken Sie im Arbeitsbereich-Fenster (Seite Dateien) auf den gleichnamigen Knoten, um die Datei in ein Editorfenster zu laden. 5. Geben Sie Ihren Quelltext ein und sichern Sie. 6. Kompilieren und linken Sie das Projekt (Befehl Erstellen/ erstellen). 7. Führen Sie das Programm aus (Befehl Erstellen/Ausführen von ).
38
Programmerstellung
Die Einstellungen für den Compiler und Linker werden in entsprechenden Dialogfeldern gesetzt (Befehl Projekt/Einstellungen ab VisualC++ 5.0). Zur Erstellung von Windows-Programmen doppelklicken Sie im Schritt 1 auf das Symbol Win32-Anwendung statt auf Win32-Konsolenanwendung.
39
Zeichensatz
Sprachkonzepte und Syntax Elemente der Sprache Zeichensatz Beschreibung Man unterscheidet zwischen zwei Zeichensätzen: Dem Basiszeichensatz, den jeder ANSI-C/C++-Compiler zur Aufsetzung des Programmcodes bereitstellt. Dem Ziel- oder Umgebungszeichensatz, den das Programm bei der Ausführung verwendet, etwa für Ein- und Ausgaben.
Der Basiszeichensatz Der Basiszeichensatz, den jeder C/C++-Compiler versteht und in dessen Zeichen der Programmquelltext aufgesetzt wird, enthält die folgenden Zeichen: a
b
c
d
e
f
u
v
w
x
y
z
A
B
C
D
E
F
U
V
W
X
Y
Z
0
1
2
3
4
5
g
h
i
j
k
l
m
n
o
p
q
r
s
t
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
6
7
8
9 >
%
:
;
.
?
*
+
-
/
^
_
{
}
[
]
#
(
)
<
&
|
~
!
=
,
\
"
'
sowie: Leerzeichen
Neue Seite (FF)
Horizontaler Tabulator (HT)
Neue Zeile (NL)
Vertikaler Tabulator (VT)
41
Sprachkonzepte und Syntax
Quell- und Zielzeichensatz Der Zielzeichensatz ist compilerspezifisch. Compiler, die unter Windows laufen, verwenden derzeit üblicherweise den ANSI-Zeichensatz von Windows, dessen erste 128 Zeichen den Zeichen des traditionellen ASCII-Zeichensatzes entsprechen (siehe Anhang). In Zukunft wird sich wohl UNICODE durchsetzen (das bereits von Windows NT 4.0 unterstützt wird). Auf die Zeichen dieses Zeichensatzes können Sie in Kommentaren und Zeichenbzw. String-Literalen zurückgreifen. In Zeichen- und String-Literalen kann man darüber hinaus folgende Escape-Sequenzen verwenden: Zeichen
Escape-Sequenz
Neue Zeile
NL (LF)
\n
Horizontaler Tabulator
HT
\t
Vertikaler Tabulator
VT
\v
Rücktaste
BS
\b
Wagenrücklauf
CR
\r
Seitenvorschub
FF
\f
Signalton
BEL
\a
Backslash
\
\\
Fragezeichen
?
\?
Hochkomma
'
\'
Anführungszeichen
"
\"
Oktaler Code
\ooo
Hexadezimaler Code
\xhhh
Beispiele #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char kaempfer[100]; // in Bezeichnern sind Umlaute // nicht erlaubt (nur Basiszeichensatz), strcpy(kaempfer,"Hägar"); // in String-Literalen schon printf("Es kämpft für Sie: %s\n", kaempfer); return 0; }
42
Trigraphsequenzen
Trigraphsequenzen Beschreibung Trigraphsequenzen sind Folgen von jeweils drei Zeichen, die als Ersatz für bestimmte Zeichen des Basiszeichensatzes verwendet werden können.
Anwendung Der Basiszeichensatz ist eng an die englische Sprache und die englische Tastaturbelegung geknüpft. Für Programmierer anderer Nationen kann die Eingabe bestimmter Zeichen auf Grund ihrer landesspezifischen Tastaturbelegung unbequem, wenn nicht sogar nur über ALT+ASCII-Code möglich sein. Der ANSI-Standard definiert aus diesem Grund eine Reihe von Trigraphsequenzen, die vom Compiler zu Beginn der Kompilation in die entsprechenden Zeichen des Basiszeichensatzes umgewandelt werden. Synonym
Bedeutung
Synonym
Bedeutung
??=
#
??!
|
??/
\
??<
{
??'
^
??>
}
??(
[
??-
~
??)
]
Tip Für die deutsche Tastaturbelegung bedeuten die Trigraphsequenzen keine große Erleichterung. Um den Programmcode lesbar zu gestalten, sollte man auf Trigraphsequenzen verzichten.
Beispiele ??=include <stdio.h> int main(int argc, char **argv) ??< printf("Hallo??/n"); return 0; ??>
Schlüsselwörter und Symbole Beschreibung C/C++ kennt eine Reihe von Schlüsselwörtern und Symbolen, die innerhalb der Sprache eine feste Bedeutung haben.
43
Sprachkonzepte und Syntax
Schlüsselwörter asm (nur C++)
false (nur C++)
sizeof
auto
float
static
bool (nur C++)
for
static_cast (nur C++)
break
friend (nur C++)
struct
case
goto
switch
catch (nur C++)
if
template (nur C++)
char
inline (nur C++)
this (nur C++)
class (nur C++)
int
throw (nur C++)
const
long
true (nur C++)
const_cast (nur C++)
mutable (nur C++)
try (nur C++)
continue
namespace (nur C++)
typedef
default
new (nur C++)
typeid (nur C++)
delete (nur C++)
operator (nur C++)
typename (nur C++)
do
private (nur C++)
union
double
protected (nur C++)
unsigned
dynamic_cast (nur C++)
public (nur C++)
using (nur C++)
else
register
virtual (nur C++)
enum
reinterpret_cast (nur C++)
void
explicit (nur C++)
return
volatile
export (nur C++)
short
wchar_t (nur C++)
extern
signed
while
Warnung Schlüsselwörter dürfen nicht als Bezeichner für Variablen, Funktionen, etc. verwendet werden.
Symbole C/C++ kennt eine erstaunliche Zahl an bedeutungstragenden Symbolen, wobei ein Großteil auf die vielen Operatoren der Sprache fällt. Symbol
Kurzbeschreibung
Symbol
Kurzbeschreibung
;
Abschluß einer Anweisung oder Deklaration
--
Dekrement
{ }
Anweisungsblock definieren
->
Elementverweis-Operator
[ ]
Array-Definition
.
Punkt-Operator
[ ]
Array-Indizierung
*
Zeigerdeklaration
( )
Typumwandlung
*
Dereferenzierungs-Operator
44
Schlüsselwörter und Symbole
Symbol
Kurzbeschreibung
Symbol
Kurzbeschreibung
( )
Parameterliste von Funktionen
==
Test auf Gleichheit
...
variable Parameterzahl
<
Kleiner als-Vergleich
=
Zuweisung
>
Größer als-Vergleich
+
Addition
<=
Kleiner-gleich-Vergleich
+=
Addition und Zuweisung
=>
Größer-gleich-Vergleich
-
Subtraktion
<<
Bitweise Linksverschiebung
-=
Subtraktion und Zuweisung
<<=
Bitweise Linksverschiebung und Zuweisung
*
Multiplikation
>>
Bitweise Rechtsverschiebung
*=
Multiplikation und Zuweisung
>>=
Bitweise Rechtsverschiebung und Zuweisung
/
Division
?:
Bedingungsoperator
/=
Division und Zuweisung
^
Bitweises exklusives ODER
%
Modulo-Operator
^=
Bitweises exklusives ODER und Zuweisung
%=
Modulo-Operator und Zuweisung
|
Bitweises ODER
!
Logische Verneinung
|=
Bitweises ODER und Zuweisung
!=
Vergleich auf Ungleichheit
||
Logisches ODER
&
Referenzdeklaration
~
Komplement
&
unär: Adresse des Operanden
:
Klasseninitialisierer
&
binär: bitweise UND-Verknüpfung
::
Bereichsoperator (nur C++)
&=
Bitweise UND-Verknüpfung und Zuweisung
.*
Dereferenzierung von Zeigern auf Klassenelemente (nur C++)
&&
Logische UND-Verknüpfung
->*
Dereferenzierung von Zeigern auf Klassenelemente (nur C++)
,
Sequentielle Auswertung
#
Zeichenkettenbildung
++
Inkrement
##
Grundsymbolverbindung
Alternative Zeichen Für Programmierer mit ungünstigen nationalen Tastaturbelegungen gibt es für einige Symbole alternative Zeichenfolgen (ähnlich den Trigraphsequenzen). Alternative
für
<%
{
Anweisungsblock beginnen
%>
}
Anweisungsblock beenden
<:
[
Array-Definition und -Indizierung
45
Sprachkonzepte und Syntax
Alternative
für
:>
]
Array-Definition und -Indizierung
%:
#
Zeichenkettenbildung
%:%:
##
Grundsymbolverbindung
and
&&
Logische UND-Verknüpfung
and_eq
&=
Bitweise UND-Verknüpfung und Zuweisung
bitand
&
Bitweise UND-Verknüpfung
bitor
|
Bitweises ODER
compl
~
Komplement
not
!
Nicht-Operator, Verneinung
not_eq
!=
vergleicht zwei arithmetische Operanden auf Ungleichheit
or
||
Logisches ODER
or_eq
|=
Bitweises ODER und Zuweisung des Ergebnisses
xor
^
Bitweises exklusives ODER
xor_eq
^=
Bitweises exklusives ODER und
Eigene Bezeichner: Definition und Deklaration Beschreibung Nur mit den Schlüsselwörtern und Operatoren der Sprache C/C++ kann man keine Programme schreiben. C/C++ erlaubt daher die Einführung eigener Elemente in ein Programm. Alle vom Programmierer eingeführten Elemente müssen deklariert und definiert werden.
Anwendung Die wichtigsten Elemente, die ein Programmierer in ein Programm einführen kann, sind: ● ●
●
Variablen, um Daten zwischenzuspeichern, eigene Datentypen, um die Verwaltung der Daten zu organisieren und zu vereinfachen (einschließlich der Definition von Klassen), Funktionen, um zusammengehörige Anweisungen zusammenzufassen und den Quelltext zu modularisieren.
Warnung Die beiden Begriffe Deklaration und Definition werden häufig in etwas nachlässigem Sprachgebrauch synonym gebraucht, auch wenn sie eigentlich Unterschiedliches bedeuten.
46
Eigene Bezeichner: Definition und Deklaration
Deklaration Deklaration bedeutet, dem Compiler einen Namen (Bezeichner) einer Variablen, Funktion, Klasse, eines Templates, Datentyps oder Sprungmarke (Label) bekanntzumachen. Neben dem Namen des neuen Elements enthält die Deklaration bereits alle nötigen Informationen, die den Compiler in die Lage versetzen die ordnungsgemäße Verwendung des Elements im Programmquelltext zu kontrollieren, und Anweisungen, in denen das Element verwendet wird, in Maschinencode zu übersetzen. ●
●
● ●
Mit der Deklaration einer Variablen wird ihr Name, ihr Typ, die Speicherklasse und die Sichtbarkeit des Bezeichners festgelegt. Bei der Deklaration einer Funktion wird ihr Name, ihr Rückgabewert, die Liste der Parameter und die Sichtbarkeit festgelegt. Mit einer Deklaration geht keine Speicherreservierung einher. Eine Variablendeklaration ist automatisch auch eine Definition, es sei denn, die Deklaration ist als extern gekennzeichnet und enthält keine explizite Initialisierung, oder es wird ein statisches Element einer Klasse deklariert.
Definition Erst durch die Definition stellt der Compiler Speicherplatz für ein Element zur Verfügung. ●
Für Variablen wird in diesem Speicher der Wert der Variablen abgelegt.
●
Für Funktionen wird in diesem Speicher der Code der Funktion abgelegt.
●
Die Zuordnung von Namen zu Speicherbereichen muß eindeutig sein, d. h., ein Name kann nicht gleichzeitig auf zwei verschiedene Speicherbereiche weisen.
In C bedeutet dies, daß innerhalb eines Gültigkeitsbereiches jeder Bezeichner nur einmal definiert verwendet werden kann. In C++ können, dank der Möglichkeit der Überladung, Bezeichner von Funktionen und Operatoren innerhalb eines Gültigkeitsbereiches gleichzeitig in mehreren Definitionen benutzt werden. (Der Compiler löst dies, indem er die Namen um eine codierte Form der Parameter oder Operanden erweitert, so daß jedem erweiterten Namen wieder genau ein Speicherbereich zukommt.)
Verweise Siehe Gültigkeitsbereiche, Sichtbarkeit, Lebensdauer
47
Sprachkonzepte und Syntax
Variablen und Datentypen Variablen und Datentypen Beschreibung Ganz egal, welche Art von Programm Sie schreiben: Die Hauptaufgabe eines Programmes besteht immer darin, Daten zu verarbeiten. Während das Programm abläuft, werden diese Daten an verschiedenen Stellen im Arbeitsspeicher abgelegt. Um auf die Daten zugreifen zu können, ohne sich mit Speicheradressen herumärgern zu müssen, werden Variablen benutzt.
Variablen Eine Variable ist ein Name für einen Speicherbereich, in den ein Datenobjekt abgelegt werden kann (beispielsweise eine ganze Zahl, eine Gleitkommazahl, eine Zeichenkette, etc.). Über den Namen der Variablen kann auf den Speicherbereich zugegriffen werden, um die sich dort befindenden Daten zu lesen oder Daten dorthin zu schreiben. Der Compiler legt zu diesem Zweck eine Symboltabelle an, in der zu jedem Variablenname die Anfangsadresse des zugehörigen Speicherbereichs vermerkt ist. Bei der Kompilation kann er dann jedes Vorkommen eines Variablennamens durch die passende Adresse ersetzen. Für das Anlegen dieser Symboltabelle ist es aber notwendig, daß jede Variable vor ihrer Verwendung deklariert wird, beispielsweise: int erste_Zahl;
Die wichtigste Angabe bei der Deklaration ist, neben dem Namen der Variablen, der Datentyp.
Datentypen Je nach Art der Daten, die in einer Variablen abgelegt werden können, unterscheidet man verschiedene Datentypen, beispielsweise: ●
int für ganze Zahlen
●
double für reelle Zahlen (auch Gleitkommazahlen genannt)
●
char für Zeichen (Buchstaben, Ziffern, Sonderzeichen)
●
bool für Boolesche Variablen, die einen Wahrheitswert annehmen können (true, false)
Die Datentypen bestimmen ●
48
die interne Darstellung der Werte der Variablen im Rechner. (Bedenken Sie, daß alle Daten im Speicher als eine Folge von Nullen und Einsen dargestellt
Variablen und Datentypen
werden müssen. Die Codierung eines Zeichens in eine Bitfolge erfolgt dabei nach anderen Regeln als die Codierung einer ganzen Zahl oder einer Gleitkommazahl), ●
●
●
die Größe des Speicherbereichs. (Eine Gleitkommazahl benötigt mehr Speicher als ein einzelnes Zeichen, und eine Strukturvariable benötigt meist mehr Speicher als eine Gleitkommazahl), den Wertebereich. (Dieser ergibt sich letztendlich aus der Größe des Speicherbereichs und der Art der Codierung), die Operationen, die auf den Werten des Datentyps ausgeführt werden können. (Beispielsweise kann man ganze Zahlen addieren, nicht aber Zeichenketten).
C/C++ unterscheiden zwischen elementaren Datentypen, die in der Sprache verankert sind (int, float, double, char, wchar_t und bool für C++), und zusammengesetzten Datentypen, die vom Programmierer definiert werden (Aufzählungen, Arrays, Strukturen und Klassen in C++). Nach ihren gemeinsamen Eigenschaften und Einsatzgebieten bei der Programmierung kann man die Datentypen weiter klassifizieren: ●
●
●
Integrale Typen. Der Datentyp bool, die Datentypen char und wchar_t, die Datentypen int und long, Aufzählungen (enum), Bitfelder. Integrale Datentypen sind als Datentyp für switch-Variablen erlaubt. Arithmetische Typen. Alle integralen Typen plus die Datentypen float, double und long double. Sie sind als Operanden für die arithmetischen Operatoren (+, --, *, /) erlaubt. Skalare Typen. Arithmetische Datentypen und Zeiger. Sie sind als Schleifenvariablen für for erlaubt. char wchar_t short int long
class struct union array Integertypen
Objekttypen
Aufzählungen
Arithmetische Typen Gleitkommatypen
Skalare Typen
Bitfelder
float double long double
Zeigertypen
49
Sprachkonzepte und Syntax
Die elementaren Datentypen short, int, long float, double, long double char, wchar_t bool
Beschreibung Die elementaren Datentypen sind Datentypen, die fest in Form von Schlüsselworten in der Sprache verankert sind.
Anwendung Mit Hilfe der elementaren Datentypen kann der Programmierer Variablen für die verschiedenen Arten von Daten deklarieren (Zahlen, Zeichen, Wahrheitswerte). Darüber hinaus kann man auf der Grundlage der elementaren Datentypen eigene zusammengesetzte Datentypen definieren. Die Programmiersprache C/C++ kennt folgende elementare Datentypen: ●
die Datentypen short, int und long für Ganzzahlwerte
●
die Datentypen float, double und long double für Gleitkommazahlen die Datentypen char und wchar_t für Zeichen (Buchstaben, Ziffern, Satzzeichen, etc.)
●
den Datentyp bool für Wahrheitswerte (nur C++)
●
Der Wertebereich der Datentypen – also der Bereich von Werten, die man Variablen dieses Datentyps zuweisen kann – hängt von der Anzahl Bytes ab, die der Compiler für eine Variable dieses Datentyps im Speicher bereitstellt. Bis auf den Datentyp bool sind alle diese Datentypen vorzeichenbehaftet, d. h., man kann den Variablen diesen Typs negative wie positive Werte zuweisen. Durch Voranstellung des Schlüsselworts unsigned kann man festlegen, daß für den Datentyp nur positive Werte erlaubt sind. Das Schlüsselwort signed gibt explizit an, daß ein Datentyp vorzeichenbehaftet ist, und ist üblicherweise redundant, da die Datentypen ehedem vorzeichenbehaftet sind. Einzige Ausnahme: der Datentyp char.
Warnung Für den Datentyp char ist gemäß ANSI nicht festgelegt, ob der Datentyp vorzeichenbehaftet ist oder nicht. Im Zweifelsfall empfiehlt sich bei der Variablendeklaration die explizite Deklaration als signed char oder unsigned char. Bezeichner
Bytes
Wertebereich*
bool
1
false, true
signed char
1
-128 bis 127
50
Speicherbelegung der Integer-Typen
Bezeichner
Bytes
Wertebereich*
unsigned char
1
0 bis 255
wchar_t
2
0 bis 65.535
int signed int
4
-2.147.483.648 bis 2.147.483.647
unsigned int
4
0 bis 4.294.967.295
short, short int
2
-32.768 bis 32.767
unsigned short int
2
0 bis 65.535
long, long int signed long int
4
-2.147.483.648 bis 2.147.483.647
unsigned long int
4
0 bis 4.294.967.295
float
4
ca. -3.4E +38 bis 3.4E+38
double
8
ca. -1.8E +308 bis 1.8E+308
long double
10
ca. -3.4E +4932 bis 3.4 E+4932
* Die angegebenen Wertebereiche sind für 32-Bit-Systeme typische Wertebereiche. Es ist allerdings zu beachten, daß die Wertebereiche von der Codierung der Werte in Binärdarstellungen abhängt. Diese Binärcodierung wird allerdings nicht bis ins Detail vom ANSI-Standard vorgegeben und ist daher implementierungsspezifisch – hängt also letztendlich vom jeweiligen Compiler ab.
Tip Die minimalen und maximalen Werte für die einzelnen Datentypen sind in den ANSI-C Headerdateien limits.h und float.h bzw. für C++ in den Spezialisierungen der Template-Klasse numeric_limits (aus limits) festgelegt und können von dort abgefragt werden.
Speicherbelegung der Integer-Typen short, int, long
Beschreibung Vorzeichenbehaftete Integer-Werte werden üblicherweise gemäß dem 2er-Komplement binärcodiert. Das erste Bit von links codiert dabei das Vorzeichen: 0 steht für positive Zahlen, 1 für negative Zahlen. Multiplikationen mit -1 entsprechen der Invertierung aller Bits mit anschließender Addition von 1.
51
Sprachkonzepte und Syntax
R|∑ a ⋅ 2 | ∂ ba , K , a g := S ||∑ a ⋅ 2 - 2 T n −1 i=0
n
0
i
n −1 i=0
i
i
i
für an = 0 n
für an = 1
Speicherbelegung int i = 3; 00000000
00000000
00000000
00000011
&i
Speicherbelegung der Gleitkommatypen float, double, long double
Beschreibung Gleitkommazahlen werden durch zwei Werte codiert, von denen der eine Mantisse, der andere Exponent genannt wird. In dieser sogenannten Exponential-Schreibweise wird z. B. die Zahl 54.123 folgendermaßen aussehen: 0,54123*10e4. Die Zahl 0,54123 ist in dem Beispiel die (normalisierte) Mantisse, die Zahl 4 der Exponent zur Basis 10 (Compiler verwenden allerdings üblicherweise die Basis 2). Der ANSI-Standard schreibt nicht explizit vor, wie Gleitkommazahlen zu codieren sind, die Codierung muß aber dem von ANSI vorgegebenen Gleitkommamodell folgen, und die Charakteristika der Codierung müssen in den Konstanten aus der Header-Datei float.h festgehalten werden (numeric_limits und numeric_limits<double> aus der Header-Datei limits für C++).
x = s ⋅ be ⋅
LM∑ f N p
k=1
k
OP Q
⋅ b− k , emin ≤ e ≤ emax
s = Vorzeichen b = Basis des Exponenten e = Exponent p = Anzahl Stellen der Mantisse (bestimmt die Genauigkeit) fk = Ziffer der Mantisse
52
Vorzeichen integraler Datenypen
Speicherbelegung Vorzeichenbit (1) Exponent (8)
Mantisse (23)
= single precision
Mantisse (52)
= double precision
Vorzeichenbit (1) Exponent (11)
Vorzeichen integraler Datenypen Beschreibung Durch Angabe der Schlüsselwörter unsigned beziehungsweise signed kann man bei der Variablendeklaration festlegen, ob in dem Datentyp das Vorzeichen codiert sein soll oder nicht. Entsprechend verschiebt sich der Wertebereich des Datentyps.
Anwendung Wertebereichsverschiebungen durch signed/unsigned: Datentyp
signed
unsigned
char
-128 bis 127
0 bis 255
short
-32768 bis 32767
0 bis 65535
int
-2.147.483.648 bis
0 bis 4.294.967.295
2.147.483.647 long
-2.147.483.648 bis
0 bis 4.294.967.295
2.147.483.647
Warnung ●
●
Variablen von unsigned Datentypen können nicht überlaufen. Wird versucht, Werte außerhalb ihres Gültigkeitsbereichs an sie zuzuweisen, wird automatisch der Modulo-Operator auf den Wert angewandt. Beachten Sie, daß in C/C++ die Datentypen von Operanden intern angeglichen werden (Arithmetische Konvertierungen). Vergleicht man beispielsweise einen signed-Wert mit einem unsigned-Wert, wird der signed-Wert als unsigned-Wert interpretiert. Auf diese Weise kann ein kleiner negativer Wert zu einem großen positiven Wert werden. (Beachten Sie insbesondere, daß der Rückgabewert des sizeof-Operators unsigned ist.)
53
Sprachkonzepte und Syntax ●
Liegt eine char-Variable mit Vorzeichen im Bereich von -128 bis 127, liegt eine entsprechende char-Variable ohne Vorzeichen im Bereich zwischen 0 und 255. Dies kann bei der Portierung unter Umständen zu Problemen führen. Man kann dem durch die Voranstellung eines der Qualifizierer signed oder unsigned vorbeugen.
Variablendeklaration datentyp variablenname;
Beschreibung Variablen sind vom Programmierer eingeführte Bezeichner, denen Werte zugewiesen werden können. Welche Werte einer Variablen zugewiesen werden können, hängt vom Datentyp der Variablen ab. Der Compiler verbindet die Variable mit einem Speicherbereich, in dem der jeweils aktuelle Wert der Variablen abgespeichert wird.
Anwendung Variablen sind für ein Programm wie ein Zwischenspeicher, in dem Daten abgelegt und bei Bedarf wieder hervorgeholt und weiter verarbeitet werden können. Eine Variable wird durch eine Deklaration eingeführt und ist grundsätzlich vom Ort ihrer Deklaration ab verfügbar.
Warnung Die Verfügbarkeit einer Variablen hängt von mehreren Faktoren ab: dem Ort der Deklaration, den bei der Deklaration verwendeten Spezifizierern, dem Gültigkeitsbereich und der Lebensdauer der Variablen.
54
Variablendeklaration
Syntax datentyp variablenname; datentyp var1, var2, var2; ●
●
datentyp: Bei der Deklaration wird zuerst der Datentyp durch Verwendung des entsprechenden Typbezeichners angegeben. Erlaubt sind sowohl elementare wie auch zuvor definierte, zusammengesetzte Datentypen. variablenname: Durch mindestens ein Leerzeichen getrennt, folgt auf den Typbezeichner der Name der Variablen. Es können mehrere Variablen gleichzeitig deklariert werden, wobei die Bezeichner durch Kommata getrennt werden.
Initialisierung Variablen kann man direkt im Zuge der Deklaration einen Wert zuweisen. int monate = 12;
Spezifizierer spezifizierer datentyp variablenname;
Die Variablendeklaration kann durch eine Reihe von speziellen Schlüsselwörtern näher spezifiziert werden: Schlüsselwort const
Legt fest, daß der Variablen nach der Initialisierung (Wertzuweisung im Zuge der Deklaration) kein anderer Wert mehr zugewiesen werden kann.
const int monate = 12; Schlüsselwort volatile
Teilt dem Compiler mit, daß diese Variable nicht etwaigen Optimierungen im Zuge der Kompilation zum Opfer fallen darf.
volatile double var; Speicherklassenspezifizierer
Zur expliziten Vergabe einer Speicherklasse. Zusammen mit dem Ort der Deklaration legt die Speicherklasse die Sichtbarkeit und Lebensdauer der Variablen fest.
auto extern register static
Speicherklasse
auto
Das Schlüsselwort auto weist einer Variablen explizit die automatische Speicherklasse zu. Diese Speicherklassenangabe kann nur innerhalb von Blockbereichen (und für Parameter) verwendet werden und ist im übrigen überflüssig, da lokale Variablen standardmäßig auto sind.
55
Sprachkonzepte und Syntax
Der Speicherplatz für automatische Variablen wird bei Abarbeitung des Blocks, in dem sie deklariert werden, auf dem Stack erzeugt, und beim Verlassen des Blocks wieder freigegeben.
Speicherklasse
register
Grundsätzlich entspricht der Spezifizierer register dem Schlüsselwort auto. Es teilt dem Compiler darüber hinaus aber mit, daß auf die derart deklarierte Variable so schnell wie möglich zugegriffen werden soll. Für PCs bedeutet dies, daß die Variablen in die Register des Prozessors geladen werden sollen. Der Compiler braucht dies jedoch nicht zu tun. In C kann im Gegensatz zu C++ die Adresse eines Objekts, das mit register deklariert worden ist, nicht mehr ermittelt werden, gleichgültig, ob dieses Objekt sich in einem Prozessorregister befindet oder nicht. In C gibt es daher keine Zeiger auf Register-Variablen. Grundsätzlich sollte man heutzutage die Aufteilung der Register den mittlerweile sehr leistungsfähigen Compilern überlassen und auf die Spezifikation des Schlüsselworts register verzichten.
Speicherklasse
static
Grundsätzlich impliziert das Schlüsselwort static lokale Gültigkeit und einmalige Allokation (letzteres bedeutet, daß statische Elemente nicht auf dem Stack angelegt werden). Damit ergeben sich drei wichtige Einsatzbereiche: Bezeichner, die global deklariert sind, haben üblicherweise externe Bindung (das heißt, in Programmen, die aus mehreren Modulen zusammengelinkt werden, bezeichnen sie in allen Modulen das gleiche Objekt). Werden Sie als static deklariert, sind sie nur innerhalb ihres Moduls gültig. Lokale Variablen von Funktionen werden bei Aufruf der Funktion neu allokiert und bei Verlassen der Funktion zerstört. Wird eine solche Variable als static deklariert, wird nur beim ersten Aufruf der Funktion Speicherplatz für die Variable reserviert und gegebenenfalls initialisiert. Zusätzlich endet ihre Lebensdauer erst mit dem Programm und nicht der Funktion. Die Sichtbarkeit bleibt unverändert. Klassen können nicht als static deklariert werden, aber Klassenelemente (siehe Kategorie Klassen). ●
56
Ein statisches Klassen-Datenelement erfüllt ähnliche Aufgaben wie eine globale Variable. Ein solches Datenelement wird nur einmal initialisiert (und zwar außerhalb der Klasse!) und dann von allen Instanzen der Klasse geteilt. (Üblicherweise hat jede Instanz der Klasse natürlich eine eigene Kopie der Klassenelemente.)
Variablendeklaration
●
Eine statische Klassenmethode ist eine Methode, die nur auf statische Klassenelemente zugreifen kann und wie ein statisches Datenelement von allen Instanzen der Klasse oder über den Klassennamen aufgerufen werden kann.
Deklarationen von Funktionsparametern und Funktionsdeklarationen in einem Blockbereich können nicht als static spezifiziert werden.
Speicherklasse,
extern
Mit dem Spezifizierer extern kann einem Objekt oder einer Funktion externe Bindung zugewiesen werden. Externe Bindung bedeutet dabei, daß alle Vorkommen eines Bezeichners mit externer Bindung sich auf ein und dasselbe Objekt beziehen. Bei der internen Bindung beziehen sich nur die Bezeichner aus einer Datei auf ein und dasselbe Objekt. Bezeichner ohne Bindung identifizieren eindeutige Objekte. ●
●
●
●
Objekte und Funktionen, die in einem Namensbereich oder global deklariert sind, haben automatisch externe Bindung, es sei denn, sie wurden schon zuvor mit einem anderen Speicherklassenspezifizierer deklariert. (In C++ haben zudem auch globale Objekte, die als const deklariert sind, interne Bindung.) Klassenelemente und Funktionsparameter können nicht als extern deklariert werden. Objektdefinitionen ohne gleichzeitige Initialisierung werden durch den Spezifizierer als reine Deklaration gekennzeichnet. Auf diese Weise kann ein Objekt mit externer Bindung in allen Modulen bekannt gemacht werden, ohne daß es zu mehrfachen Definitionen kommt. C++ erlaubt die mehrfache Definition von Funktionen gleichen Namens, soweit sich diese durch Anzahl und Typen ihrer Parameter unterscheiden. Intern wird diese Namensmehrdeutigkeit vom Compiler so aufgelöst, daß er den einzelnen Funktionsdefinitionen neue Namen zuordnet, in die die Parameter in codierter Form mit aufgenommen sind. Dies ist nicht immer erwünscht, beispielsweise, wenn Funktionen aus kompiliertem C-Code benutzt werden. Die Namenserweiterung kann daher durch den Bindungs-Spezifizierer extern "C" unterdrückt werden.
Beispiele short int woche; char zeichen; int tag, monat; int jahr = 1998; // Initialisierung const int monate = 12; // konstante Variable
Verweise Siehe Gültigkeitsbereiche, Sichtbarkeit, Lebensdauer
57
Sprachkonzepte und Syntax
Gültigkeitsbereiche Beschreibung In C/C++ wird jeder Variablen in Abhängigkeit davon, an welcher Stelle des Programms die Variable deklariert wird, ein Gültigkeitsbereich zugeordnet.
Anwendung Der Gültigkeitsbereich einer Variablen ist der Bereich eines Programms, in dem eine Variable gültig ist. Variablen können nur innerhalb ihres Gültigkeitsbereichs und nach ihrer Deklaration verwendet werden.
Tip Diese automatische Vergabe von Gültigkeitsbereichen kann vom Programmierer durch die Aufnahme bestimmter Speicherklassenspezifizierer in die Deklaration an seine Bedürfnisse angepaßt werden.
Gültigkeitsbereiche ●
Blockbereich: Variablen, die innerhalb eines Anweisungsblocks (abgegrenzt durch die geschweiften Klammern {..}) deklariert werden, sind von lokaler Gültigkeit. Sie sind nur innerhalb des Blocks bekannt und existieren nur solange, wie der Block abgearbeitet wird. Der Compiler erzeugt diese Variablen dynamisch. Nach der Beendigung des Blocks wird der Speicherplatz der lokalen Variablen freigegeben. Damit ist auch ihr Wert verloren. Lokale Variablen werden zumeist innerhalb von Funktionen deklariert. C erlaubt Deklarationen nur zu Beginn eines Blocks, also vor irgendwelchen Anweisungen. C++ erlaubt Deklarationen dagegen an jeder beliebigen Stelle im Block (sogar in Schleifenheadern).
●
●
●
●
58
Dateibereich: Variablen, die außerhalb jeden Anweisungsblocks, jeder Klasse und jeden Namensbereiches deklariert werden, sind von globaler Gültigkeit und können ab dem Punkt ihrer Deklaration benutzt werden. Sie haben globale Lebensdauer, das heißt, sie existieren vom Start des Programms bis zu dessen Ende. Funktionsbereich: Dieser Bereich gilt nur für die Vergabe von Marken für die goto-Anweisung , denn Marken dürfen nicht aus der Funktion, in der sie deklariert wurden, herausweisen. Klassenbereich (nur C++): Elemente von Klassen sind – mit Ausnahme statischer Elemente – nur innerhalb ihrer Klasse gültig. Sie werden bei der Instanzbildung erzeugt und bei der Auflösung der Instanz zerstört. Namensbereiche (nur C++): In C++ ist es möglich, den globalen Gültigkeitsbereich durch die Deklaration von Namensbereichen aufzuteilen. Die Dekla-
Namensbereiche
ration eines Namensbereiches faßt eine Reihe von Deklarationen in geschweifte Klammern zusammen. Eingeleitet wird die Deklaration durch das Schlüsselwort namespace.
Beispiel #include <stdio.h> namespace Spezielles { int var1;
// Deklarationen in Namensbereich
class demo { // Deklarationen in Klassenbereich int wert; public: demo(): wert(3) {} }; } void func(int param) { int var; }
// Deklarationen in Blockbereich
int var;
// Deklarationen in Dateibereich
int main() { ... return 0; }
Verweise Siehe Namensbereiche Siehe Sichtbarkeit, Lebensdauer
Namensbereiche namespace bereichsname {
}
Beschreibung Mit Hilfe des Schlüsselwortes namespace kann der Programmierer den globalen Gültigkeitsbereich (Dateibereich) in Unterbereiche aufteilen.
59
Sprachkonzepte und Syntax
Zudem können Deklarationen aus Namensbereichen gezielt in bestimmte Gültigkeitsbereiche eingeführt werden (etwa in den Blockbereich einer Funktion oder einen anderen Namensbereich).
Anwendung Die Aufteilung des globalen Gültigkeitsbereichs ist vor allem für Programmierer interessant, die mit anderen Programmierern zusammen an umfangreicheren Softwareprojekten arbeiten. Würden in so einem Fall alle Programmierer ihre globalen Variablen in einem gemeinsamen globalen Gültigkeitsbereich deklarieren, kann es bei der Zusammenführung der Quelltextdateien zu einem Programm schnell zu Fehlermeldungen des Linkers wegen mehreren globalen Variablen gleichen Namens kommen. Durch die Einrichtung individueller Namensbereiche kann man dem abhelfen. Zudem steht es dem Programmierer, der Elemente aus einem Namensbereich verwenden will, frei, diese bei Bedarf einzeln anzusprechen oder mit Hilfe des usingSchlüsselwortes alle Deklarationen aus dem Namensbereich mit einer Anweisung in seinen eigenen globalen Gültigkeitsbereich oder den Blockbereich einer speziellen Funktion einzuführen.
Deklaration von Namensbereichen namespace bezeichner { // Deklarationen } ●
●
●
Namensbereiche können nur im globalen Bereich oder innerhalb eines anderen Namensbereiches deklariert werden. Namensbereichen, die ohne eigenen Bezeichner deklariert wurden, wird der Bezeichner unique zugewiesen, der bei der Kompilation durch einen eindeutigen Bezeichner ersetzt wird. Dieser Bezeichner ist in jeder Übersetzungseinheit (Datei) verschieden. Die Deklaration eines Objektes im unbenannten Namensbereich kommt daher einer Deklaration als static im globalen Bereich gleich. Elemente eines Namensbereiches können direkt innerhalb der Deklaration des Namensbereiches definiert werden oder außerhalb unter Verwendung des Namensbereich-Bezeichners und des Bereichsoperators.
Namensbereiche verwenden using namespace NameDesNamensbereichs;
Mit Hilfe des Schlüsselwortes using kann ein Namensbereich in einen anderen Gültigkeitsbereich eingeführt werden. Danach sind alle in dem neu eingeführten Namensbereich deklarierten Elemente auch in dem Gültigkeitsbereich bekannt, in den der Namensbereich eingeführt wurde.
60
Namensbereiche
Nach Einführung des Namensbereichs kann man im umliegenden Gültigkeitsbereich auf die Elemente aus dem Namensbereich direkt über den Bezeichner des Elements (ohne Voranstellung des Namensbereichs) zugreifen. ●
●
●
Gibt es im umliegenden Gültigkeitsbereich ein Element, das genauso heißt wie eines der Elemente aus dem eingeführten Namensbereich, verdeckt das Element aus dem umliegenden Gültigkeitsbereich das Element aus dem Namensbereich. Durch Voranstellung des Namensbereichs kann auf das Element des Namensbereichs zugegriffen werden. Unbenannte Namensbereiche sind immer im umliegenden Namensbereich bekannt. Namensbereiche können nicht in Klassen, wohl aber in Methoden von Klassen eingeführt werden.
Namensbereiche erweitern namespace bezeichner { // Deklarationen } namespace bezeichner { // weitere Deklarationen }
Namensbereiche können mehrfach deklariert werden, wobei nachfolgende Deklarationen als Erweiterungen behandelt werden. Auf diese Weise können Namensbereiche über Dateigrenzen hinweg deklariert, ausgetauscht und erweitert werden.
Zugriff auf Elemente in Namensbereichen Elemente, die in Namensbereichen deklariert sind, können außerhalb des Namensbereichs nicht einfach über den Namen, unter dem sie im Namensbereich deklariert sind, aufgerufen werden. Entweder ●
●
●
muß der vollständige Bezeichner (einschließlich des Namens des Namensbereichs) verwendet werden, oder der Bezeichner wird durch eine using-Anweisung eingeführt. Dies entspricht einer Neudeklaration. Nach der Neudeklaration kann auf das Element des Namensbereichs ohne Voranstellung des Namensbereichs zugegriffen werden. Gibt es allerdings im umliegenden Gültigkeitsbereich bereits einen gleichlautenden Bezeichner scheitert die Neudeklaration. der Namensbereich und alle in ihm enthaltenen Deklarationen werden durch eine using-Anweisung eingeführt. Danach kann man auf die Elemente des Namensbereichs ohne Voranstellung des Namensbereichs zugreifen. Es liegt dabei jedoch keine Neudeklaration vor. Werden im umliegenden Gültigkeitsbereich und im Namensbereich gleichlautende Bezeichner verwendet, verdecken die
61
Sprachkonzepte und Syntax
Bezeichner des umliegenden Gültigkeitsbereichs die Elemente aus dem Namensbereich. Durch vollständige Qualifizierung (Voranstellung des Namensbereichs vor den Bezeichner) kann jedoch auch auf die verdeckten Elemente zugegriffen werden.)
Synonyme namespace Deklarationen_für_speziellen_Zweck namespace Spez1 = Dekarationen_für_speziellen_Zweck
Für die Bezeichner von Namensbereichen können Synonyme eingeführt werden. Für Namensbereiche mit extralangen Namen können so kürzere Aliase definiert werden.
Beispiele Das folgende Programm demonstriert die Definition und Einführung von Namensbereichen: namespace A { int i = 1; } namespace B { int i = 2; using namespace A; } int main() { //cout << i; cout << A::i; using namespace A; cout << i; using namespace B; //cout << i; return 1; }
// Definition eines Namensbereichs
// Einführung eines Namensbereichs
// Fehler, da i nicht global deklariert // und kein Namensbereich angeschaltet // ok, Zugriff über vollständigen Qualifizierer // ok, liefert A::i // Fehler, kann A::i oder B::i sein
Das zweite Beispiel zeigt Möglichkeiten und Fallstricke bei der Erweiterung von Namensbereichen: namespace demo { int i = 1; void func1(int); void func2(int); class X { int i = 2; }; } namespace demo { //int i = 3;
62
// demo::i
// demo::X::i
// Erweiterung // Fehler: doppelte Definition
Sichtbarkeit und Verdeckung void func1(float); // Überladung void func2(int); // ok: Redeklaration namespace { int i = 4; // demo::unique::i } }
Verweise Siehe Kapitel über Verwendung der Standardbibliotheken
Sichtbarkeit und Verdeckung Beschreibung Prinzipiell ist jedes per Deklaration eingeführte Element ab dem Ort der Deklaration in seinem Gültigkeitsbereich sichtbar kann also über seinen Namen angesprochen und benutzt werden.
Verdeckung Wird ein und derselbe Bezeichner mehrfach benutzt, um Elemente in ineinander verschachtelten Gültigkeitsbereichen zu deklarieren (beispielsweise für eine globale Variable und eine gleichlautende lokale Variable in einer Funktion), verdeckt die Variable des innersten Gültigkeitsbereichs alle Variablen der umschließenden Gültigkeitsbereiche. In C++ kann man auf verdeckte Variablen aus dem globalen Gültigkeitsbereich mit Hilfe des Gültigkeitsbereichoperators weiterhin zugreifen: ●
●
für globale Variablen aus dem Dateibereich stellt man dem Bezeichner einfach den Operator voran ::var_name für Variablen aus Namensbereichen gibt man zusätzlich den Namensbereich an: namensbereich::var_name
Zudem kann man in abgeleiteten Klassen auf verdeckte Elemente der Basisklassen über den Gültigkeitsbereichoperator zugreifen: basisklasse::element.
Warnung Die Mehrfachverwendung eines Bezeichners innerhalb eines Gültigkeitsbereichs ist nur in Ausnahmefällen möglich: ●
Klassen- und enum-Bezeichner können durch Bezeichner für Variablen, Funktionen und enum-Elemente im gleichen Gültigkeitsbereich verdeckt werden.
63
Sprachkonzepte und Syntax ●
Die Überladung von Funktionen in C++ beruht auf der Verwendung des gleichen Bezeichners für die Definition mehrerer Funktionen in einem Gültigkeitsbereich. (Der Compiler löst die Namen intern anhand der Parameterliste auf.)
Beispiele int wert = 1; void func() { int wert = 2; cout << " wert = " cout << "::wert = " };
// global deklariert // lokal deklariert, verdeckt globales wert << wert << endl; // Ausgabe: 2 << ::wert << endl; // Ausgabe: 1
Lebensdauer Beschreibung Wenn Sie eine Variable definieren, erzeugt der Compiler für diese Variable ein Objekt im Speicher (auf das Sie über den Namen der Variablen zugreifen können). In ähnlicher Weise werden Objekte im Speicher erzeugt, wenn Sie dynamischen Speicher reservieren, wenn bei einem Funktionsaufruf die Parameter eingerichtet werden, wenn Instanzen von Klassen gebildet werden (wobei Klassenobjekte erst nach Aufruf des Konstruktors existieren). Wie lange diese Objekte verfügbar und existent sind, hängt von verschiedenen Faktoren ab.
Anwendung ●
●
●
Ort der Deklaration. Variablen (einschließlich der Instanzen von Klassen), die in einem Blockbereich definiert werden, existieren nur bis zum Ende des Blockbereichs und werden bei Verlassen des Blocks aufgelöst (liegt daran, daß der Speicher für die Variablen auf dem Stack abgelegt wird). Variablen, die global deklariert werden, existieren bis zum Programmende. Speicherklassenspezifizierer static. Für Variablen, die innerhalb eines Blockbereichs als static deklariert werden, wird der Speicher nicht auf dem Stack reserviert. Die Variablen existieren bis zum Programmende. Art der Speicherreservierung. Objekte, für die der Speicher dynamisch reserviert wird (mit malloc() oder new), wird der Speicher auf dem Heap reserviert und wird erst freigegeben, wenn der Programmierer dies explizit fordert (free() oder delete) oder das Programm beendet wird.
Verweise Siehe Funktionen, Funktionen und der Stack
64
Zusammengesetzte Datentypen
Zusammengesetzte Datentypen Zusammengesetzte Datentypen Beschreibung Mit den elementaren Datentypen ist bereits der Grundstock für die Datenverarbeitung in Programmen gelegt. Tatsächlich käme man sogar ohne die Möglichkeit zur Definition weiterer Datentypen aus, doch die Programmierung würde sich dann in vielen Fällen äußerst mühsam gestalten. Aus diesem Grunde sieht C/C++ verschiedene Möglichkeiten zur Definition eigener Datentypen vor. Bevor wir uns diese aber im einzelnen anschauen werden, sollte man sich noch einmal klar machen, daß man mit der Definition eines neuen Datentyps nicht nur die eigenen Bedürfnisse, sondern auch die Bedürfnisse des Compilers befriedigen muß. Konkret bedeutet dies, daß der Compiler wissen muß, wie er für Variablen dieses Datentyps Speicher belegen muß, wie die Werte der Variablen codiert und im Speicher abgelegt werden sollen, welche Operationen auf den Variablen des Datentyps erlaubt sind und schließlich wie Typkonvertierungen vorgenommen werden können. Aus all diesen Anforderungen könnte man nun ablesen, daß die Möglichkeiten zur Definition eigener Datentypen entweder sehr eingeschränkt oder mit sehr viel Arbeit verbunden sind. Nun beides ist nicht der Fall. Der Trick dabei ist einfach, daß man die neuen selbst definierten Datentypen aus den elementaren Datentypen ableitet. Insgesamt stehen Ihnen folgende Möglichkeiten zur Definition eigener Datentypen zur Verfügung: Definition eines Datentyps durch Aufzählung der Wertemenge des Datentyps (enum) Definition eines Datentyps durch Zusammenfassung mehrerer Variablen eines Datentyps (Arrays) Definition eines Datentyps durch Zusammenfassung mehrerer Variablen verschiedener Datentypen (Strukturen) Definition eines Datentyps durch Zusammenfassung mehrerer Variablen verschiedener Datentypen, und durch Bereitstellung typspezifischer Funktionen und Operatoren (Klassen). Dies ist die freieste und weitreichendste Form der Typdefinition. Schließlich gibt es noch die Möglichkeit, Zeiger zu definieren, mit denen man direkt auf Speicherbereiche zugreifen und Objekte im Speicher manipulieren kann.
65
Sprachkonzepte und Syntax
Aufzählungstypen enum {element1, element2, ...}; enum typbezeichner {element1, element2, ...};
Beschreibung Aufzählungstypen sind Datentypen, deren mögliche Werte durch explizite Aufzählung von Integer-Konstanten festgelegt werden. Die aufgeführten Werte können im wesentlichen wie im umgebenden Gültigkeitsbereich definierte Konstanten verwendet werden.
Anwendung Bei allen anderen Datentypen sind die Wertebereiche durch die interne Codierung der Werte in Binärdarstellung vorgegeben. Welche Werte beispielsweise eine intVariable annehmen kann, hängt nicht von Ihnen, sondern von Ihrem Compiler ab. Bestenfalls können Sie über Spezifizierer wie short, long oder unsigned auf den Wertebereich einwirken. Bei einem Aufzählungstyp können Sie durch explizite Auflistung selbst angeben, welche Werte zum Wertebereich des neuen Aufzählungstyps gehören. Dies kann einen Programmcode lesbarer und leichter verständlich machen. In C++ kommt hinzu, daß der Compiler prüfen kann, daß Variablen eines Aufzählungstyps auch wirklich nur Werte aus seinem Wertebereich zugewiesen werden. Da intern alle Werte eines Aufzählungstyps durch Integer-Konstanten repräsentiert werden, kann man Werte aus Aufzählungen beispielsweise auch in switch-Verzweigungen verwenden.
Syntax enum {element1, element2, ...}; enum typbezeichner {element1, element2, ...}; ●
●
66
elemente: Die in einem Aufzählungstyp definierten Elemente werden wie Integer-Konstanten behandelt. Damit die Konstanten nicht undefinierte Werte annehmen, werden sie vom Compiler automatisch initialisiert. Nichtinitialisierte Elemente bekommen dabei den Wert ihres Vorgängers +1 zugewiesen. Dem ersten Element wird 0 zugewiesen. Mit Hilfe des Zuweisungsoperators kann der Programmierer den Elementen selbst Werte zuordnen. Es sind allerdings nur Integer-Werte erlaubt oder Werte, die als Integer-Werte interpretiert werden können. Verschiedene Elemente dürfen gleiche Werte haben. typbezeichner: Wird ein typbezeichner spezifiziert, kommt dies einer Typdefinition gleich. Später können dann Variablen dieses Typs definiert werden. In C lautet der Typ enum typbezeichner, in C++ reicht einfach der typbezeichner.
Arrays
Variablendeklaration enum {elem1 = 1, elem2 } var1, var2;
Oder enum typbez {elem1 = 1, elem2 }; enum typbez var1, var2;
Wertzuweisung var1 = elem2; var1 = typbez(3);
Warnung Der Compiler reserviert für enum-Variablen Speicher, der groß genug sein muß, um alle Integer-Werte zwischen dem kleinsten und größten Wert des enum-Typs aufnehmen zu können. Prinzipiell kann man enum-Variablen jeden Wert innerhalb dieses Wertebereichs zuweisen, auch wenn der Wert nicht explizit in der Aufzählung definiert ist (in C++ erfordert dies eine explizite Typumwandlung).
Beispiele #include <stdio.h> enum {ERROR_LESEN = 101,ERROR_SCHREIBEN
= 102};
void fehlermeldung(int fehlernummer) { switch(fehlermeldung) { case ERROR_LESEN: /* Anweisungen */ break; case ERROR_SCHREIBEN: /* Anweisungen */ break; default: break; } } int main() { fehlermeldung(ERROR_NO_MEM); return 0; }
Arrays typ feldname[groesse];
Beschreibung Ein Array, auch Datenfeld oder Feld genannt, ist die Zusammenfassung von mehreren Daten des gleichen Typs zu einer Variablen.
67
Sprachkonzepte und Syntax
Anwendung Arrays ermöglichen Ihnen, eine (nahezu) beliebige Zahl von Variablen gleichen Datentyps en bloc zu definieren: int i[100];
Der Vorteil der Array-Deklaration liegt jedoch nicht nur in der eingesparten Tipparbeit. Die einzelnen Elemente eines Arrays werden im Speicher hintereinander, in einem zusammenhängenden Block abgelegt. Dies bringt eine Reihe interessanter Vorteile, beispielsweise die Möglichkeit, über einen Index auf einzelne Elemente im Block zuzugreifen: i[3] = i[2]+i[1];
Syntax typ feldname[groesse]; ●
●
●
typ: Die Typangabe legt fest, aus welchen Elementen das Array gebildet wird, und liefert zusammen mit der groesse Informationen über den Speicherplatzbedarf (groesse * sizeof(typ)). Folgende Datentypen sind für Elemente eines Arrays nicht zulässig: Referenzen, Funktionen, abstrakte Klassen. feldname: Frei wählbarer Name für die Array-Variable. Array-Typen sind immer an Variablen gebunden. Dies ist eine Besonderheit der Arrays. groesse: Die groesse legt die Anzahl der Elemente fest, aus denen das Array besteht. Als Größenangabe sind nur konstante Ausdrücke erlaubt, die größer Null sind. Die Größenangabe kann auch weggelassen werden. In solch einem Fall muß eine vollständige Deklaration nachfolgen, oder das Array muß bei der Deklaration mit Hilfe einer Initialisierungsliste initialisiert werden. (Aus der Anzahl der Initialisierungswerte wird dann die Dimension errechnet.)
Variablendeklaration int feld[3]; struct person leser[40];
// Array für drei int-Objekte // 40 Elemente vom // Typ struct person
Oder #define ANZAHL 400; double feld[ANZAHL];
Initialisierung Es ist möglich, bei der Definition eines Arrays Initialisierungen vorzunehmen. Dem Namen des Arrays folgt dann das Gleichheitszeichen und in geschweifte Klammern gefaßt die einzelnen Werte, die zugewiesen werden sollen.
68
Arrays
●
●
Es ist nicht erforderlich, alle Elemente zu initialisieren. Elemente, die nicht initialisiert worden sind, erhalten automatisch den Wert 0. Fehlt die Größenangabe, wird die Größe aus der Anzahl der Elemente errechnet.
float werte[3] = { 2.34, 5.76 };
float werte[] = { 2.34, 5.76 };
/* entspricht: werte[0] = 2.34; werte[1] = 5.76; werte[2] = 0; */ /* entspricht float werte[2] */
Zugriff auf Array-Elemente Auf die einzelnen Elemente des Arrays wird über einen Index zugegriffen, der in eckigen Klammern steht. Das erste Element eines Arrays hat immer den Index 0. Das letzte Element hat den Index groesse – 1. feld[0] = 13;
Oder #define ARRAY_GROESSE 20 int array[ARRAY_GROESSE]; for (i=0; i < ARRAY_GROESSE; i++) array[i] = i;
Warnung Die Indizierung der Elemente im Array beginnt immer mit 0, weswegen beispielsweise die Indizes für ein Array, das als feld[20] deklariert ist, von 0 bis 19 laufen. Der Zugriff über höhere Indizes ist möglich (der Compiler kontrolliert nicht die Verwendung korrekter Indizes), führt aber fast ausnahmslos zu Laufzeitfehlern. Wenn man sowohl die Arraygröße angibt als auch eine Initialisierungsliste definiert, darf letztere nicht mehr Elemente enthalten als durch die Arraygröße festgelegt.
Beispiele #include <stdio.h> #include <stdlib.h> #include <mem.h> #define MAX 100 int main() { int feld[MAX]; int i; memset(feld, 0, sizeof(feld));
// Anzahl der Elemente in Array
// Array deklarieren
// Initialisieren des Arrays
69
Sprachkonzepte und Syntax // Array ganz durchlaufen und Zufallswerte zuweisen for (i = 0; i < MAX; i++) { feld[i] = rand() % 100; } // Array durchlaufen und Werte ausgeben for (i = 0; i < MAX; i++) { printf("Element %d \t = %d\n", i, feld[i] ); } return 0; }
Verweise Siehe Mehrdimensionale Arrays Siehe Zeichenketten (Strings)
Mehrdimensionale Arrays typ feldname[dim1][dim2];
Beschreibung Zwei- oder mehrdimensionale Arrays sind Arrays, deren Elemente wiederum Arrays sind. Auch mehrdimensionale Arrays werden nacheinander im Speicher abgelegt. Das Prinzip funktioniert so, daß der letzte Index sich am schnellsten verändert. Die Elemente des Arrays int matrix[3][2];
würden im Speicher also in folgender Reihenfolge abgelegt: matrix[0][0] matrix[0][1] matrix[1][0] matrix[1][1] matrix[2][0] matrix[2][1]
Anwendung Mehrdimensionale Arrays werden am sinnvollsten dort eingesetzt, wo Daten verwaltet werden sollen, die von sich aus schon mehrdimensional organisiert sind, so daß sich eine 1:1-Entsprechung zwischen der natürlichen Anordnung der Daten und ihrer Abspeicherung im Array ergibt.
70
Mehrdimensionale Arrays
Ein Beispiel wären die Elemente einer zweidimensionalen Matrix, die sich wunderbar in einem zweidimensionalen Array verwalten lassen.
Syntax typ feldname[dim1][dim2]; ●
typ: Die Typangabe legt fest, aus welchen Elementen das Array gebildet wird.
●
feldname: Frei wählbarer Name für die Array-Variable.
●
dim: Die Definition enthält so viele Paare eckiger Klammern mit Größenangaben, wie das Array Dimensionen besitzt. Bei mehrdimensionalen Arrays kann die erste eckige Klammer leer bleiben. Dies erklärt sich dadurch, daß ein mehrdimensionales Array als ein einfaches Array angesehen wird, dessen Elemente wiederum Arrays sind. Die Größe des Arrays darf später deklariert werden, aber die Größe der Elemente muß bei der Deklaration feststehen.
Variablendeklaration int feld[3][2];
Initialisierung Mehrdimensionale Arrays können auf zwei Arten initialisiert werden. Entweder wird für jede Komponente eine eigene Initialisierungsliste benutzt, die in geschweifte Klammern eingeschlossen wird, oder jeder Komponenten des untergeordneten Aggregattyps werden die Werte einfach der Reihe nach zugewiesen. int matrix[][2] = {
{ 1, 2 }, { 10, 20 }, { 100, 200 } };
Oder int matrix[3][2] = { 1, 2 , 10, 20, 100, 200 };
Zugriff auf Array-Elemente Um auf ein Element eines mehrdimensionalen Arrays zuzugreifen, muß man für jede Dimension einen Index angeben. var = matrix[2][0];
Obige Anweisung greift auf das dritte Unterarray (Indizierung beginnt mit 0!) und in dem Unterarray auf das erste Element zu (wäre im Beispiel aus dem Initialisierungsabschnitt das Element mit dem Wert 100).
Beispiele #include <stdio.h> #define SPALTEN 10 #define ZEILEN 5
71
Sprachkonzepte und Syntax int main() { int x[ZEILEN][SPALTEN]; int i = 0; int j = 0; for (i = 0; i < ZEILEN; i++) { for (j = 0; j < SPALTEN; j++) { x[i][j] = 'a' + i; } } for (i = 0; i < ZEILEN; i++) { for (j = 0; j < SPALTEN; j++) { printf("%c ",x[i][j]); } printf("\n"); } return 0; }
Ausgabe a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
a b c d e
Verweise Siehe Arrays
Zeichenketten (Strings) charTyp str[groesse];
Beschreibung Arrays bieten sich auch als Variablen für Zeichenketten (Strings) an. In diesem Fall sind die Elemente des Arrays die Zeichen der Zeichenkette.
Anwendung
72
Zeichenketten (Strings)
ANSI C/C++ verwendet zur Repräsentation von Text die sogenannten ASCIIZStrings. Dies sind Zeichenketten, die durch das Zeichen \0 abgeschlossen werden. Variablen, die einen solchen Strig aufnehmen können, kann man auf verschiedene Weise deklarieren: ●
als Array mit Elementen eines char-Typs.
●
als Zeiger auf einen char-Typ.
●
als Instanz einer string-Klasse (nur C++).
Entscheidet man sich für ein Array, genießt man den Vorteil, daß zusammen mit der Array-Deklaration auch gleich der Speicher für den String reserviert wird. Man muß aber aufpassen, daß man der Array-Variablen keinen String zuweist, der aus mehr Zeichen besteht als das Array reserviert hat.
Warnung Beachten Sie zudem, daß ein String in C/C++ immer mit dem Zeichen \0 abgeschlossen werden muß. Dieses Terminierungszeichen ist bei der Berechnung des Speicherbedarfs mit einzurechnen.
Syntax charTyp feldname[groesse]; ●
charTyp: Als Elementtyp wählt man in diesem Fall char, signed char, unsigned char oder wchar_t.
● ●
feldname: Frei wählbarer Name für die Array-Variable. dim: Anzahl der Zeichen, für die Speicher reserviert wird und die in dem Array abgelegt werden können.
Initialisierung char str[ ]
= {'e', 'i', 'n', 's', '\0'};
// mit Initialisierungsliste
Oder char str2[50] = "Dies ist ein String"; // mit String-Literal wchar_t str3[10] = L"zwei"; // Wide Character-Zeichen (UNICODE)
Bei der Initialisierung mit String-Literalen wird das abschließende Nullterminierungszeichen \0 automatisch durch den Compiler angehängt.
Zuweisung Die Zuweisung mit Hilfe des Zuweisungsoperators ist nur bei der Initialisierung möglich. Danach bedient man sich am besten der Funktionen der Laufzeitbibliothek (C-Header-Datei string.h). strcpy(str2, "Neuer Text"); strcat(str1, str2);
// kopieren // anhängen
73
Sprachkonzepte und Syntax
Zugriff auf einzelne Zeichen str1[4] = 'c';
Beispiele #include <stdio.h> #include <string.h> int main() { char string1[] = "C und C++"; char string2[] = "C++ und C"; if(strcmp(string1, string2) > 0) printf("%s ist groesser als %s\n",string1,string2); else printf("%s ist kleiner oder gleich %s\n", string1, string2); return 0; }
Ausgabe C und C++ ist kleiner oder gleich C++ und C
Verweise Siehe Arrays Siehe Zeichenketten, C-Strings
Strukturen struct typbezeichner { typ1 element1; typ2 element2; };
Beschreibung Strukturen fassen mehrere Daten, die unterschiedlichen Datentypen angehören können, zu einer Variablen zusammen. In C++ ist eine Struktur ein Sonderfall einer Klasse, in der alle Elemente standardmäßig public sind.
Anwendung Strukturen sind Datentypen, in denen mehrere Variablen verschiedener Datentypen zusammengefaßt werden können. Die in der Struktur zusammengefaßten »Variablen« bezeichnet man als Felder oder Elemente der Struktur. Der Vorteil dabei ist, daß mehrere, logisch zusammengehörende Daten in einem Datentyp vereinigt werden können.
74
Strukturen
Ein typisches Beispiel wäre die Definition von Variablen zum Abspeichern von Vektoren. Für einen zweidimensionalen Vektor benötigt man zwei int-Werte für die x- und die y-Koordinate. Diese als einzelne int-Variablen zu definieren, ist unbequem und äußerst unübersichtlich: int v1_x, v1_y; int v2_x, v2_y;
// erster Vektor // zweiter Vektor
Besser ist die Einführung eines Strukturtyps für Vektoren: struct int int }; struct
vektor { x; y; vektor v1, v2;
Warnung ●
●
Eine Struktur kann keine Variable ihres eigenen Typs als Element enthalten. Zeiger oder Referenzen auf Variablen ihres eigenen Typs sind aber erlaubt. In C++ sind Strukturen Klassen mit public-Zugriff auf die Strukturelemente. In C++ dürfen Strukturen daher auch Funktionen als Elemente enthalten.
Syntax struct typbezeichner { typ1 element1; typ2 element2; /* weitere Elemente */ }; ●
●
typbezeichner: Wird ein typbezeichner spezifiziert, kommt dies einer Typdefinition gleich. Später können dann Variablen dieses Typs definiert werden. In C lautet der Typ struct typbezeichner, in C++ reicht einfach der typbezeichner. typ element: Innerhalb der Klammern werden die Elemente der Struktur deklariert.
Definition von Strukturvariablen Strukturvariablen können entweder nach der Definition der Struktur mit Hilfe des Typbezeichners definiert werden (vorausgesetzt ein Typbezeichner wurde angegeben) oder direkt im Zuge der Strukturdefinition. struct struct person struct
person { /* Definition */ }; person leser; autor; // nur C++ person leser[40]; // 40 Elemente vom Typ struct peson
Oder struct { int x;
75
Sprachkonzepte und Syntax int y; float laenge; } vektor1, vektor2;
Initialisierung Strukturvariablen können bei ihrer Definition direkt initialisiert werden. Dazu werden die Werte, mit denen die Strukturelemente initialisiert werden sollen, in geschweiften Klammern aufgelistet und mittels des Zuweisungsoperators der Variablen zugewiesen. ●
Es ist nicht erforderlich, alle Elemente der Struktur zu initialisieren.
struct person { char name[20]; int plz; char ort[20]; char strasse[20]; }; struct person leser = {"Klaus Müller", 7652 , "Entenhausen", "Duckstr. " };
Zugriff auf Strukturelemente Es gibt zwei Möglichkeiten, um auf die Elemente einer Struktur zuzugreifen. Das benutzte Verfahren hängt davon ab, ob man über eine Strukturvariable oder einen Zeiger auf eine Struktur, auf die Elemente zugreift. strukturvariable.strukturelement strukturzeiger->strukturelement
Beispiele struct person { char name[20]; int plz; char ort[20]; }; int main() { struct person leser; struct person *pleser; pleser = &leser; leser.plz = 5100; strcpy(leser.name, "Otte"); strcpy(pleser->ort, "Karlsbad"); printf("Leser: %s, PLZ: %d Ort: %s\", leser.name, leser.plz, leser.ort); printf("Leser: %s, PLZ: %d Ort: %s\",
76
Bitfelder pleser->name, pleser->plz, pleser->ort); return 0; }
Bitfelder struct typbezeichner { intTyp1 element1 : Zahl; intTyp2 element2 : Zahl; };
Beschreibung Bitfelder sind besondere Elemente in Strukturtypen, für die man explizit angeben kann, wieviel Bit für das Element benötigt werden.
Anwendung Bitfelder werden benutzt, wenn Daten gespeichert werden sollen, die weniger als 1 Byte Speicherplatz benötigen. Hierzu gehören zum Beispiel 1 Bit große Flags, die ausreichen, um Wahrheitswerte darzustellen. Nach der Typangabe und dem optionalen Namen des Elements folgt ein Doppelpunkt. Daran schließt sich ein ganzzahliger Wert an, der bestimmt, wie viele Bits diesem Element zur Verfügung gestellt werden sollen. Bitfelder werden traditionell häufig zur Speichereinsparung eingesetzt. Dahinter verbirgt sich die Idee, daß die Bits der Bitfelder hintereinander abgespeichert werden, daß eine Struktur mit zwei Bitfeldern von 3 und 5 Bit Länge, also beispielsweise ingesamt nur 8 aufeinanderfolgende Bits (1 Byte), belegen würde; ob dem so ist, hängt allerdings vom Compiler ab. Deswegen und wegen des heute relativ billigen Arbeitsspeicher, ist von dem Einsatz von Bitfeldern zur Speicherersparnis eher abzuraten.
Warnung ●
● ●
Ein einzelnes Bitfeld muß nach ANSI C immer den Datentyp int, signed int oder unsigned int besitzen. C++ erlaubt dagegen jeden Typ, der als Integer interpretierbar ist, also auch char, short, long und Aufzählungstypen. Für Bitfelder können keine Zeiger und Referenzen deklariert werden. Unbenannte Bitfelder der Größe 0 bewirken, daß das nächste Bitfeld in einer neuen Allokationseinheit (meist int oder word) beginnt.
77
Sprachkonzepte und Syntax
Syntax struct typbezeichner { intTyp1 element1 : Zahl; intTyp2 element2 : Zahl; /* weitere Elemente */ }; ●
●
typbezeichner: Wird ein typbezeichner spezifiziert, kommt dies einer Typdefinition gleich. Später können dann Variablen dieses Typs definiert werden. In C lautet der Typ struct typbezeichner, in C++ reicht einfach der typbezeichner. typ element : zahl: Innerhalb der Klammern werden die Bitfelder deklariert. Als Typ dürfen nur int-Typen verwendet werden. Auf den Elementnamen folgt ein Doppelpunkt und die Angabe der Bits, die für das Bitfeld reserviert werden sollen. Gibt man keinen Elementnamen an, erhält man ein sogenanntes unbenanntes Bitfeld, mit dessen Hilfe man die Ausrichtung der Bitfelder im Speicher steuern kann. Ein unbenanntes Bitfeld der Größe 0 weist den Compiler an, das nächste Element an den vom Compiler intern zur Speicheraufteilung verwendeten Bytegrenzen auszurichten (reduziert die Zugriffszeiten).
Beispiele struct s_karten { unsigned int wert unsigned int farbe unsigned int spieler unsigned int gespielt } karten[32];
: : : :
3; 2; 2; 1;
int main() { int farbe, wert, loop; for(farbe=0; farbe<4; farbe++) for(wert=0; wert<8; wert++) { karten[8*farbe+wert].farbe = farbe; karten[8*farbe+wert].wert = wert; karten[8*farbe+wert].spieler = 0; karten[8*farbe+wert].gespielt = 0; } for(loop=0; loop < 32; loop++) printf("Farbe = %d, Wert = %d\n",karten[loop].farbe, karten[loop].wert); return 0; }
Verweise Siehe Strukturen
78
Unions
Unions union typbezeichner { Typ1 element1; Typ2 element2; };
Beschreibung Eine Union ist ein Datentyp, der für verschiedene Elemente deklariert wird, dessen Variablen aber nur eines der Elemente aufnehmen können. Die Größe einer Unionvariablen entspricht der Größe des größten Elements.
Anwendung Unions sparen gegenüber Strukturen Speicher, wenn nur ein Element benötigt wird, dessen Typ aber vom Programmierer nicht vorhergesehen werden kann.
Warnung Eine Union kann keine Variable ihres eigenen Typs als Element enthalten. Zeiger oder Referenzen auf Variablen ihres eigenen Typs sind aber erlaubt. Wird eine Unionvariable bei ihrer Definition initialisiert, kann nur dem ersten Unionelement der Wert zugewiesen werden. In C++ gibt es einige Besonderheiten zu beachten: ●
In C++ sind Unions Klassen und dürfen auch Funktionen als Elemente enthalten.
●
Virtuelle Elementfunktionen sind nicht erlaubt.
●
Unions können nicht Bestandteil von Klassenhierarchien sein.
●
Klassen mit eigenen Definitionen für Konstruktor, Destruktor, Kopierkonstruktor oder Zuweisungsoperator können nicht Element einer Union sein.
Syntax union typbezeichner { typ1 element1; typ2 element2; /* weitere Elemente */ }; ●
●
typbezeichner: Wird ein typbezeichner spezifiziert, kommt dies einer Typdefinition gleich. Später können dann Variablen dieses Typs definiert werden. In C lautet der Typ union typbezeichner, in C++ reicht einfach der typbezeichner. typ element: Innerhalb der Klammern werden die Elemente der Union deklariert.
79
Sprachkonzepte und Syntax
Definition von Unionvariablen union Login { /* Definition */ }; union Login user;
Oder union Login{ char passwort[10]; int kennung; } user1, user2;
Initialisierung Bei der Initialisierung wie der Zuweisung kann man nur einem Element der Union einen Wert zuweisen. union Login user = {"Geheim"};
Zugriff auf Strukturelemente unionvariable.strukturelement unionzeiger->strukturelement
Beispiele #define KENNUNG union Login{ char passwort[10]; int kennung; }; int main() { union Login user1; #ifdef KENNUNG printf("Geben Sie ihre Kennummer ein: \n"); scanf("%d",&user1.kennung); fflush(stdin); #else printf("Geben Sie ihr Passwort ein: \n"); scanf("%s",user1.passwort); fflush(stdin); #endif printf("\n"); printf("Groesse der Union: return 0; }
Verweise Siehe Strukturen
80
%d\n",sizeof(Login));
Zeiger
Zeiger und Referenzen Zeiger typ* zeigervariable;
Beschreibung Ein Zeiger ist eine Variable, in der sich die Adresse eines Datenobjektes oder einer Funktion befindet. Die Verwendung von Zeigern bringt verschiedene Vorteile mit sich.
Anwendung Einsatzgebiete für Zeiger ●
●
●
●
●
●
Speicherersparnis. Zeigervariablen speichern Adressen. Ihr Speicherbedarf ist also auf 16 oder 32 Bit festgelegt, wohingegen die Objekte, auf die sie zeigen, wesentlich größer sein können. Übergabe an Funktionen. Durch die Übergabe von Zeigern (call by reference) an Funktionen wird die Funktion in die Lage versetzt, auf Objekte zuzugreifen, die außerhalb ihres Gültigkeitsbereiches liegen. Zudem ist die Übergabe von Zeigern wesentlich schneller als das Kopieren großer Datenobjekte. (In C++ werden aus den gleichen Gründen oft Referenzen übergeben.) Dynamische Speicherverwaltung. Mittels Zeigern kann der Programmierer selbst entscheiden, wann Speicher reserviert und wann freigegeben wird. Zeiger erlauben die Implementierung dynamischer Datenstrukturen wie Listen und Bäume. Rekursive Datenstrukturen. Zeiger können für Datentypen definiert werden, die deklariert, aber noch nicht definiert sind. Auf diese Weise kann eine Klasse beispielsweise Zeiger auf ihre eigenen Instanzen haben. Arrays von Zeigern auf Funktionen sind erlaubt, i.G. zu Arrays von Funktionen. Generische Implementierungen. Mit Zeigern lassen sich viele Probleme effizienter und/oder allgemeiner lösen. Beispielsweise durch Verwendung von Zeigern auf void, Zeigern auf Funktionen, um Funktionen als Argumente an andere Funktionen zu übergeben, Basisklassenzeiger auf abgeleitete Objekte zur Verwaltung von Objekten verschiedener abgeleiteter Klassen in einem Array, Nutzung virtueller Methoden statt expliziter Laufzeitidentifizierung,
81
Sprachkonzepte und Syntax
Warnung Ein Zeiger kann auf jedes beliebige Datenobjekt zeigen, mit zwei Ausnahmen: ●
Referenzen und
●
Bitfelder.
Syntax typ* zeigervariable; ●
●
●
typ: Datentyp der Objekte, auf die der Zeiger weist. Diese Angabe wird für die korrekte Dereferenzierung des Zeigers benötigt. *: kennzeichnet die Variable als Zeiger und kann sowohl hinter der Typangabe stehen als auch vor dem Zeigernamen. zeigervariable: Name des Zeigers.
Initialisierung Die Initialisierung eines Zeigers erfolgt durch die Zuweisung einer Adresse und eines mit der Adresse verbundenen Speicherbereichs. Die Initialisierung kann auf verschiedene Weisen geschehen. 1. Zuweisung der Adresse eines existierenden Objekts. ●
Die Adresse einer Variablen kann über den Adreß-Operator & ermittelt werden.
●
Zeiger sind selbst Adressen.
●
Die Namen von Arrays und Funktionen stellen ebenfalls Adressen dar. int i_var = 13; int *ptr1 = &i_var; int *ptr2 = ptr1; int feld[10] = {11,11,11}; ptr3 = feld;
2. Zuweisung von NULL. ●
Zeiger, die auf kein Objekt gerichtet sind, sollten mit der NULL-Adresse initialisiert werden. Auf diese Weise kann leicht getestet werden, ob der Zeiger auf ein Objekt weist oder nicht. double *ptr4 = NULL;
82
Zeiger
3. Allokation durch malloc() oder new. ●
Die Funktion malloc() sowie der Operator new dienen dazu, Speicher zu reservieren und die Adresse an den Zeiger zu übergeben. Auf diese Weise werden Objekte eingerichtet, die nur über Zeiger erreichbar sind. double *ptr5 = new double; int *ptr6 = (int*) malloc(3*sizeof(int));
Nichtinitialisierte Zeiger enthalten undefinierte Bitmuster, die bei der Dereferenzierung als Adresse interpretiert werden. Dies führt unweigerlich zu Programmfehlern und oftmals zu Programmabstürzen.
Dereferenzierung Zeiger sind Adressen. Dereferenzierung bedeutet, über diese Adresse auf das referenzierte Objekt zuzugreifen. Dazu dienen die Dereferenzierungs-Operatoren *, >,.* und ->* . ● ●
●
●
*: der eigentliche Dereferenzierungs-Operator. ->: eine Verbindung aus Dereferenzierungs- und Punkt-Operator, der einen Zeiger auf einen Klassentyp dereferenziert und gleichzeitig auf ein Element des referenzierten Objekts zugreift. .*: dereferenziert einen Zeiger auf ein Klassenelement für ein gegebenes Klassenobjekt. ->*: dereferenziert einen Zeiger auf ein Klassenelement für einen gegebenen Zeiger auf ein Klassenobjekt.
Beispiel #include <stdio.h> int main() { int i; int *p_i = &i; struct koord { int x,y; } pos; struct koord *p_pos1 = &pos; // Dereferenzierung *p_i = 2; (*p_pos1).x = 3; p_pos1->y = p_pos1->x+1;
// Deklaration und Initialisierung
// Deklaration und Initialisierung
// Dereferenzierung einer Struktur mit * // Dereferenzierung einer Struktur mit ->
printf("x = %d; y = %d\n",pos.x, pos.y); return 0; }
83
Sprachkonzepte und Syntax
liefert folgende Ausgabe: x = 3; y = 4
Warnung ●
●
●
Das Zeichen * wird also in der Deklaration dazu benutzt, eine Zeigervariable zu spezifizieren. In Anweisungen teilt es dem Compiler mit, daß nicht die Adresse, die in der Zeigervariablen gespeichert ist, gefragt ist, sondern das Objekt, das an dieser Adresse abgelegt ist. Ein Zeigername ist eine Adresse. Diese Adresse verweist auf die Speicherzelle, in der der Wert des Zeigers abgelegt wird. Der Wert eines Zeigers ist ebenfalls eine Adresse. Diese Adresse wird benutzt, um auf andere Datenobjekte zu verweisen. Dereferenzieren Sie niemals Zeiger, die ungültige Adressen beinhalten (nichtinitialisierte Zeiger, Zeiger auf Objekte, die nicht mehr existieren, etc.).
Spezielle Zeiger Zeiger auf Zeichenketten
char*
Ein Zeiger vom Typ char* weist auf ein einzelnes Zeichen. Viele Bibliotheksfunktionen, die Zeichenketten verarbeiten (strcpy(), strlen() etc.), erwarten als Argument solch einen Zeiger auf das erste Zeichen einer zu verarbeitenden Zeichenkette. Die so übergebene Zeichenkette beginnt mit der Adresse, die der Zeiger liefert, und endet mit dem ersten Auftauchen des Nullzeichens '\0'. Bei der Verwendung der entsprechenden Bibliotheksfunktionen ist daher darauf zu achten, daß die übergebenen Zeiger auch stets auf nullterminierte Zeichenketten verweisen, und daß Zeichenketten nur in Speicherbereiche kopiert oder geschrieben werden, die groß genug sind, die gesamte Zeichenkette einschließlich Nullterminierungszeichen aufzunehmen. Ansonsten kommt es zu unerwünschten Speicherzugriffen – meist begleitet von Programmabstürzen. char str[20] = "Hello World!"; // reserviere Speicher so groß wie der String in str plus '\0' p_str = (char*) malloc(strlen(str)+1); strcpy(p_str,str);
Zeiger auf void
void*
Ein void* (Zeiger auf void) ist ein universeller, typenloser Zeiger. Er wird eingesetzt, wenn der Typ des Zeigers nicht feststeht oder allgemein gehalten werden soll. Erst in einer nachfolgenden expliziten Typumwandlung wird festgelegt, wie der zum Zeiger gehörende Speicherplatz interpretiert werden soll, ob zum Beispiel als floatWert oder als String.
84
Spezielle Zeiger // Vergleichsfunktion für Verwendung mit qsort() // vergleicht zwei Adressen nach dem Namen int vgl(const void* p1,const void* p2) { struct adresse* adr1,*adr2; adr1=(struct adresse*)p1; adr2=(struct adresse*)p2; return strcmp(adr1->name,adr2->name); }
Zeiger auf Funktionen
*func
Zeiger auf Funktionen werden dazu benötigt, Funktionen als Parameter an andere Funktionen zu übergeben. Rückgabetyp (*zeigername) (Parametertypen); ●
● ●
Rückgabetyp: gibt den Rückgabetyp der Funktionen an, auf die der Zeiger gerichtet werden kann. zeigername: ist der Name des Zeigers. Parametertypen: Liste der Parametertypen der Funktionen, auf die der Zeiger gerichtet werden kann.
void func(int i, class person leser) {...}; void (*func_ptr) (int, class person) = func;
Konstante Zeiger
*const
Bei der Zeigerdeklaration unterscheidet man zwischen const-Zeigern, die nicht auf andere Objekte umgelenkt werden können, und Zeiger auf const-Objekte. Letztere werden häufig in Funktionsdefinitionen verwendet, um zu verhindern, daß in der Funktion über den Zeigerparameter das Objekt, auf das der Zeiger weist, verändert wird. char c = 'A'; const char *ptr_c; // Zeiger auf const-Objekt char const *ptr_c; // Zeiger auf const-Objekt char *const ptr_c = &c; // const-Zeiger int func (int par1, const char *par2);
Zeiger auf Datenelemente
class::*
Mit Hilfe des Bereichsoperators :: kann ein Zeiger einer Klasse zugeordnet werden. Zur Definition und Initialisierung des Zeigers wird dabei lediglich der Name der Klasse verwendet. Erst beim Zugriff über den Zeiger werden Instanzen oder Zeiger auf Instanzen benötigt. // Definition: int person::* int_ptr;
// person sei eine Klasse
// Initialisierung: int person::* int_ptr = &person::plz;
85
Sprachkonzepte und Syntax // Zugriff: class person leser, *p_leser; leser.*int_ptr = 66111; p_leser->*int_ptr = 66111;
Zeiger auf Methoden
class::*func
Mit Hilfe des Bereichsoperators :: kann ein Zeiger einer Klasse zugeordnet werden. Zur Definition und Initialisierung des Zeigers wird dabei lediglich der Name der Klasse verwendet. Erst beim Zugriff über den Zeiger werden Instanzen oder Zeiger auf Instanzen benötigt. // Definition: void (person::*func_ptr) (int);
// person ist eine Klasse
// Initialisierung: void (person::*func_ptr) (int) = &leser::print_adresse; // Zugriff: class person leser, *p_leser; (leser.*func_ptr) (64); (p_leser->*func_ptr) (64);
Zeiger auf statische Klassenelemente
class::static*
Zeiger auf statische Klassenelemente werden wie Nicht-Element-Zeiger behandelt, d. h., der Zeiger wird ohne eine Klassenangabe dereferenziert: int *int_ptr = &Klassenname::Statisches_Datenelement; int a = *int_ptr;
Basisklassenzeiger
(basis*)
In C++ kann jedes abgeleitete Objekt auch als Objekt einer seiner eindeutigen Basisklassen angesehen werden. Entsprechend kann ein Zeiger auf ein abgeleitetes Objekt ohne Probleme in einen Zeiger auf eines seiner eindeutigen Basisklassen umgewandelt werden. Dies erlaubt beispielsweise, die Verwaltung abgeleiteter Objekte unterschiedlicher Klassen als Basisklassenobjekte in einem gemeinsamen Array. Basisklassenzeiger werden zumeist in Zusammenhang mit virtuellen Methoden eingesetzt. class Basis {...}; class Abg1 : public Basis {...}; class Abg2 : public Basis {...};
// Klassenhierarchie
class Basis
// Array
*feld[10];
feld[0] = new Abg1; feld[1] = new Abg2;
86
// Zeiger in Array // aufnehmen
Referenzen (nur C++)
this-Zeiger
this
Jede nicht-statische Methode einer Klasse erhält vom Compiler intern einen zusätzlichen Parameter zugewiesen. Dies ist der this-Zeiger, der auf die Anfangsadresse der jeweiligen Instanz verweist, für die die Methode aufgerufen wird. #include using namespace std; class demo { public: void func() };
{cout << this;}
int main() { class demo d1,d2; cout << "This-Zeiger der Instanz d1 = " << d1.func() << endl; cout << "This-Zeiger der Instanz d2 = " << d2.func() << endl; return 0; }
Referenzen (nur C++) typ& referenzname = var;
Beschreibung Referenzen verweisen wie Zeiger auf Objekte, können jedoch nur einmal mit einem Objekt initialisiert und danach nicht mehr auf andere Objekte umgelenkt werden. Zudem entfällt für Referenzen die typische Zeigersyntax sie werden wie ganz normale Variable, quasi als Synonyme der Variablen, auf die sie verweisen, verwendet. Technisch gesehen entspricht eine Referenz damit einem const-Zeiger, der bei jedem Zugriff automatisch dereferenziert wird.
Anwendung Referenzen, auch Aliase genannt, sind im Gebrauch den Zeigern verwandt und werden in ähnlicher Weise zur Übergabe von Variablen an Funktionen eingesetzt, wenn die Funktionen die übergebenen Variablen direkt ändern (call by reference) oder Laufzeitverschlechterungen durch Kopieren großer Datenobjekte vermieden werden sollen. Referenzen haben dabei aber den Vorteil, ●
●
daß sie wie normale Variablen angesprochen werden (die zeigertypische Dereferenzierungssyntax entfällt) daß ihr Einsatz sicherer ist als der von Zeigern (da sie immer auf ein genau definiertes Objekt verweisen)
87
Sprachkonzepte und Syntax
Warnung Eine Referenzvariable muß direkt bei ihrer Deklaration initialisiert werden, außer ●
sie ist explizit als extern deklariert,
●
es handelt sich um einen Funktionsparameter,
●
es handelt sich um ein Klassenelement.
Eine Referenzvariable kann nur auf eine Variable gesetzt werden. Sie kann nicht auf eine andere Variable umgesetzt werden! Es lassen sich keine Zeiger auf Referenzen definieren. Es lassen sich keine Arrays von Referenzen bilden. Es gibt keine Referenzen auf Referenzen. Referenzen können nicht vom Typ void& sein.
Syntax typ& referenzname = var; ● ●
typ: Datentyp des Objekts, auf das die Referenz verweist. &: Referenzoperator &, der in der Deklaration eine Variable als Referenz kennzeichnet.
●
referenzname: Name der deklarierten Referenzvariablen.
●
= var: Referenzen müssen direkt bei der Definition initialisiert werden.
Beispiele Das folgende Beispiel demonstriert die Übergabe von Argumenten an Referenzparameter. #include using namespace std; class Leute { int alter; public: Leute(int age) {alter = age;} void altern() {alter++;} int get_alter() {return alter;} }; // Funktion mit Referenzparameter void uebergebe_referenz(Leute& par) { par.altern(); } int main()
88
Typdefinitionen { Leute dennis(30); cout << "Dennis ist " << dennis.get_alter() << " Jahre alt\n"; // Übergabe des Klassenobjekts an Referenzparameter uebergebe_referenz(dennis); cout << "Dennis ist " << dennis.get_alter() << " Jahre alt\n"; return 0; }
Ausgabe Dennis ist 30 Jahre alt Dennis ist 31 Jahre alt
Warnung Bei der Parameterübergabe müssen Datentyp des Referenzparameters und Datentyp des Arguments genau übereinstimmen. Ansonsten gilt: ●
●
Für const-Referenzparameter nimmt der Compiler eine Typumwandlung vor und erzeugt eine zu dem Referenzparamter passende temporäre Variable. Der Referenzparameter weist dann auf diese temporäre Variable. Die Zurücklieferung von Werten über den Parameter an das Aufrufargument ist daher nicht mehr möglich, für const-Parameter aber auch sicherlich nicht vorgesehen. Für nicht-const-Referenzparameter gibt der Compiler eine Fehlermeldung aus. (Einige ältere Compiler geben nur eine Warnung aus und legen eine temporäre Variable an.)
Typdefinitonen und Typumwandlungen Typdefinitionen typedef
Beschreibung C erlaubt die Definition beliebiger Datentypen auf der Grundlage der im Sprachumfang enthaltenen Typen. Bei einer Typdefinition wird ein neuer Typ spezifiziert und mit einem Bezeichner für den Typ verbunden. Eine solche Typdefinition kann durch die Schlüsselworte struct, union, class oder typedef erfolgen.
89
Sprachkonzepte und Syntax
Anwendung Mit typedef werden im Grunde keine neuen Typen definiert, sondern lediglich Synonyme für bereits mögliche Typspezifikationen eingerichtet. Dies kann aus zwei Gründen geboten sein: ●
●
Eine Typdefinition soll per Präprozessorschalter anpaßbar sein (erstes Beispiel). Eine komplexe Typdefinition soll durch ein einfaches Synonym repräsentiert werden (zweites Beispiel)
Warnung typedef-Typen sind nicht erweiterbar: typedef int INT; unsigned INT x;
// nicht erlaubt
Syntax typedef typ_spezifikation typbezeichner; ●
●
typ_spezifikation: Hier wird der neue Typ definiert. Die Form der Typangabe entspricht der Angabe in Variablendeklarationen. typbezeichner: Name für den neuen Typ.
Beispiele Typdefinitionen in Abhängigkeit von Compiler-Schaltern: #if defined (LONG) typedef wchar_t long; #else typedef wchar_t int; #endif
Typdefinitionen als Vereinfachungen: typedef unsigned int UINT; typedef void (*func_typ) (int, int);
// Beispiel aus windows.h // definiert func_typ
Standardkonvertierungen Beschreibung In verschiedenen Situationen nimmt der Compiler automatische Typkonvertierungen vor.
Anwendung Die wichtigsten automatischen Konvertierungen sind:
90
Standardkonvertierungen
Bezeichner ●
●
●
Auf der rechten Seite von Zuweisungen werden Bezeichner als R-Werte (Wert, nicht Speicheradresse) aufgefaßt. Array-Namen werden auf der rechten Seite von Anweisungen (inklusive Funktionsaufrufen) als Zeiger auf ihr erstes Element aufgefaßt. Funktionsnamen repräsentieren auf der rechten Seite von Anweisungen den Rückgabewert der Funktion.
Typangleichung ●
●
Arithmetische Datentypen können ineinander konvertiert werden, wenn der Datentyp, in den konvertiert wird, geeignet ist, den zu konvertierenden Wert aufzunehmen (bei Konvertierung von Gleitkommazahlen in Integer-Werte geht dabei der Nachkommaanteil verloren). Für binäre Operatoren, die arithmetische Operanden übernehmen, gleicht der Compiler automatisch die Typen beider Operanden an – man bezeichnet dies als die standardmäßige arithmetische Konvertierung.
Boolesche Wahrheitswerte ●
●
Null-Zeiger und Null-Repräsentationen arithmetischer Datentypen können in den Booleschen Wert false konvertiert werden, andere Werte dieser Datentypen werden entsprechend als true interpretiert. Umgekehrt kann der Boolesche Wert false in eine arithmetische Null, true in eine Eins konvertiert werden.
Zeiger und Klassen ●
● ●
●
●
●
Arraynamen werden auf der rechten Seite von Anweisungen (inklusive Funktionsaufrufen) als Zeiger auf ihr erstes Element aufgefaßt. Funktionsnamen können als Zeiger auf sich selbst aufgefaßt werden. Zeiger auf spezielle Datentypen können in Zeiger auf void verwandelt werden (der umgekehrte Weg ist nur durch explizite Typumwandlung erreichbar). Ein Zeiger auf eine abgeleitete Klasse kann in einen Zeiger auf eine eindeutige, zugängliche Basisklasse konvertiert werden. Unter den gleichen Voraussetzungen kann die Instanz einer abgeleiteten Klasse als Instanz einer Basisklasse interpretiert werden. Zeiger auf Elemente einer Basisklasse können in Zeiger auf Elemente einer eindeutigen, zugänglichen abgeleiteten Klasse konvertiert werden.
91
Sprachkonzepte und Syntax ●
Wenn Sie einer Klasseninstanz einen Wert zuweisen und in der Klasse ein entsprechender Konstruktor definiert ist, der mit genau einem Argument des Typs aufgerufen wird, dem auch der Wert angehört, wird der Konstruktor wo nötig zur automatischen Konvertierung von Werten des entsprechenden Datentyps in Objekte der Klasse herangezogen (kann durch explicit-Deklaration unterbunden werden).
const und volatile ●
Die Qualifizierer const und volatile können automatisch beigefügt, aber nicht abgezogen werden (so kann ein nicht-const Argument an einen const-Parameter übergeben werden, aber nicht umgekehrt).
Beispiele Operandenkonvertierung float f = 3/4;
float f = 3/4.0;
// // // // // // //
ergibt 0.0; Integer-Division von 3/4 ergibt 0 0 wird für Zuweisung in 0.0 umgewandelt ergibt 0.75; 4.0 ist Gleitkommazahl, der Integer-Operand 3 wird daher in Gleitkommazahl umgewandelt Es wird eine Gleitkomma-Division durchgefuehrt
Konstruktoren mit einem Argument class Feld { public: int anzahl; Feld(int a) {anzahl = a;} }; ... Feld stack(3); stack = 13; // impliziter Aufruf: stack = Feld(13);
Typumwandlung (typ) wert
Beschreibung Gelegentlich ist es wünschenswert, daß eine Variable eines bestimmten Datentyps als einem anderen Datentyp angehörig behandelt wird. Sofern eine solche Typumwandlung durchführbar ist, kann sie durch den C-Cast-Operator () oder die C++-Operatoren const_cast, dynamic_cast, static_cast und reinterpret_cast, die als Schlüsselworte in die Sprache aufgenommen sind, explizit herbeigeführt werden.
92
Typumwandlung
Anwendung Für Typkonvertierungen gibt es fünf typische Gründe: ●
●
●
●
Sie wollen eine Gleitkommazahl in eine Integer-Zahl verwandeln oder umgekehrt. Letzeres ist beispielsweise geboten, wenn Sie eine Integer-Zahl durch eine andere Integer-Zahl teilen wollen, das Ergebnis aber mit Divisionrest in einer Gleitkommavariablen gespeichert werden soll. Sie haben einen void-Zeiger vorliegen, den Sie in einen Zeiger auf einen bestimmten Datentyp umwandeln wollen. Sie haben einen Basisklassenzeiger auf ein abgeleitetes Objekt, den Sie in einen Zeiger auf die abgeleitete Klasse umwandeln wollen. Sie wollen durch die Typumwandlung eines Zeigers eine bestimmte Speicherinterpretation erzwingen. Diese Möglichkeit ist selten erforderlich, dafür aber recht fehleranfällig.
Der Cast-Operator von C Der Cast-Operator kann benutzt werden, um in einer Anweisung den Typ des Ausdrucks der rechten Seite in den Typ des Bezeichners der linken Seite umzuwandeln: bezeichner1 = (Typ_von_bezeichner1) AUSDRUCK; bezeichner1 = (Typ_von_bezeichner1) bezeichner2;
Eine zweite Form der Syntax wandelt den Typ einer Variablen direkt um bezeichner = Datentyp();
Die Cast-Operatoren von C++ Die C++-Operatoren sind vor allem dazu gedacht, die verschiedenen Konvertierungen nach ihrem Gefahrenpotential zu kategorisieren, und die Textsuche nach den Konvertierungen zu erleichtern. Operator
Einsatzgebiet
static_cast(var)
Zur Umwandlung zwischen »nahe verwandten« Typen – also alle Fälle, in denen der Compiler auch eine implizite Konvertierung vornehmen könnte, plus einige einfache Sonderfälle.
reinterpret_cast(var)
Für fast alle anderen erlaubten Umwandlungen.
const_cast(var)
Entfernt eine const- oder volatile-Deklaration aus einem Typ.
dynamic_cast(var)
Zur Umwandlung zwischen Zeigern oder Referenzen auf Klassentypen einer gemeinsamen Klassenhierarchie
93
Sprachkonzepte und Syntax
Beispiele int i = int(3.14); double var = (double) 13/2); int ptr = (int *) malloc(80 * sizeof(int)); Abgeleitet *p_abg = new Abgeleitet; Basis *p_bp = p_abg; p_abg = dynamic_cast (p_bp);
Konstanten Konstanten Beschreibung Neben den Variablen stellen die Konstanten die zweite Möglichkeit zur Repräsentation von Daten in Programmen dar. Der Unterschied zwischen Variablen und Konstanten liegt auf der Hand: Während eine Variable im Laufe eines Programms unterschiedliche Werte annehmen und repräsentieren kann, stellt eine Konstante immer einen festen Wert dar. C/C++ kennt 3 verschiedene Typen von Konstanten: Literale Symbolische Konstanten Konstante Variablen
Anwendung Konstanten stellen eine Möglichkeit dar, feststehende Daten direkt im Programmcode zu verankern: etwa um den Zahlenwert von PI in eine Formel einzubauen oder in Form einer String-Tabelle für Fehlermeldungen des Programms.
Literale Beschreibung Die einfachste Form der Konstante ist ein konstanter Wert. Um sie von den konstanten Variablen zu unterscheiden, bezeichnet man die konstanten Werte auch als Literale.
94
Literale
Anwendung Konstante Werte werden bei der Übersetzung durch den Compiler hartcodiert, d. h., die Werte sind nicht irgendwo im Speicher abgelegt, von wo sie bei Bedarf ausgelesen werden können, sondern die Werte stehen direkt in den betreffenden Maschinenbefehlen. Die einzige Ausnahme: für String-Literale wird Speicher reserviert, doch hat der Programmierer üblicherweise keinen Zugriff auf diesen Speicherbereich. printf("Hello World\n");
Die automatische Speicherallokation erlaubt allerdings die Zuweisung von StringLiteralen an Zeiger auf char. char *str1; str1 = "Hello World\n"; Datentyp
Literal
bool
true, false
char
'c', 'Ü', '\n', '\\', 'ab' (Multibyte-Zeichen)
wchar_t
L'c', L'Ü', L'\n', L'\\', L'ab' (Multibyte-Zeichen)
Zeichenketten (char*)
"Dies ist ein String"
Zeichenketten (wchar_t*)
L"Dies ist ein String"
short
12, -128
int
12, -128, 128u, 1277U 012, -077 (Oktal) 0x12, 0XEE (Hexadezimal)
long
12l, 1400000L, 12lu
float
47.11f, 12.0F, 10e-2F
double
47.11, 12.0, 10e-2
long double
47.11l, 12.0L, 10e-2L
Warnung Wichtig bei der Verwendung von Literalen ist, daß Sie sich an die vorgesehene Syntax zur Angabe der Werte halten, damit der Compiler aus dem Wert auch den zugehörigen Datentyp ablesen kann.
95
Sprachkonzepte und Syntax
Beispiele int i = 513; // Zuweisung einer Konstanten an eine Variable double feld[30]; // Angabe der Array-Groesse if( (x < 100) && (x > 50)) // Zur Formulierung von if(!strcmp(str,"Vergleichstext"); // Bedingungen
Verweise Siehe #define Siehe const-Variablen
#define symbolische Konstanten Beschreibung Symbolische Konstanten sind Bezeichner, die als Aliase für einen bestimmten Wert definiert sind.
Anwendung Wird eine Konstante mehrfach verwendet, ist es sinnvoll, sie vorab zu deklarieren und mit einem symbolischen Namen zu verbinden, der dann im weiteren Verlauf des Programms die Konstante repräsentiert. Dies ermöglicht die PräprozessorDirektive #define: #define
symbolBezeichner
KONSTANTE
Danach kann der symbolische Bezeichner im weiteren Quelltext stellvertretend für die Konstante eingesetzt werden. Bei der Kompilation werden alle Vorkommen des symbolischen Bezeichners durch die zugehörige Konstante ersetzt. Der Vorteil besteht einmal darin, ●
●
daß Sie eine Konstante mit einem aussagekräftigen Namen verbinden können, und zum anderen nur die #define-Anweisung anpassen müssen, wenn Sie die Konstante mit einem anderen Wert verbinden wollen (beispielsweise ist es sinnvoll, Obergrenzen für Arrays, Abbruchbedingungen für Schleifen oder Schalter für die bedingte Kompilation mit Hilfe von #define am Dateianfang festzulegen, damit man sie später bei Bedarf schnell findet und zentral ersetzen kann).
Vordefinierte Konstanten Eine Reihe interessanter Konstanten sind bereits in den Header-Dateien der Standardbibliothek vordefiniert, so zum Beispiel
96
#define – symbolische Konstanten
Header-Datei
Konstante
Wert
stddef.h
NULL
Makro, das zu implementierungsspezifischer Nullzeigerkonstante expandiert wird, beispielsweise 0 oder 0L.
stdio.h
EOF
Üblicherweise -1 bzw (wint_t)(0xFFFF) für wchar_t
FOPEN_MAX
Makro, das zu Integer-Wert expandiert wird, der die Mindestanzahl der Dateien anzeigt, die gleichzeitig geöffnet werden können.
CHAR_BIT
8 (Anzahl Bits in Typ char)
INT_MIN
-2147483647L-1 (für 32-Bit-System)
INT_MAX
2147483647L (für 32-Bit-System)
FLT_RADIX
2 (Basis der Exponentdarstellung)
FLT_DIG
6 (Anzahl der Dezimalstellen)
limits.h
Beispiele #define STUNDE 60 #define dutzend 12 #define Name "C++Compiler"
Der symbolische Bezeichner ist lediglich ein Aliasname für ein Literal. Vor der eigentlichen Übersetzung in Objektcode geht der Compiler (genauer gesagt der Präprozessor) den Quelltext durch und ersetzt alle Vorkommen des symbolischen Bezeichners durch die zugehörige Konstante. Wenn es dann an die eigentliche Übersetzung geht, sieht der Quelltext so aus, daß es keine symbolischen Konstanten, sondern nur noch Literale gibt: Original-Quelltext #define MAX 30 double feld[MAX]; ... for (int i = 0; i < MAX; i++) {...} ... for (int i = 0; i < MAX; i++) {...} ... if (i < MAX) {...}
Nach Bearbeitung durch Präprozessor: double feld[30]; ... for (int i = 0; i < 30; i++) {...} ... for (int i = 0; i < 30; i++) {...} ... if (i < 30) {...}
Verweise Siehe Zusammengesetzte Datentypen, Aufzählungstypen
97
Sprachkonzepte und Syntax
const konstante Variablen Beschreibung Die letzte Form der Konstanten ist die konstante Variable eine Variable, deren Wert nach der Initialisierung nicht mehr verändert werden kann.
Anwendung Konstante Variablen werden mit den Schlüsselwort const deklariert. Sie sind aus Sicht des Compilers keine echten Konstanten, sondern vielmehr Variablen, die er vorm Überschreiben schützt. In C wie C++ kann man mit const ●
●
beliebige Objekte (also auch Variablen selbst definierter Datentypen wie Strukturen und Klassen) als konstant deklarieren, verhindern, daß als Zeiger übergebene Funktionsargumente in einer Funktion verändert werden,
●
Zuweisungsmöglichkeiten erweitern,
●
stärker von der Typprüfung des Compilers profitieren.
Warnung In C++ wird eine konstante Variable wie eine echte Konstante angesehen. In C, das nach ANSI ebenfalls das Schlüsselwort const kennt, können konstante Variablen jedoch nicht an Stellen verwendet werden, an denen der Compiler eine Konstante bzw. einen konstanten Ausdruck erwartet – also ●
nicht bei der Angabe der Größe eines Arrays,
●
nicht als case-Marken.
Beispiele const int monate = 12; int func (const int par1, const char *par2);
Verweise Siehe Literale Siehe #define Siehe Klassen, Konstante Datenelemente und Methoden
98
Operatoren
Operatoren Operatoren Beschreibung ANSI C/C++ kennt mehr als 40 Operatoren. Mit diesem Wert übersteigt bei C/C++, und das ist bei Programmiersprachen nicht üblich, die Anzahl der Operatoren die Anzahl der Schlüsselwörter der Sprache. Der größte Teil der Operatoren stammt bereits aus der Zeit der ersten Sprachdefinition durch Kernighan & Ritchie. Bei den hier aufgelisteten Formen der Operatoren handelt es sich um ihre ursprünglichen Definitionen und nicht um überladene Versionen. Die meisten der Operatoren können von Programmen überladen werden (nicht jedoch für die elementaren Datentypen, für die sie bereits standardmäßig definiert sind) und nur unter Beibehaltung ihrer üblichen Syntax. Operator
Kurzbeschreibung
+
addiert zwei Operanden
+=
addiert zwei Operanden und weist Ergebnis zu
-
subtrahiert zwei Operanden
-=
subtrahiert zwei Operanden und weist Ergebnis zu
*
multipliziert zwei arithmetische Operanden
*=
multipliziert zwei arithmetische Operanden und weist Ergebnis zu
/
dividiert zwei arithmetische Operanden
/=
dividiert zwei arithmetische Operanden und weist Ergebnis zu
%
Modulo-Operator, ermittelt den Rest einer Ganzzahldivision
%=
Modulo-Operator mit gleichzeitiger Zuweisung
!
Nicht-Operator, Verneinung
not
Nicht-Operator, Verneinung
!=
vergleicht zwei arithmetische Operanden auf Ungleichheit
not_eq
vergleicht zwei arithmetische Operanden auf Ungleichheit
&
Bei einem Operanden liefert dieser Operator die Adresse des Operanden
&
Bei zwei Operanden wird eine bitweise UND-Verknüpfung ausgeführt
bitand
Bitweise UND-Verknüpfung
&=
Bitweise UND-Verknüpfung und Zuweisung
and_eq
Bitweise UND-Verknüpfung und Zuweisung
&&
Logische UND-Verknüpfung
99
Sprachkonzepte und Syntax
Operator
Kurzbeschreibung
and
Logische UND-Verknüpfung
()
Cast-Operator, erzwingt Typumwandlung
,
Operator für sequentielle Auswertung, Kommaoperator
++
inkrementiert den Operanden
--
dekrementiert den Operanden
->
Elementverweis-Operator
.
Punkt-Operator
*
Indirektions-Operator
=
Zuweisungsoperator
==
testet auf Gleichheit
<
Kleiner als
>
Größer als
<=
Kleiner als oder gleich
=>
Größer als oder gleich
<<
Bitweise Linksverschiebung
<<=
Bitweise Linksverschiebung und Zuweisung des Ergebnisses
>>
Bitweise Rechtsverschiebung
>>=
Bitweise Rechtsverschiebung und Zuweisung des Ergebnisses
?:
Bedingungsoperator
[]
Array-Indizierung
^
Bitweises exklusives ODER
xor
Bitweises exklusives ODER
^=
Bitweises exklusives ODER und Zuweisung des Ergebnisses
xor_eq
Bitweises exklusives ODER und Zuweisung des Ergebnisses
|
Bitweises ODER
bitor
Bitweises ODER
|=
Bitweises ODER und Zuweisung des Ergebnisses
or_eq
Bitweises ODER und Zuweisung des Ergebnisses
||
Logisches ODER
or
Logisches ODER
~
Komplement
compl
Komplement
:
Klasseninitialisierer
::
Bereichsoperator
.*
Dereferenzierung von Zeigern auf Klassenelemente
->*
Dereferenzierung von Zeigern auf Klassenelemente
100
Vorzeichen
Operator
Kurzbeschreibung
sizeof
ermittelt Größe des Operanden in Bytes
new
Dynamische Speicherreservierung
new[]
Dynamische Speicherresevierung für Arrays
delete
Speicherfreigabe
delete[]
Speicherfreigabe für Arrays
#
Zeichenkettenbildung
##
Grundsymbolverbindung
defined
testet, ob Makro definiert ist
typeid
Dynamische Typinformation
Klassifizierung Alle Operatoren lassen sich, je nachdem, mit wie vielen Operanden sie arbeiten, einer der folgenden Gruppen zuordnen: ●
unäre Operatoren (arbeiten mit einem Operanden),
●
binäre Operatoren (arbeiten mit zwei Operanden) und
●
ternäre Operatoren (arbeiten mit drei Operanden).
Vorzeichen Beschreibung Unärer Operator der festlegt, ob ein Zahlenwert positiv oder negativ ist.
Anwendung Mit den unären Operatoren »+« und »-« können Sie arithmetischen Typen ein Vorzeichen zuweisen. Werte ohne Vorzeichen sind automatisch positiv. Operator
Bedeutung
+
positiver Wert
-
negativer Wert
Beispiele int var1, var2; var1 = -3; var2 = 3 * -var1;
101
Sprachkonzepte und Syntax
Arithmetische Operatoren Beschreibung Binäre Operatoren zur Durchführung der algebraischen Verknüpfungen: Addition, Subtraktion, etc., sowie der Divisionsrestberechnung.
Anwendung Mit den arithmetischen Operatoren können Sie, wie es der Name vermuten läßt, einfache arithmetische Operationen durchführen, wie sie auch in anderen Programmiersprachen üblich sind. Operator
Bedeutung
+
Addition
-
Subtraktion
*
Multiplikation
/
Division %
Modulo, Rest einer Ganzzahldivision
Warnung ●
●
●
Werden in einem Ausdruck mehrere Operatoren verwendet, gilt: Punktrechnung geht vor Strichrechnung. Vorzeichen, Inkrement und Dekrement kommen vor Punktrechnung. Bei gleicher Priorität werden die Operatoren von links nach rechts abgearbeitet. Generell gilt, daß jeder Operator einer Prioritätstufe zugeordnet ist, und Operatoren höherer Priorität vor Operatoren niedrigerer Priorität ausgewertet werden. Durch Setzen von Klammern können Sie die Reihenfolge der Auswertung von Operatoren selbst festlegen.
Beispiele var1 var2 var2 var2
= = = =
5+3; 20 % var1; 3 * 4 + var1; 3 * (4 + var1);
// = 4 // = 20 // = 36
Verweise Siehe Priorität und Assoziativität
102
In- und Dekrementoperator
In- und Dekrementoperator Beschreibung Inkrement und Dekrement bezeichnet die Erhöhung beziehungsweise Erniedrigung eines Wertes um 1 Einheit.
Anwendung Die Programmiersprache C/C++ stellt zwei Operatoren zur Verfügung, mit denen der Wert einer Variablen inkrementiert (um eins erhöht) oder dekrementiert (um eins vermindert) werden kann, wobei die Operatoren vor oder nach der Variablen stehen können. lwert++ ++lwert lwert---lwert
Die Position des Operators legt fest, wann die Erhöhung oder Erniedrigung der Variablen berechnet wird. Wichtig ist dies, wenn der Operator in komplexeren Ausdrücken verwendet wird. int var1, var2 = 12, var3 = 12; var1 = 3 * ++var2; // ergibt var1 = 39 var1 = 3 * var3++; // ergibt var1 = 36
Die Notation, bei der der Operator vor der Variablen steht, wird Präfix-Schreibweise genannt und führt dazu, daß der Wert der Variablen zuerst neu berechnet wird, bevor er benutzt wird. In der Postfix-Schreibweise (der Operator steht nach der Variablen) wird der Wert des Operanden benutzt, bevor er verändert wird.
Operator
Bedeutung
++
Inkrementoperator, erhöht seinen Operanden um 1
--
Dekrementoperator, vermindert seinen Operanden um 1
Warnung ● ●
●
Die In- und Dekrementoperatoren sind nicht für Aufzählungstypen definiert. Die In- und Dekrementoperatoren können auch bei Zeigern benutzt werden. In diesem Fall wird die Adresse um die Größe des Objekts, auf das der Zeiger zeigt, erhöht oder vermindert. Das Inkrement einer Variablen vom Typ bool ergibt 1, das Dekrement ist nicht definiert.
103
Sprachkonzepte und Syntax
Beispiel #include <stdio.h> int main() { int x,y; x = 5; y = 5; printf("Wert von ++x ist %d\n", ++x); printf("Wert von y++ ist %d\n", y++); printf("x nach der Inkrementierung: %d\n", x); printf("y nach der Inkrementierung: %d\n", y); return 0; }
Das Programm erzeugt folgende Ausgabe: Wert von ++x ist 6 Wert von y++ ist 5 x nach der Inkrementierung: 6 y nach der Inkrementierung: 6
Zuweisungen Beschreibung Mit Zuweisungsoperatoren können Werte zugewiesen und verändert werden. Neben dem einfachen Zuweisungsoperator gibt es auch die sogenannten zusammengesetzten Zuweisungsoperatoren, mit denen in einer Anweisung sowohl eine Berechnung als auch eine Zuweisung durchgeführt werden kann.
Anwendung Grundsätzlich gilt, daß mit einer Zuweisung dem linken Operanden der Wert des rechten Operanden zugewiesen wird. Damit dies möglich ist, muß der linke Operand veränderbar sein, sich also auf eine Adresse im Speicher beziehen. Diese Ausdrücke werden als »L-Wert« bezeichnet.
Einfache Zuweisung
=
Bei der einfachen Zuweisung erhält der linke Operand den Wert des rechten Operanden. int main() { int bestand, eingang; bestand = 10; eingang = 20; bestand = bestand + eingang; return 0; }
104
Zuweisungen
Zusammengesetzte Zuweisungen
<x>=
Neben dem einfachen Zuweisungsoperator »=« gibt es die zusammengesetzten Zuweisungsoperatoren, mit denen durch einen Operator gleichzeitig eine Berechnung und eine Zuweisung durchgeführt werden kann. Die zusammengesetzten Zuweisungsoperatoren haben das Format: <x>=
wobei <x> durch die Operatoren +, –, *, /, %, <<, >>, &, ^ oder | ersetzt werden kann. int main() { int bestand, eingang; bestand = 10; eingang = 20; bestand += eingang; return 0; }
Zuweisung an Klasseninstanzen
operator =
Zuweisungen an Klasseninstanzen erfolgen durch Aufruf des überladenen Operators =. Wird der Operator nicht in der Klasse überladen, wird der Standardoperator aufgerufen, der elementweise kopiert. Neben der Zuweisung einer Klasseninstanz an eine Instanz der gleichen Klasse, erlaubt der Standard-Zuweisungsoperator die Zuweisung eines Objekts einer eindeutigen als public abgeleiteten Klasse an ein Objekt ihrer Basisklasse, aber nicht die Zuordnung eines Objekts einer Basisklasse an ein Objekt ihrer abgeleiteten Klasse. Durch Kopierkonstruktoren werden Zuweisungen bei der Initialisierung vorgenommen. class Basis { ... }; class Abgeleitet : public Basis { ... }; int main() { Abgeleitet abg; Basis bs; //abg = bs; bs = abg;
// Fehler
105
Sprachkonzepte und Syntax
Vergleichende Operatoren Beschreibung Mit den vergleichenden Operatoren, die auch relationale Operatoren genannt werden, wird ein Vergleich zwischen zwei Operanden, die auch Ausdrücke sein können, durchgeführt. Das Ergebnis dieser Operation ist entweder wahr oder falsch.
Anwendung Ausdrücke, die vergleichende Operatoren enthalten, werden häufig als Kontrollbedingung in do-, while- oder for-Schleifen und if-Anweisungen eingesetzt. Operator
Bedeutung
==
Gleich
!=
Ungleich
<
Kleiner als
>
Größer als
<=
Kleiner als oder gleich
>=
Größer als oder gleich
Warnung ●
Das Ergebnis eines Vergleichs ist ein Wahrheitswert (true, false).
●
Jeder Wert, der ungleich 0 ist, wird von C/C++ als true interpretiert.
●
Zwei Zeiger gleichen Typs, die auf das gleiche Objekt weisen, sind gleich.
●
Zeiger auf verschiedene Elemente einer Klasse sind ungleich, wobei spätere Deklarationen höhere Adressen haben. Dies gilt allerdings nur, wenn kein Zugriffsspezifizierer zwischen den Deklarationen steht. Sollte dies der Fall sein, ist das Ergebnis undefiniert.
Beispiele if (var1 <= var2) funktion(); do { ergebnis = funktion(); } while (ergebnis != 0);
Logische Operatoren Beschreibung Die logischen Operatoren dienen der Verknüpfung oder Negierung logischer Aussagen.
106
Logische Operatoren
Anwendung Die Programmiersprache C kennt drei logische Operatoren: UND, ODER und NICHT. Beim logischen UND- und ODER-Operator wird der Wahrheitswert der Operanden verknüpft, der logische NICHT-Operator konvertiert den logischen Wert seines Operanden in dessen Gegenteil. Die Operanden müssen als Boolesche Werte (true, false) interpretierbar sein. Operator
Bedeutung
&&
Logisches UND
||
Logisches ODER
!
Logisches NICHT
logisches UND
&&
Mit dem Operator »&&« wird eine logische UND-Verknüpfung seiner Operanden durchgeführt. Das Ergebnis der Verknüpfung hat nur dann den Wert true (ungleich Null), wenn beide Operanden den Wert true (ungleich Null) besitzen. Die Operanden werden von links nach rechts ausgewertet. Wenn der erste Operand den Wert false (also Null) ergibt, wird der zweite Operand nicht ausgewertet. Die folgende Wahrheitstabelle zeigt Ihnen die möglichen Kombinationen: 1. Operand
2. Operand
Ergebnis
wahr
wahr
wahr
wahr
falsch
falsch
falsch
wahr
falsch
falsch
falsch
falsch
logisches ODER
||
Der logische ODER-Operator verknüpft seine Operanden, so daß das Ergebnis der Verknüpfung den Wert true hat, wenn einer oder beide Operanden den Wert true hat. Die Operanden werden von links nach rechts ausgewertet. Wenn der erste Operand den Wert true (also ungleich Null) ergibt, wird der zweite Operand nicht ausgewertet. Die folgende Wahrheitstabelle zeigt Ihnen die möglichen Kombinationen: 1. Operand
2. Operand
Ergebnis
wahr
wahr
wahr
wahr
falsch
wahr
falsch
wahr
wahr
falsch
falsch
Falsch
107
Sprachkonzepte und Syntax
Beispiele Das Beispielprogramm gibt eine formatierte ASCII-Tabelle auf den Bildschirm aus. In Zeile 7 wird der logische UND-Operator eingesetzt, um zu erreichen, daß die Ausgabe nur dann erfolgt, wenn spalte einen Wert kleiner als 13 hat (pro Zeile sollen dreizehn Zeichen ausgegeben werden) und der Wert von zeichen kleiner ist als 256 (um auch nur die ASCII-Zeichen auszugeben). In Zeile 8 wird der logische ODER-Operator eingesetzt, um zu erreichen, daß auch die Zeichen des erweiterten IBM-Zeichensatzes ausgegeben werden, da isprint() nur für die ASCII-Zeichen mit den Codes 32 bis 126 das Resultat true liefert. #include int main() { int zeile, spalte, zeichen; printf("ASCII-Tabelle\n"); for(zeile = 0, zeichen = 0; zeile < 20; zeile++) { for(spalte=0; spalte<13 && zeichen<256; spalte++) { if (isprint(zeichen) || zeichen>127) printf("%3c %3d else printf(" %3d zeichen++; } printf("\n"); } return 0; }
logisches NICHT
!
Der logische NICHT-Operator verkehrt den Wahrheitswert seines logischen Operanden in dessen Gegenteil. Dabei wird der Wert 0 (false) in den Wert 1 (true) umgewandelt und alle Werte ungleich 1 zu 0.
Beispiele Das folgende Programm enthält eine Endlosschleife, in der der Anwender aufgefordert wird, eine Zahl einzugeben. In der Schleife wird mit einer if-Abfrage überprüft, ob die Zahl 0 eingegeben wurde. Wenn ja, wird die Schleife mit break beendet. #include <stdio.h int main() { int zahl: for (;;) { printf("Geben Sie eine Zahl ein: ");
108
Datenzugriff scanf("%d", &zahl); if (!zahl) break; } return 0; }
Verweise Siehe Kategorie Programmsteuerung, if-Bedingung Siehe Kategorie Programmsteuerung, Schleifen
Datenzugriff Beschreibung Mehrere Operatoren von C und C++ dienen dem Zugriff auf Daten.
Anwendung Diese Operatoren benötigt man, um auf einzelne Elemente eines Arrays, einer Struktur, Union oder Klasse zuzugreifen. Auch der Gültigkeitsbereichoperator und die Operatoren zur Dereferenzierung von Zeigern, die den Zugriff auf die Objekte ermöglichen, auf die die Zeiger verweisen, gehören in diese Kategorie. Operator
Bedeutung
*
Indirektions-Operator
[]
Array-Indizierung
.
Punkt-Operator
->
Elementverweis-Operator
.*
Dereferenzierung von Zeigern auf Klassenelemente
->*
Dereferenzierung von Zeigern auf Klassenelemente
::
Gültigkeitsbereichoperator
Dereferenzierungs-Operator
*
Der Dereferenzierungs-Operator »*« dient dazu, einen Zeiger zu dereferenzieren und auf dessen Objekt zuzugreifen. Der Typ des Zeigers bestimmt den Typ des Ergebnisses. Wenn also der Operand ein Zeiger auf int ist, hat das Ergebnis den Datentyp int. int func(int *zeiger) { int ergebnis; *zeiger = ergebnis; }
/* Wert zuweisen */
109
Sprachkonzepte und Syntax
Warnung ●
●
Der Dereferenzierungs-Operator kann nicht auf einen unvollständigen Typ, beispielsweise einen Zeiger auf void, angewendet werden. Das Zeichen »*« wird ebenfalls bei der Deklaration von Variablen benutzt, um anzugeben, daß die deklarierte Variable ein Zeiger ist. Es handelt sich dann dabei nicht um einen Dereferenzierungs-Operator. Die Bedeutung ergibt sich somit immer aus dem Kontext.
Array-Indizierung
[]
Der Operator »[ ]« kann verwendet werden, um gezielt auf einzelne Elemente eines Arrays oder eines dynamisch reservierten Feldes zuzugreifen. #define FELD_GROESSE 20 int array[FELD_GROESSE]; int dyn_feld = (int*) malloc(FELD_GROESSE*sizeof(int)); for (i=0; i < Feld_GROESSE; i++) { array[i] = i; dyn_feld[i] = i; };
Warnung ● ●
Das erste Element eines Arrays (Feldes) besitzt den Index 0. Intern wird der Indizierungsoperator vom Compiler durch eine Zeigerberechnung aufgelöst. Die Syntax array[i] ist daher identisch zu *(array+i).
Punkt-Operator
.
Der Punkt-Operator dient dazu, auf einzelne Elemente einer Klasse, Struktur oder einer Union zuzugreifen. objekt.element ●
objekt: muß der Name einer Klasse, Struktur oder einer Union sein.
●
element: muß der Name eines Elements sein.
struct person { char name[20]; int plz; }; int main() { struct person leser; strcpy(leser.name, "Otto"); leser.plz = 5100; return 0; }
110
Datenzugriff
Elementverweis-Operator
->
Der Operator bietet eine vereinfachte Syntax, um über Zeiger auf Klasseninstanzen (inklusive Strukturen und Unions) auf deren Elemente zugreifen zu können. pObjekt->element ●
pObjekt: Zeiger auf eine Variable vom Typ Klasse, Struktur oder Union.
●
element: ein Element.
struct person { char name[20]; int plz; }; int main() { struct person p_leser; strcpy(p_leser->name, "Otto"); p_leser->plz = 5100; return 0; }
Zeiger auf Klassenelemente
->*, .*
Die beiden Operatoren dienen dazu, Zeiger auf Klassenelemente zu dereferenzieren. klassenobj.*p_classelement p_klassentyp->*p_classelement ● ●
●
p_classelement: Zeiger auf ein Element der Klasse T. klassenobj: Instanz der Klasse T oder einer von T abgeleiteten Klasse, von der aus das Element der Basisklasse T eindeutig erreichbar ist. p_klassentyp: Zeiger auf Instanz der Klasse T oder einer von T abgeleiteten Klasse, von der aus das Element der Basisklasse T eindeutig erreichbar ist.
#include <stdio.h> #include <stdlib.h> // Definition der Klasse class extreme { public: // liefert Maximum der Parameter int maximum(int var1, int var2) {return ( (var1 > var2) ? var1 : var2);} // liefert Minimum der Parameter int minimum(int var1, int var2) {return ( (var1 < var2) ? var1 : var2);} }; int main() { class extreme extr; int extremum; int wert1, wert2; wert1 = rand()%20; wert2 = rand()%40;
// Instanzbildung
111
Sprachkonzepte und Syntax // Definition der Zeiger auf Methoden int (extreme::*z_max) (int, int); int (extreme::*z_min) (int, int); // Initialisierung der Zeiger z_max = extreme::maximum; z_min = extreme::minimum; // Dereferenzierung der Zeiger zum Funktionsaufruf extremum = (extr.*z_max) (wert1, wert2); printf("Das Maximum von %d und %d ist %d\n",wert1, wert2, extremum); extremum = (extr.*z_min) (wert1, wert2); printf("Das Minimum von %d und %d ist %d\n",wert1, wert2, extremum); return 0; }
Bereichsoperator
::
Objekte und Funktionen gleichen Namens und gleicher Signatur (Parametertypen) aus unterschiedlichen Gültigkeitsbereichen verdecken sich gegenseitig. Mittels des Bereichsoperators kann aus dem aktuellen Gültigkeitsbereich auf verdeckte Variablen und Funktionen anderer Gültigkeitsbereiche zugegriffen werden. ::global_name
Der unäre Operator dient dazu, auf verdeckte globale Variablen zugreifen zu können (siehe erstes Beispiel). space::name
In seiner binären Form wird der Operator eingesetzt, um ●
auf bestimmte Namensbereiche zuzugreifen,
●
auf statische Klassenelemente zuzugreifen,
●
auf Basisklassenelemente zuzugreifen.
Der Zugriff funktioniert nur, wenn der Name eindeutig aufgelöst werden kann (siehe zweites Beispiel).
Beispiele Zugriff auf verdeckte, globale Variable: #include <stdio.h> int i = 1; int main() { int i = 2; printf("i = %d\n",i); printf("i = %d\n",::i); return 0; }
112
// lokales i verdeckt globales i // Zugriff auf globales i
Bitweise Operatoren
Zugriff auf Variablen aus einem Namensbereich: namespace space1 { int i = 2; } class basis { public: int i; void func() {cout << space1::i;}; // Zugriff auf Namensbereich }; class abgeleitet : public basis { public: int i; void func() {cout << basis::i;}; // Zugriff auf Basisklasse };
Verweise Siehe Kategorie Klassen, Zugriff von außerhalb der Klasse
Bitweise Operatoren Beschreibung Operatoren zur bitweisen Manipulation von Daten. Als Operanden sind allerdings nur IntegerWerte erlaubt.
Anwendung Mit Hilfe der Bit-Operatoren lassen sich bestimmte Programmieraufgaben sehr elegant und effizient lösen. Voraussetzung für den sicheren Gebrauch der Operatoren sind allerdings Kenntnisse über die Binärcodierung der manipulierten Daten.
bitweises UND
&
Wenn der Operator »&« mit zwei Operanden benutzt wird, handelt es sich um den bitweisen UND-Operator. Die Operanden können beliebige Integer-Werte sein. Die beiden Operanden werden bitweise verglichen. Das Bit im Ergebnis wird nur dann gesetzt, wenn in beiden Operanden die korrespondierenden Bits gesetzt sind. Dieser Operator kann eingesetzt werden, um Bits in seinem ersten Operanden gezielt zu löschen. In der ASCII-Tabelle unterscheiden sich die Klein- von den Großbuchstaben des englischen Alphabets (also nicht die deutschen Umlaute) binär dadurch, daß bei den Kleinbuchstaben immer Bit 5 (von rechts her gezählt) gesetzt ist. Um einen
113
Sprachkonzepte und Syntax
Klein- in einen Großbuchstaben umzuwandeln, soll Bit 5 des ersten Operanden gelöscht werden. Um dies zu erreichen, werden im zweiten Operanden, der Maske, alle Bits, bis auf Bit 5, das gelöscht werden soll, gesetzt. Dadurch bleiben die anderen Bits unverändert. & & & &
0110 0001 1101 1111 -------------------0100 0001
Buchstabe 'a' Maske (dezimal 223) Buchstabe 'A'
bitweises ODER
|
Der Operator für die bitweise ODER-Verknüpfung »|« vergleicht die Bitmuster seiner Operanden und setzt das entsprechende Bit im Ergebnis, wenn eines der Bits in den Operanden gesetzt ist. Dieser Operand kann beispielsweise dazu verwendet werden, um gezielt einzelne Bits zu setzen, ohne die anderen Bits der ersten Operanden zu verändern. Ein Beispiel dafür ist die Umwandlung von Groß- in Kleinbuchstaben. In der ASCII-Tabelle unterscheiden sich die Klein- von den Großbuchstaben des englischen Alphabets (also nicht die deutschen Umlaute) binär dadurch, daß bei den Kleinbuchstaben immer Bit 5 gesetzt ist. Um einen Großbuchstaben in den entsprechenden Kleinbuchstaben umzuwandeln, braucht also nur Bit 5 gesetzt zu werden, was Sie im folgenden Beispiel sehen: 0100 0001 | 0010 0000 -------------------0110 0001
Buchstabe 'A' Maske dezimal 32 Buchstabe 'a'
bitweises exklusives ODER
^
Der Operator für die bitweise exklusive ODER-Verknüpfung vergleicht die Bitmuster seiner Operanden und setzt das entsprechende Bit im Ergebnis, wenn eines der Bits in den Operanden, aber nicht beide, gesetzt ist. 0001 1001 ^ 0000 1100 ---------------------0001 0101
114
Bitweise Operatoren
bitweises Komplement
~
Der bitweise Komplement-Operator »~« kippt alle Bits seines Operanden. Durch Invertierung werden im Ergebnis alle Bits, die im Operanden gesetzt waren, gelöscht, und alle Bits, die gelöscht waren, gesetzt. Die Zahl 4 besitzt folgendes Bitmuster: 0000
0100
Durch die Operation ~4 erhalten wir folgendes Muster: 1111
1011
Rechtsverschiebung
>>
Bei der bitweisen Rechtsverschiebung werden die einzelnen Bits eines Operanden um die festgelegte Anzahl nach rechts verschoben. Der Operator kann beispielsweise für eine schnelle Programmierung der Division durch die Potenzen der Zahl 2 benutzt werden. Der Ausdruck 80 >> 3 entspricht 80 / 2^3 und erzeugt folgendes Ergebnis: dezimal
binär
vor Verschiebung
80
0101 0000
nach Verschiebung
10
0000 1010
Linksverschiebung
<<
Bei der bitweisen Linksverschiebung werden die einzelnen Bits eines Operanden um die festgelegte Anzahl nach links verschoben. Der Operator kann beispielsweise für eine schnelle Programmierung der Multiplikation mit den Potenzen der Zahl 2 benutzt werden. Der Ausdruck 3 << 2 entspricht 3 * 2^2 und erzeugt folgendes Ergebnis: dezimal
binär
vor Verschiebung
3
0000 0011
nach Verschiebung
12
0000 1100
Beispiele #include <stdio.h> int main() { printf(" a & 223 printf(" A | 0x20 printf(" 25 ^ 12 printf(" 3 << 2
= = = =
%c\n", %c\n", %d\n", %d\n",
'a' 'A' 25 3
& 223); | 0x20); ^ 12); << 2);
115
Sprachkonzepte und Syntax printf(" 80 >> 3 printf("~4 return 0; }
= %d\n", 80 >> 3 ); = %d\n", ~4);
Das Programm erzeugt folgende Ausgabe: a A 25 3 80 ~4
& 223 | 0x20 ^ 12 << 2 >> 3
= = = = = =
A a 21 12 10 -5
Verweise Siehe Streamoperatoren
Streamoperatoren Beschreibung In C++ sind die Operatoren >> und << für das formatierte Einlesen und Schreiben von und in Streams überladen.
Anwendung Der Einsatz der Streamoperatoren für die Ein- und Ausgabe ist recht komfortabel und sehr übersichtlich. int wert = 3; cout << "Wert = " << wert << endl;
// cout ist der Standardausgabestream
Die Programmierung mit Streamoperatoren ist zudem in vorbildlicher Weise objektorientiert: ●
●
die Streamoperatoren sind standardmäßig für alle elementaren Datentypen überladen (int, float, char, etc.). durch Überladung kann das Einsatzgebiet der Operatoren auf selbst definierte Klassentypen ausgedehnt werden.
Durch sogenannte Manipulatoren kann die Formatierung der Ein- und Ausgabe gesteuert werden.
Beispiele int wert; cout << "Geben Sie einen Wert ein:" << endl; cin >> wert; cout << left; cout << "Wert = " << wert << endl;
116
Die Operatoren new und delete
Verweise Siehe Referenz der C++-Laufzeitbibliothek, Header-Dateien iostream, iomanip, etc.
Die Operatoren new und delete Beschreibung Die Operatoren new und delete dienen zur dynamischen Speicherverwaltung und können in C++-Programmen die Funktionen malloc() und free() ersetzen. Intern sind die Operatoren meist mit Hilfe der Funktionen malloc() und free() implementiert, sie erlauben aber eine einfachere und erweiterte Handhabung.
new-Operator
new
Der new-Operator dient zur dynamischen Speicherallokation. Für die Implementierung des Operators sind im ANSI C++-Standard allein 6 Operatorfunktionen vorgesehen. Die verschiedenen Operatorfunktionen sind für die Unterscheidung von einfachen Objekten und Arrays als Operanden, und für die einfache Steuerung der Fehlerbehandlung und die Zuweisung eines bestimmten Speicherbereiches gedacht. Zudem gibt es zwei Formen des Aufrufs, je nachdem, ob die Typangabe geklammert wird oder nicht. Dies ist notwendig, um in Typangaben, die Operatoren wie &, * oder [] enthalten, unterscheiden zu können, ob die Deklarations- oder die Zuweisungsoperatoren gemeint sind: new [freie_arg] Typ [initialisierer] new [freie_arg] (Typ) [initialisierer] ●
freie_arg: Argumentenliste, die überladenen Formen übergeben werden kann.
●
Typ: Datentyp, der für den Speicher zu reservieren ist.
●
initialisierer: Wert, der benutzt wird, den reservierten Speicherplatz zu initialisieren.
Beispiel 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
long double *p, *q, **pp; p = new long double; pp = new long double*; p = new long double(1.2); //q = new long double * 3; q = new long double [3]; //q = new long double [3](1,2,3); class demo { int i; } *p_demo;
117
Sprachkonzepte und Syntax 11: 12: 13: 14: 15: 16:
p_demo = new demo[10]; void (**func_ptr)(int); //func_ptr = new void(*[3])(int); func_ptr = new (void(*[3])(int)); char ziel[sizeof(demo)]; p_demo = new (ziel) demo;
Erläuterung In Zeile 2 wird ein Zeiger auf Objekte des Datentyps long double eingerichtet, in Zeile 3 ein Zeiger auf einen Zeiger auf Objekte des Datentyps long double. In Zeile 4 wird der Zeiger p eingerichtet und der Speicherbereich, auf den der Zeiger p verweist, mit dem Wert 1.2 initialisiert. In Zeile 5 wird versucht, einen Zeiger auf ein Array von 3 long double Werten einzurichten. Die verwendete Syntax ist ähnlich dem Gebrauch der Funktion malloc(), führt hier aber zu Fehlern, da der Compiler den Aufruf als (new long double)*3 versteht. In Zeile 6 erfolgt die korrekte Allokation des Arrays. Zeile 7: syntaktisch korrekt, aber Arrays können nicht initialisiert werden. In Zeile 11 wird ein Array von Klassenobjekten eingerichtet. In Zeile 12 wird ein Zeiger func_ptr auf Funktionszeiger deklariert. Danach soll für den Zeiger func_ptr Speicher für ein Array von Funktionszeigern reserviert werden, dessen erstes Element ihm als Adresse übergeben wird. Die Zuweisung in Zeile 13 führt wie in Zeile 5 zu einem Interpretationsfehler. In Zeile 14 wird daher die zweite Syntaxform gewählt, bei der die Typangabe eingeklammert ist. In Zeile 15 wird Speicher von der Größe eines demo-Objekts reserviert. In Zeile 16 wird das demo-Objekt in dem Speicherbereich, auf den ziel verweist, eingerichtet (Plazierungsform des Operators).
Warnung ●
●
●
●
Objekte, die mit new eingerichtet wurden, haben unbegrenzte Lebensdauer. Der Speicherplatz muß daher mittels des delete-Operators wieder freigegeben werden. Wird Speicher für ein Array reserviert, liefert der new-Operator einen Zeiger auf das erste Array-Element zurück. Bei der Reservierung von Arrays müssen alle Dimensionen durch konstante, positive Ausdrücke angegeben werden; die erste Dimension kann durch einen allgemeinen Ausdruck spezifiziert werden. Arrays können nicht initialisiert werden.
118
Typumwandlung und -identifizierung
●
●
●
●
Klassenobjekte können nur mit new eingerichtet werden, wenn die Klasse einen Standardkonstruktor besitzt, oder die Argumente für den Konstruktor dem Operator übergeben werden. Referenzen können nicht mit new eingerichtet werden, da sie keinen Speicherplatz haben. Der Operator berechnet den Speicherbedarf für den einzurichtenden Datentyp selbst, weswegen der erste Parameter einer new-Operatorfunktion immer vom Typ size_t ist (zu beachten bei Überladung). Wird der new-Operator überladen, sollte auch eine entsprechende Form des delete-Operators implementiert werden.
delete-Operator
delete
Der delete-Operator dient zur dynamischen Freigabe von mittels new allokiertem Speicher. delete p_objekt delete [] p_objekt
Warnung ●
Zeiger auf Konstanten können nicht gelöscht werden.
●
Für Klassen wird beim Löschen der Destruktor aufgerufen.
●
●
Die zweite Version ist zur Freigabe von Arrays. Die Dimension wird dabei nicht angegeben. Aufrufe mit Nullzeigern sind ohne Effekt.
Beispiele class demo *p; p = new demo[10]; delete[] p; int **p; p = new int*; delete p;
Typumwandlung und -identifizierung Beschreibung Mit Hilfe der Operatoren zur Typumwandlung kann man innerhalb bestimmter Grenzen Werte und Objekte eines Datentyps in einen anderen Datentyp umwandeln. Mit Hilfe des typeid-Operators kann man in C++ den Typ eines Klassenobjekts zur Laufzeit feststellen.
119
Sprachkonzepte und Syntax
Anwendung Gelegentlich ist es wünschenswert, daß eine Variable eines bestimmten Datentyps als einem anderen Datentyp angehörig behandelt wird. Sofern eine solche Typumwandlung durchführbar ist, kann sie durch den C-Cast-Operator () oder die C++Operatoren const_cast, dynamic_cast, static_cast und reinterpret_cast, die als Schlüsselworte in die Sprache aufgenommen sind, explizit herbeigeführt werden. Die C++-Operatoren sind vor allem dazu gedacht, die verschiedenen Konvertierungen (die auch mit dem Cast-Operator () vorgenommen werden könnten) nach ihrem Gefahrenpotential und dem Einsatzgebiet zu kategorisieren. Bei Auftreten eines Fehlers kann der Programmierer dann eventuell aus der Art des Fehlers auf die betreffende Typumwandlung schließen und mit Hilfe der Suchfunktion seines Editors schnell zu allen Vorkommen des jeweiligen Operators springen. Operator
Beschreibung
static_cast(var)
Zur Umwandlung zwischen »nahe verwandten« Typen – also alle Fälle, in denen der Compiler auch eine implizite Konvertierung vornehmen könnte, plus einige einfache Sonderfälle.
reinterpret_cast(var)
Für fast alle anderen erlaubten Umwandlungen.
const_cast(var)
Entfernt eine const- oder volatile-Deklaration aus einem Typ.
dynamic_cast(var)
Zur Umwandlung zwischen Zeigern oder Referenzen auf Klassentypen einer gemeinsamen Klassenhierarchie
Der Cast-Operator
()
Der Cast-Operator kann benutzt werden, um in einer Anweisung den Typ des Ausdrucks der rechten Seite in den Typ des Bezeichners der linken Seite umzuwandeln: bezeichner1 = (Typ_von_bezeichner1) AUSDRUCK; bezeichner1 = (Typ_von_bezeichner1) bezeichner2;
Eine zweite Form der Syntax wandelt den Typ einer Variablen direkt um bezeichner = Datentyp();
Beispiele intptr = (int *) malloc(80 * sizeof(int)); printf("%f\n", (double) 13/2); int i = int(3.14); int i = int(); // weist undefinierten Wert zu
120
Typumwandlung und -identifizierung
Typumwandlung
const_cast (nur C++)
Der Operator const_cast dient dazu, die Qualifizierer const und volatile aus einer Typangabe zu entfernen oder gegeneinander auszutauschen. Den Operator const_cast kann man beispielsweise dazu nutzen, um eine Variable, die durch const-Deklaration grundsätzlich vor ungewollter Überschreibung geschützt wurde, für eine nachfolgende Zuweisung veränderbar zu machen. const_cast < Zieltyp > (variable) ●
●
Zieltyp: Angabe des Typs, in den die variable umgewandelt werden soll. Der Zieltyp muß mit dem Typ der variable bis auf die Qualifizerer const oder volatile übereinstimmen. variable: Umzuwandelndes Objekt.
Beispiele const int* p; int* q; p = q; q = p; q = const_cast (p);
// korrekt: Zuweisung eines int*-Objekts // an const int* // Fehler: const int* an int* // ok.
Typumwandlung
dynamic_cast (nur C++)
Der dynamic_cast-Operator kann dazu verwendet werden, Zeiger (oder Referenzen) auf Objekte einer Basisklasse in Zeiger (oder Referenzen) auf Objekte einer abgeleiteten Klasse umzuwandeln oder vice versa. UpCast. In einem Objekt einer abgeleiteten Klasse sind die Elemente der Basisklassen praktisch als Teilobjekte enthalten. Ist T eine solche eindeutige public Basisklasse, kann ein Zeiger auf das Gesamtobjekt der abgeleiteten Klasse in einen Zeiger auf das Teilobjekt der Basisklasse umgewandelt werden (gleiches gilt für Referenzen). Dieser Vorgang gehört zu den Standardkonvertierungen von C++ und erfordert an sich nicht den Einsatz des dynamic_cast-Operators. DownCast. Umgekehrt kann ein Zeiger auf ein Basisklassenobjekt, das Teil eines abgeleiteten Objekts ist, in einen Zeiger auf das abgeleitete Objekt zurückverwandelt werden, wenn es sich um eine polymorphe Basisklasse (Klasse, die virtuelle Methoden enthält) handelt. (Für nicht polymorphe Basisklassen kann man den static_cast-Operator verwenden.) class_zeiger1 = dynamic_cast < Zieltyp > (class_zeiger2) ●
●
●
class_zeiger1: Zeiger oder Referenz auf Klassenobjekt, dem der umgewandelte Zeiger (Referenz) class_zeiger2 zugewiesen wird. Zieltyp: Angabe des Typs, in den class_zeiger2 umgewandelt werden soll (Typ von class_zeiger1 oder void* ). class_zeiger2: Umzuwandelnder Zeiger (Referenz).
121
Sprachkonzepte und Syntax
Beispiele #include using namespace std; class Basis { public: virtual void func() {cout << "basisklasse" << endl;} }; class Abgeleitet : public Basis { public: void func_abg() {cout << "abgeleitete Klasse" << endl;} }; int main() { Abgeleitet *p_abg = new Abgeleitet; Basis *p_bp = new Basis; // Umwandlung Zeiger auf abgeleitete Klasse zu Basisklasse // p_bp = p_abg; ginge auch p_bp = dynamic_cast (p_abg); if(p_bp != NULL) p_bp->func_abg(); // Fehler // Umwandlung Zeiger auf Basisklasse in abgeleitete Klasse // p_abg = p_bp; nicht möglich p_abg = dynamic_cast (p_bp); if(p_abg != NULL) p_abg->func_abg(); // o.k. return 0; }
Zudem kann man unter bestimmten Voraussetzungen mit dynamic_cast auch einen CrossCast von einer Basisklasse in eine andere Basisklasse bewirken. Im Fehlerfall wird für Referenzen eine bad_cast-Exception ausgelöst und für Zeiger ein NULLZeiger zurückgeliefert.
Typumwandlung
static_cast (nur C++)
Mit dem Operator static_cast können alle gängigen Konvertierungen explizit vorgenommen werden. Darüber hinaus erlaubt er eine Konvertierung eines Zeigers auf eine nicht-polymorphe Basisklasse in eine ihrer abgeleiteten Klassen, und ergänzt hierin den Operator dynamic_cast. bezeichner1 = ●
static_cast < Zieltyp > (bezeichner2)
bezeichner1: Objekt, dem das umgewandelte Objekt bezeichner2 zugewiesen wird.
●
Zieltyp: Angabe des Typs, in den bezeichner2 umgewandelt werden soll.
●
bezeichner2: Umzuwandelndes Objekt.
122
Typumwandlung und -identifizierung
Beispiele #include using namespace std; class Basis { public: void func() {cout << "basisklasse" << endl;} }; class Abgeleitet : public Basis { public: void func_abg() {cout << "abgeleitete Klasse" << endl;} }; int main() { Abgeleitet *p_abg = new Abgeleitet; Basis *p_bp = new Basis; // Umwandlung Zeiger auf abgeleitete Klasse zu Basisklasse p_bp = dynamic_cast (p_abg); if(p_bp == NULL) cout << "cast gescheitert" << endl; else p_bp->func_abg(); // Fehler // Umwandlung Zeiger auf Basisklasse in abgeleitete Klasse p_abg = static_cast (p_bp); if(p_abg == NULL) cout << "cast gescheitert" << endl; else p_abg->func_abg(); // o.k. return 0; }
Typumwandlung
reinterpret_cast (nur C++)
Der Operator reinterpret_cast ist für besonders kritische Konvertierungen gedacht, beispielsweise die Konvertierung von Integer-Typen in Zeiger und vice versa, sowie zur Konvertierung von Zeigern auf Funktionen. bezeichner1 = reinterpret_cast < Zieltyp > (bezeichner2) ●
bezeichner1: Objekt, dem das umgewandelte Objekt bezeichner2 zugewiesen wird.
●
Zieltyp: Angabe des Typs, in den bezeichner2 umgewandelt werden soll.
●
bezeichner2: Umzuwandelndes Objekt.
123
Sprachkonzepte und Syntax
Warnung ●
●
●
●
Werden Integer-Typen in Zeiger umgewandelt oder umgekehrt, ist nicht sichergestellt, daß der Wert des konvertierten Objektes korrekt interpretiert werden kann. Es ist allerdings sichergestellt, daß bei einer nachfolgenden Rückkonvertierung der ursprüngliche Wert wieder erscheint (natürlich vorausgesetzt, daß zwischen den Konvertierungen keine Zuweisung an die Variable erfolgte). Entsprechendes gilt, wenn Zeiger auf Funktionen in Zeiger auf Funktionen anderen Typs oder in Zeiger auf Objekte umgewandelt werden. Werden Zeiger oder Referenzen konvertiert, sind undefinierte Klassenbezeichner in der Typangabe erlaubt.
Typidentifizierung
typeid
Der Operator typeid erzeugt für das ihm übergebene Objekt eine entsprechende Instanz der Klasse type_info (definiert in typeinfo). In dieser Instanz, die vom Operator als Referenz zurückgeliefert wird, sind Informationen zum Datentyp des übergebenen Objekts abgelegt. Die type_info-Objekte können direkt mit Hife der Operatoren == und != verglichen werden. Zudem kann die Methode name() aufgerufen werden, die einen String zurückliefert, der den Typ des typeid-Operanden identifiziert.
Warnung Laut ANSI-Standard sind Typen, die sich nur in den CV-Qualifizierern unterscheiden (const, volatile) identisch. Einige Compiler differenzieren hier aber.
Beispiele #include #include using namespace std; class demo {int wert;}; int main() { demo d1(); const demo d2(); if(typeid(d1) == typeid(d2)) cout << "gleich" << endl; if(typeid(d1).name() == typeid(d2).name()) cout << typeid(d1).name() << endl; return 0; }
124
Sonstige Operatoren
Sonstige Operatoren sizeof-Operator
sizeof
Der sizeof-Operator liefert als Ergebnis die Größe seines Operanden in Byte zurück. Der Rückgabewert ist vom vorzeichenlosen Integer-Typ size_t. Als Operand kann sowohl ein Datentyp als auch eine Variable übergeben werden. Typische Einsatzgebiete für den sizeof-Operator sind Speicherbedarfberechnungen für die dynamische Speicherallokation sowie die Berechnung der Anzahl an Elementen in einem Array.
Warnung ●
●
●
●
●
●
Für Referenzen liefert der sizeof-Operator die Größe des referenzierten Objektes. Nicht zulässig sind: Funktionen, Bitfelder, undefinierte Klassen, Arrays mit fehlender Dimensionsangabe sowie der Typ void. Wenn einer der Typen char, unsigned char oder signed char als Argument verwendet wird, ist das Ergebnis per Definition immer 1. Die Größe eines Zeichenliterals (sizeof('Z')) entspricht in C der Größe von int, in C++ der Größe von char. Wird sizeof auf ein Array angewendet, ist das Ergebnis die Gesamtgröße des Arrays in Bytes. Doch Vorsicht! Wenn sizeof innerhalb einer Funktion benutzt wird, um die Größe eines Arrays zu ermitteln, das an einen Parameter der Funktion übergeben wurde, liefert sizeof die Größe des Zeigers auf das Array zurück, da Arraynamen als Zeiger übergeben werden. Handelt es sich beim Argument um eine Klasse, Struktur oder Union, wird die tatsächliche Größe zurückgegeben, in der auch Füllbytes zur Ausrichtung an Speichergrenzen enthalten sein können. Das Ergebnis der sizeof-Operation kann dann von der Summe des Speicherplatzbedarfs der einzelnen Elemente verschieden sein.
Beispiele int main() { int feld[123]; // liefert anz = 123; int anz = sizeof(feld)/sizeof(int); // reserviert Feld für 123 double-Werte double *ptr = (double*) malloc(anz*sizeof(double)); return 0; }
125
Sprachkonzepte und Syntax
Adreß-Operator
&
Wenn der Operator »&« mit einem Operanden benutzt wird, liefert er die Adresse seines Operanden zurück. Als Operand kann jeder Bezeichner eingesetzt werden, der einen L-Wert verkörpert.
Warnung ●
●
Der Operator kann nicht auf das Element eines Bitfeldes oder auf ein Objekt, das mit der Speicherklasse register definiert wurde, angewendet werden. Funktions- und Array-Namen können direkt als Adressen interpretiert werden, weswegen für sie die Anwendung des Adreß-Operators nicht erforderlich ist.
Beispiele // Integer-Wert von Tastatur einlesen int zahl; scanf("%d", &zahl); // Zeiger auf eine Variable richten int zahl_wert; int *zahl_zeiger; zahl_zeiger = &zahl_wert; // Uebergabe der Funktion demo() an func() void demo() {...} void func(int* i, void (*f)() ) {...} func(&zahl_wert, demo);
Bedingungs-Operator (bedingte Bewertung)
?:
ANSI C/C++ kennt einen einzigen ternären Operator, der auch Bedingungs-Operator genannt wird. Der Bedingungs-Operator (?:) ist eigentlich eine Kurzform der if-else-Anweisung. Das allgemeine Format dieses Operators lautet folgendermaßen: Bedingung ? Ausdruck1 : Ausdruck2
Auswertung ●
●
Wenn die Auswertung der Bedingung nicht 0 (also wahr) ergibt, wird Ausdruck1 ausgeführt. Ergibt die Auswertung der Bedingung 0 (also unwahr), wird Ausdruck2 ausgewertet.
126
Sonstige Operatoren
Beispiele int max, eins, zwei; eins = 1; zwei = 2; max = (eins > zwei) ? eins : zwei;
Die gleiche Zuweisung kann auch als if-else-Anweisung formuliert werden: if (eins > zwei) max = eins; else max = zwei;
Sequentielle Auswertung (Komma-Operator)
,
Wenn das Zeichen »,« nicht eine Liste von Elementen trennt (Funktionsparameter, Initialisierungswerte etc.), dient es zur sequentiellen Auswertung. Er wird üblicherweise an Stellen verwendet, an denen nur ein Ausdruck erlaubt ist, um zwei oder mehr Ausdrücke auszuwerten.
Warnung ●
●
●
Das Ergebnis der durch den Komma-Operator getrennten Ausdrücke hat den gleichen Wert und Typ wie der rechte Operand. Die Operanden können einen beliebigen Typ besitzen; Typumwandlungen werden nicht durchgeführt. In Listen müssen die entsprechenden Ausdrücke geklammert werden.
Beispiele func(int par1, float par2) {..};
Das nächste Beispiel verwendet den Komma-Operator im Reinitialisierungsteil einer for-Schleife, um bei jedem Schleifendurchlauf nicht nur eine, sondern beide Schleifenvariablen i und j zu inkrementieren. for (i=j=0; i<MAXWERT; i++, j++) { ...
Im dritten Beispiel wird zuerst der linke Operand (i++) ausgewertet. Anschließend wird das Ergebnis von zahl2/4 der Variablen zahl1 zugewiesen. zahl1 = (i++, zahl2 / 4);
Wird der Operator in Listen eingesetzt, müssen die Anweisungen geklammert werden. func(i, (f=3.14, f+0.0015));
127
Sprachkonzepte und Syntax
Synonyme In C++ wurden für einige Operatoren synonyme Bezeichnungen eingeführt. Synonym
Operator
bitand
&
and
&&
bitor
|
or
||
xor
^
compl
~
and_eq
&=
or_eq
|=
xor_eq
^=
not
!
not_eq
!=
Priorität und Assoziativität Beschreibung Um vorhersagen zu können, wie ein Ausdruck ausgewertet wird, müssen Sie wissen, wie der Compiler Ausdrücke auswertet. Priorität
Jeder Operator gehört einer bestimmten Prioritätsklasse an. In einem Ausdruck werden die Operatoren in der Reihenfolge ihrer Priorität abgearbeitet, wobei Operatoren höherer Priorität zuerst ausgewertet werden.
Assoziativität Gibt es in einem Ausdruck mehrere Operatoren gleicher Priorität, werden diese in der für die Operatoren charakteristischen Richtung abgearbeitet. Operanden
In welcher Reihenfolge die Operanden eines binären Operatoren ausgewertet werden, ist nicht festgelegt (meist wird zuerst der linke, dann der rechte Operand ausgewertet). Für die logischen Operatoren ist vorgeschrieben, daß der linke Operand zuerst ausgewertet wird. Ist der linke Operand eines &&-Operators gleich 0 (false), wird der rechte Operand nicht mehr ausgewertet. Gleiches gilt für den ||-Operator, wenn der linke Operand gleich 1 (true) ist.
Klammern
Durch das Setzen von Klammern kann man die Reihenfolge, in der die Operatoren abgearbeitet werden, beeinflussen.
Priorität und Assoziativität der einzelnen Operatoren können Sie der nachfolgenden Tabelle entnehmen.
128
Überladung von Operatoren
Priorität
Operator
18
:: (expr)
17
[] -> .
Assoziativität
L-R
Postfix: ++ -typeid, cast-Operatoren 16
unär: + – * ! ~ & ()
R-L
Präfix: ++ -new, delete, sizeof 15
.* ->*
L-R
14
*/%
L-R
13
binär: + -
L-R
12
<< >>
L-R
11
< <= > >=
L-R
10
== !=
L-R
9
& (bitand)
L-R
8
^ (xor)
L-R
7
| (bitor)
L-R
6
&&
L-R
5
||
L-R
4
= *= /* %= += – = <<= >>=
R-L
&= |= ^= 3
?:
R-L
2
throw
L-R
1
,
L-R
Überladung von Operatoren Beschreibung Die Überladung von Operatoren wird eingesetzt, um das Verhalten eines Operators an die ihm übergebenen Operanden anzupassen. Der Vorteil für den Programmierer besteht darin, daß er die zur Verfügung stehenden Operatoren für eigene Datentypen, genauer gesagt Klassen, umdefinieren kann. Ein Laufzeitvorteil ergibt sich dadurch nicht, da sich hinter den überladenen Operatoren Funktionen verbergen.
129
Sprachkonzepte und Syntax
Anwendung Operatoren können innerhalb der Deklaration ihrer Klasse oder außerhalb im Dateibereich überladen werden. Bei Überladung im Klassenbereich ist der erste Operator automatisch ein Objekt der Klasse und wird bei der Definition der Operatorfunktion nicht mehr als Parameter aufgeführt. Im Klassenbereich überladene Operatoren haben Zugriff auf die private und protected Elemente der Klasse (siehe nachfolgende Beispiele). Bei Überladung im Dateibereich müssen alle Operanden bei der Definition der Operatorfunktion als Parameter aufgeführt werden. Im Dateibereich überladene Operatoren haben keinen Zugriff auf die private und protected Elemente der Klasse. Man kann dem aber abhelfen, indem man die Operatorfunktion in der Klasse als friend deklariert (siehe Überladung der Stream-Operatoren). Operatoren, die aneinandergereiht werden können (beispielsweise var1 = var2 = var3;), müssen dazu eine Referenz auf ihren linken Operanden als Rückgabewert zurückliefern.
Warnung ● ●
●
●
●
Es können keine neuen Operatoren definiert werden. Folgende Operatoren können nicht überladen werden: ::, ?:, ., .*, #, ##, defined, typeid, die C++-Cast-Operatoren. Folgende Operatoren können nur in einem Klassenbereich überladen werden: =, (), [], -> . Mindestens ein Operand eines überladenen Operators muß eine Klasse oder eine Referenz auf eine Klasse sein. (Die Funktionsweise der Operatoren für die elementaren Datentypen kann also nicht geändert werden.) Die allgemeine Auslegung des Operators (Anzahl Operanden, Priorität, Vorgabeargumente) kann nicht verändert werden.
Überladung unärer Operatoren Unäre Operatoren, die außerhalb des Klassenbereichs überladen werden, erwarten die Übergabe eines Operanden vom Klassentyp. Unäre Operatoren, die innerhalb einer Klasse überladen werden, dürfen nur durch nicht-statische Elementfunktionen ohne Parameter überladen werden. Die aufrufende Klasseninstanz wird automatisch als Operand genommen. Syntax: Deklaration in Klasse Rückgabetyp operator @() {}
// @ gleich !, ++, --, ...
Syntax: Deklaration in Dateibereich Rückgabetyp operator @(Klassentyp) {}
130
// @ gleich !, ++, --, ...
Überladung von Operatoren
Beispiele class person { int alter; public: ... operator ++ () { ++alter; return *this; } };
//Praefix-Inkrement
Die Deklaration eines int-Parameters für den Postfix-Inkrement-Operator spezifiziert keinen wirklichen zweiten Operanden, sondern dient nur zur Unterscheidung vom Präfix-Inkrement-Operator.
Überladung binärer Operatoren Binäre Operatoren, die außerhalb des Klassenbereichs überladen werden, erwarten die Übergabe beider Operanden. Einer der Operanden muß ein Klassentyp sein. Binäre Operatoren, die innerhalb einer Klasse überladen werden, dürfen nur durch nicht-statische Elementfunktionen mit einem Parameter überladen werden. Die aufrufende Klasseninstanz wird automatisch als erster Operand genommen.
Beispiele class person { .. }; class adressbuch { /* enthält als Datenelemente eine Menge von person-Einträgen, die nicht als statisches Array, sondern als dynamische Liste oder Baum verwaltet werden */ ... public: person& operator[] (int); }; person& adressbuch::operator[] (int i) { class person *gefunden; /* durchsucht dynamische Struktur */ ... return *gefunden; }; int main() { class adressbuch persoenlich; class person eintrag; ... eintrag = persoenlich[3]; return 0; }
131
Sprachkonzepte und Syntax
Programmsteuerung Programmsteuerung Beschreibung Je komplexer Programme werden, desto öfter ist es erforderlich, je nach Situation und Zustand des Programms zu verschiedenen Anweisungen zu verzweigen. Zur Steuerung des Programmflußes gibt es veschiedene Konzepte: Verzweigungen Schleifen Exceptions Bedingte Kompilation
Anwendung ●
●
●
●
Verzweigungen. Verzweigungen ermöglichen es dem Programmierer, einen oder mehrere Anweisungsblöcke nur in Abhängigkeit von bestimmten Bedingungen ausführen zu lassen. Schleifen. Schleifen ermöglichen es dem Programmierer, einen Anweisungsblock mehrmals hintereinander ausführen zu lassen. Exceptions. Exeptions und das Konzept der Exceptions-Behandlung ermöglichen es einem Programmierer, die Fehlerbehandlung weitgehend von dem Programmfluß des eigentlichen Programmcodes zu trennen. Dazu gehört auch, daß der Ort der Fehlerentstehung nicht mehr mit dem Ort der Fehlerbehandlung zusammenfallen muß. Bedingte Kompilation. Mit Hilfe von Präprozessor-Direktiven kann der Programmierer steuern, welche Codeblöcke in Abhängigkeit von PräprozessorSchaltern kompiliert und in das ausführbare Programm aufgenommen werden sollen und welche nicht.
Die if-Bedingung Beschreibung Die if-Bedingung kontrolliert, ob eine nachfolgende Anweisung oder ein Anweisungsblock ausgeführt wird oder nicht.
132
Die if-else-Verzweigung
Anwendung Auf das Schlüsselwort if folgt in Klammern eine Bedingung. Bei Ausführung des Programms wird diese Bedingung ausgewertet. Ergibt die Bewertung das Ergebnis true (also einen Wert ungleich Null), wird die zu if gehörende Anweisung ausgeführt. Liefert die Auswertung des Ausdrucks als Ergebnis false, wird die Ausführung des Programms mit der ersten Anweisung hinter der Verzweigung fortgesetzt.
Syntax if (Bedingung) Anweisung;
●
●
if (Bedingung) { Anweisungsblock; }
Bedingung: Die Bedingung, die ausgewertet wird, muß einen booleschen Wert ergeben. Hier kann beispielsweise eine Ganzzahl, eine arithmetische Operation, ein Vergleich oder der Aufruf einer Funktion mit entsprechendem Rückgabetyp stehen. Anweisung/Anweisungsblock: Auf die if-Bedingung kann eine einzelne Anweisung oder ein Anweisungsblock folgen. Eine einzelne Anweisung muß immer mit einem Semikolon (dem Anweisungsbegrenzer) beendet werden. Wenn ein Anweisungsblock ausgeführt werden soll, muß dieser in geschweifte Klammern eingeschlossen werden.
Warnung Nie ein Semikolon hinter die Bedingung setzen. Dies würde der Compiler als eine leere Anweisung interpretieren. Die if-Bedingung bezöge sich dann nur auf diese leere Anweisung.
Beispiele if (cZeichen == 'a') printf("Das Zeichen ist a."); if ( isdigit(cZeichen) ) printf("Das Zeichen ist eine Zahl");
Die if-else-Verzweigung Beschreibung Die if-else-Verzweigung regelt, welcher von zwei Anweisungsblöcken in Abhängigkeit von einer Bedingung ausgeführt werden soll.
133
Sprachkonzepte und Syntax
Anwendung Die einfache if-Bedingung kann nur steuern, ob ein einzelner Anweisungsblock ausgeführt werden soll oder nicht. Es gibt aber auch Fälle, in denen man – je nachdem, ob die Bedingung true oder false ergibt – den einen oder anderen Anweisungsblock ausführen möchte. Diese Sprachkonstruktion wird durch das Schlüsselwort else eingeleitet. if (Bedingung) Anweisung; else Anweisung;
if (Bedingung) { Anweisungsblock } else { Anweisungsblock }
Beispiele if (cZeichen == 'a') printf("Das Zeichen ist a."); else printf("Das Zeichen ist nicht a.");
if-else-Ketten Beschreibung Sprachkonstrukte, in denen der else-Teil einer if-else-Verzweigung selbst wiederum eine ifelse-Verzweigung ist.
Anwendung Mit den Schlüsselworten if und else ist es möglich, sogenannte else-if-Ketten zu erstellen, bei denen mehrere Ausdrücke überprüft werden können. if (Bedingung) Anweisung; else if (Bedingung) Anweisung; else if (Bedingung) Anweisung; else Anweisung;
Auswertung der else-if-Ketten Die Bedingungen werden in der Reihenfolge ausgewertet, in der sie im Programmcode stehen. Wenn eine der Bedingungen als Ergebnis true liefert, wird der zugehörende Anweisungsteil ausgeführt und damit die Abarbeitung der Kette beendet. Die zum letzten else gehörenden Anweisungen werden ausgeführt, wenn keine der vorher überprüften Bedingungen das Ergebnis true liefert.
134
switch-Verzweigung
Beispiele printf("Geben Sie ein: Zahl Operator Zahl \n"); scanf("%f %c %f", &zahl1, &Operator, &zahl2); if (Operator == '+') printf("= %f", zahl1 + zahl2); else if (Operator == '-') printf("= %f", zahl1 – zahl2); else if (Operator == '*') printf("= %f", zahl1 * zahl2); ...
Verweise Siehe switch-Verzweigung
switch-Verzweigung Beschreibung Komplexe Sprunganweisung, die in Abhängigkeit von einem Ausdruck zu verschiedenen Einsprungmarken in einen Anweisungsblock verzweigt.
Anwendung Eine einfachere und meist auch übersichtlichere Programmiertechnik als die im vorigen Abschnitt beschriebenen if-else-Ketten bietet die switch-Anweisung. Auch mit ihr kann ein Programm zwischen mehreren Alternativen auswählen – allerdings i.G. zur if-else-Kette nur in Abhängigkeit vom Wert eines Ausdrucks. switch(Ausdruck) { case Konstante1: break; case Konstante2: break; case Konstante3: break; case Konstante4: break; default: Anweisung; } ●
●
Anweisung; Anweisung; Anweisung; Anweisung;
Ausdruck: Ausdruck ist ein ganzzahliger Wert (auch Aufzählungstyp), der mit allen innerhalb der switch-Anweisung stehenden case-Marken verglichen wird. Hier kann auch der Aufruf einer Funktion stehen, die eine Ganzzahl als Ergebnis zurückgibt. Gleichzeitig kann hier, wie bei der if-Anweisung, eine Zuweisung des Funktionsergebnisses erfolgen. case Konstante: Konstanten, die mit dem Ausdruck verglichen werden.
135
Sprachkonzepte und Syntax ●
●
Anweisung: Anweisung wird ausgeführt, wenn die zugehörende Konstante und der switch-Ausdruck identisch sind. Wenn einmal eine Übereinstimmung gefunden wurde, werden alle Anweisungen (auch die zu anderen Konstanten gehörenden) ausgeführt, bis entweder das Schlüsselwort break die switch-Anweisung beendet oder der switch-Block beendet ist. default: Zu dem zu default gehörenden Anweisungsblock wird verzweigt, wenn die Überprüfung des switch-Ausdrucks mit den case-Marken keine Übereinstimmung ergibt.
Ausführung: Auswertung der switch-Anweisung ●
●
●
●
●
●
●
Die Konstanten bei den case-Marken werden in der Reihenfolge, in der sie im Programmcode stehen, mit dem switch-Ausdruck verglichen. Wenn an einer Stelle Ausdruck und Konstante identisch sind, wird zu der zugehörigen Anweisung verzweigt. Ergeben alle Vergleiche das Ergebnis false, wird, falls die default-Marke vorhanden ist, zu den Anweisungen hinter default verzweigt. Da die Konstanten nur als Einsprungmarken dienen, wird die Ausführung nach gefundener Übereinstimmung solange fortgesetzt, bis die switch-Anweisung zu Ende ist. Darum wird (es sei denn, dieses Durchfallen wird explizit gewünscht) jede zu einem case gehörende Anweisung mit dem Schlüsselwort break beendet. Trifft der Compiler auf das break, wird die switch-Anweisung sofort verlassen. Innerhalb einer switch-Anweisung dürfen keine zwei Konstanten den gleichen Wert haben. Sollte dies doch der Fall sein, erzeugt der Compiler eine Fehlermeldung. Zeichenkonstanten werden in switch-Anweisungen automatisch in ihren Integer-Wert umgewandelt. Innerhalb des switch-Blocks können keine Variablen definiert und initialisiert werden.
Beispiele #include <stdio.h> int main(){ float zahl1, zahl2; char Operator; printf("Ein kleiner Taschenrechner:\n"); printf("Geben Sie ein: Zahl Operator Zahl \n"); scanf("%f %c %f", &zahl1, &Operator, &zahl2); switch (Operator) { case '+':
136
Schleifen printf("= %f", zahl1 + break; case '-': printf("= %f", zahl1 – break; case 'X': case 'x': case '*': printf("= %f", zahl1 * break; case ':': case '/': printf("= %f", zahl1 / break; default: printf("Operator nicht } // end of switch
zahl2);
zahl2);
zahl2);
zahl2);
bekannt");
printf("\n\n"); return 0; }
Verweise Siehe if-Anweisung
Schleifen for, while
Beschreibung Schleifen werden eingesetzt, wenn ein Anweisungsblock mehrere Male hintereinander ausgeführt werden soll. Schleifen bestehen aus ●
●
einer Abbruchbedingung, die festlegt, wann die Ausführung der Schleife zu beenden ist, und einem in geschweifte Klammern eingefaßten Anweisungsblock.
Anwendung In C/C++ gibt es while, for und do-while-Schleifen. Prinzipiell aber läßt sich jede Schleife als while-Schleife formulieren. Unabhängig von der verwendeten Schleife kann diese auf mehrere Weisen verlassen werden: ●
Abbruchbedingung der Schleife wird erfüllt.
●
Abbruch durch break , return oder exit().
●
Sprung aus Schleife heraus durch goto oder longjmp().
137
Sprachkonzepte und Syntax
Warnung Es ist Aufgabe des Programmierers sicherzustellen, daß eine Schleife auch wieder verlassen wird, da das Programm sonst in einer Endlosschleife verharren kann.
Die for-Schleife Beschreibung Mit dem Schlüsselwort for wird eine Schleife eingeleitet, die durch einen dreiteiligen Schleifenkopf kontrolliert wird. Dieser Schleifenkopf steht in runden Klammern und enthält die drei Elemente Initialisierung, Bedingung und Veränderung, die durch Semikolon voneinander getrennt werden.
Anwendung Die for-Schleife wird immer dann eingesetzt, wenn die gewünschten Wiederholungen des Anweisungsblocks abzählbar sind (im Gegensatz zur while-Schleife, die auch eingesetzt werden kann, wenn die Anzahl der Wiederholungen nicht berechnet werden kann). In der Praxis sieht dies meist so aus, daß man eine eigene Variable definiert (die sogenannte Schleifenvariable), diese zu Beginn der Schleife initialisiert und dann bei jedem Durchgang der Schleife um einen bestimmten Betrag erhöht oder erniedrigt, bis sie einen bestimmten Wert übersteigt oder unterschreitet, und dadurch die Schleifenbedingung nicht mehr erfüllt ist. Die Syntax der for-Schleife ist speziell auf diese Formulierung einer Schleife abgestimmt.
Syntax for (Initialisierung; Bedingung; Reinitialisierung) Anweisung; ●
●
●
Initialisierung: Die im Initialisierungsteil stehenden Anweisungen werden ausgeführt, bevor die Schleife beginnt. Dort werden üblicherweise die Schleifen- oder Zählvariablen initialisiert. Es ist auch möglich, in dieser Anweisung mehrere Variablen zu initialisieren. Die Zuweisungen werden dann durch Kommata voneinander getrennt. Bedingung: Der Bedingungsteil enthält einen Ausdruck, der wahr sein muß, damit der zur for-Schleife gehörende Anweisungsteil ausgeführt wird. Reinitialisierung: Die hier stehenden Anweisungen werden nach jedem Durchlauf des zu for gehörenden Anweisungsblocks abgearbeitet. Hier wird meist die Zählervariable erhöht oder vermindert. Es kann dort jedoch auch jede andere Anweisung aufgenommen werden. Stehen hier mehrere Anweisungen, werden sie durch Kommata getrennt.
138
Die while-Schleife
●
Anweisung: Der Anweisungsteil kann aus einer einzelnen Anweisung oder aus einem Anweisungsblock bestehen. Eine einzelne Anweisung muß immer mit einem Semikolon (dem Anweisungsbegrenzer) beendet werden. Wenn ein Anweisungsblock ausgeführt werden soll, muß dieser in geschweifte Klammern eingeschlossen werden.
Warnung Eine Sonderform der for-Schleife stellt die Endlosschleife dar. for(;;)
Sie erhält man, wenn alle drei Elemente der Definition der for-Schleife wegfallen, und wenn nur die Semikolons, die die Elemente trennen, stehenbleiben. Endlosschleifen dieser Art können, da keine Abbruchbedingung vorhanden ist, nur mit break, return oder goto verlassen werden.
Beispiele #include <stdio.h> int main() { int zaehler; for(zaehler = 0; zaehler < 10; zaehler++) printf("Der Wert von zaehler beträgt: %d\n", zaehler); return 0; }
Verweise Siehe Abbruchbefehle Siehe while-Schleife
Die while-Schleife Beschreibung Die while-Schleife ist die allgemeinste Form der Schleife, mit der man einen Anweisungsblock in Abhängigkeit von einer Schleifenbedingung mehrfach ausführen lassen kann.
Anwendung Die while-Schleife gleicht in der Syntax der if-Bedingung, nur daß die Bedingung nicht kontrolliert, ob der Anweisungsblock überhaupt ausgeführt wird, sondern wie oft er ausgeführt wird.
139
Sprachkonzepte und Syntax
Ansonsten verfügen aber auch while-Schleifen – genau wir for-Schleifen – häufig über Schleifenvariablen, die in die Schleifenbedingung eingehen und innerhalb des Anweisungsblocks der Schleife verändert werden – nur, daß Initialisierung und Veränderung der Schleifenvariable nicht wie bei der for-Schleife in den Schleifenkopf integriert werden können. Die while-Schleife wird aber auch häufig eingesetzt, wenn der Schleifenabbruch nicht von einer Schleifenvariablen, sondern von anderen Ereignissen (z. B. Benutzereingaben) abhängt. Die Schleife wird dann üblicherweise mittels einer break-Anweisung verlassen.
Syntax while (Bedingung) Anweisung; ●
●
Bedingung: Die Bedingung, die ausgewertet wird, muß einen Booleschen Wert ergeben. Hier kann beispielsweise eine Ganzzahl, eine arithmetische Operation, ein Vergleich oder der Aufruf einer Funktion mit entsprechendem Rückgabetyp stehen. Anweisung: Das Element Anweisung kann eine einzelne Anweisung sein oder aus einem Anweisungsblock bestehen. Eine einzelne Anweisung muß immer mit einem Semikolon (dem Anweisungsbegrenzer) beendet werden. Wenn ein Anweisungsblock ausgeführt werden soll, muß dieser in geschweifte Klammern eingeschlossen werden.
Beispiele Die while-Schleife ist die allgemeinste Form der Schleife. So kann man beispielsweise die folgende for-Schleife for (i=1; i <= 5; i++) printf("%d ", i);
auch als while-Schleife formulieren: i = 1; while (i <= 5) { printf("%d ", i); i++; }
Ausführung: Auswertung der while-Schleife ●
●
Die Bedingung der while-Schleife wird getestet, bevor die zur Schleife gehörende Anweisung ausgeführt wird. Die Schleife endet, wenn die Bedingung nicht mehr den Wert true ergibt.
140
Die do-while-Schleife
Liefert die Auswertung der Bedingung der while-Schleife bereits beim Eintritt den Wert false, werden die zur Schleife gehörenden Anweisungen nie ausgeführt.
●
Verweise Siehe Abbruchbefehle Siehe for-Schleife
Die do-while-Schleife Beschreibung Neben der while-Schleife gibt es noch das Konstrukt der do-while-Schleife, die mit dem Schlüsselwort do eingeleitet wird und bei der die Schleifenbedingung am Ende des Anweisungsblocks steht.
Anwendung Der wichtigste Unterschied zur while-Schleife besteht darin, daß der Anweisungsteil der do-Schleife mindestens einmal ausgeführt wird, da die Bewertung des Ausdrucks, der die Bedingung zum Abbruch oder der Schleife enthält, immer nach dem Durchlaufen des Anweisungsteils stattfindet.
Syntax do Anweisung; while (Bedingung);
Beispiele #include <stdio.h> int main() { int zahl = 99; do { printf("Geben Sie eine Zahl ein. "); printf("99 um Programm zu beenden.\n"); scanf("%d", &zahl); printf("Die eingelesene Zahl "); printf("war: %d\n", zahl); } while (zahl != 99); return 0; }
141
Sprachkonzepte und Syntax
Verweise Siehe for-Schleife Siehe while-Schleife
Abbruchbefehle für Schleifen Beschreibung Mit einer Reihe von Befehlen kann man Schleifen bei Bedarf vorzeitig abbrechen.
Anwendung Grundsätzlich kontrolliert die Schleifenbedingung, wie oft der Anweisungsblock einer Schleife auszuführen ist. Darüber hinaus gibt es aber auch die Möglichkeit, einzelne Schleifendurchgänge gezielt zu überspringen oder eine Schleife vorzeitig abzubrechen. continue
Mit der continue Anweisung wird zum Ende des Schleifenblocks gesprungen. Die Ausführung der nach continue stehenden Anweisungen unterbleibt und der nächste Schleifendurchgang beginnt.
break
Das Schlüsselwort break wurde bereits zusammen mit der switch-Anweisung vorgestellt. Neben der Beendigung der switch-Verzweigung können Sie auch die Ausführung einer Schleife mittels break beenden. Im Gegensatz zu continue wird dabei nicht nur der aktuelle Schleifendurchgang abgebrochen, sondern die Schleife wird ganz verlassen.
Neben continue und break gibt es noch weitere Schlüsselwörter und Funktionen, die man zum Verlassen einer Schleife – oder allgemein eines Anweisungsblocks – verwenden kann. return
Mit dem Schlüsselwort return kann die Ausführung einer Funktion unterbrochen und sofort an der Stelle fortgesetzt werden, an der die Funktion aufgerufen wurde.
exit()
Mit der Funktion exit() kann ein Programm an beliebiger Stelle abgebrochen werden. Dies wird meist benutzt, wenn ein schwerwiegender Fehler aufgetreten ist, der den weiteren Programmablauf sinnlos oder unmöglich macht.
throw
Löst eine Exception aus und springt zum nächsten passenden ExceptionHandler (nur für die Fehlerbehandlung).
goto
Mit goto kann zu einem beliebigen anderen Punkt innerhalb der aktuellen Funktion verzweigt werden, der durch eine Sprungmarke gekennzeichnet wurde. Die Sprungmarke erhält einen Namen und wird, wie die Marken der switch-Anweisung, mit einem Doppelpunkt abgeschlossen.
longjmp()
Setzt das Programm in einen Zustand zurück, der zuvor durch einen Aufruf von setjmp() abgespeichert wurde.
142
Funktionen
Beispiele Überspringen einzelner Iterationen mit continue: #include <stdio.h> int main() { int zahl; do { printf("Geben Sie eine Zahl ein. "); printf("99 um Programm zu beenden.\n"); scanf("%d", &zahl); if (zahl < 0) continue; printf("Die eingelesene Zahl "); printf("war: %d\n", zahl); } while (zahl != 99); return 0; }
Vorzeitiges Abbrechen einer Schleife mit break: int main() { int zahl; for (zahl = 3; zahl < 15; zahl++) { if (zahl == 8) break; printf("zahl hat folgenden Wert: %d\n", zahl); } return 0; }
Verweise Siehe for-Schleife Siehe while-Schleife
Funktionen Funktionen Beschreibung C-Programme bestehen aus einer beliebigen Anzahl von Funktionen, deren Namen innerhalb der Konventionen von C (siehe Grundlagenteil) frei gewählt werden dürfen.
143
Sprachkonzepte und Syntax
Anwendung Sinn einer Funktionsdefinition ist es, einen Anweisungsblock, der eine bestimmte Aufgabe erledigt (beispielsweise die Ausgabe auf Konsole, die Berechnung des Sinus, etc.), mit einem Namen zu verbinden. Wird dann irgendwo im Programm die Funktion über ihren Namen aufgerufen, wird die Programmausführung mit der ersten Anweisung aus dem Anweisungsteil der Funktion fortgesetzt. Nach Abarbeitung der Funktion springt die Programmausführung in die Zeile des Funktionsaufrufs zurück. Die Aufteilung des Quelltextes in Funktionen ermöglicht es, redundanten Code zu vermeiden, häufig benötigte Funktionen zu sogenannten Bibliotheken zusammenzufassen und die Programme modularer und damit wartungsfreundlicher zu gestalten. Zum Datenaustausch mit der aufrufenden Funktion kann eine Funktion eine beliebige Anzahl Parameter und einen Rückgabwert definieren. Funktionen können an beliebiger Stelle im Quelltext definiert werden (nicht aber innerhalb anderer Funktionen). Bevor man eine Funktion aufrufen kann, muß sie aber dem Compiler bekannt sein. Dies erreicht man, indem man die Funktionsdefinition an den Anfang der Quelltextdatei setzt oder indem man die Funktion an beliebiger Stelle definiert und lediglich eine Vorwärtsdeklaration der Funktion an den Programmanfang setzt. Letzteres System liegt auch der Verwendung von Header-Dateien für Bibliotheken zugrunde.
Beispiele #include <stdio.h> /* Deklarationen für printf, scanf, fflush */ double fakul(int zahl); /* Vorwärtsdeklaration zu fakul() */ int main() { int zahl; double fakultaet; printf("Geben Sie die Zahl ein: "); scanf("%d",&zahl); fflush(stdin); fakultaet = fakul(zahl); printf("\nDie Fakultaet von %d ist %lf\n", zahl, fakultaet); return 0; } double fakul(int zahl) { int loop; double fakultaet = 1; for(loop=zahl; loop>=1; loop--) { fakultaet *= loop; } return fakultaet; }
144
Funktionsdeklaration und -definition
Funktionsdeklaration und -definition Beschreibung Mit der Funktionsdeklaration werden der Rückgabetyp, der Name und die Parameter festgelegt. Eine Funktionsdefinition enthält zusätzlich den Funktionskörper, der in geschweiften Klammern die Anweisungen umfaßt.
Anwendung Bei der Funktionsdefinition wird ein Anweisungsblock mit einem Funktionsnamen verbunden, so daß beim Aufruf des Funktionsnamens im Programmcode der Anweisungsblock der Funktion ausgeführt wird. Zudem wird durch Angabe der Parameter und des Rückgabetyps der Funktion festgelegt, wie die Funktion Daten beim Aufruf übernimmt und bei ihrer Rückkehr zurückliefert.
Syntax Typ func_name (PARAMETERLISTE) { FUNKTIONSKöRPER } ●
●
●
●
Typ: Mit der Typangabe wird festgelegt, welchen Rückgabewert die Funktion hat. Arrays und Funktionstypen sind als Rückgabetyp nicht zulässig, wohl aber Zeiger auf diese Objekte. func_name: Der Name der Funktion. Dieser Name muß in C, nicht aber in C++, eindeutig sein (siehe Überladung). PARAMETERLISTE: In einer Definition stehen hier durch Kommata getrennt die einzelnen Parameter mit ihren Typen. In einer Deklaration reicht die Angabe der Parametertypen, die der Compiler zur Typenüberprüfung und zur Auflösung überladener Namen braucht (C++). In C++ können Vorgabeargumente an die Parameter vergeben werden. FUNKTIONSKÖRPER: Der Funktionskörper ist ein Anweisungsblock, der in geschweifte Klammern eingeschlossen wird und bei Aufruf der Funktion ausgeführt wird.
145
Sprachkonzepte und Syntax
Warnung ●
●
●
● ●
●
●
●
●
●
●
Funktionen haben standardmäßig die Speicherklasse extern. Darüber hinaus kann man Funktionen auch als static deklarieren. Die standardmäßige extern-Deklaration von Funktionen bewirkt, daß die Funktionen externe Bindung haben (es sei denn, es gibt im Quelltext eine vorangehende static-Deklaration der Funktion). Alle extern-Deklarationen einer bestimmten Funktion beziehen sich daher über Dateigrenzen hinweg auf ein und dieselbe Definition der Funktion. Auf diese Weise ist es möglich, über eine Vorwärtsdeklaration in einer Quelltextdatei eine Verbindung zu einer Funktion herzustellen, die in einer anderen Quelltextdatei oder einer Bibliothek definiert ist . Deklariert man eine Funktion als static, hat die Funktion interne Bindung, d. h., der Name der Funktion ist nur innerhalb der eigenen Quelltextdatei bekannt. Auf diese Weise kann man in den verschiedenen Quelltextdateien eines Programmes gleichnamige Funktionen definieren (aber natürlich nur eine pro Datei), ohne daß es zu Linker-Fehlern wegen Mehrfachdefinitionen kommt. Funktionen, die keinen Wert zurückgeben sollen, erhalten die Typangabe void. Funktionen, für die kein Rückgabetyp angegeben wurde, bekommen in C automatisch den Rückgabetyp int zugewiesen (in C++ ist dies ein Fehler). Werden in einer Funktionsdeklaration, die nicht Teil einer Definition ist, keine Parameter spezifiziert, wird dies unter C als Funktion mit beliebig vielen Parametern interpretiert, in C++ als Funktion ohne Parameter. Durch Angabe des Schlüsselworts void statt einer Parameterliste kann in C eindeutig eine Funktion ohne Parameter deklariert werden. Neben dem überflüssigen Schlüsselwort auto ist register die einzige Speicherklassenangabe, die für Parameter vergeben werden kann. Dadurch wird beim Aufruf der Funktion, sofern möglich, das so deklarierte Argument in ein Prozessorregister geladen. Die Angabe ... am Ende der Parameterliste bedeutet, daß noch beliebig viele Argumente übergeben werden können. Funktionsname und die Parameterliste bilden zusammengenommen die Signatur der Funktion. Innerhalb des Funktionskörpers können eigene Variablen definiert werden, die, wenn nicht anders angegeben, die Speicherklasse auto besitzen und lokalen Gültigkeitsbereich haben. Diese lokalen Variablen werden beim Aufruf der Funktion auf dem Stack erzeugt und beim Verlassen der Funktion automatisch wieder entfernt. Soll der Wert einer lokalen Variablen auch nach dem Verlassen der Funktion erhalten bleiben, kann die Speicherklasse static benutzt werden. Meist wird dies benutzt, um die Werte lokaler Variablen von Funktionsaufruf zu Funktionsaufruf weiterzureichen.
146
Die Funktion main()
●
●
In C++ kann man Funktionen als inline deklarieren, um den Compiler anzuweisen, wenn möglich, die Funktionsaufrufe nicht durch einen Sprung in den Anweisungsteil der Funktion, sondern direkt durch Anweisungen, die mit der Ausführung der Funktion identisch sind, zu ersetzen. Dies ist allerdings nur eine Empfehlung an den Compiler. In C++ kann man mit Hilfe des Schlüsselwortes throw bei der Funktionsdeklaration eine Liste der Exceptions angeben, die in der Funktion ausgelöst werden dürfen. Treten in der Funktion Exceptions anderer Typen auf, wird eine unexpected-Exceptions ausgelöst.
Beispiele int main(); int func1(char* s, int n); void func2();
Vorgabeargumente in C++: int
func3(double par, char* s = "Vorgabe", int n = 2);
Überladene Funktionen in C++: void func(int n); int func(int n, int m); int func(float f);
Warnung Die alte auf Kernighan/Ritchie zurückgehende Form der Funktionsdefinition, bei der die Parameter unter dem Funktionsnamen definiert wurden, ist in C obsolet und in C++ nicht mehr möglich. // func_name(a,b) // int a, double b; // { ...
Verweise Siehe Klassen, Methoden
Die Funktion main() Beschreibung Der ANSI-Standard schreibt vor, daß eine der Funktionen in einem C/C++-Programm den Namen main() tragen muß. Diese Funktion ist die Eintrittsfunktion eines Programms, der nach dem Start des Programms automatisch die Kontrolle übergeben wird. Zusätzlich soll sie die Möglichkeit bieten, Kommandozeilenargumente zu übernehmen.
147
Sprachkonzepte und Syntax
Anwendung Der ANSI-Standard sieht folgende Definitionen vor: int main() { } int main(int argc, char *argv[]) {
}
Die meisten Compiler erlauben aber auch void als Rückgabewert: void main() { } void main(int argc, char *argv[]) { }
main() ohne Argumente
main()
Programme, die keine Argumente aus dem Programmaufruf übernehmen, definieren main() ohne Parameter. int main () { } void main ( ) { }
main() und ihre Parameter
main(int, char**)
Der ANSI-Standard sieht auch vor, daß der Funktion main() optional zwei Argumente übergeben werden können. Die Namen für diese Argumente sind frei wählbar, jedoch haben sich die Namen argc und argv eingebürgert. void main(int argc, char *argv[]) { ●
●
}
argc: (Abkürzung für »argument count«) ist der übliche Name für das erste Argument der main()-Funktion. Der Datentyp von argc ist int. Der Wert von argc entspricht der Anzahl der Zeichenketten in dem Array, auf das argv zeigt. Per Definition kann argc nie einen negativen Wert annehmen. argv: (Abkürzung für »argument vector«) ist der übliche Name für das zweite Argument der main()-Funktion. argv zeigt auf ein Array mit Zeigern vom Typ char. Die Zeichenketten werden vom Lademodul des Systems an das Programm übergeben.
Die Anzahl der Parameter wird durch das Argument argc festgelegt. Per Definition zeigt argv[0] (also der erste Zeiger) immer auf den Namen des Programms, während argv[argc] ein NULL-Zeiger ist. Die einzelnen Parameter befinden sich in argv[1] bis argv[argc-1].
Beispiele #include <stdio.h> #include <stdlib.h> int main( int argc, char *argv[]) { int i; printf( "\nBefehlszeilen-Argumente:\n" ); for( i = 0; i < argc; i++ ) printf( " argv[%d] %s\n", i, argv[i] );
148
Datenaustausch zwischen Funktionen return( EXIT_SUCCESS ); }
Nachstehend sehen Sie die Ausgabe des Programms, wenn der Name des Programms MAIN.EXE ist, und es mit folgender Befehlszeile aufgerufen wurde: C:\CC>main eins zwei drei Befehlszeilen-Argumente: argv[0] C:\CC\MAIN.EXE argv[1] eins argv[2] zwei argv[3] drei
Verweise Siehe Grundlagenteil, Aufbau eines C/C++-Programms
Datenaustausch zwischen Funktionen Beschreibung Prinzipiell besteht ein C-Quelltext nur aus Deklarationen und Definitionen. Anweisungen finden sich nur in Funktionsdefinitionen. Damit Funktionen zusammenarbeiten können, müssen sie Informationen, sprich Daten, austauschen. Dazu gibt es in C verschiedene Konzepte.
Anwendung Parameter
Parameter sind spezielle Variablen einer Funktion, denen beim Aufruf der Funktion Werte zugewiesen werden. Werden als Parameter Zeiger definiert, kann die Funktion auf diesem Weg auch Daten zurückliefern.
Rückgabewert
Ein einzelner Wert, den die Funktion an den Aufrufer zurückgibt.
Globale Variablen
Eine weitere Möglichkeit, Daten zwischen Funktionen auszutauschen, läuft über die globalen Variablen. Dieser Weg ist sehr einfach, hat aber den Nachteil, daß die Unabhängigkeit und Abgeschlossenheit der Funktion dadurch beeinträchtigt wird.
Statische Variablen Sollte es erforderlich sein, daß eine lokale Variable nicht nach dem Verlassen der Funktion gelöscht werden darf, damit ihr Wert beim nächsten Aufruf der Funktion noch erhalten ist, wird die Variable als static deklariert.
149
Sprachkonzepte und Syntax
Verweise Siehe nachfolgende Abschnitte
Der Rückgabewert Beschreibung Funktionen können das Ergebnis ihrer Berechnung über den Rückgabewert zurückliefern. Dazu wird der Typ des Rückgabewerts im Funktionskopf spezifiert. Mittels der Anweisung return (ergebnis);
wird der Wert dann an die aufrufende Funktion zurückgeliefert.
Anwendung Es gibt zwei Möglichkeiten, das Konzept des Rückgabewertes sinnvoll zu nutzen: ●
●
Das Ergebnis der Funktion wird einer Variablen der aufrufenden Funktion zugewiesen. Die Funktion meldet zurück, ob sie ihre Aufgabe erfolgreich gelöst hat, oder ob in der Funktion ein Fehler aufgetreten ist.
Warnung Funktionen, die keinen Wert zurückliefern, erhalten den Rückgabetyp void. Eine solche Funktion kann return nur ohne Wert aufrufen: return; .
Verweise Siehe Parameter Siehe Kategorie Programmsteuerung, Abbruchbefehle für Schleifen
Parameter Beschreibung Die Parameter dienen dazu, Werte von der aufrufenden Funktion an die aufgerufene Funktion zu übergeben. Parameter sind stets lokale Variablen, deren Gültigkeitsbereich der äußerste Block der Funktion ist. Zur Übergabe von Daten an Parameter benutzt C standardmäßig ein Verfahren, das man »call by value« nennt.
150
Parameter
Wert kopieren
call by value
Dies bedeutet, daß beim Funktionsaufruf der Wert des übergebenen Arguments in die lokale Variable kopiert wird. Dieses Konzept stellt sicher, daß als Argument übergebene Variablen nicht von den aufgerufenen Funktionen verändert werden können:
Beispiele #include <stdio.h> void change(int param) { printf("\tParam nach Funktionsaufruf: %d\n",param); param = param*param; printf("\tParam nach Berechnung: %d\n",param); } int main() { int var = 10; printf("var vor Funktionsaufruf: %d\n",var); change(var); printf("var nach Funktionsaufruf: %d\n",var); return 0; }
liefert folgende Ausgabe: var vor Funktionsaufruf: 10 Param nach Funktionsaufruf: 10 Param nach Berechnung: 100 var nach Funktionsaufruf: 10
Adresse kopieren
call by reference
Das Pendant heißt »call by reference«. Dabei wird nicht der Wert einer Argumentvariablen übergeben, sondern ihre Adresse! Da die Funktion in diesem Fall statt einer Kopie eine Referenz auf das Aufrufargument erhält, kann sie dieses verändern. Referenzparameter sind daher neben dem Rückgabewert ein weiteres Mittel, um Daten von der aufgerufenen Funktion an die aufrufende Funktion zurückzugeben. Für »call by reference«-Übergaben gibt es folgende Möglichkeiten: Parameter
Argument
Zeiger:
Typ *
ptr
typ *
&var
(Zeiger vom Typ typ*) (Adresse einer Variablen vom Typ typ)
Referenz, nur C++:
151
Sprachkonzepte und Syntax
Parameter
Argument
typ &
var
(Variable vom Typ typ)
Beispiele #include <stdio.h> void change1(int param) { param = param*param; } void change2(int *param) { *param = *param * *param; } void change3(int ¶m) { param = param*param; } int main() { int var = 10; change1(var); change2(&var); change3(var);
printf("var : %d\n",var); printf("var : %d\n",var); printf("var : %d\n",var);
return 0; }
liefert folgende Ausgabe: var : 10 var : 100 var : 10000
Warnung Gelegentlich werden Zeiger übergeben, ohne daß eine Änderung des übergebenen Wertes erwünscht wäre. Dies kann zum Beispiel bei der Übergabe von Arrays der Fall sein, die immer als Zeiger übergeben werden, oder wenn Zeiger auf größere Datenstrukturen übergeben werden, um das Kopieren zu sparen. In solchen Fällen kann durch den Spezifizierer const in der Parameterdeklaration die entsprechende Variable vor dem Überschreiben geschützt werden.
Tip In C++ werden statt Zeigern häufig Referenzen zur effizienten Übergabe von Argumenten an Funktionen benutzt.
Verweise Siehe Rückgabewert
152
Funktionsargumente und Vorgabe
Funktionsargumente und Vorgabe Beschreibung In C++ können in der Funktionsdeklaration Vorgabeargumente vergeben werden. Parametern, für die Vorgabeargumente deklariert wurden, brauchen beim Funktionsaufruf keine Argumente mehr übergeben zu werden.
Anwendung Auf diese Weise kann der Aufruf für häufig übergebene Argumente vereinfacht werden.
Warnung ●
●
●
●
Auf einen Parameter mit Vorgabeargument dürfen keine Parameter ohne Vorgabeargumente mehr folgen. Bei einer Redeklaration dürfen weitere Vorgabeargumente angegeben werden, es dürfen jedoch keine bereits deklarierten Vorgabeargumente redeklariert werden. Als Vorgabeargumente können auch Ausdrücke oder Variablen deklariert werden. Es dürfen jedoch keine lokalen Variablen verwendet werden. Die Reihenfolge, in der die Funktionsargumente ausgewertet werden, ist nicht festgelegt und kann somit von Compiler zu Compiler verschieden sein.
Beispiele int void char* char* int
func1(int n, int m = 3); func2(char *s = "Vorgabe"); func3(int x, int y=4); func3(int x = 2, int y); func4(int a = 0, int b);
// gueltige Redeklaration // Fehler
153
Sprachkonzepte und Syntax
Funktionen und der Stack Beschreibung Der Stack ist ein dynamisch wachsender und schrumpfender Speicherbereich, der vom Compiler verwaltet wird. Hier legt der Compiler alle Informationen ab, die er für die Protokollierung und Verwaltung der Funktionsaufrufe benötigt. Immer wenn Sie eine Funktion aufrufen, wird der Stack um einen neuen Datenblock erweitert. In diesem Datenblock werden die Parameter der Funktion, die lokalen Variablen der Funktion und einige interne Informationen (von denen die Rücksprungadresse zum Code der aufrufenden Funktion die wichtigste ist) abgelegt. Diesen Datenblock nennt man auch den Stack-Frame oder Stackrahmen der Funktion.
Stack-Aktionen ●
●
Solange eine Funktion abgearbeitet wird, bleibt ihr Stack-Frame auf dem Stack liegen. Werden aus einer Funktion heraus weitere Funktionen aufgerufen, werden deren Stack-Frames auf den Stack-Frame der jeweils zuvor aufgerufenen Funktion draufgepackt.
154
Funktionen und der Stack
● ●
Wird eine Funktion beendet, wird ihr Stack-Frame vom Stack entfernt. Der Stack ist also immer das genaue Abbild der zur Zeit noch in Ausführung befindlichen Funktionen. Die Stapelreihenfolge der Funktionen zeigt an, welche Funktion von welcher anderen Funktion aufgerufen wurde.
Stack-Aufbau Für einen von oben nach unten wachsenden Stack gilt: ●
●
● ●
Die unterste Funktion ist die aktuelle Funktion, deren Anweisungen gerade abgearbeitet werden. Die darüberliegende Funktion hat die unterste Funktion aufgerufen und wartet nun, daß die aufgerufene Funktion beendet wird. Die oberste Funktion ist die Eintrittsfunktion main(). Die einzelnen Stack-Frames von oben nach unten gelesen zeigen an, über welche Funktionen die aktuelle Funktion aufgerufen wurde.
Der genaue Aufbau der Stack-Frames hängt von dem Rechner ab, für den das Programm kompiliert wurde (die Abbildung ist also nur als eine Art allgemeines Modell zu verstehen). Sie können der Stack-Verwaltung Ihres Rechners aber ein wenig nachspüren, indem Sie sich der Debug-Tools Ihres Compilers bedienen (Stack-Anzeige) oder sich die Adressen der Parameter und lokalen Variablen ausgeben lassen.
Beispiele #include <stdio.h> #include <stdlib.h> func(int par1, int par2, int par3) { int lokal1, lokal2, lokal3; printf("%d. Aufruf von func\n",par1); printf("\t Adresse von par3 = %p\n",&par3); printf("\t Adresse von par2 = %p\n",&par2); printf("\t Adresse von par1 = %p\n",&par1); printf("\t Adresse von lokal1 = %p\n",&lokal1); printf("\t Adresse von lokal2 = %p\n",&lokal2); printf("\t Adresse von lokal3 = %p\n",&lokal3); if(par1<2) func(++par1,0,0); } int main() { func(1,0,0); return 0; }
155
Sprachkonzepte und Syntax
Ausgabe 1. Aufruf von func Adresse Adresse Adresse Adresse Adresse Adresse
von von von von von von
par3 par2 par1 lokal1 lokal2 lokal3
= = = = = =
0065FE00 0065FDFC 0065FDF8 0065FDEC 0065FDE8 0065FDE4
par3 par2 par1 lokal1 lokal2 lokal3
= = = = = =
0065FDE0 0065FDDC 0065FDD8 0065FDCC 0065FDC8 0065FDC4
2. Aufruf von func Adresse Adresse Adresse Adresse Adresse Adresse
von von von von von von
Erläuterung Die obige Ausgabe ist das Abbild einer sich selbst aufrufenden Funktion. Die Adressen der Parameter werden in umgekehrter Reihenfolge ausgegeben, weil der Compiler den letzten Parameter zuerst auf dem Stack ablegt. Wie man der Ausgabe entnehmen kann, wird der Stack im Speicher von oben nach unten aufgebaut, d. h., der Stack-Frame der Funktion, die als letztes aufgerufen wird, und der damit den obersten Stack-Frame bildet, liegt im Speicher an der niedrigsten Speicheradresse. Der Stack-Frame für den ersten Aufruf von func() beginnt an der Adresse 0065FE00, der Stack-Frame für den zweiten Aufruf an der Adresse 0065FDE0. Im Stackrahmen werden zuerst die drei int-Parameter abgelegt. Jeder int-Parameter belegt 4 Byte (wie für 32-Bit-Systeme üblich). Dann folgt ein Leerraum, in dem Daten abgelegt werden, auf die wir keinen direkten Zugriff haben (vermutlich die Rücksprungadresse und ein Zeiger auf den Beginn des vorangehenden Stack-Frames). Danach folgen die lokalen Variablen, die gemäß ihrem Datentyp wiederum jeweils 4 Byte belegen.
Warnung ●
●
Wenn in C/C++ eine Funktion aufgerufen wird, wird für die Funktion Speicherplatz reserviert. Die Einrichtung und Verwaltung des Speicherbereichs der Funktion kostet auch Laufzeit. Diesen Zeitverlust bezeichnet man als Function Overhead. In dem für die Funktion bereitgestellten Speicherbereich liegen die Speicherzellen der Funktionsparameter, der lokalen Variablen und der Rücksprungadresse, die sicherstellt, daß nach Abarbeitung der Funktion wieder an die richtige Stelle im Programm zurückgesprungen wird. Bei Beenden der Funktion gibt
156
Funktionen mit beliebig vielen Argumenten
C/C++ diesen Speicherbereich wieder frei, es entsteht also kein Speicherverlust. Es sei denn, Sie verwenden rekursive Funktionen. Ist die Rekursionstiefe sehr groß oder die lokalen Variablen der rekursiven Funktion sehr speicheraufwendig, kann es passieren, daß während der Rekursion der Speicher vollständig belegt wird, da für jeden Funktionsaufruf Speicherplatz reserviert werden muß. Das Programm stürzt dann mit einer entsprechenden Fehlermeldung ab. Dies passiert zwangsläufig, wenn ihre Rekursion keine Abbruchbedingung erreicht.
Verweise Siehe Kategorie Variablen und Datentypen, Gültigkeitsbereiche
Funktionen mit beliebig vielen Argumenten func(typ param, ...)
Beschreibung C/C++ erlaubt auch die Definition von Funktionen mit unbekannter Parameterzahl. Das kommt nicht gänzlich überraschend, denn schließlich erlauben auch einige gängige Bibliotheksfunktionen eine variable Anzahl von Argumenten: printf() und scanf().
Anwendung Die Implementierung von Funktionen mit beliebig vielen Argumenten beruht auf zwei Elementen: double mittel(int anzahl, ...) ●
●
Der Ellipse. Die Ellipse ... in der Parameterliste zeigt an, daß bei Aufruf noch eine beliebige Zahl von Argumenten folgen kann. Die Ellipse muß am Ende der Parameterdefinition stehen, und ihr muß ein ordentlicher Parameter vorausgehen, an dem man innerhalb der Funktion ablesen kann, wie viele Parameter folgen. Die va_Makros aus <stdarg.h>. Diese werden benötigt, um die Argumente zu übernehmen und auszuwerten.
Beispiele double mittel(int anzahl, ...) double summe = 0; va_list ap; int loop;
{
157
Sprachkonzepte und Syntax va_start(ap, anzahl); for(loop=1;loop<= anzahl;loop++) summe += va_arg(ap,int); va_end(ap); return summe/anzahl; }
Inline-Funktionen (nur C++) Beschreibung Inline-Funktionen sind Funktionen deren Aufrufe im Quelltext der Compiler möglichst nicht durch wirkliche Funktionsaufufe und das damit verbundene Anlegen eines Stack-Frames auflöst, sondern statt dessen den Anweisungsblock der Funktion so in den Quelltext einbaut, daß dessen Ausführung wirkt, als wäre die Funktion aufgerufen worden.
Anwendung Aufrufe von Funktionen bedingen eine zeitliche Verzögerung, den sogenannten Function Overhead. Man kann dies vermeiden, indem man Makros statt Funktionen einsetzt. Ein Makroaufruf wird vom Compiler beim Übersetzen durch die Makrodefinition ersetzt. Makros bringen jedoch einige Probleme mit sich: ●
Sie nehmen keine Typüberprüfung vor, und
●
Ihre Formulierung ist fehleranfälliger als die Definition von Funktionen.
C++ bietet daher die Alternative, Funktionen als inline zu deklarieren, indem das Schlüsselwort inline einfach an den Anfang des Funktionskopfes gestellt wird. Das Schlüsselwort inline weist den Compiler an, ähnlich wie bei einem Makro, die Funktionsaufrufe durch die Funktionskörper zu ersetzen. Der Nachteil gegenüber dem Gebrauch von Makros liegt darin, daß die inline-Direktive vom Compiler nur als Empfehlung angesehen wird, die er nicht beachten muß.
Warnung ●
●
Allgemein ist zum Gebrauch von Makros und Inline-Funktionen zu sagen, daß sie zwar die Laufzeit eines Programms beschleunigen, dafür aber den Code aufblähen. Sie eignen sich daher vor allem für kleinere Funktionen, die nicht allzu oft aufgerufen werden. Methoden von Klassen, die innerhalb der Klasse definiert werden, werden vom Compiler automatisch als Inline-Funktionen behandelt.
158
Spezifizierer für Funktionen
Die inline-Deklaration ist nur eine Empfehlung. Für umfangreichere oder komplexere Funktionen übergeht der Compiler üblicherweise die inline-Deklaration.
●
inline-Funktionen müssen in jeder Übersetzungseinheit des Programms iden-
●
tisch definiert werden (siehe Praxisteil, Elemente modulübergreifend nutzen).
Verweise Siehe Funktionen und der Stack
Spezifizierer für Funktionen Beschreibung Einsatzbereich und Arbeitsweise einer Funktion können durch eine Reihe von speziellen Schlüsselwörtern, die der Funktionsdeklaration voran- oder nachgestellt werden, manipuliert werden.
Spezifizierer für Funktionen Funktionen können mit einer der Speicherklassenangaben extern oder static versehen werden: static
Die Verwendung von static als Schlüsselwort vor der Typangabe einer Funktion verengt deren Sichtbarkeit auf die Datei, in der sie definiert ist.
extern
Die Verwendung von extern als Schlüsselwort vor der Typangabe einer Funktion macht sie in allen Dateien sichtbar, die zu dem Programm gehören (Funktionen sind per Voreinstellung extern).
-
Fehlt die Angabe, hat eine Funktion standardmäßig die Speicherklasse extern.
In C++, nicht in C, können Funktionen als inline deklariert werden. inline
Die inline-Deklaration ist eine Empfehlung an den Compiler, Funktionsaufrufe durch direkten, äquivalenten Code zu ersetzen.
159
Sprachkonzepte und Syntax
Spezielle Spezifizierer für Methoden static
static-Methoden sind klassenspezifisch, aber nicht instanzspezifisch.
const
const-Methoden können nur mutable-Instanzvariablen der Klasse verändern. Zudem können für const-Instanzen nur const-Methoden der Klasse aufgerufen werden. (Einige Compiler geben bei Zuwiderhandlung allerdings nur eine Warnung aus.) Das Schlüsselwort const wird hinter die Parameterliste gesetzt.
volatile
volatile-Methoden. Für volatile-Instanzen können nur volatile-Methoden der Klasse aufgerufen werden. Das Schlüsselwort volatile wird hinter die Parameterliste gesetzt.
virtual
virtual-Methoden werden bei der Vererbung eingesetzt und schalten die späte Bindung für eine Methode ein.
Methoden können nicht als extern deklariert werden. extern ist im Grunde ein Bezug auf eine globale Deklaration, die üblicherweise in externen Bindung resultiert. Dies ist für Methoden weder erlaubt noch sinnvoll.
Beispiele extern int func1(); static int func2(); inline int func3(); class demo { static int func4(); int func5() const; int func6() volatile; virtual int func7(); };
Verweise Siehe Funktionsdeklaration und -definition Siehe Inline-Funktionen Siehe Kategorie Klassen, Methoden
Überladung Beschreibung C++ erlaubt die Definition mehrerer Funktionen (oder Operatoren) gleichen Namens, sofern die Funktionen sich in Anzahl oder Typ der Parameter unterscheiden. Die Definition mehrerer Funktionen gleichen Namens bezeichnet man als Überladung.
160
Überladung
Realisierung und Auflösung Prinzipiell gilt für C++ ebenso wie für C, daß Objekte über einen eindeutigen Namen adressiert werden können. C++ lockert dieses Dogma für den Programmierer etwas auf, indem Funktionenbezeichner automatisch um codierte Informationen zu den Parametern respektive Operanden erweitert werden. Auf diese Weise kann der Programmierer identische Bezeichner vergeben, während der Compiler mit eindeutigen Namen arbeitet. Dies erklärt auch, warum beispielsweise Variablen oder Funktionen ohne Parameter nicht überladen werden können. Umgekehrt muß der Compiler in der Lage sein, beim Aufruf einer überladenen Funktion anhand der übergebenen Argumente die aufzurufende Funktion festzustellen. Man bezeichnet dies als die Auflösung einer Überladung. Zuerst sucht der Compiler dabei nach einer überladenen Funktion, die exakt in den Datentypen ihrer Parameter mit den Datentypen der Argumente aus dem Funktionsaufruf übereinstimmt.
● ●
Ein Typ T kann nicht von seiner Referenz T& unterschieden werden. Ein Typ T kann nicht von const T oder volatile T unterschieden werden, wohl aber T* von const T*.
Findet der Compiler keine exakte Übereinstimmung, sucht er die bestmögliche Übereinstimmung. ●
●
●
Zuerst wendet der Compiler die arithmetischen Promotionen auf die Argumente an. Dann versucht er es mit Standardkonvertierungen (double zu int, int zu double, Abgeleitet* zu Basis*, etc.), Am Schluß berücksichtigt er noch benutzerdefinierte Konvertierungen für Klassen.
Warnung Überladene Funktionen müssen in einem gemeinsamen Gültigkeitsbereich liegen, sonst findet keine Überladung, sondern die übliche Verdeckung statt. Der Rückgabetyp spielt bei der Überladung keine Rolle – überladene Funktionen können also nicht anhand des Rückgabetyps unterschieden werden. Findet der Compiler für ein Set von Argumenten zwei oder mehr überladene Versionen, die alle gleich gut zu den Argumenten passen, gibt er eine Fehlermeldung aus, daß er die Überladung nicht eindeutig auflösen kann. void func(char) {}; void func(short) {}; void call(char c, int i, short s)
161
Sprachkonzepte und Syntax { func(c); func(s); func(i); }
// ruft func(char) auf // ruft func(short) auf // Fehler: ambiguity
Das Problem liegt hier darin, daß es keine überladene Funktion für den Datentyp int gibt, daß aber sowohl char als auch short durch integrale Promotion in int verwandelt werden können.
Verweise Siehe Kategorie Variablen und Datentypen, Gültigkeitsbereiche Siehe Überladen, Überschreiben, Verdecken
Überladung von Funktionen Beschreibung C++ erlaubt die Definition mehrerer Funktionen (oder Operatoren) gleichen Namens, sofern die Funktionen sich in Anzahl oder Typ der Parameter unterscheiden. Die Definition mehrerer Funktionen gleichen Namens bezeichnet man als Überladung.
Anwendung Die Überladung von Funktionen wird eingesetzt, um das Verhalten einer Routine an die ihr übergebenen Parametertypen anzupassen. Der Vorteil für den Programmierer besteht darin, daß er nicht mehrere Funktionsnamen für eine Routine zu vergeben braucht, und der Compiler die Aufgabe übernimmt, die Parameter zu checken und die richtige Version der Routine aufzurufen.
Warnung Überladene Funktionen müssen in einem gemeinsamen Gültigkeitsbereich liegen, sonst findet keine Überladung, sondern die übliche Verdeckung statt.
Beispiele int max(int a, int b) { return ( a > b ? a : b); } const char* max(const char* s1, const char* s2) { return (strcmp(s1,s2) ? s1 : s2); } int max(int a, char* s) { return ( a > strlen(s) ? a : strlen(s)); }
162
Überladung von Methoden
Überladung von Methoden Beschreibung Methoden werden ebenso überladen, wie oben für Funktionen im Dateibereich ausgeführt wurde. Besonderheiten ergeben sich jedoch für die Überladung ererbter Methoden, da Basisklasse und abgeleitete Klasse unterschiedliche Klassenbereiche darstellen.
Warnung ● ●
●
Ererbte Funktionen können in abgeleiteten Klassen nicht überladen werden. In der abgeleiteten Klasse deklarierte Funktionen verdecken gleichnamige Funktionen der Basisklassen, die weiterhin über den Bereichsoperator aufrufbar sind. Soll eine Überladung simuliert werden, müssen die Versionen der Basisklasse zuerst überschrieben werden.
Beispiele class basis { public: void func(char * s); void func(char c); }; class abgeleitet : public basis { public: void func(int wert); } int main() { class abgeleitet abg; abg.func("Test"); // Fehler abg.func('c'); // Fehler abg.func(3.2); // wird als 3 interpretiert abg.basis::func("Test"); return 0; }
Überladen, Überschreiben, Verdecken Überladene Funktionen ●
befinden sich im gleichen Gültigkeitsbereich.
●
sind anhand ihrer Parameter unterscheidbar.
●
dienen zur Anpassung einer Funktion an verschiedene Parametertypen.
163
Sprachkonzepte und Syntax
Überschriebene Methoden ●
liegen in verschiedenen Gültigkeitsbereichen, wobei die überschriebene Methode in einer Basisklasse und die überschreibende Methode in einer von der Basisklasse abgeleiteten Klasse definiert ist.
●
haben identische Namen und Parametertypen.
●
werden üblicherweise als virtual deklariert.
●
dienen der Implementierung polymorphen Verhaltens in Klassenhierarchien.
●
sind über den Gültigkeitsbereichoperator weiterhin erreichbar.
Verdeckte Methoden ●
liegen in verschiedenen Gültigkeitsbereichen, wobei die überschriebene Methode in einer Basisklasse und die überschreibende Methode in einer von der Basisklasse abgeleiteten Klasse definiert ist.
●
haben nicht-identische Namen und Parametertypen.
●
sind über den Gültigkeitsbereichoperator weiterhin erreichbar.
Klassen Klassen Beschreibung C++ basiert auf der Sprache C. Die vollständige Aufwärtskompatibilität garantiert, daß C++ die Syntax von C vollkommen einschließt. Objektorientiertes Programmieren ist daher nicht gleichzusetzen mit Programmieren in C++, sondern beginnt erst mit der Verwendung der objektorientierten Konzepte, um die C++ den C-Standard erweitert. Die wichtigsten dieser Konzepte (Kapselung, Vererbung, Polymorphie, Templates) gehen zurück auf den neuen Datentyp class.
Anwendung Klassen dienen dazu, Datenelemente und Elementfunktionen (Methoden) in einem Datentyp zu kapseln. Sie gestatten es, verschiedene Zugriffsberechtigungen für die einzelnen Elemente zu vergeben, und können durch Vererbung Klassenhierarchien aufbauen.
164
Klassen
Syntax klassentyp klassenname { ELEMENTLISTE } instanzen; ●
● ●
[: BASISKLASSEN]
klassentyp: Als Klassentyp können neben dem Schlüsselwort class auch struct und union verwendet werden. Die Verwendung der Schlüsselwörter struct und union beruht darauf, daß in C++ Strukturen und Unions spezielle Klassen darstellen. Dies erleichtert die Verwendung von C-Modulen in C++-Programmen, sollte den Programmierer aber nicht dazu verführen, beide Konzepte zu vermischen. Es ist empfehlenswert, Klassen stets als class zu deklarieren und Unions und Strukturen nur gemäß der C-Konvention zu verwenden. klassenname: Name der zu deklarierenden Klasse. :BASISKLASSEN: Durch Kommata getrennte Liste von Klassen, deren Elemente übernommen (geerbt) werden. Der Zugriff der abgeleiteten Klasse auf die ererbten Elemente kann durch die Spezifizierer public, protected und private modifiziert werden. Ein Eintrag in dieser Liste hat folgende Syntax: [virtual] [public, protected, private] basisklassenname
●
●
ELEMENTLISTE: In der Elementliste werden Datenelemente und Methoden der Klasse deklariert und unter Umständen auch schon definiert. Der Zugriff auf die Elemente kann durch die Spezifizierer public, protected und private geregelt werden. Alle Elemente haben ihre Klasse als Gültigkeitsbereich. instanzen: Hinter der abschließenden Klammer können durch Kommata getrennt bereits Instanzen, also Variablen der Klassentyps, eingerichtet werden.
Warnung ●
●
Eine Basisklasse muß deklariert sein, bevor sie zur Vererbung herangezogen werden kann! Ein Zugriffsspezifizierer gilt für alle folgenden Klassenelemente bis zum Auftreten eines neuen Zugriffsspezifizierers.
●
Zugriffsspezifizierer können nicht kombiniert werden.
●
Zugriffsspezifizierer dürfen in der Klassendeklaration mehrfach auftauchen.
●
Klassenelemente am Anfang der Deklaration, für die kein Zugriffsspezifizierer angegeben wurde, gelten automatisch als private.
Beispiele class beispiel { int wert; public: beispiel() {wert = 1;}; protected:
// private // Konstruktor
165
Sprachkonzepte und Syntax int gebe_wert_aus() {return wert;}; private: int loesche_wert(); };
Verweise Siehe Variablen und Datentypen, Strukturen Siehe Variablen und Datentypen, Unions Siehe Vererbung und Polymorphie
Klassen und OOP Beschreibung Das Konzept, welches hinter den Klassen und allgemein der objektorientierten Programmierung steht, beruht auf der alltäglichen Erfahrung, daß wir die Objekte der realen Welt nach zwei Maßstäben beurteilen. Einmal nach Merkmalen wie Form und Farbe, zum anderen nach bestimmten »Verhaltensweisen«, die ein Objekt aufweist, und die beispielsweise festlegen, wie man mit ihm umzugehen hat. In der objektorientierten Terminologie entsprechen die Merkmale den Datenelementen und die Verhaltensweisen den Methoden (Elementfunktionen).
Beispiel Objekte, die gleiche Merkmale und Verhaltensweisen aufweisen, können in einer Klasse zusammengefaßt werden. So könnte eine Klasse videorecorder folgendermaßen aussehen: class videorecorder { /* Merkmale */ char *hersteller; int anzahl_videokoepfe; boolean zeitlupe, longplay, zweikanalton; public: /* Verhaltensweisen */ void anschalten(); void abspielen(); void aufnahme(); void in_zeitlupe_abspielen(); void ausschalten(); };
Bei der Bildung einer Variablen der Klasse – im folgenden als Instanzbildung bezeichnet – werden den Merkmalen Werte zugewiesen. Auf diese Weise entsteht durch Spezialisierung eine Instanz, die nur noch einen besonderen Videorecorder repräsentiert. (Ein weiteres Beispiel wäre die Klasse Mensch und ihre Instanzen, nämlich jeder einzelne von uns – die Individuen. Eine Geburt wäre folglich eine Instanzbildung.)
166
Kapselung
Verweise Siehe Kapselung Siehe Instanzbildung
Kapselung Beschreibung Die Zusammenfassung von Datenelementen und Methoden wird als Kapselung bezeichnet. Zudem verbinden sich mit dem Begriff noch zwei weitere Designkriterien: Information hiding und Abgeschlossenheit
Information hiding Klassen sollten vornehmlich über ihre Methoden angesprochen werden. Diese Funktionen bilden die Schnittstelle zwischen Klasse und Programm. (Die genaue Festlegung der Schnittstelle erfolgt über die Vergabe von Zugriffsspezifizierern.) Nach der Implementierung der Klasse ist es für den Programmierer wichtig, daß er die Klasse nur noch über ihre Schnittstelle einzusetzen braucht, ohne sich weitere Gedanken um deren Implementierung machen zu müssen. Für die fehlerlose Bearbeitung der Funktionsaufrufe und das korrekte Verhalten der Klasse, trägt diese selbst Sorge. Wurde zum Beispiel eine Klasse videorecorder implementiert, um Videorecorder über den Computer anzusteuern, braucht der Programmierer nur noch eine Instanz der Klasse zu bilden und kann danach die Instanz einsetzen wie einen realen Videorecorder: my_recorder.anschalten(); my_recorder.in_zeitlupe_abspielen(); my_recorder.ausschalten();
Die Ansteuerung des Videorecorders über die parallele Schnittstelle oder die Kontrolle, ob der Videorecorder überhaupt über Zeitlupe verfügt, nimmt die Klasse selbst vor, d. h.,sie versteckt solche Details vor dem Programmierer und entlastet ihn dadurch.
Abgeschlossenheit Aus der Forderung des Information hidings ergibt sich das Kriterium der Abgeschlossenheit. Die Schnittstelle einer Klasse sollte möglichst vollständig sein, in dem Sinne, daß die Klasse auch sinnvoll eingesetzt werden kann. Eine Klasse videorecorder, die über keine Funktion in_zeitlupe_abspielen() verfügt, wäre von vornherein zur Bedienung von Videorecordern mit Zeitlupe ungeeignet.
167
Sprachkonzepte und Syntax
Umgekehrt sollten in einer Klasse nur Daten und Funktionen enthalten sein, die die Objekte der Klasse auch wirklich kennzeichnen. Eine Methode berechne_bruch() würde in der Klasse videorecorder keinen Sinn machen.
Verweise Siehe Praxisteil, Kategorie Klassen
Datenelemente Beschreibung Variablen, die innerhalb einer Klasse definiert werden, stellen die Datenelemente der Klasse dar.
Anwendung Wird eine Instanz von der Klasse gebildet, wird der Instanz Speicher für die Datenelemente zur Verfügung gestellt. Jede Instanz hat also ihre eigene Kopie der Datenelemente, die man daher auch Instanzvariablen nennt. Gleichzeitig wird die Instanzbildung dazu genutzt, die Datenelemente zu initialisieren. Diese Aufgabe übernimmt der Konstruktor. Klassen stellen einen eigenen Gültigkeitsbereich dar, d. h., die Instanzvariablen verfallen mit ihrer Instanz (wofür der Destruktor Sorge trägt), und Namen aus anderen Gültigkeitsbereichen werden verdeckt. Neben der Deklaration von Datenelementen bekannter Typen ist es auch möglich, innerhalb einer Klasse eine lokale Typdefinition vorzunehmen.
Warnung Datenelemente ● ●
●
können als public, protected oder private deklariert werden. werden bei der Instanzbildung eingerichtet und zusammen mit der Instanz aufgelöst. können als static, const, volatile oder mutable deklariert werden.
Beispiele class demo { int anzahl; int *feld; public: char *str; ...
168
// private Daten
// public Daten
Statische Datenelemente
Verweise Siehe Konstruktor und Destruktor Siehe Zugriffsspezifizierer
Statische Datenelemente Beschreibung Statische Datenelemente werden bei der Instanzbildung nicht kopiert. Statt dessen greifen alle Instanzen auf die gleiche Variable zu.
Anwendung Statische Elemente müssen außerhalb der Klasse, im umgebenden namespace (oder Dateibereich), definiert werden und können nicht nur über Instanzen, sondern auch über den Klassennamen angesprochen werden:
Warnung Statische Elemente können nicht als auto, register oder extern deklariert werden.
Beispiele class Demo { public: static int instanzenzaehler; Demo() {instanzenzaehler++;} ~Demo() {instanzenzaehler--;} }; int Demo::instanzenzaehler = 0; int main() { Demo lok; cout << Demo::instanzenzaehler << endl; cout << lok.instanzenzaehler << endl; return 0; }
Verweise Siehe Statische Methoden
169
Sprachkonzepte und Syntax
Konstante Datenelemente Beschreibung Datenelemente, die als const deklariert werden, können nach der Instanzbildung nicht mehr verändert werden.
Warnung const-Datenelemente müssen in der Konstruktorliste initialisiert werden.
Beispiele class Demo { public: const int j; Demo() : j(2) { ... };
// konstante Instanzvariable // Initialisierung
Verweise Siehe Konstante Methoden Siehe Kategorie Konstanten
Klasseninstanzen als Datenelemente Beschreibung Instanzvariablen können auch vom Datentyp einer Klasse sein, man spricht dann auch von eingebetteten Objekten.
Anwendung Eingebettete Objekte werden im Zuge der Instanzbildung des ihnen übergeordneten Objekts erzeugt und mit diesem aufgelöst. Zur Einrichtung des eingebetteten Objekts muß ein Konstruktor des eingebetteten Objekts aufgerufen werden. Dies kann ein Standardkonstruktor sein, der Programmierer muß sich dann nicht um die Einrichtung kümmern. Es kann auch ein Konstruktor mit Argumenten sein, der Programmierer muß dann den Konstruktor des übergordneten Objekts um eine Konstruktorliste erweitern, in der der Konstruktor des eingebetteten Objekts aufgerufen wird. (Beachten Sie, daß der Konstruktor über den Objektnamen und nicht wie bei Basisklassen über den Klassennamen aufgerufen wird.) Zur Auflösung des eingebetteten Objekts wird sein Destruktor aufgerufen.
170
Klasseninstanzen als Datenelemente
Warnung ●
●
●
Eine Klasse kann keine Instanzen von Klassen als Element enthalten, wenn diese Klassen nicht zuvor definiert wurden. Da die Definition einer Klasse mit dem Semikolon abschließt, folgt daraus, daß eine Klasse keine Instanz ihres eigenen Typs als Element enthalten kann. Wohl aber ist es möglich, Zeiger oder Referenzen auf nicht definierte Klassen zu definieren, sofern der Klassenname bekannt ist. Die umliegende Klasse hat keine besonderen Zugriffsrechte auf die Elemente des eingebetteten Objekts.
Beispiele Zeiger auf Objekte von noch nicht definierten Klassen: class demo; class beispiel { int a; class beispiel falsch;
// kann von Compiler nicht // aufgelöst werden
class beispiel* richtig; class demo& auch_richtig; };
Instanziierung eingebetteter Objekte durch den Standardkonstruktor: class A { public: int i; A() {i = 1;} // Standardkonstruktor }; class B { public: int n; A obj; B() {n = 2;} };
Instanziierung eingebetteter Objekte über Konstruktorliste: class A { public: int i; A(int i_par) {i = i_par;} }; class B { public: int n; A obj; B(int p1, int p2) : obj(p1) {n = p2;} };
171
Sprachkonzepte und Syntax
Verweise Siehe Lokale und verschachtelte Klassen Siehe Konstruktor Siehe Vererbung und Polymorphie, Vererbung versus Einbettung
Lokale und verschachtelte Klassen Beschreibung Klassen, die im Anweisungsteil einer Funktion definiert werden, werden als lokale Klassen bezeichnet. Klassen, die im Definitionsteil einer Klasse definiert werden, werden als verschachtelte oder eingeschlossene Klassen bezeichnet.
Warnung ●
● ●
Eine eingeschlossene Klasse kann Typen und statische Elemente der einschließenden Klasse direkt benutzen. Eingeschlossene Klassen dürfen keine statischen Elemente enthalten. Die eingeschlossene Klasse hat keine besonderen Zugriffsrechte auf die Elemente der einschließenden Klasse, d. h., sie kann nur über Instanzen der eingeschlossenen Klasse auf deren Elemente zugreifen (Ebenso hat auch die einschließende Klasse keine besonderen Zugriffsrechte auf die eingeschlossene Klasse).
Beispiele int i; class global { static int n; public: int i; static float f; class intern { void func(global i = 3; f = 3; ::i = 3; glob.i = 3; n = 3; } ...
172
&glob) { // Fehler // korrekt // i aus Dateibereich // i aus Klasse global // Fehler
Methoden
Verweise Siehe Klasseninstanzen als Datenelemente
Methoden Beschreibung Funktionen, die als Elemente einer Klasse definiert werden, bezeichnet man als Methoden.
Anwendung Methoden können sowohl innerhalb als auch außerhalb der Klassendeklaration definiert werden. ●
●
Methoden, die innerhalb der Klassendefinition definiert werden, werden automatisch als inline interpretiert. Auch wenn das Schlüsselwort inline nur als Empfehlung für den Compiler gedacht ist, ergibt sich daraus, daß innerhalb einer Klassendefinition nur kurze Funktionen definiert werden sollten. Zum einen, um Speicherplatz zu sparen, zum anderen, um die Klassendefinition übersichtlich zu halten. Bei der Definition außerhalb der Klassendefinition muß der Klassenname mit dem Bereichsoperator vorangestellt werden.
Definition innerhalb der Klassendefinition: class Demo { int wert; public: Demo() {wert = 1;} int gebe_wert_aus() {return wert;} };
Definition außerhalb der Klassendefinition: class Demo { int wert; public: Demo(); int func(); }; inline Demo::Demo() int Demo::func() { ... }
{wert = 1;}
173
Sprachkonzepte und Syntax
Warnung Methoden ●
können als public, protected oder private deklariert werden.
●
haben Zugriff auf alle Elemente der Klasse.
●
stellen neben Friends die einzige Möglichkeit dar, auf private Elemente ihrer Klasse zuzugreifen.
●
können als virtual deklariert werden.
●
können als inline deklariert werden.
●
●
●
können als const deklariert werden. Methoden, die als const deklariert sind, sind die einzigen Methoden, die von const-deklarierten Instanzen aufgerufen werden können. (Das Schlüsselwort const wird der Parameterliste nachgestellt). können als volatile deklariert werden. Methoden, die als volatile deklariert sind, sind die einzigen Methoden, die von volatile-deklarierten Instanzen aufgerufen werden können. (Das Schlüsselwort volatile wird der Parameterliste nachgestellt). können nur von Methoden der gleichen Klasse überladen werden.
Verweise Siehe Überladung Siehe Kategorie Vererbung und Polymorphie, Überschreibung und virtuelle Methoden
Statische Methoden Beschreibung Statische Methoden sind Methoden, die mit dem Schlüsselwort static deklariert werden. Statische Methoden sind ebenso wie statische Datenelemente klassen- und nicht instanzspezifisch. Statische Methoden besitzen keinen this-Zeiger und können daher nur andere statische Elemente (Daten wie Methoden) aufrufen.
Anwendung Statische Methoden können über Instanzen oder den Klassennamen aufgerufen werden.
Warnung Statische Methoden können nicht als virtual deklariert werden.
174
Konstante Methoden
Beispiele class Demo { private: static int standardwert; public: static void std_setzen(int i) { if( i > 0 && i < 100 ) standardwert = i; } }; int Demo::standardwert = 1; int main() { Demo obj1, obj2; Demo::std_setzen(33); return 0; }
Verweise Siehe Statische Datenelemente
Konstante Methoden Beschreibung Konstante Methoden sind Methoden, die mit dem Schlüsselwort const deklariert werden. Methoden, die als const deklariert werden, können Elementdaten nicht ändern (es sei denn, diese sind explizit als mutable deklariert) und können Zeiger oder Referenzen auf Elementdaten nur als const zurückgeben.
Anwendung const-Methoden sind die einzigen Methoden, die von const-deklarierten Instanzen
aufgerufen werden können. (Manche Compiler geben bei Zuwiderhandlung allerdings nur eine Warnung aus). Das Schlüsselwort const wird der Parameterliste nachgestellt.
Beispiele class Demo { public: int i; const int j; mutable int n; Demo() : j(2) {} // Initialisierung void inkr() const { i++; // Fehler
175
Sprachkonzepte und Syntax j++; n++; } void func() {...}
// Fehler // ok
};
Konstante Klasseninstanzen: const demo obj2; obj2.func(); obj2.inkr();
// Fehler // o.k.
Verweise Siehe Konstante Datenelemente
Zugriffsspezifizierer Beschreibung Die Zugriffsspezifizierer public, private und protected regeln, wie von innerhalb und außerhalb der Klasse auf deren Elemente zugegriffen werden kann.
Anwendung Die Zugriffsspezifizierer sind daher ein wichtiges Instrument der Kapselung. Erst durch diese Spezifizierer kann eine Klasse vor Mißbrauch geschützt werden. Die Zugriffsspezifizierer werden sowohl bei der Klassendefinition wie auch bei der Vererbung eingesetzt.
Zugriffsspezifizierer bei der Klassendefinition Innerhalb der Klassendefinition kann man die Zugriffsspezifizierer verwenden, um nachfolgend definierten Klassenelementen einer Zugriffsebene zuzuordnen. class demo { ... public: ... protected: ... private: ... };
176
Zugriffsspezifizierer
Spezifizierer
Bedeutung
public
Das Element kann von jeder Methode der eigenen und abgeleiteten Klassen benutzt werden. Der Zugriff von außen erfolgt über den Namen der Instanz und einem der Zugriffsoperatoren:
instanz_name.elementname referenz_der_instanz.elementname zeiger_auf_instanz->element_name private
Das Element kann nur von Methoden und Friends der Klasse, in der es deklariert wurde, benutzt werden.
protected
Es gilt das gleiche wie für private. Zusätzlich ist jedoch die Benutzung durch Methoden von Klassen, die von der deklarierten Klasse abgeleitet sind, möglich. Gibt es keine abgeleiteten Klassen, sind private und protected identisch.
Zugriffsspezifizierer bei der Vererbung Im Zuge der Vererbung kann man die Zugriffsspezifizierer verwenden, um den Zugriff über Instanzen der abgeleiteten Klasse auf die geerbten Elemente zu verschärfen. class abgeleitet : public basisklasse { ... Spezifizierer
Bedeutung
public
Für die geerbten Elemente gelten die gleichen Zugriffsrechte, die in der Basisklasse festgelegt wurden.
private
Alle geerbten Elemente sind von außen als private anzusehen.
protected
public-Elemente werden als protected-Elemente angesehen.
Warnung ●
Ein Zugriffsspezifizierer gilt für alle folgenden Klassenelemente bis zum Auftreten eines neuen Zugriffsspezifizierers.
●
Zugriffsspezifizierer können nicht kombiniert werden.
●
Zugriffsspezifizierer dürfen in der Klassendeklaration mehrfach auftauchen.
●
●
Klassenelemente am Anfang der Deklaration, für die kein Zugriffsspezifizierer angegeben wurde, gelten automatisch als private. Zu den Zugriffsspezifizierern im weiteren Sinne zählt auch das Schlüsselwort friend (siehe Zugriff von außerhalb der Klasse).
177
Sprachkonzepte und Syntax
Zugriff auf Klassenelemente Prinzipiell werden Klassen so ausgelegt, daß sie ihre Datenelemente durch die Deklaration als private verstecken und Manipulationen nur über eine Auswahl bestimmter als public deklarierter Methoden ermöglichen. In der Praxis gibt es durch die Verwendung der Zugriffsspezifizierer bei der Klassendeklaration sowie bei der Vererbung und des Zugriffsspezifizierers friend eine Vielzahl von Situationen mit unterschiedlichen Zugriffsberechtigungen. Um den Überblick zu behalten, werden diese Situationen in den folgenden Abschnitten zusammengefaßt.
Verweise Siehe nachfolgende Abschnitte Siehe Vererbung und Polymorphie, Zugriffsbeschränkung bei der Vererbung
Zugriff innerhalb der Klasse el_name
Beschreibung Zugriff innerhalb einer Klasse bedeutet, daß innerhalb des Funktionskörpers von Methoden auf andere Elemente (Daten oder Funktionen) der eigenen Klasse zugegriffen wird. Dies ist der innerste Bereich, der praktisch keinen Beschränkungen unterliegt. Der Zugang zu den Elementen erfolgt direkt über deren Namen.
Warnung ●
●
●
Methoden, die als const deklariert sind, können auf alle Datenelemente zugreifen, aber nur mutable-Datenelemente verändern. Statische Methoden können nur statische Elementdaten direkt ansprechen. (Dies liegt daran, daß statische Methoden keinen this-Zeiger besitzen.) Nichtstatische Elemente müssen über eine existierende Instanz (die beispielsweise als Argument übergeben werden kann) angesprochen werden. Für geerbte Elemente aus Basisklassen gelten die Zugriffsrechte, die in der Basisklasse festgelegt wurden.
Beispiele class demo { private: int wert1, wert2; int func1() {return wert1*wert2;}
178
Zugriff von außerhalb der Klasse public: ... void func2(int i) { wert1=i+1; wert2 = func1(); } };
Verweise Siehe nachfolgende Abschnitte Siehe this-Zeiger
Zugriff von außerhalb der Klasse objekt.el_name
Beschreibung Von außerhalb kann auf Elemente der Klasse T nur über eine Instanz der Klasse T in Verbindung mit einem der Operatoren . -> .* ->* oder über Friends der Klasse zugegriffen werden.
Zugriff über Instanz
.,->
Der Zugriff unterliegt den Einstellungen durch die Modifizierer public, protected und private, d. h. Elemente, die in der Klasse als protected oder private deklariert sind (dies gilt auch für ererbte Elemente), können auf diese Weise nicht angesprochen werden. class demo { int wert1; // standardmäßig private int func1(); public; int wert2; int func2(); }; int main() { class demo d, *ptr_d; int dummy; dummy = d.wert1; // Fehler dummy = ptr_d->func1(); // Fehler dummy = d.wert2; dummy = ptr_d->func2(); }
179
Sprachkonzepte und Syntax
Zugriff über Zeiger auf Klassenelemente
.*,->*
Auch hier gilt, daß nur auf public-Elemente zugegriffen werden kann. Wie die Syntax zur Definition, Initialisierung und Dereferenzierung von Zeigern auf Klassenelemente aussieht, entnehmen Sie bitte dem Abschnitt Operatoren, Datenzugriff.
Zugriff über Friends
friend
Manchmal ist es sinnvoll, die strengen Regeln der Zugriffsberechtigungen durch public, protected und private zu umgehen und einer beliebigen Funktion (also nicht bloß Klassenfunktionen) Zugriff auf protected oder private Elemente zu erteilen. Das Schlüsselwort friend ermöglicht dies. Um eine Friend-Funktion einzurichten, bedarf es zweierlei: ●
Erstens muß die Funktion in der Klasse, auf die sie Zugriff haben soll, als friend deklariert werden, und
●
Zweitens muß eine Referenz oder Instanz der Klasse an die Funktion übergeben werden.
#include using namespaces std; class demo { int wert; friend int func(demo&); // Friend-Deklaration }; // Friend-Funktion int func(demo& objekt) { return objekt.wert; } int main() { class demo d; cout << func(d); return 0; }
Warnung ●
●
Beachten Sie die Feinheit, daß die Deklaration als friend in der Klasse vorgenommen werden muß. Die Klasse bestimmt also selbst, welche Funktionen friend sind und welche nicht, und kann ihre Daten immer noch vor unkontrolliertem Gebrauch schützen. Private Elemente, die über keine public-Methoden verfügen, können über friend-Funktionen erreicht werden.
180
Zugriff auf Basisklassenelemente
●
Neben der Deklaration einzelner Funktionen als friend können Sie auch sämtliche Funktionen einer Klasse auf einen Schlag als friend deklarieren, indem Sie die ganze Klasse als friend deklarieren.
Verweise Siehe Kategorie Operatoren, Datenzugriff
Zugriff auf Basisklassenelemente Beschreibung Für abgeleitete Klassen muß man zwischen den eigenen und den geerbten Elementen sowie dem Zugriff aus den Methoden der abgeleiteten Klasse oder dem Zugriff von außen unterscheiden.
Zugriff aus der abgeleiteten Klasse Die Methoden, die in der abgeleiteten Klasse deklariert sind, können sowohl auf die eigenen als auch die geerbten Elemente direkt über den Elementnamen zugreifen. Während der Zugriff auf die eigenen Elemente jedoch keinerlei Beschränkungen unterliegt, wird der Zugriff auf die geerbten Elemente durch die Zugriffsspezifizierer aus der Basisklassendeklaration geregelt. Eine Methode der abgeleiteten Klasse hat Zugriff auf public- und protected-Elemente, nicht aber auf private-Elemente. Geerbte private-Elemente können in den Methoden der abgeleiteten Klasse nur auf dem Umweg über geerbte public- oder protected-Methoden angesprochen werden. class Basis { int basis_priv; protected: int basis_prot; public: int basis_pub; }; class Abgeleitet : private Basis { int abg_priv; public: void abg_func(int n) { basis_pub = n; basis_prot = n; //basis_priv = n; // Fehler, kein Zugriff } };
181
Sprachkonzepte und Syntax
Zugriff von außen Nach außen gibt es keinen Unterschied zwischen den eigenen und den geerbten Elementen einer Klasse. Wichtig ist nur, welche Zugriffsrechte die abgeleitete Klasse für die geerbten Elemente nach außen weitergibt. Grundsätzlich gelten die Zugriffsrechte, die in der Basisklasse, aus der die Elemente stammen, spezifiziert wurden. Man kann aber durch die Zugriffsspezifizierer der Vererbung die Zugriffsrechte verschärfen (siehe Abschnitt Vererbung und Polymorphie, Zugriffsbeschränkung bei der Vererbung). Für einzelne Elemente kann man die bei der Vererbung eingeengten Zugriffsrechte durch Redeklaration oder mit Hilfe des Schlüsselworts using wieder auflockern.
Zugriff auf verdeckte/überschriebene Methoden Wird eine geerbte Methode in der abgeleiteten Klasse durch Deklaration einer gleichnamigen Methode mit gleicher Signatur überschrieben oder durch die Deklaration einer gleichnamigen Methode mit abweichender Signatur verdeckt, kann man mit Hilfe des Gültigkeitsbereichsoperators weiterhin auf die Version der Basisklasse zugreifen. Dies funktioniert ebenso gut von innerhalb wie von außerhalb der Klasse. class Basis { public: void func(int) {cout << "Basis-Version" << endl;}; }; class Abgeleitet : public Basis { public: void func(char*) { // Zugriff auf verdeckte Basisklassenversion Basis::func(3); cout << "abgel. Version" << endl; }; }; int main() { class Abgeleitet abg; // Zugriff auf verdeckte Basisklassenversion abg.Basis::func(3); // ok return 0; }
Verweise Siehe Kategorie Vererbung und Polymorphie, Zugriffsbeschränkung und Zugriffsrechte
182
Der Konstruktor
Der Konstruktor klassenname()
{}
Beschreibung Jede Instanzbildung eines Klassenobjekts ist mit dem Aufruf eines Konstruktors verbunden.
Anwendung Der Programmierer kann im Anweisungsteil des Konstruktors für die Initialisierung der Datenelemente und für Eingangsarbeiten (beispielsweise der Reservierung von dynamischem Speicher) sorgen. Durch Definition eines oder mehrerer eigener Konstruktoren (Überladung) kann die Instanzbildung gesteuert und an die Übergabe verschiedener Argumenttypen angepaßt werden.
Der Standardkonstruktor Um sicherzustellen, daß jede Klasse über einen Konstruktor verfügt, weist der Compiler Klassen ohne explizit deklarierten Konstruktor einen Standardkonstruktor zu (Konstruktor, der ohne Argument aufgerufen werden kann). Wird ein Konstruktor explizit definiert, steht der Standardkonstruktor des Compilers nicht mehr zur Verfügung; der Programmierer kann aber einen eigenen Standardkonstruktor definieren. Der Standardkonstruktor vereinfacht die Einrichtung eingebetteter und geerbter Objekte sowie von Arrays von Klassenobjekten, die ansonsten nur durch Angabe von Konstruktorlisten bzw. Initialisierungslisten zu bewerkstelligen ist.
Warnung ●
Ein Konstruktor hat immer den gleichen Namen wie seine Klasse.
●
Ein Konstruktor hat keinen Rückgabewert (auch nicht void).
●
●
Konstruktoren können nicht als virtual, static, const, mutable oder volatile deklariert werden. Konstruktoren können überladen werden. Die Überladung wird meist dazu genutzt, dem Konstruktor Argumente für die Initialisierung der Datenelemente der Klasse zu übergeben.
●
Konstruktoren werden nicht vererbt.
●
Es können keine Zeiger auf Konstruktoren definiert werden.
●
Konstruktoren können nach der Instanzbildung nicht mehr aufgerufen werden.
183
Sprachkonzepte und Syntax ●
Konstruktoren, die mit genau einem Argument aufgerufen werden können, können implizite Konvertierungen vom Argumenttyp in den Typ ihrer Klasse durchführen. Der Compiler nimmt diese Konvertierung in das Repertoire seiner Standardkonvertierungen auf. Um ungewollte Standardkonvertierungen zu verhindern, kann ein solcher Konstruktor als explicit deklariert werden.
Beispiele class demo { int privat; public: demo(int i) {privat = i;} explicit demo(float f) {privat = (int) f;}; }; int main() { class demo instanz(100); class demo instanz2 = 100;//implizite Konvertierung class demo instanz3(3.2); class demo instanz4 = 3.2;//Fehler: explicit class demo instanz5; //Fehler, Standardkonstruktor //nicht mehr verfügbar return 0; }
Verweise Siehe Kopierkonstruktor Siehe Destruktor Siehe Klasseninstanzen als Datenelemente Siehe Kategorie Vererbung und Polymorphie, Vererbung und Konstruktor
Der Kopierkonstruktor klassenbezeichner(klassenbezeichner&)
Beschreibung Der Kopierkonstruktor ist ein Konstruktor, der Instanzen seiner Klasse auf der Grundlage anderer Instanzen der Klasse erzeugt. Die Instanz, die als Grundlage für die neu anzulegende Instanz verwendet werden soll, wird dem Konstruktor als Parameter übergeben.
184
Der Kopierkonstruktor
Anwendung Der Kopierkonstruktor wird aufgerufen, wenn Sie eine Klasseninstanz mit einer anderen Klasseninstanz (der gleichen Klasse) initialisieren. Wurde innerhalb der Klasse kein eigener Kopierkonstruktor deklariert, wird der Standardkopierkonstruktor aufgerufen, der die Klassenelemente 1:1 kopiert. Der Kopierkonstruktor ist eine von zwei Möglichkeiten, Klasseninstanzen zu kopieren. Die zweite Option ist der Zuweisungsoperator =, der ebenfalls als 1:1Kopierer vordefiniert ist und bei Bedarf überladen werden muß. Beachten Sie, daß Kopierkonstruktoren implizit auch bei der Übergabe von Klassenobjekten an Funktionen/Methoden und bei der Zurücklieferung von Klassenobjekten als Rückgabewerte von Funktionen/Methoden aufgerufen werden (sofern nicht Zeiger oder Referenzen verwendet werden).
Warnung 1:1-Kopieren wie vom Standard-Kopierkonstruktor vorgenommen, führt meist zu Fehlern, wenn die Klasse Zeiger enthält. Dies liegt einfach daran, daß für einen Zeiger die in dem Zeiger aufbewahrten Adressen kopiert werden, so daß nach der Kopierinitialisierung die beiden Zeiger der Vorlage und der Kopie auf das gleiche Objekt verweisen.
Beispiele #include using namespace std; class demo { int i; int* p; public: demo(int wert) { i = wert; p= &i; } demo(const demo& inst) { i = inst.i; p = &i; } }; int main() { demo a(3); demo b(a);
// Kopierkonstruktor
// Verwendung des Kopierkonstruktors
return 0; }
185
Sprachkonzepte und Syntax
Verweise Siehe Konstruktor
Der Destruktor ~klassenname()
{}
Beschreibung Destruktoren werden automatisch aufgerufen, wenn Klasseninstanzen ihre Gültigkeit verlieren (beispielsweise bei Verlassen des Gültigkeitsbereichs oder bei delete-Aufrufen für Zeiger auf Klasseninstanzen). Sie lösen die Instanz auf und geben den reservierten Speicher frei.
Warnung Für Destruktoren gelten ähnliche Regeln wie für Konstruktoren: ●
● ●
●
Destruktoren tragen immer den Namen ihrer Klasse, jedoch mit vorangehender Tilde. Ein Destruktor hat keinen Rückgabewert (auch nicht void). Destruktoren können nicht als static, const, mutable oder volatile deklariert werden. Eine Klasse ohne explizit deklarierten Destruktor bekommt einen Standarddestruktor zugewiesen.
●
Destruktoren werden nicht vererbt.
●
Es können keine Zeiger auf Destruktoren definiert werden.
Destruktoren unterscheiden sich von Konstruktoren: ●
Destruktoren können keine Argumente übergeben werden, woraus folgt:
●
Destruktoren können überschrieben, aber nicht überladen werden.
●
Destruktoren können als virtual deklariert werden.
●
Basisklassen sollten im Zweifelsfalle einen virtuellen Destruktor definieren.
●
●
Destruktoren von Klassen, die von Basisklassen mit virtuellem Destruktor abgeleitet sind, sind automatisch virtuell. Destruktoren können explizit aufgerufen werden, was aber selten notwendig ist.
Verweise Siehe Konstruktor
186
Standardmethoden
Standardmethoden Es gibt 5 Methoden, für die der Compiler automatisch Standardversionen erzeugt, wenn die Klasse keine eigenen Implementierungen vorsieht. ●
Standardkonstruktor
●
Destruktor
●
Kopierkonstruktor
●
Zuweisungsoperator
●
Adreßoperator (liefert den Wert des this-Zeigers zurück).
Der this-Zeiger this
Beschreibung Der this-Zeiger ist ein vom Compiler definierter Zeiger, der in allen nicht-statischen Methoden einer Klasse verfügbar ist und jeweils auf das aktuelle Objekt der Klasse verweist, über das die Methode aufgerufen wurde.
Anwendung Werden mehrere Instanzen einer Klasse gebildet, bekommt jede Instanz eine eigene Kopie der Datenelemente der Klasse. Die Methoden der Klasse stehen aber nur einmal im Arbeitsspeicher und werden von allen Instanzen einer Klasse gemeinsam benutzt. Da die Methoden üblicherweise auf den Elementdaten der Klasse operieren und diese instanzspezifisch sein sollen, muß sichergestellt werden, daß die jeweilige Methode nur auf die Daten zugreift, die zur aufrufenden Instanz gehören. Der Compiler weist aus diesem Grunde jeder Instanz einen Zeiger auf sich selbst zu – den this-Zeiger. Bei jedem Aufruf einer Methode wird dieser Zeiger automatisch an die Methode übergeben. Somit ist der this-Zeiger in jeder Methode verfügbar (mit Ausnahme statischer Methoden).
Beispiele #include using namespace std; class demo { public: void func() };
{ cout << this; }
187
Sprachkonzepte und Syntax int main() { class demo d1; cout << "This-Zeiger der Instanz d1 = "; d1.func(); return 0; }
Verweise Siehe Klassen, Methoden
Instanzbildung klassentyp obj;
Beschreibung Als Instanzbildung bezeichnet man die Einrichtung von Klassenobjekten (Variablen eines Klassentyps). Bei der Instanzbildung wird ein konkretes Objekt einer Klasse gebildet, indem den Datenelementen Werte zugeordnet werden. Für die Einrichtung des Speichers und die Initialisierung der Datenelemente ist der Konstruktor verantwortlich, der automatisch bei der Instanzbildung aufgerufen wird.
Bildung konstanter Instanzen Wird eine Instanz bei ihrer Einrichtung als const deklariert, bedeutet dies, daß alle Datenelemente, die in der Klasse nicht explizit als mutable deklariert sind, const sind, d. h., ihr Wert kann nicht verändert werden. Auch können nur als const deklarierte Methoden aufgerufen werden.
Instanzbildung und Arrays Bei der Initialisierung von Klassen-Arrays (Arrays von Klassenobjekten) muß jedes Array-Element durch einen Konstruktor eingerichtet werden. Dies kann durch einen Standardkonstruktor (Konstruktor ohne Parameter) geschehen oder durch eine Initialisierungsliste und expliziten Aufruf für jedes Array-Element:
Beispiele class demo { int privat; public: demo() {}; demo(int i) }; int main() {
188
// Standardkonstruktor {privat = i;}
Vererbung class demo d[2] = {demo(10), demo(100)}; // Einrichtung mit // Initialisierungsliste const class demo e[200]; // Einrichtung mit Standardkonstruktor return 0; }
Verweise Siehe Konstruktor
Vererbung und Polymorphie Vererbung Beschreibung Vererbung ist ein weiteres, ganz wesentliches Konzept der objektorientierten Programmierung, welches es ermöglicht, neue Klassen auf der Basis bereits vorhandener Klassen zu definieren.
Anwendung Die einfachste Form der Vererbung besteht aus einer Basisklasse und einer abgeleiteten Klasse. Durch die Vererbung wird festgelegt, daß die abgeleitete Klasse – zusätzlich zu den in ihrer Definition aufgeführten Eigenschaften (Datenelemente und Methoden) – über sämtliche Elemente der Basisklasse verfügt. Auf diese Weise kann praktisch jede Klasse von jeder anderen Klasse abgeleitet werden, was jedoch nicht immer sinnvoll ist. Zusätzlich zu den mit dem Klassenkonzept und der Kapselung verbundenen Vorteilen bringt die Vererbung weitere Annehmlichkeiten mit sich: ●
●
Gemeinsame Eigenschaften von Klassen brauchen durch Verlegung in eine Basisklasse nur einmal implementiert zu werden, wodurch auch die Wartbarkeit des Quellcodes erleichtert wird. Umgekehrt gewährt die Ableitung von Basisklassen ein einheitliches Erscheinungsbild verwandter Klassen.
Verschiedene Konzepte erlauben es, die Gestaltung von Klassenhierarchien flexibel zu gestalten: ●
Polymorphie (virtuelle Funktionen)
●
Friends
189
Sprachkonzepte und Syntax
Andere Konzepte dienen dazu, die Klassen vor Mißbrauch zu schützen und Klassenhierarchien strenger zu organisieren: ●
Zugriffsmodifizierer
●
abstrakte Klassen
Syntax class klassenname : BASISKLASSENLISTE { Liste der Klassenelemente }; ●
BASISKLASSENLISTE: In der Basisklassenliste werden, durch Kommata voneinander getrennt, die Basisklassen aufgeführt, deren Eigenschaften geerbt werden sollen. Ein Eintrag in dieser Liste hat folgende Syntax: [virtual] [public, protected, private] Basisklassenname
●
●
Die Zugriffsspezifizierer public, protected und private regeln bei der Vererbung nicht den Zugriff aus den Methoden der abgeleiteten Klassen auf die geerbten Elemente (dies wird schon durch die Zugriffsspezifizierer in der Deklaration der Basisklasse geregelt), sondern welche Zugriffsrechte die abgeleitete Klasse für die geerbten Elemente nach außen weitergibt (beispielsweise, wenn über eine Instanz der abgeleiteten Klasse auf diese Elemente zugegriffen wird, oder wenn die abgeleitete Klasse selbst als Basisklasse benutzt wird). Wird kein Zugriffsspezifizierer angegeben, wird die Klasse private vererbt. Das Schlüsselwort virtual ist nur für die Mehrfachvererbung von Bedeutung.
Warnung Eine Basisklasse muß deklariert sein, bevor sie zur Vererbung herangezogen werden kann! Sinnvolle Klassenhierarchien gründen darauf, daß ●
●
●
in Basisklassen die gemeinsamen Eigenschaften der abgeleiteten Klassen zusammengefaßt sind, abgeleitete Klassen natürliche Erweiterungen und/oder Spezialisierungen ihrer Basisklassen sind, man Objekte der abgeleiteten Klassen immer auch als Objekte ihrer Basisklassen ansehen kann.
Beispiele class Basis { public: void func1() {cout << "Basisklasse" << endl;} }; class Abgeleitet : public Basis {
190
Vererbung versus Einbettung public: void func2() {cout << "Abgeleitete Klasse" << endl;} }; int main() { Abgeleitet obj; obj.func1(); obj.func2(); return 0; }
Verweise Siehe Zugriffsbeschränkung bei der Vererbung
Vererbung versus Einbettung Beschreibung Um in einer Klasse die Eigenschaften einer anderen Klasse nutzen zu können, gibt es neben der Möglichkeit der friend-Deklaration zwei Möglichkeiten: die Vererbung und die Einbettung. Vererbung
Einbettung
class X
class X
{
{
...
...
};
};
class Y: public class X
class Y
{
{
...
class X var;
};
... };
Vererbung bringt Ihnen die Vorteile des Polymorphismus, der Zugriffsbeschneidungen und die Möglichkeit, Instanzen der abgeleiteten Klasse wie Objekte der Basisklasse zu behandeln.
Die Verwendung von Elementobjekten ist dagegen einfacher und besser zu überschauen.
Tip Sind Sie sich einmal unschlüssig, ob Sie die Funktionalität einer Klasse auf dem Weg über die Vererbung oder durch die Deklaration eines Elementobjekts zur Verfügung stellen, versuchen Sie sich klar zu machen, in welcher Beziehung die
191
Sprachkonzepte und Syntax
Objekte, die die Klassen repräsentieren, zueinander stehen. Stellen Sie sich einfach die Frage: »Kann man ein Objekt der Klasse Y auch als ein Objekt der Klasse X betrachten, oder sieht es vielmehr so aus, daß zu einem Objekt der Klasse Y auch ein Objekt der Klasse X gehört« Auf einen kurzen Nenner gebracht, heißt das » ist ein Y ein X «(Vererbung) oder » enthält Y ein X «(Einbettung) Für drei Klassen Auto, Motor und Sportwagen würde dies beispielsweise bedeuten, daß man die Klasse Sportwagen von der Klasse Auto ableiten würde (denn jeder Sportwagen ist auch ein Auto). Dagegen enthält jedes Auto einen Motor, man würde also in der Klasse Auto ein Objekt der Klasse Motor einbetten (und damit auch an die Klasse Sportwagen vererben).
Verweise Siehe Praxisteil, Kategorie Vererbung und Polymorphie
Zugriffsbeschränkung bei der Vererbung public, protected, private
Beschreibung Die Zugriffsspezifizierer public, protected und private können dazu benutzt werden, bei der Vererbung die Zugriffsrechte der geerbten Elemente zu verändern.
Anwendung Die folgende Tabelle zeigt wie sich die Zugriffsrechte für die geerbten Elemente bei Verwendung der Zugriffsmodifizierer public, protected und private ändern. Die Zugriffsspezifizierer der Vererbung regeln dabei nicht den Zugriff aus den Methoden der abgeleiteten Klassen auf die geerbten Elemente (dies wird schon durch die Zugriffsspezifizierer in der Deklaration der Basisklasse geregelt). Die Zugriffsspezifizierer der Vererbung betreffen nur die Zugriffsrechte, die die abgeleitete Klasse für die geerbten Elemente nach außen weitergibt (beispielsweise, wenn über eine Instanz der abgeleiteten Klasse auf diese Elemente zugegriffen wird, oder wenn die abgeleitete Klasse selbst als Basisklasse benutzt wird). Ausgangsbasis sind die Zugriffsrechte, die in der Basisklasse vorgesehen wurden. Diese können durch die Zugriffsspezifizierer der Vererbung verschärft werden.
192
Zugriffsbeschränkung bei der Vererbung
Zugriffsspezifizierer der Vererbung
Basisklasse
abgeleitete Klasse
public
public
public
protected
protected
private
private
public
protected
protected
protected
protected
private
private
private
public
private
protected
private
private
private
Warnung Wird kein Zugriffsspezifizierer angegeben, wird die Klasse private vererbt.
Beispiele #include using namespace std; class Basis { int w_priv; public: int w_pub; };
// private
class Abgeleitet: private Basis { public: void func() { w_priv = 2; // Zugriff verweigert, private-Element w_pub = 2; // ok } }; int main() { class Abgeleitet obj; cout << obj.w_pub << endl;
// Zugriff verweigert, private-Vererbung
return 0; }
Verweise Siehe Kategorie Klassen, Zugriffsspezifizierer Siehe Zugriffsrechte für einzelne Elemente auflockern
193
Sprachkonzepte und Syntax
Zugriffsrechte für einzelne Elemente auflockern Beschreibung Zugriffsbeschränkungen durch die Vererbung können in der abgeleiteten Klasse explizit wieder aufgelockert werden. Einer ererbten Eigenschaft kann jedoch nie eine weitreichendere Zugänglichkeit zugewiesen werden, als sie in der Basisklasse aufweist.
Beispiele class Basis { int w_priv; // private public: int w_pub1; int w_pub2; int w_pub3; }; class Abgeleitet: private Basis { // Redeklaration der Zugriffsrechte public: Basis::w_pub1; // ist wieder public using Basis::w_pub2; // ist wieder public }; int main() { class Abgeleitet obj; cout << obj.w_pub1 << cout << obj.w_pub2 << cout << obj.w_pub3 << cout << obj.w_priv << ...
endl; endl; endl; endl;
// Zugriff auf geerbte Elemente // kein Zugriff // kein Zugriff
Tip Der neue ANSI-Standard empfiehlt die Redeklaration durch die using-Anweisung.
Verweise Siehe Kategorie Klassen, Zugriffsspezifizierer
Vererbung und Konstruktor Beschreibung Konstruktoren und Destruktoren werden nicht vererbt. Daraus folgt, daß bei der Instanzbildung einer abgeleiteten Klasse die ererbten Datenelemente vom Konstruktor der jeweiligen Basisklasse eingerichtet werden müssen. Tatsächlich bilden die von einer Basisklasse geerbten Elemente in der abgeleiteten Klasse eine Art Unterobjekt mit eigenen Konstruktoren, Destruktoren,
194
Vererbung und Konstruktor
Zugriffsrechten, etc. Dieses Konzept ist die Grundlage für die in der objektorientierten Programmierung sehr gebräuchlichen Reduzierung eines abgeleiteten Objekts in ein Objekt einer seiner Basisklassen.
Anwendung Basisklassenobjekte einrichten
Standardkonstruktor
Ist in der Basisklasse ein Standardkonstruktor vorhanden, braucht sich die abgeleitete Klasse überhaupt nicht um die Einrichtung und Initialisierung der geerbten Elemente zu kümmern, sondern kann dies dem Compiler überlassen, der automatisch im Zuge der Instanzbildung für Objekte der abgeleiteten Klasse den Standardkonstruktor der Basisklasse aufruft. class Basis { public: int b_wert; Basis() { b_wert = 333;} }; class Abgeleitet : public Basis { public: int abg_wert; Abgeleitet(int i) { abg_wert = i;} }; int main() { class Abgeleitet obj(11);
Basisklassenobjekte einrichten
Spezieller Konstruktor
Um einen Konstruktor mit Argumenten für die Einrichtung der Basisklassenobjekte aufzurufen, muß man eine Konstruktorliste anlegen. Abgeleitet(int b1, double b2) : Basis1(b1), Basis(b2)
{ }
Die Konstruktorliste folgt mit einem Doppelpunkt auf den Namen des abgeleiteten Konstruktors. Um Argumente aus der Instanzbildung an einen Basisklassenkonstruktor weiterzugeben, muß für das jeweilige Argument im Konstruktor der abgeleiteten Klasse ein Parameter für das Argument definiert werden. class Basis { public: int b_wert; Basis(int par) { b_wert = par;} }; class Abgeleitet : public Basis { public:
195
Sprachkonzepte und Syntax int abg_wert; Abgeleitet(int i) Abgeleitet(int i, int n) : Basis(n)
{ abg_wert = i;} { abg_wert = i;}
}; int main() { class Abgeleitet obj(1,2);
Basisklassenobjekte einrichten
Reinitialisierung
Schließlich kann man im Konstruktor der abgeleiteten Klasse den geerbten Elementen neue Werte zuweisen. Auf diese Weise kann man die durch die Basisklassenkonstruktoren vorgesehene Initialisierung an die Bedürfnisse der abgeleiteten Klasse anpassen oder den Code übersichtlicher gestalten. Beachten Sie aber, daß ●
●
dies nichts daran ändert, daß die geerbten Elemente zuvor von einem Konstruktor ihrer eigenen Klasse eingerichtet werden, der Konstruktor der abgeleiteten Klasse im Gegensatz zum Konstruktor der Basisklasse auf Elemente, die in der Basisklasse als private deklariert sind, nicht zugreifen kann.
class Basis { public: int b_wert; Basis() }; class Abgeleitet : public Basis { public: int abg_wert; Abgeleitet(int i)
{ b_wert = 333;}
{ abg_wert = i; b_wert = 0;}
}; int main() { class Abgeleitet obj(11);
Mehrfachvererbung und Konstruktoren Klassen mit mehreren direkten Basisklassen Wird eine Klasse von mehreren direkten Basisklassen abgeleitet, beispielsweise class B1 {...}; class B2 {...}; class A: public B1, public B2 {...};
werden deren Konstruktoren in der Reihenfolge ihrer Auflistung bei der Vererbung abgearbeitet. Zuletzt wird der Anweisungsteil des Konstruktors der abgeleiteten Klasse ausgeführt.
196
Nicht vererbbare Methoden
Klassen mit direkten und indirekten Basisklassen Wird eine Klasse von einer Basisklasse abgeleitet, die selbst wieder von einer anderen Klasse abgeleitet ist, beispielsweise class B1 {...}; class B2: public B1 {...}; class A: public B2 {...};
wird zur Einrichtung des Basisklassenobjekts B2 der Konstruktor von B2 aufgerufen. Da das B2-Objekt selbst wieder ein Basisklassenobjekt der Klasse B1 enthält, wird als nächstes dessen Konstruktor aufgerufen und ausgeführt. Dann wird der Anweisungsteil des B1-Konstruktors und zuletzt der Anweisungsteil des Konstruktors der Klasse A ausgeführt.
Verweise Siehe Kategorie Klassen, Der Konstruktor Siehe Mehrfachvererbung
Nicht vererbbare Methoden Nicht vererbbar sind: ●
Konstruktor
●
Destruktor
●
Zuweisungsoperator
●
Auch Friend-Deklarationen, die ehedem keine wirklichen Elemente darstellen, werden nicht vererbt.
Polymorphie Beschreibung Der Begriff der Polymorphie ist eng an das Konzept der Vererbung gebunden. Er bezeichnet das Phänomen, daß eine Methode in verschiedenen abgeleiteten Klassen einer Hierarchie unterschiedlich implementiert sein kann.
Anwendung Auf diese Weise können einander ähnliche Klassen über eine Reihe gleichlautender und gleichartig zu benutzender Funktionen verfügen, die es dem Programmierer erlauben, Instanzen dieser Klassen gleich zu behandeln, ohne auf klassenspezifische
197
Sprachkonzepte und Syntax
Details, die in der Implementierung der Methoden versteckt sind (Information hiding), zu achten. Es liegt nahe, Methoden, die von mehreren Klassen benötigt werden, über eine gemeinsame Basisklasse zu vererben. In den abgeleiteten Klassen werden die Methoden überschrieben, um eine klassenspezifische Behandlung zu implementieren. Methoden, die überschrieben werden sollen, werden üblicherweise in der Basisklasse mit dem Schlüsselwort virtual deklariert. Gelegentlich wird auch das Überladen von Funktionen und Operatoren als Polymorphie bezeichnet.
Beispiele class ZeichenObjekt { protected: struct Punkt referenzpunkt; virtual int zeichne(struct Punkt p) { // zeichne Referenzpunkt an Koordinate p ... } }; class Rechteck : public ZeichenObjekt { protected: struct Punkt ecken[4]; virtual int zeichne(struct Punkt p) { // zeichne Rechteck mit linker unterer Ecke in // Koordinate p ... }; }; class Kreis : public ZeichenObjekt { protected: float radius; virtual int zeichne(struct Punkt p) { // zeichne Kreis mit Mittelpunkt in Koordinate p ... } };
Verweise Siehe Überschreibung und virtuelle Methoden Siehe Basisklassenzeiger
198
Überschreibung und virtuelle Methoden
Überschreibung und virtuelle Methoden Beschreibung Überschreibung bedeutet im Gegensatz zum Überladen, daß es nur eine Implementierung der Methode innerhalb einer Klasse gibt. (Die überschriebene Basisklassenversion kann allerdings über den Namen der Basisklasse und den Gültigkeitsbereichoperator aufgerufen werden. Dies wird häufig benutzt, um innerhalb der Implementierung der überschreibenden Methode die Basisklassenversion aufzurufen.)
Anwendung Der Compiler erkennt das Überschreiben einer Methode daran, daß neben dem Methodennamen auch Anzahl und Typen der Parameter übereinstimmen. Überschriebene Methoden bzw. Methoden, die überschrieben werden sollen, werden in der Basisklasse sowie in der abgeleiteten Klasse üblicherweise als virtual deklariert. Das Schlüsselwort virtual modifiziert dabei die Art und Weise, in der der Compiler eine Bindung zwischen einer Instanz und ihren Elementen herstellt..
Späte Bindung
virtual
Das Schlüsselwort virtual ist notwendig, um die Namen von überschriebenen Methoden in Klassenhierarchien richtig aufzulösen. Das Problem der Namensauflösung entsteht überhaupt erst dadurch, daß ein Objekt einer abgeleiteten Klasse praktisch auch ein Objekt der Basisklasse(n) ist. Dieser Umstand macht es möglich, einem Zeiger oder einer Referenz vom Typ der abgeleiteten Klasse die Adresse einer Instanz der Basisklasse zuzuweisen. Wird dieser Zeiger (oder die Referenz) nun dazu benutzt, eine Methode aufzurufen, die in Basisklasse und abgeleiteter Klasse deklariert ist (wobei die abgeleitete Klasse die Deklaration in der Basisklasse überschreibt), stellt sich die Frage, welche Version aufgerufen werden soll: ●
Die Methode der abgeleiteten Klasse, die dem Typ des Zeigers (der Referenz) entspricht? Diese Art der Namensauflösung wird als frühe Bindung bezeichnet, da sie zur Kompilierzeit vorgenommen wird.
Oder ●
Die Version der Basisklasse, die dem Objekt entspricht, auf das der Zeiger (die Referenz) verweist? Diese Art der Namensauflösung wird späte Bindung genannt, da sie erst zur Laufzeit erfolgt. Späte Bindung wird durch die Aufnahme des Schlüsselwortes virtual in die Funktionsdeklaration (zumindest der Basisklassen-Version) vereinbart.
199
Sprachkonzepte und Syntax
Beispiele #include using namespace std; class Basis { public: virtual void identifziere() {cout << "Dies ist die Basisklasse\n";}; }; class Abgeleitet : public Basis { public: virtual void identifziere() {cout << "Dies ist die abgeleitete Klasse\n";}; }; int main() { Abgeleitet abgeleitet_klasse; Basis *zeiger = new Basis;
// Zeiger vom Typ 'Zeiger auf Basis'
zeiger = &abgeleitet_klasse; zeiger->identifiziere(); }
Ausgabe: »Dies ist die abgeleitete Klasse«
Warnung ●
Für Klassen, die virtuelle Methoden enthalten (polymorphe Klassen) legt der Compiler eine virtuelle Tabelle an, in der alle virtuellen Methoden eingetragen sind. Zudem erhält die Klasse einen Zeiger auf ihre virtuelle Tabelle. Wird für ein Objekt der Klasse eine virtuelle Methode aufgerufen, greift der Compiler über diesen Zeiger auf die virtuelle Methode zu und entnimmt dieser die korrekte Adresse für den Aufruf der virtuellen Methode. Auf diese Weise ist sichergestellt, daß auch für Zeiger auf Klassenobjekte immer die zu dem Objekt passende Implementierung aufgerufen wird. Die späte Bindung erzeugt damit zusätzlichen Objektcode und bedingt längere Laufzeiten.
●
Konstruktoren können nicht als virtual deklariert werden.
●
Statische Methoden können nicht als virtual deklariert werden.
Verweise Siehe Vererbung und Destruktor
200
Vererbung und Destruktor
Vererbung und Destruktor Beschreibung Bei der Auflösung von Klassenobjekten wird automatisch der Destruktor aufgerufen. Dies ist unter anderem auch der Fall, wenn für einen Zeiger auf ein Klassenobjekt der delete-Operator aufgerufen wurde. Dabei sollte sichergestellt sein, daß der Destruktor der Klasse aufgerufen wird, dem das Objekt (und nicht der Zeiger, der auf das Objekt verweist) angehört.
Anwendung Aus den vorangehenden Ausführungen zur späten Bindung und den oben aufgeführten Gründen sollten Klassen, die Teil einer Vererbungshierarchie sind, über einen virtuellen Destruktor verfügen. Da der Destruktor, den der Compiler standardmäßig zuweist, nicht als virtual deklariert ist, sollte man in Basisklassen von Klassenhierarchien einen eigenen virtuellen Destruktor definieren, auch wenn man keinen Code in den Anweisungsteil schreibt.
Beispiele class Basis { ... public: virtual ~Basis() {} };
Abstrakte Methoden und Klassen Beschreibung Abstrakte Methoden sind Methoden ohne Anweisungsteil, die erfordern, daß sie in abgeleiteten Klassen überschrieben und mit einem Anweisungsteil versehen werden.
Abstrakte Methoden Abstrakte oder "rein virtuelle" Methoden werden als virtual deklariert und auf 0 gesetzt. Sie dienen lediglich als Vorgabe einer Schnittstelle für abgeleitete Klassen und haben keinen Definitionskörper. Sie müssen daher in den abgeleiteten Klassen definiert werden. virtual Typ name (PARAMETERLISTE) = 0;
201
Sprachkonzepte und Syntax
Abstrakte Klassen Klassen, die über abstrakte Methoden verfügen, werden automatisch zu abstrakten Klassen, mit der Konsequenz, daß von ihnen keine Instanz erzeugt werden kann. Sie stehen in Klassenhierarchien stets ganz oben, um geeignete Schnittstellen vorzugeben und so der Hierarchie ein einheitliches Gepräge zu verleihen.
Warnung Es ist zu beachten, daß Klassen, die von einer abstrakten Klasse abgeleitet wurden, und für die es versäumt wurde, die abstrakten Methoden zu überschreiben (und damit mit einem Definitionskörper zu versorgen), selbst zu abstrakten Klassen werden.
Beispiele class Basis { public: virtual void func() = 0; ... }; class Abgeleitet: public Basis { public: virtual void func() { ... } };
Basisklassenzeiger Beschreibung Da in einer abgeleiteten Klasse die Elemente der Basisklassen als Unterobjekte enthalten sind, kann man ein abgeleitetes Objekt wie eines seiner Basisklassenobjekte behandeln, es quasi auf sein Basisklassenunterobjekt reduzieren. Insbesondere in Verbindung mit Basisklassenzeigern und virtuellen Methoden ist dies ein äußerst nützliches Konzept, das in vielen Fällen äußerst effektive und elegante generische Lösungen erlaubt.
Anwendung Ein Basisklassenzeiger ist wie ein generischer Zeiger, dem man die Adressen abgeleiteter Objekte zuweisen kann. (Unter der Voraussetzung, daß das Basisklassenunterobjekt in dem abgeleiteten Objekt eindeutig identifizierbar und zugänglich ist.) Mit Hilfe dieser generischen Zeiger kann man
202
RTTI – Laufzeittypidentifizierung
●
Objekte verschiedener abgeleiteter Klassen mit gemeinsamer Basisklasse in einem Array (oder dynamischen Feld) verwalten, class ZeichenObjekt { ... }; class Rechteck : public ZeichenObjekt { ... }; class Kreis : public ZeichenObjekt { ... }; int main() { class ZeichenObjekt *geomFig[10]; geomFig[0] = new Kreis; geomFig[1] = new Rechteck; ...
●
generische Funktionen/Methoden implementieren, denen man Objekte verschiedener abgeleiteter Klassen mit gemeinsamer Basisklasse als Argumente übergeben kann. void markieren(const ZeichenObjekt& objekt)
{
...
}
RTTI Laufzeittypidentifizierung Beschreibung Um sinnvoll mit Basisklassenzeigern auf abgeleitete Objekte arbeiten zu können, muß es eine Möglichkeit geben, wieder auf die volle Funktionalität des abgeleiteten Objekts zuzugreifen. Das eigentliche Problem bei der Rückverwandlung ist die Typidentifizierung: welchem abgeleiteten Klassentyp gehört das Objekt an, auf das der Basisklassenzeiger verweist.
Anwendung Für die Typidentifizierung gibt es in C++ verschiedene Möglichkeiten: Virtuelle Methoden
Über einen Basisklassenzeiger soll für ein abgeleitetes Objekt eine in der abgeleiteten Klasse überschriebene Methode aufgerufen werden. base_ptr = &abg_obj;base_ptr->ueberschriebeneMethode(); Die Typidentifizierung erfolgt automatisch durch den Compiler. Soweit möglich und sinnvoll sollte man diese Form der Typidentifizierung nutzen.
203
Sprachkonzepte und Syntax
dynamic_cast
Über einen Basisklassenzeiger soll für ein abgeleitetes Objekt eine in der abgeleiteten Klasse neu definierte Methode oder ein neu definiertes Datenelement aufgerufen werden.
base_ptr = &abg_obj; if(abg_ptr = dynamic_cast(base_ptr)) { abg_ptr->neueMethode(); abg_ptr->Datenelement(); } Der Programmierer benutzt den dynamic_cast-Operator zur Umwandlung des Basisklassenzeigers in einen Zeiger auf Objekte einer abgeleiteten Klasse und überläßt dem Operator die Aufgabe sicherzustellen, daß diese Umwandlung korrekt ist. Wird in Fällen eingesetzt, in denen man mit virtuellen Methoden nicht weiterkommt. typeid
Vergleicht zwei Datentypen, beispielsweise den Typ eines Objekts, auf das ein Basisklassenzeiger verweist, mit dem Typ einer abgeleiteten Klasse.
base_ptr = &abg_obj; if(typeid(*base_ptr) == typeid(Abgeleitet)) { cout << "Objekt ist vom Typ Abgeleitet"; } Der Operator dient lediglich der Typidentifizierung und wird meist verwendet, wenn keine Rückverwandlung des Basisklassenzeigers erforderlich ist (sonst würde man möglichst den Operator dynamic_cast verwenden). Sollte möglichst sparsam eingesetzt werden. Den virtuellen Methoden und dem dynamic_cast-Operator ist – wo möglich – der Vorzug zu geben.
Mehrfachvererbung Beschreibung Mehrfachvererbung bedeutet, daß eine Klasse mehr als eine direkte Basisklasse hat. class Abgeleitet: public Basis1, public Basis2
Anwendung Die Mehrfachvererbung ist interessant, wenn man ein Objekt einer abgeleiteten Klasse als Objekt mehrerer Basisklassen verstehen kann oder, anders ausgedrückt, wenn ein Objekt in sich die Eigenschaften mehrerer direkter Basisklassen vereinigt. Ein typisches Beispiel wäre ein Amphibienfahrzeug, daß sowohl ein Auto als auch ein Boot darstellt.
204
Virtuelle Basisklassen
Warnung: Namenskonflikte bei der Mehrfachvererbung Bei der Mehrfachvererbung kann es schnell zu Namenskonflikten kommen, wenn sich in den Basisklassen gleichlautende Elemente verbergen. Derartige Namenskonflikte können mit Hilfe des Bereichsoperators aufgelöst werden.
Beispiele class Basis1 { public: void func(); }; class Basis2 { public: void func(); }; class Abgeleitet: public Basis1, public Basis2 { ... }; int main() { Abgeleitet abg; abg.func(); abg.Basis1::func(); ...
// Fehler // korrekt
Verweise Siehe Virtuelle Basisklassen
Virtuelle Basisklassen Beschreibung Virtuelle Basisklassen sind Basisklassen, die mit dem Schlüsselwort virtual vererbt werden. Für virtuelle Basisklassen stellt der Compiler sicher, daß die Elemente der Basisklassen in allen abgeleiteten Klassen nie mehr als einmal auftauchen.
205
Sprachkonzepte und Syntax
Anwendung Kreisschlüsse in der Vererbungshierarchie führen dazu, daß Elemente mehrmals vererbt werden.
Das obige Vererbungsschema zeigt wie die Klasse Amphibienfahrzeug jeweils zwei Vorkommen der Elemente max_geschw und get_geschw() der Basisklasse Fahrzeug erbt: einmal auf dem Weg über die Klasse Boot und einmal über Auto. Dies kann in bestimmten Klassenhierarchien sinnvoll sein (beispielsweise für das Amphibienfahrzeug, das als Boot und Auto unterschiedliche Maximalgeschwindigkeiten hat). Oftmals ist es jedoch unerwünscht. Man kann dies vermeiden, indem man die Basisklasse als virtual vererbt.
Beispiele class Basis { ... }; class Abgeleitet_1a : virtual public Basis { ... }; class Abgeleitet_1b : virtual private Basis { ... }; class Abgeleitet_2 : public Abgeleitet_1a, Abgeleitet_1b ... };
206
{
Templates
Warnung Virtuell geerbte Elemente können nicht auf dem Weg über die direkten Basisklassenkonstruktoren eingerichtet werden. Statt dessen muß für virtuelle Basisklassen ein entsprechender Konstruktor wie ein Basisklassenkonstruktor direkt in die Konstruktorliste aufgenommen werden. Der Programmierer steht damit vor den gewohnten zwei Alternativen: ●
●
Er kann die Initialisierung dem Standardkonstruktor der virtuellen Basisklasse überlassen – falls ein Standardkonstruktor verfügbar ist. Er kann einen Konstruktor der virtuellen Basisklasse in die Konstruktorliste aufnehmen. class Fahrzeug class Boot : virtual public Fahrzeug class Auto : virtual public Fahrzeug class Amphibienfahrzeug : public Boot, public Auto { public: Amphibienfahrzeug(int preis) : Fahrzeug (preis) { } };
Eine Basisklasse kann einer abgeleiteten Klasse gleichzeitig virtuell wie nicht-virtuell vererbt werden. ●
●
Jeder Pfad, über den die Basisklasse nicht-virtuell vererbt wird, erzeugt in der abgeleiteten Klasse ein eigenes Basisklassenobjekt. Alle Pfade, über die die Basisklasse virtuell vererbt wird, erzeugen in der abgeleiteten Klasse ein gemeinsames Basisklassenobjekt.
Templates Templates Beschreibung Template bedeutet übersetzt Schablone. Templates sind vorgefertigte, typunabhängige Muster für Klassen oder Funktionen. Wo immer in einer Klassen- oder Funktionsdeklaration ein Datentyp erforderlich ist, kann in der Template-Deklaration ein Platzhalter stehen. Diese Platzhalter werden im Kopf der Template-Deklaration aufgelistet und bei der Instanzbildung durch die gewünschten Typen ersetzt, woraufhin der Compiler eine entsprechende Instanz (Spezialisierung) der Templateklasse erzeugt, d. h., eine Klassen- oder Funktionsdefinition erzeugt.
207
Sprachkonzepte und Syntax
Anwendung Templates erinnern an Makros, sind diesen jedoch in puncto Vielseitigkeit und Sicherheit überlegen. Compiler, die Templates unterstützen, haben daher meist die üblichen C-Makros als Templates implementiert. Templates werden oft mit Hilfe von überladenen Operatoren und Methoden implementiert, um einen typengerechten Einsatz dieser Funktionen / Operatoren innerhalb der Template-Elementfunktionen zu gewähren.
Warnung ●
●
Werden Platzhalter für Typen durch ein anderes Schlüsselwort als class oder typename eingeführt, erwartet der Compiler für diesen Platzhalter eine entsprechende Konstante. Gleitkommatypen sind nicht zugelassen. Um in einer Quelltextdatei ein Template zu verwenden, das in einer anderen Quelltextdatei des Programms definiert ist, müßten Sie im Prinzip die Template-Definiton über eine Header-Datei in die Quelltextdatei aufnehmen. Alternativ können Sie das Template auch mit dem Schlüsselwort export definieren. Um das Template in anderen Quelltextdateien verwenden zu können, brauchen Sie dann nur noch die Template-Deklaration anzugeben.
Beispiele // Quelltextdatei1.cpp export template int func(T a, T b) { if (a == b) return 1; else return 0; } // Quelltextdatei1.cpp template int func(T a, T b); int var1, var2; ... func(var1, var2);
208
// Template-Definition
// Template-Deklaration
Funktionentemplates
Verweise Siehe nachfolgende Abschnitte
Funktionentemplates Beschreibung Funktionentemplates sind typunabhängige Vorlagen für Funktionen, aus denen der Compiler bei Bedarf durch Spezialisierung für vorgegebene Datentypen echte Funktionen erzeugen kann.
Anwendung Funktionentemplates haben gegenüber der Überladung von Funktionen den Vorteil, daß der Anweisungsteil nur einmal aufgesetzt werden muß. (Voraussetzung ist natürlich, daß die Anweisungen für alle Datentypen, für die das Template zu instanziieren ist, gleich sind). Ansonsten kann man natürlich auch Funktionentemplates überladen.
Syntax template Rückgabetyp funktionsname(PARAMETERLISTE) { ●
●
};
TYPENLISTE: In der Typenliste werden durch Kommata getrennt, die später zu ersetzenden Platzhalter aufgelistet. Jedem Platzhalter wird dabei das Schlüsselwort class (oder typename) vorangestellt. Die weitere Deklaration folgt der Syntax zur Funktionsdeklaration, wobei allerdings statt definierter Typen bereits die Platzhalter verwendet werden können.
Beispiele template T max(T a, T b) return a > b ?: a, b; }
{
Instanziierung: cout << max(3, -14) << endl;
Warnung Durch mehrfache Instanziierung und Überladung kann es zu Uneindeutigkeiten (ambiguities) kommen, d. h. der Compiler kann für einen Funktionsaufruf nicht mehr entscheiden, welche Implementierung der Funktion aufzurufen ist.
209
Sprachkonzepte und Syntax
Verweise Siehe Instanzbildung und Spezialisierung
Klassentemplates Beschreibung Klassentemplates sind typunabhängige Vorlagen für Klassen, aus denen der Compiler bei Bedarf durch Spezialisierung für vorgegebene Datentypen echte Klassen erzeugen kann.
Anwendung Das beste Beispiel für den Einsatz von Klassentemplates sind die Container der Laufzeitbibliothek, die alle als Templates definiert sind. Dies erlaubt es, den Datentyp der Elemente, die im Container verwahrt werden sollen, als Template-Parameter zu definieren. Der Container kann dann unabhängig vom Typ der Elemente implementiert und bei Bedarf für die gewünschten Datentypen instanziiert werden.
Syntax template class klassenname ●
{
};
TYPENLISTE: In der Typenliste werden durch Kommata getrennt, die später zu ersetzenden Platzhalter aufgelistet. Jedem Platzhalter ist dabei das Schlüsselwort class (oder typename) vorangestellt.
Warnung ●
●
● ●
Methoden innerhalb von Klassentemplates sind automatisch Funktionentemplates, d. h., sie können die Platzhalter des Klassentemplates gebrauchen, ohne daß eine explizite Template-Deklaration für diese Platzhalter erforderlich ist. Friend-Funktionen, die in Klassentemplates deklariert werden, sind nicht automatisch Funktionentemplates, d. h., standardmäßig sind solche Funktionen Friends zu allen typisierten Klassen, die aus dem Template gebildet werden. Erst wenn eine friend-Funktion in ihrer Deklaration einen Platzhalter des Klassentemplates enthält, gibt es für jede typisierte Klasse eine eigene friendFunktion. Man kann friend-Funktionen auch als spezialisierte Templates deklarieren, um so anzuzeigen, daß die friend-Deklaration nur für die angegebenen Spezialisierungen des Templates gelten. Friend-Templates dürfen nicht im Template definiert werden. Werden in einem Template statische Elemente definiert, erhält jede Spezialisierung des Templates seinen eigenen Satz von statischen Elementen.
210
Methoden in Klassentemplates
●
●
Templates können sowohl von Templates als auch von normalen Klassen abgeleitet werden und können wiederum ihrerseits als Basisklasse für andere Templates oder Klassen verwendet werden. Benutzerdefinierte Namen in Template-Deklarationen werden standardmäßig als Bezeichner für Variablen aufgefaßt. Damit ein Name als Typbezeichner erkannt wird, muß der Name innerhalb des Templates oder im umgebenden Gültigkeitsbereich definiert worden sein oder durch das Schlüsselwort typename gekennzeichnet werden.
●
Template-Klassenelemente können nicht als virtual deklariert werden.
●
Lokale Klassen dürfen keine Template-Klassenelemente enthalten.
Beispiele template class demo { T var; public: demo() { var = 1.5;} T func() { T tmp; tmp=var++; return tmp; }; };
Instanziierung: demo<double> obj;
Verweise Siehe Instanzbildung und Spezialisierung
Methoden in Klassentemplates Beschreibung Methoden von Klassentemplates sind automatisch Funktionentemplates, d. h., sie können die Platzhalter des Klassentemplates gebrauchen, ohne daß eine explizite Template-Deklaration für diese Platzhalter erforderlich ist. Innerhalb des Templates unterscheidet sich ihre Syntax praktisch nicht von Methoden gewöhnlicher Klassen. Außerhalb des Klassentemplates, zum Beispiel zur ausgelagerten Definition, müssen sie als Element ihres Klassentemplates gekennzeichnet sein:
211
Sprachkonzepte und Syntax
Syntax template Rückgabetyp klassenname::funkt_name(PARAMETERLISTE) { };
Warnung Gemäß dem neuen ANSI-C++-Standard können innerhalb von Klassentemplates auch Funktionentemplates mit eigenen Platzhaltern deklariert werden. Dieses Konzept ist allerdings recht schwierig zu implementieren und wird nicht von allen Compilern unterstützt. Methoden von Klassentemplates müssen prinzipiell in der gleichen Datei definiert werden wie das Klassentemplate. Um beide Teile zu trennen, muß man da Schlüsselworte verwenden (siehe Einleitung zu Templates).
Beispiele template class demo { T var1; S var2; public: T func(); }; template T demo::func() { return var1; }
Verweise Siehe Klassentemplates
Instanziierung und Spezialisierung Beschreibung Ein Template ist lediglich eine Schablone, eine Vorlage für später zu erstellende Funktionen oder Klassen. Der nächste Schritt nach der Definition eines Templates besteht also darin, auf der Grundlage des Templates eine oder mehrere Funktionen (oder Klassen) zu erzeugen, mit denen man arbeiten kann. Die grundsätzliche Vorgehensweise dabei ist, dem Compiler anzugeben, welche konkreten Datentypen er für die Platzhalter des Templates verwenden soll, und diesem dann die Aufgabe zu überlassen, aus dem typunabhängigen Template und den spezifizierten Datentypen eine echte Funktion oder Klasse zu generieren. Die Funktionen oder Klassen, die auf der Grundlage eines Templates erzeugt werden, bezeichnet man als Spezialisierungen.
212
Implizite Instanziierung
Anwendung Um dem Compiler anzuzeigen, daß er für einen bestimmten Satz von Datentypen aus einem Template eine Spezialisierung erzeugen soll, gibt es drei Möglichkeiten: ●
Implizite Instanziierung
●
Explizite Instanziierung
●
Explizite Spezialisierung
Verweise Siehe nachfolgende Abschnitte
Implizite Instanziierung Beschreibung Bei der impliziten Instanzbildung überläßt der Programmierer fast die gesamte Arbeit dem Compiler. Der Programmierer muß lediglich dafür Sorge tragen, daß der Compiler alle Informationen hat, um den Platzhaltern des Templates konkrete Datentypen zuweisen zu können.
Anwendung Für Funktionentemplates genügt es meist, die Funktion mit entsprechenden Argumenten aufzurufen. Der Compiler entnimmt die Typinformation dann den Argumenten. Typen für Klassen oder Rückgabewerte von Funktionen können nicht auf diese Weise abgeleitet werden. In so einem Fall muß man die Typen für die Platzhalter in spitzen Klammern explizit angeben.
Implizite Instanziierung für Funktionentemplates template void demo_func(T1 a, T2 b) ... }
{
int main() { demo_func(13,14); demo_func(13.2,14.3); return 0; }
Erläuterung demo_func(13,14);
213
Sprachkonzepte und Syntax
Der Compiler erkennt, daß eine Spezialisierung des Templates demo_func benötigt wird. Anhand der Typen der Aufrufargumente kann er die gewünschten Typen für die Platzhalter T1 und T2 bestimmen. Der Compiler erzeugt also eine Spezialisierung demo_func und ruft diese mit den Argumenten (13,14) auf demo_func(13.2,14.3);
Der Compiler erkennt, daß eine weitere Spezialisierung des Templates demo_func benötigt wird. Den Typ für den ersten Platzhalter T1 entnimmt er den Templateargumenten, die in spitzen Klammern übergeben wurden. Den Typ für den zweiten Platzhalter T2 entnimmt er dem Typ des zugehörigen Aufrufarguments. Der Compiler erzeugt also eine Spezialisierung demo_func und ruft diese mit den Argumenten (13.2,14.3) auf.
Warnung Bei der Instanziierung von Funktionen aus Funktionentemplates berücksichtigt der Compiler keine Standardkonvertierungen. Wenn ein Funktiontemplate zwei Parameter des gleichen Platzhaltertyps definiert, kann man das Template nicht instanziieren, indem man ihm zwei Argumente unterschiedlicher Typen übergibt – auch wenn diese durch Standardkonvertierungen ineinander umwandelbar sind. Eine einmal erzeugte Spezialisierung eines Funktionentemplates kann wie jede Funktion mit konvertierbaren Argumenten aufgerufen werden.
Implizite Instanziierung für Klassentemplates Bei der Instanziierung von Klassentemplates müssen die Datentypen für die Platzhalter immer vollständig angegeben werden: template class demo_class { T var; public: T func() { return var; } }; int main() { demo_class obj; return 0; }
// implizite Instanziierung
Verweise Siehe explizite Instanziierung und Spezialisierung
214
Explizite Instanziierung
Explizite Instanziierung Die explizite Instanziierung ist eine Aufforderung an den Compiler, die gewünschte Spezialisierung des Templates sofort zu erzeugen, auch wenn diese Spezialisierung noch gar nicht benötigt wird. Eingeleitet wird die explizite Instanziierung durch das Schlüsselwort template. Die Argumente für die Templateparameter werden allerdings nicht an das Schlüsselwort template, sondern an den Namen der Funktion oder Klasse angehängt (dies dient der Unterscheidung zur expliziten Spezialisierung).
Explizite Instanziierung für Funktionentemplates template void demo_func(T1 a, T2 b) { cout << a << " " << b <<endl; } // Explizite Instanziierung template void demo_func(int, int); template void demo_func(int, double); int main() { demo_func(13,14); demo_func(13.2,14.3); ...
Explizite Instanziierung für Klassentemplates Bei der Instanziierung von Klassentemplates müssen die Datentypen für die Platzhalter immer vollständig angegeben werden: template class demo_class { T var; public: T func() { var = 1; return var; } }; template class demo_class; int main() { demo_class obj; ...
Verweise Siehe implizite Instanziierung und Spezialisierung
215
Sprachkonzepte und Syntax
Explizite Spezialisierung Beschreibung Während wir unter einer Spezialisierung einfach eine konkrete Funktion oder Klasse verstehen, die auf der Basis eines Templates generiert wurde, sprechen wir von expliziter Spezialisierung, wenn ●
●
einzelne oder alle Platzhalter eines Templates durch konkrete Datentypen ersetzt werden, und wenn für dieses stärker typabhängige Template eine eigene Definition vorgesehen wird.
Syntax Die Syntax der Spezialisierung erfordert zwei Templateparameterlisten in spitzen Klammern: template <erste_Liste> ●
●
Name
In der ersten Liste, die auf das Schlüsselwort template folgt, werden die Templateparameter aufgeführt, die in der Spezialisierung noch weiter verwendet werden. In der zweiten Liste, die auf den Namen der Spezialisierung folgt, werden Argumente für die Templateparameter des primären Templates übergeben.
Diese Syntax erlaubt auch die partielle Spezialisierung: template template template template
class demo class demo class demo <> class demo
{...}; {...}; {...}; {...};
Die erste Zeile enthält die Definition des primären Templates. Darunter folgen zwei partielle Spezialisierungen, die nur noch einen Platzhalter verwenden. In der Templateargumentenliste wird festgelegt, welche Datentypen oder Platzhalter mit den Platzhaltern des primären Templates korrespondieren. Die letzte Zeile definiert eine vollständige explizite Spezialisierung.
Warnung ●
●
●
Explizite Spezialisierungen haben stets Vorrang vor impliziten oder expliziten Instanziierungen. Eine explizite Instanzbildung oder Spezialisierung muß im gleichen Namensbereich (namespace) erfolgen wie das zu spezialisierende Template. Für Klassentemplates kann man auch einzelne Methoden explizit spezialisieren.
216
Explizite Spezialisierung
Die obige Syntax mit dem vorangehenden Schlüsselwort template und der nachfolgenden Templateparameterliste ist noch recht neu. Die meisten Compiler erlauben Spezialisierungen nur in Form einer vereinfachten Syntax ohne das Schlüsselwort template: // aeltere Form der Spezialisierung int vergleich(char *a, char *b) return(strcmp(a,b)); }
{
Beispiele // Funktionentemplate template int vergleich(T a, T b) cout << "Erstes Template:\t"; return(a > b); }
{
// Spezialisierung für Datentyp char * template <> int vergleich(char *a, char *b) return(strcmp(a,b)); }
{
Verweise Siehe implizite und explizite Instanziierung
Der Präprozessor Beschreibung Neben der reinen Sprachdefinition beschreibt der ANSI-C-Standard unter anderem auch die Phasen, die erfolgen müssen, damit der Sourcecode eines Programms in den von der Maschine ausführbaren Code übersetzt werden kann. Eines der beim Übersetzungsvorgang beteiligten Programme ist der sogenannte Präprozessor, der der Name läßt es bereits vermuten vorbereitende Arbeiten an dem Quellcode durchführt. (In neueren Compilern ist das Präprozessing kein getrennter Prozeß mehr, sondern wird beim Kompilieren mit ausgeführt.) Erst nachdem der Präprozessor den Code bearbeitet hat, wird er zur eigentlichen Code-Generierung an den Compiler weitergeleitet.
Anwendung Der Präprozessor führt neben einfachen Quelltextersetzungen, die automatisch ablaufen, weitere Aufgaben aus: ●
Kommentare werden entfernt (und durch Leerzeichen ersetzt).
●
Zeilenumbrüche, denen ein Backslash vorangeht, werden entfernt.
217
Sprachkonzepte und Syntax
Aufeinanderfolgende String-Literale werden zusammengefaßt (konkateniert).
●
Whitespace-Zeichen zwischen Tokens werden gelöscht. (Ein Token ist ein Bezeichner, der im Programm syntaktische Bedeutung hat.)
●
Trigraph-Sequenzen werden ersetzt.
●
auch einige ausgeklügeltere Ersetzungen, die vom Programmierer gesteuert werden können. Diese Ersetzungen werden mit Hilfe bestimmter Präprozessor-Direktiven explizit im Quelltext definiert. Mit ihrer Hilfe kann der Programmierer ●
Header- und Quelldateien in den Quelltext kopieren lassen (#include),
●
symbolische Konstanten einführen (#define), über »Schalter« steuern, welche Code-Abschnitte übersetzt werden sollen (bedingte Kompilierung mit #ifdef, etc.).
●
Die Anweisungen an den Präprozessor beginnen immer mit dem Zeichen »#« und stehen immer allein in einer Zeile. Die nachfolgende Tabelle enthält eine Übersicht über Präprozessor-Direktiven und -Operatoren, die der ANSI-Standard vorschreibt. Direktive
Bedeutung
defined
Operator, der testet, ob ein Makro definiert ist
#
Operator zur Zeichenkettenbildung
##
Operator zur Grundsymbolverbindung
#define
definiert ein Symbol oder Makro
#elif
else-if-Operator
#else
else-Operator
#endif
Ende der #if-Direktive
#error
erzeugt eine Fehlermeldung
#if
if-Operator
#ifdef
Äquivalent zu #if defined
#ifndef
Äquivalent zu #if !defined
#include
Datei einbinden
#line
Aktuelle Zeile verändern
#pragma
Anweisung für den Compiler
#undef
Definition eines Symbols entfernen
218
Quellcode einfügen und ersetzen
Quellcode einfügen und ersetzen #include
Beschreibung Die für den Programmierer wichtigste Direktive an den Präprozessor ist sicherlich die Direktive #include. Mit ihr wird der Präprozessor angewiesen, die als Argument angegebene Datei in die aktuelle Datei einzukopieren.
Anwendung Meist wird hiermit eine sogenannte Header-Datei aufgenommen, die Deklarationen, symbolische Konstanten, Makros und/oder Typdefinitionen enthalten kann. Das Einbinden der Datei kann an jeder beliebigen Stelle der Sourcedatei erfolgen. Es existiert jedoch eine stillschweigende Übereinkunft darüber, daß das Einbinden am Anfang der Datei stattfindet. Die Verwendung von Header-Dateien empfiehlt sich besonders für Programme, die aus mehreren Quelltextdateien bestehen. Die gemeinsam benutzbaren Typdefinitionen, Konstanten, Funktionsdeklarationen können auf diese Weise durch eine einzeilige Anweisung allen beteiligten Dateien bekanntgemacht werden.
Syntax Zwei verschiedene Formen der #include-Direktive sind möglich: #include
Der Compiler sucht nach der angegebenen Datei in bestimmten in der Entwicklungsumgebung des Compilers spezifizierten Verzeichnissen. #include "datei"
Der Compiler sucht nach der angegebenen Datei in einer compilerspezifischen Reihenfolge. Üblicherweise wird zuerst im aktuellen Verzeichnis gesucht.
Beispiele #include <stdio.h> #include "Unterverzeichnis\typen.h"
Verweise Siehe Grundlagenteil, Verwendung der Standardbibliotheken
219
Sprachkonzepte und Syntax
Makros definieren #define makroname
Beschreibung Mit der Präprozessor-Direktive #define wird die Definition eines Makros eingeleitet.
Anwendung Eine Makrodefinition besitzt die folgende Form: #define makroname neuer_text
Der Präprozessor ersetzt, nachdem er die Makrodefinition zur Kenntnis genommen hat, jedes Vorkommen von makroname mit neuer_text. Der Name eines Makros wird nach den gleichen Regeln wie ein Variablenname gebildet. Es hat sich jedoch eingebürgert, Makronamen in Großbuchstaben zu schreiben. Diese Nomenklatur – wird sie konsequent eingehalten – macht auf den ersten Blick deutlich, ob es sich um einen Variablen-/Funktionsnamen oder ein Makro handelt.
Warnung Die Festlegung des Ersatztextes hört am Ende der Zeile auf. Um auch Makros schreiben zu können, die sehr lang und komplex sind, ist es möglich, vor der Zeilenschaltung das Zeichen Backslash »\« einzufügen und die Definition des Makros auf der bzw. den folgenden Zeilen fortzuführen. Wird kein Ersatztext angegeben, wird nur der Makroname eingeführt, der als eine Art Compiler-Schalter bei der bedingten Kompilierung eingesetzt werden (und jederzeit mit #undef makroname wieder entfernt werden) kann.
Beispiele #define GROESSE 20 int feld[GROESSE]; ... for (int i = 0; i < GROESSE; i++) {
...
Verweise Siehe Argumente in Makros Siehe Direktiven zur bedingten Kompilierung Siehe Kategorie Konstanten, #define
220
Argumente in Makros
Argumente in Makros #define name()
Beschreibung Es ist auch möglich, in Makros Argumente zu verwenden, innerhalb des Makros mit den Argumenten zu arbeiten und das gleiche Makro für verschiedene Argumente zu verwenden.
Anwendung Auf diese Weise können Funktionen durch Makroaufrufe ersetzt werden, wodurch der leidige Function Overhead vermieden und typunabhängige Implementierungen möglich werden.
Warnung Die Übergabe von Argumenten führt aber schnell zu unerwünschten Nebeneffekten, so daß in C++ der Deklaration von inline-Funktionen zur Vermeidung des Function Overheads beziehungsweise die Programmierung von überladenen und Operatoren sowie von Templates zur Erreichung typunabhängiger Implementierungen der Vorzug zu geben ist.
Beispiele #define flaeche(a,b) (a*b) // falsch ergebnis = flaeche(4,6); // liefert 24 ergebnis = flaeche(3+1,5+1); // liefert 9!
Erläuterung Das falsche Ergebnis resultiert aus der falschen Definition des Makros. Der Präprozessor ersetzt die zweite Zuweisung durch ergebnis = 3+1*5+1
und dies führt zu dem falschen Ergebnis, da die Multiplikation die höhere Priorität hat. Alle Argumente, die in einer Makrodefinition vorkommen, sollten daher geklammert werden, um diese Nebeneffekte, die manchmal schwer zu finden sind, zu vermeiden. Richtig wäre: #define flaeche(a,b) ((a)*(b))
/* so ist's richtig */
Verweise Siehe Kategorie Templates, Funktionentemplates
221
Sprachkonzepte und Syntax
Direktiven zur bedingten Kompilierung Beschreibung Mit den Direktiven zur bedingten Kompilierung können verschiedene Bereiche einer Datei wahlweise kompiliert oder von der Kompilation ausgeschlossen werden.
Anwendung Mit Hilfe der bedingten Kompilierung kann man beispielsweise verschiedene Versionen eines Programms aufbauen. Diese Versionen können sein: ●
eine Version, die Debugging-Code enthält,
●
Programme, die auf unterschiedlicher Hardware laufen oder
●
Versionen, die mit unterschiedlichen Compilern arbeiten sollen.
Die Direktiven zur bedingten Kompilierung werten einen Makronamen aus, um zu ermitteln, welche Bereiche der Quelldatei vom Präprozessor an den Compiler weitergeleitet bzw. aus dem Sourcecode ausgeschlossen werden sollen. Der Anweisungsblock zur bedingten Kompilierung umfaßt mehrere Zeilen: ●
●
● ●
●
Die Zeile, in der die zu testende Bedingung steht. Sie wird mit #if, #ifdef oder #ifndef eingeleitet. Zeilen, die den Code beinhalten, wenn die auszuwertende Bedingung den Wert wahr ergibt (optional). Eine optionale else-Zeile, die mit #else oder #elif eingeleitet wird. Zeilen, die den Code beinhalten, wenn die auszuwertende Bedingung den Wert unwahr ergibt (optional). Eine Zeile, mit der der Block zur bedingten Kompilierung abgeschlossen wird: die #endif-Anweisung.
#if Die Präprozessor-Direktive #if leitet die bedingte Kompilierung ein. Danach steht ein konstanter Ausdruck, der ausgewertet wird. Ergibt die Auswertung wahr, wird der nachfolgende Teil (entweder bis zum nächsten #elif, #else oder #endif) an den Compiler weitergeleitet.
#elif Die Direktive #elif ist eine Abkürzung für else if. Dieser Ausdruck wird nur dann ausgewertet, wenn die Auswertung der vorhergehenden Direktive das Resultat unwahr erbrachte. #elif kann in einem Block zur bedingten Kompilierung mehrmals vorkommen. Ergibt die Auswertung wahr, wird der nachfolgende Teil (entweder bis zum nächsten #elif, #else oder #endif) an den Compiler weitergeleitet.
222
Direktiven zur bedingten Kompilierung
#else #else kann in einem Anweisungsblock nur einmal vorkommen. Die hinter #else stehenden Anweisungen werden bis zum abschließenden #endif an den Compiler wei-
tergeleitet, wenn keine der vorhergehenden Auswertungen das Ergebnis wahr erbrachte.
Ende des bedingten Kompilierblocks
#endif
Die Präprozessordirektive #endif muß für jedes #if, #ifdef und #ifndef verwendet werden, um das Ende des Blockes zur bedingten Kompilierung anzuzeigen.
Bedingte Kompilierung und Makrodefinition
#ifdef, #ifndef
Bei den Direktiven #ifdef und #ifndef handelt es sich um die alte Form der Direktiven zur bedingten Kompilierung. Mit ihnen kann nur getestet werden, ob der als Argument angegebene Bezeichner definiert ist oder nicht. Mit #if defined bzw. #if !defined ist es darüber hinaus möglich, den zurückgelieferten Wert zu verknüpfen, was mit #ifdef und #ifndef nicht möglich ist. #ifdef bezeichner #ifndef bezeichner
Der Operator
defined
Der Präprozessor-Operator defined liefert den Wert wahr (entspricht 1) zurück, wenn der als Argument angegebene Bezeichner zuvor mit #define definiert wurde. Ist der Bezeichner nicht definiert, gibt der Operator den Wert unwahr (entspricht 0) zurück. Die beiden folgenden Anweisungen sind identisch: #if defined MAXZEILEN #ifdef MAXZEILEN
Beispiele #if defined(ZIELSYSTEM) && (MAXSPALTEN==80) #define ARRAY_GROESSE 160 #elif !defined(ZIELSYSTEM) #define ERROR1 #error Datei kann nicht kompiliert werden. #endif
223
Sprachkonzepte und Syntax
Zeichenkettenbildung und Grundsymbolverbindung Zeichenkettenbildung
#
Der Operator zur Zeichenkettenbildung (string-izing operator) wird bei funktionsähnlichen Makros benutzt. Er bewirkt, daß ein dem Makro übergebenes Argument in eine Zeichenkette umgewandelt wird. #define makestring(s) printf(#s "\n") int main() { makestring(Dies wird ein String); makestring("Dies wird noch ein String"); ...
wird vom Präprozessor umgesetzt zu: int main() { printf("Dies wird ein String" "\n"); printf("\"Dies wird noch ein String\"" "\n"); ...
Die Ausgabe des Programms: Dies wird ein String "Dies wird noch ein String"
Grundsymbolverbindung
##
Mit dem Operator zur Grundsymbolverbindung (token-pasting operator) können einzelne Token zu einem Grundsymbol verknüpft werden. Der Operator »##« sowie alle zwischen den Token stehenden Zwischenraumzeichen werden vom Präprozessor entfernt. #define zeigevar(zahl) int main() { int var3=3; zeigevar(3); ...
printf("%d\n", var ## zahl)
wird vom Präprozessor umgesetzt zu: int main() { int var3=3; printf("%d\n", var3); return 0; }
224
Sonstige Direktiven und Symbole
Sonstige Direktiven und Symbole Diagnosemeldungen
#error
Mit der Verwendung der Präprozessor-Direktive #error erreichen Sie, daß der Compiler den (optionalen) Text auf dem stderr-Gerät ausgibt und anschließend die Kompilierung abbricht. Der Text braucht nicht in Anführungszeichen eingeschlossen zu werden. #error
Beispiele #if !defined AUTO && !defined FAHRRAD #error Definieren Sie das gewünschte Transportmittel #endif
Das vorstehende Beispiel könnte aus einem Programm stammen, bei dem über die bedingte Kompilierung, je nach gewünschtem Transportmittel, anderer Code erzeugt wird. Da das Programm nur einwandfrei kompiliert werden kann, wenn entweder AUTO oder FAHRRAD definiert ist, wird mit dem defined-Operator getestet, ob die Definition existiert. Ist dies nicht der Fall, wird der hinter #error stehende Text ausgegeben.
Zeilen-Steuerdirektive1
#line
Mit der Zeilen-Steuerdirektive #line kann die Zeilennummer innerhalb einer Datei eingestellt und der Name der Datei geändert werden. Diese Daten werden vom Compiler benutzt, um auf eventuelle Fehler zu verweisen. Durch die Verwendung von #line werden die Werte in den Symbolen __LINE__ und __FILE__ geändert.
Diese Direktive wird üblicherweise von Programmgeneratoren benutzt, um Fehler in dem erzeugten C-Code mit der Zeile und dem Dateinamen der Ursprungsdatei zu verknüpfen. #line ["dateiname"] ●
●
Zeile: Eine Ganzzahlkonstante, auf die der interne Zähler des Compilers gesetzt wird. dateiname: Dieses Argument ist optional und kann einen in Anführungszeichen eingeschlossenen Dateinamen enthalten.
Beispiele #line 200 #line 200 "ursprung.c"
225
Sprachkonzepte und Syntax
Pragma-Direktiven
#pragma
Die #pragma-Direktive ist eine compilerspezifische Direktive, d. h., die Verarbeitung der Direktive hängt nicht vom ANSI-Standard, sondern vom verwendeten Compiler ab. Pragma-Direktiven, die nicht zur Implementierung gehören, werden ignoriert. Da dies stillschweigend geschieht, muß bei der Portierung von Code auf eine andere Implementierung mit anderen Pragmas besonders sorgfältig vorgegangen werden.
Vordefinierte Symbole und Konstanten Nach ANSI C/C++ sind sechs verschiedene Konstanten vordefiniert. Makro
Bedeutung
__DATE__
Datum der Kompilierung der Datei
__FILE__
Name der Sourcedatei
__LINE__
Aktuelle Zeile in der Sourcedatei
__STDC__
Compiler entspricht ANSI-C-Standard
__TIME__
Zeit der Kompilierung der Datei
__cplusplus
C++ -Code
Exception-Behandlung Exception-Behandlung Beschreibung Unter Exception-Behandlung versteht man eine mit C++ in die Sprache eingeführte Form der Fehlerbehandlung. Die Exception-Behandlung ist nicht auf Klassen oder andere Konzepte der objektorientierten Programmierung angewiesen, arbeitet mit diesen aber besonders effektiv zusammen:
Vorteile der Exception-Behandlung So erlaubt die Exception-Behandlung eine räumliche Trennung von Fehlerauslöser und Fehlerbehandlung. Eine Bibliotheksfunktion braucht ihre Fehler also nicht selbst zu verarbeiten. Statt dessen löst sie eine Exception aus und überläßt die Behandlung des Fehlers dem aufrufenden Programm.
226
Exception-Behandlung
Der zweite Vorteil liegt darin, daß die Weiterreichung der Exception nicht über den Rückgabetyp, die Parameter oder irgendwelche globale Variablen erfolgt. Die für den korrekten Einsatz der Funktion benötigte Schnittstelle wird also nicht belastet oder aufgebläht. Dies ist besonders wichtig für Elemente wie zum Beispiel Konstruktoren, die keine Werte zurückliefern können. Da Exceptions Objekte eines bestimmten Datentyps sind, können Sie Informationen vom Ort des Fehlers zum Ort der Fehlerbehandlung übertragen. Viertens wird der Quellcode übersichtlicher, da der eigentliche Algorithmus und die Fehlerbehandlung getrennt ablaufen.
Nachteile der Exception-Behandlung Exceptions sollten nur für schwerwiegende Fehler verwendet werden, die nicht anders behoben werden können. Unbehandelte Exceptions führen, sofern vom Programmierer keine andere Exception-Behandlung vorgesehen wurde, zum Programmabbruch. Gerade Entwickler von Bibliotheken sollten daher vermeiden, für jede kleine Unstimmigkeit eine Exception auszulösen, die die Benutzer der Bibliothek dann abfangen müssen. Exceptions, die nicht in der gleichen Funktion abgefangen werden, in der sie ausgelöst wurden, führen zur Auflösung des Stacks, d. h. die auf dem Stack befindlichen Funktionen werden, ohne zu Ende ausgeführt zu werden, vom Stack entfernt. Dies wiederum kann zu Problemen führen, wenn Ressourcen, die von den Funktionen angefordert wurden, nicht mehr wie am Ende der Funktion freigegeben werden (beispielsweise Freigabe dynamisch reservierten Speichers).
227
Sprachkonzepte und Syntax
Prinzipiell gibt es zwei Möglichkeiten, diesem Problem zu begegnen: ●
Die eine Möglichkeit besteht darin, vorsorglich alle Exceptions in der Funktion abzufangen, dann die Ressourcen freizugeben und die Exceptions schließlich erneut auszuwerfen: void func() { FILE *fp = fopen("Datei.txt","rt"); try { func_that_may_throw(fp); } catch(...) { // ... fängt alle Exceptions fclose(fp); throw; // Exceptions weiterleiten } fclose(fp); }
●
Die andere Möglichkeit besteht darin, die Ressource in einer lokalen Klasseninstanz zu kapseln. Deren Destruktor wird auf jeden Fall zur Auflösung der Klasseninstanz aufgerufen und der Programmierer kann in der Implementierung des Destruktors sicherstellen, daß die Ressource freigegeben wird.
Schema einer Exception-Behandlung ●
●
●
●
Eine Exception-Behandlung beginnt mit dem Auftreten eines Fehlers, beispielsweise einer Division durch Null. Als Antwort auf den Fehler löst die Funktion, in der der Fehler auftrat, eine Exception aus. Eine Exception ist eine Instanz eines Datentyps. Die Instanz erhält üblicherweise Informationen über den aufgetretenen Fehler. Eine einfache Exception wäre eine String, beispielsweise die Fehlermeldung »Division durch Null«, meist werden aber Instanzen von Klassen benutzt. Die Exception wird mittels des Schlüsselwortes throw ausgelöst. Danach wird in den umgebenden Blockbereichen nach einem Handler für die Exception gesucht. Handler werden also für spezielle Exceptions, sprich Datentypen, definiert. Handler sind durch das Schlüsselwort catch gekennzeichnet. Die Suche wird vom innersten Block, in dem die Exception aufgetreten ist, über die umgebenden Blöcke (sprich Funktionen auf dem Stack) fortgesetzt, bis ein Handler gefunden ist oder die main()-Funktion erreicht wurde (StackAuflösung). Der Bereich, für den der Handler zuständig ist, wird mit dem Schlüsselwort try eingeleitet und in geschweifte Klammern gefaßt.
Beispiele 1: #include 2: #include <string.h> 3: using namespace std;
228
Exception-Behandlung 4: class exception { 5: int fakt1, fakt2; 6: public: 7: exception(int i, int j){fakt1=i; fakt2=j;} 8: void ausgabe() { 9: cout << "fakt1 = " << fakt1 << endl; 10: cout << "fakt2 = " << fakt2 << endl; 11: } 12: }; 13: float division(int zaehler, int fakt1, int fakt2) { 14: try { 15: if(fakt1*fakt2 == 0) { 16: throw exception(fakt1,fakt2); 17: } 18: } 19: catch(exception& fehlerinstanz) { 20: fehlerinstanz.ausgabe(); 21: throw "Fehler aufgetreten\n"; 22: } 23: return zaehler/(fakt1*fakt2); 24: } 25: int main() { 26: try { 27: float bruch; 28: bruch = division(12,3,0); 29: cout << bruch << endl; 30: } 31: catch (...) { 32: cout << "Exception aufgetreten!" << endl; 33: } 34: return 0; 35: }
Ausgabe des Programms: fakt1 = 3; fakt2 = 0; Exception aufgetreten!
Erläuterung Die Klasse exception wird definiert, um eine an den Fehler angepaßte Exception auslösen zu können. Über ihre Datenelemente werden die fehlerrelevanten Informationen transferiert, ihre Elementfunktion garantiert eine saubere Fehlerbehandlung. In Zeile 16 wird die Exception vom Typ der Klasse exception ausgelöst und eine entsprechende Instanz gebildet. Der normale Programmablauf wird daraufhin unterbrochen. Block für Block wird verlassen und nach einem Handler gesucht, der bereits in Zeile 19 gefunden wird.
229
Sprachkonzepte und Syntax
Die erste Fehlermeldung wird ausgegeben, und eine zweite Exception, diesmal vom Typ char*, wird ausgelöst. Wieder wird der Programmablauf unterbrochen, die return-Anweisung wird nicht ausgeführt. In Zeile 31 werden alle Exceptions aufgefangen – soweit nicht schon behandelt. Die zweite Fehlermeldung wird ausgegeben.
Verweise Siehe Kategorie Funktionen, Funktionen und der Stack
Exceptions auslösen throw
Beschreibung Als Anweisung eingesetzt, dient das Schlüsselwort throw dazu, Exceptions auszulösen. Bei der Funktionsdeklaration kann das Schlüsselwort verwendet werden, um anzugeben, welche Art von Exceptions die deklarierte Funktion auslösen kann.
Anwendung Exceptions auslösen
throw exception
Zum Auslösen einer Exception ruft man das Schlüsselwort throw auf und übergibt diesem das Exception-Objekt. Dies löst die Exception aus und legt fest, daß das Argument der Exception-Behandlungsroutine vom gleichen Typ wie das spezifizierte throw-Objekt oder zumindest von einem konvertierbaren Typ sein muß. Auf diese Weise können Sie unterschiedlichen Fehlern (beispielsweise Zeigerfehler, Dateifehler etc.) unterschiedliche throw-Objekte und damit unterschiedliche Behandlungsroutinen zuweisen. Nach dem Auslösen einer Exception wird der nächstgelegene Handler gesucht, d. h. derjenige Handler mit passendem Argument, in dessen try-Block zuletzt eingetreten wurde. throw-Objekte leben nur so lange, bis die Exception-Behandlung abgeschlossen wurde (d. h. der catch-Block verlassen wird).
Das Schlüsselwort throw ohne Objekt kann benutzt werden, um Exceptions innerhalb eines catch-Blocks oder einer Funktion, die in einem catch-Block aufgerufen wird, erneut auszulösen und so an den nächsten Handler weiterzureichen.
230
Exceptions auslösen
Funktionsdeklaration
func() throw;
Das Schlüsselwort throw kann auch am Ende von Funktionsdeklarationen dazu benutzt werden, die Liste der auszulösenden Exceptions festzulegen: void func() throw(exception, char*);
Warnung ●
● ●
●
Wird in einer Funktion eine Exception ausgelöst, deren Typ nicht in der throwListe aufgeführt ist (vermutlich durch eine in der Funktion aufgerufene Funktion), wird die Funktion unexpected()aufgerufen. Eine Funktion, die ohne throw deklariert wurde, kann jede Exception auslösen. Eine Funktion, die mit throw() deklariert wurde, kann keine Exception auslösen. Eine Funktion, die eine Exception der Klasse A auslösen kann, kann auch Exceptions einer von A abgeleiteten Klasse auslösen.
Beispiele class my_exception // Definition einer Exception-Klasse { int fakt1, fakt2; public: my_exception(int i, int j){fakt1=i; fakt2=j;} void ausgabe() { cout << "fakt1 = " << fakt1 << endl; cout << "fakt2 = " << fakt2 << endl; } }; float division(int zaehler, int fakt1, int fakt2) { if(fakt1*fakt2 == 0) { // Division durch Null! throw my_exception(fakt1,fakt2); // Löst Exception vom Typ } // my_exception aus return zaehler/(fakt1*fakt2); }
Verweise Siehe Exceptions abfangen Siehe Handler-Bereich festlegen
231
Sprachkonzepte und Syntax
Exceptions abfangen catch
Beschreibung Das Schlüsselwort catch zeigt den Beginn einer Behandlungsroutine für Exceptions eines bestimmten Typs an. Hinter catch steht in Klammern der Exception-Typ, der abgefangen wird.
Anwendung Mit Hilfe des Schlüsselwortes catch können Exceptions, die in einem vorangehenden try-Block ausgelöst wurden, abgefangen werden. Der Typ der ausgelösten Exception muß mit dem Typ des catch-Handlers übereinstimmen (gewisse Standardkonvertierungen werden berücksichtigt). Hinter catch steht in Klammern der Exception-Typ, der abgefangen wird. Der Typ der ausgelösten Exception muß mit dem Typ des Handlers übereinstimmen. Die Anweisung catch (...) fängt alle Exceptions auf. Wird in dem try-Block eine Exception ausgelöst, die von ihrem Datentyp zu einem catch-Handler paßt, wird der Anweisungsblock des catch-Handlers ausgeführt.
Warnung ●
●
Auf einen try-Block können mehrere Handlerdefinitionen, jeweils mit catch eingeleitet, folgen. Die Handler werden der Reihe nach überprüft, bis eine Übereinstimmung gefunden wird. Die Anweisung catch (...) fängt alle Exceptions auf. Der Handler catch (...) sollte daher nur am Ende einer Handler-Liste stehen.
Beispiele try { ... // Code, in dem Exceptions auftreten können } catch (...) { // alle Exceptions abfangen cout << "Exception aufgetreten!" << endl; }
Verweise Siehe Exceptions auslösen Siehe Handler-Bereich festlegen
232
Handler-Bereich festlegen
Handler-Bereich festlegen try
Beschreibung Gültigkeitsbereiche von Handlern fallen nicht mit den üblichen Blöcken zusammen, sondern werden durch das Schlüsselwort try eingeleitet und in geschweifte Klammern gefaßt. An den try-Block schließt sich direkt das Schlüsselwort catch mit der Liste der Handler an.
Anwendung Viele C++-Bibliotheken sind so implementiert, daß die Funktionen und Methoden der Bibliothek im Falle gravierender Fehler Exceptions auslösen. Wenn Sie diese (oder selbst definierte und ausgelöste) Exceptions abfangen wollen, müssen Sie die Anweisungen, die zur Auslösung der Exceptions führen können, in einen try-Block fassen und an den try-Block einen oder mehrere catch-Handler anfügen.
Beispiele try { ... }
// Code, in dem Exceptions auftreten können
catch (char* s) { // Exceptions vom Typ char* abfangen cout << s << endl; } catch (...) { // alle Exceptions abfangen cout << "Exception aufgetreten!" << endl; }
Verweise Siehe Exceptions auslösen Siehe Exceptions abfangen
Verwandte Funktionen Beschreibung Mit der Exception-Behandlung sind die Funktionen unexpected() und terminate() sowie set_unexpected() und set_terminate() verbunden.
233
Sprachkonzepte und Syntax
Anwendung ●
●
●
Die Funktionen unexpected() und terminate() werden benötigt, um im Falle eines Versagens der Exception-Behandlung das Programm abzubrechen. Dies ist zum Beispiel der Fall, wenn innerhalb einer Funktion eine Exception ausgelöst wird, die die Funktion nicht behandeln soll (throw-Deklaration). In diesem Fall wird die Funktion unexpected() aufgerufen, die wiederum standardmäßig terminate() aufruft. Die Funktion terminate() wird direkt aufgerufen, wenn Stack-Probleme auftauchen oder für eine ausgelöste Exception kein Handler gefunden wurde. Die Funktion terminate() ruft standardmäßig die Funktion abort() zum Beenden des Programmes auf. Die Funktionen set_unexpected() und set_terminate() dienen dazu, die Funktionsaufrufe aus unexpected() und terminate() auf eigene Funktionen umzulenken.
Beispiele #include #include <string> using namespace std; // Handlerfunktion für unexpected Exceptions void unerwartete_Ausnahme() { cout << "Damit war nicht zu rechnen!" << endl; terminate(); } // Funktion, die Exception auslöst void ausnahme() { throw string("Fehler aufgetreten!!");
234
C-Strings } // Funktion die keine Exceptions auslösen soll, // ruft sonst unexpected() auf. void func() throw() { ausnahme(); } int main() { set_unexpected(unerwartete_Ausnahme); func();
// // // //
unexpected-Handler registrieren unexpected-Exception provozieren
return 0; }
Verweise Siehe Exceptions auslösen
Zeichenketten C-Strings Beschreibung C kennt keinen eigenen Datentyp für Zeichenketten (Strings). Statt dessen werden Strings einfach als Folgen von Zeichen interpretiert. Ein solcher String beginnt an einer bestimmten Adresse im Speicher und endet mit dem Auftreten des sogenannten Nullterminierungszeichen '\0'.
Anwendung Definiert werden Zeichenketten als Zeiger oder Arrays für einen der Datentypen char (für 7- oder 8-Bit-Zeichensätze wie ASCII oder ANSI) oder wchar_t (für 16-BitZeichensätze wie UNICODE). char *ptr; char str1[25];
235
Sprachkonzepte und Syntax
Die Deklaration als Array hat den Vorteil, daß man gleich mit der Deklaration der Stringvariablen auch Speicher reserviert. Diesen muß man bei der Definition eines Zeigers auf eine Zeichenkette nachträglich selbst reservieren (mit malloc() oder new) – mit dem Vorzug, daß die Größe des Speicherbereichs zur Laufzeit ermittelt und bei Bedarf auch neuer Speicher reserviert werden kann. Zur Manipulation der Zeichenketten sind in der C-Laufzeitbibliothek eine Reihe von Funktionen definiert, die alle mit str... beginnen und in der Header-Datei string.h deklariert sind.
Warnung Wenn Sie einen C-String in Form eines Arrays definieren, beachten Sie, daß der String nicht mit dem Array, sondern mit dem Nullterminierungszeichen '\0' endet. Wenn Sie also eine Zeichenkette in ein Array kopieren wollen, achten Sie darauf, daß das Array groß genug ist, um alle Zeichen der Zeichenkette einschließlich des Nullterminierungszeichens '\0' aufzunehmen. Wenn Sie mit strlen() die Länge einer Zeichenkette ermitteln wollen, die in einem Array abgelegt ist, erhalten Sie die Anzahl der Zeichen im String (ohne Nullterminierungszeichen) und nicht die Größe des Arrays zurück. Wenn Sie mit printf() eine Zeichenkette, die in einem Array abgelegt ist, ausgeben, werden alle Zeichen von der Adresse des Arrays bis zum nächsten Nullterminierungszeichen '\0' ausgegeben. Den String-Funktionen der C-Laufzeitbibliothek haftet das Manko an, daß die Speicherverwaltung gänzlich dem Programmierer überlassen wird. Will man also beispielsweise mit Hilfe von strcpy() einen String B in einen String A kopieren, muß zuvor sichergestellt werden, daß der Speicherplatz, der für String A reserviert wurde, groß genug ist, um auch String B aufnehmen zu können. Zur Reservierung von Speicher für Strings gibt es mehrere Möglichkeiten.
Beispiele #include <stdio.h> #include <string.h> int main() { char string1[] = "C und C++"; char string2[] = "C++ und C"; if(strcmp(string1, string2) > 0) printf("%s ist groesser als %s\n",string1,string2); else printf("%s ist kleiner oder gleich %s\n", string1,string2); return 0; }
236
Die Klasse string
Verweise Siehe Die Klasse string
Die Klasse string string
Beschreibung In C++ kann man zur Programmierung mit Text auf die Klasse string (wstring für Wide Character-Strings) zurückgreifen.
Anwendung Die Klasse string geht zurück auf das Template basic_string, das in der HeaderDatei <string> definiert ist: template class basic_string;
Was wir der Einfachheit halber als string-Klasse bezeichnen, ist also in Wirklichkeit nichts anderes als ein Alias für die Instanziierung des Templates für den Zeichentyp char. typedef basic_string string;
Analog gibt es eine entsprechende Typdefinition wstring für den Zeichentyp wchar_t. Zur Manipulation der Strings verwenden Sie die in basic_string definierten Methoden und Operatoren. Die Verwendung dieser Methoden und Operatoren erleichtert nicht nur die Arbeit mit String (im Vergleich zu den C-Strings), sie ist auch wesentlich sicherer, da die Implementierung der string-Klasse für die korrekte Speicherverwaltung sorgt. Zur Unterstützung bestehenden C-Codes verfügt die Klasse string über eine Methode c_str(), die den in einem string-Objekt gekapselten Text als C-String zurückliefert.
Beispiele #include #include <string> using namespace std; int main() { string str1("C und C++"); string str2 = "C++ und C";
237
Sprachkonzepte und Syntax if(str1 > str2) cout << str1 << " ist groesser als " << str2 << endl; else cout << str1 << " ist kleiner oder gleich " << str2 << endl; return 0; }
Verweise Siehe Die Klasse string
Wide characters und Multibyte-Zeichen wchar_t
Beschreibung Der Datentyp wchar_t (in C noch mittels typedef definiert) für »wide characters« wurde eingerichtet, um mit Zeichensätzen arbeiten zu können, die entgegen dem ASCII-Zeichensatz nicht durch 8 Bit codiert werden könne (beispielsweise UNICODE). Die Wide characters sind von den Multibyte-Zeichen zu unterscheiden. Während die Wide characters zu den Integer-Typen gehören, werden Multibyte-Zeichen als Folge von Bytes behandelt. Die Anzahl der Bytes, die zu einem Multibyte-Zeichen gehören, kann dabei variieren. Dies kann sehr speichersparend sein, beispielsweise wenn man in einer Datei deutschen Text (für dessen Zeichen man nur 1 Byte benötigt) mit chinesischen Schriftzeichen (für deren Codierung mindestens 2 Byte benötigt werden) mischt. Alle gängigen Multibyte-Codierungsverfahren verwenden daher irgendwelche Escape-Sequenzen, die dazu dienen, zwischen verschiedenen Zeichensätzen hin- und herzuschalten und anzuzeigen, ob die nachfolgenden Zeichen durch eines oder zwei oder mehrere Bytes codiert werden. Innerhalb eines Programmes werden Multibyte-Zeichen allerdings wegen ihrer variablen Länge und unbequemen Handhabung kaum verwendet. Meist wandelt man daher MultibyteZeichen beim Einlesen der Datei in WideCharacter-Zeichen um, arbeitet mit diesen und schreibt das Ergebnis dann wieder als Multibyte-Zeichen in die Datei zurück.
Anwendung Die C-Bibliothek enthält etliche Methoden zur Programmierung mit Multibyte-Zeichen und Wide characters. Die Multibyte-Funktionen erkennt man an der Zeichenfolge »mb«, wie in mblen(), mbrlen(), mbtowc(), wctomb() oder mbstowcs(), wcstombs(), mbsinit(). Die Wide character-Funktionen erkennt man an dem »w« im Funktionsnamen, wie in wprintf(), wmemcpy() oder wcscpy().
238
Formatierte und unformatierte E/A
Ein- und Ausgabe Formatierte und unformatierte E/A Beschreibung Letztendlich werden Daten (Integer-Werte, Strings, Strukturvariablen, etc.) immer in Form von Bitströmen ausgetauscht. Dies bedeutet aber nicht, daß man sich auch bei der Programmierung mit Bitströmen herumschlagen müßte. C und C++ unterstützen den Datenaustausch sowohl in Form von ●
unformatierten Byte-Strömen als auch in
●
formatierten Dateneinheiten.
Anwendung Bei der formatierten Ein- und Ausgabe können die Daten in ihrem ursprünglichen Datenformat ein- und ausgegeben werden: int integer = 3; char zeichen = 'A'; printf("%d",integer); printf("%c",zeichen); cout << integer; cout << zeichen;
// formatierte Ausgabe in C // formatierte Ausgabe in C++
Bei der unformatierten Ein- und Ausgabe werden die Daten dagegen als eine Folge von Bytes behandelt: fwrite(&zeichen, sizeof(char), 1, stdout); fwrite(&integer, sizeof(int), 1, stdout); cout.put(zeichen); cout.write((char *) &integer, sizeof(int));
// C // C++
Bei der formatierten Ein- und Ausgabe können die Daten durch explizite Typumwandlung im Zuge der Ausgabe auch umformatiert werden. Zudem kann die Ausgabe mit Hilfe von Sonderzeichen (\n für Zeilenumbruch, \t für Tabulator) und speziellen Formatierungsanweisungen weiter formatiert werden.
Warnung Ein- und Ausgabe sind betriebssystemspezifische Vorgänge. Aus diesem Grund sind sie auch nicht fest (in Form von Schlüsselworten) in der Sprache verankert, sondern durch Funktionen/Klassen der Laufzeitbibliothek implementiert. Diese unterstützen aber nur die Ein- und Ausgabe für Konsolenprogramme. Für Windows-Programme (unter UNIX XWindows oder OSF Motif) kann man diese Funktionen nicht nutzen, sondern muß auf spezielle Funktionen zurückgreifen, die in den APIs der Windows-Systeme enthalten sind.
239
Sprachkonzepte und Syntax
Beispiele Umformatierung bei der formatierten Ausgabe: char zeichen = 'A'; printf("%d",zeichen); cout << (int) zeichen;
// Ausgabe: 65 // Ausgabe: 65
Verwendung von Sonderzeichen und speziellen Formatierungsanweisungen: // Feldbreite der Ausgabe auf 5 Zeichen setzen printf("%5d",integer); printf("%5c\n",zeichen); // Ausgabe: 3
A
// Feldbreite der Ausgabe auf 5 Zeichen setzen cout.width(5); cout << integer; cout.width(5); cout << zeichen << endl; // Ausgabe: 3
A
Verweise Siehe Referenz der Laufzeitbibliotheken, Beispiele zu Header-Dateien
Streams in C Beschreibung Streams sind Datenströme, über die Daten von einer Quelle zu einem Ziel bewegt werden. Die Stream-Objekte, mit denen wir arbeiten, bezeichnen jeweils ein Ende eines Datenstroms (Quelle oder Ziel), verfügen über interne Informationen über die Abwicklung des Datenaustauschs und sind standardmäßig mit einem Zwischenpuffer verbunden.
Anwendung In C stehen die folgenden Standard-Stream-Objekte zur Verfügung: stdin
Die Standardeingabe, typischerweise die Tastatur
stdout
Die Standardausgabe, typischerweise der Bildschirm (genauer gesagt, die Konsole).
stderr
Die Standard-Fehlerausgabe, typischerweise die Konsole, allerdings mit der Besonderheit, daß dieser Stream nicht gepuffert ist.
Zur Definition eigener Streams, insbesondere zur Ein- und Ausgabe in Dateien, steht der in <stdio.h> definierte Datentyp FILE zur Verfügung.
240
Streams in C // mögliche Definition für FILE: typedef struct { int level; // Fuellung des Puffers unsigned flags; // Dateistatus char fd; // Dateideskriptor unsigned char hold; // Ungetc char, wenn kein Puffer int bsize; // Puffergroesse unsigned char *buffer; // Puffer unsigned char *curp; // Aktiver Zeiger unsigned istemp; // Temporaere Datei short token; // Gueltigkeit } FILE;
Zur Manipulation der Streams und der Ein- und Ausgabe über die Streams werden die entsprechenden Funktionen der C-Laufzeitbibliothek verwendet: C-Funktion
Beschreibung
fclose
Datenstrom schließen.
feof
Testet Datenstrom auf Dateiende.
ferror
Testet Datenstrom auf Fehler.
fflush
Leert Puffer eines Datenstroms.
fgetc
Liest Zeichen von einem Datenstrom.
fgetpos
Ermittelt Positionszeiger eines Datenstroms.
fgets
Liest eine Zeile von einem Datenstrom.
fopen
Öffnet einen Datenstrom.
fprintf
Formatierte Ausgabe an einen Datenstrom.
fputc
Schreibt ein Zeichen an Datenstrom.
fputs
Schreibt einen String an Datenstrom.
freopen
Öffnet einen Datenstrom erneut.
fscanf
Liest formatiert von Datenstrom.
fseek
Positioniert Dateizeiger neu.
fsetpos
Positioniert Dateizeiger neu.
ftell
Liest Position des Dateizeigers.
getc
Liest Zeichen aus Datenstrom.
getchar
Liest Zeichen aus Datenstrom stdin.
gets
Liest String von stdin.
printf
Formatierte Ausgabe an stdout.
putc
Schreibt Zeichen in Datenstrom.
putchar
Schreibt Zeichen an Datenstrom stdout.
puts
Schreibt Zeichenkette an Datenstrom stdout.
rewind
Positioniert Dateizeiger auf Anfang des Datenstroms.
scanf
Formatierte Eingabe von stdin.
241
Sprachkonzepte und Syntax
C-Funktion
Beschreibung
setbuf
Einrichtung eines eigenen Datenstrompuffers
setvbuf
Datenstrompufferung und Puffergröße verändern.
sprintf
Formatierte Ausgabe in einen String.
sscanf
Formatierte Eingabe aus einem String.
ungetc
Zeichen wieder zurück an Datenstrom schicken.
Streams in C++ Beschreibung Streams sind Datenströme, über die Daten von einer Quelle zu einem Ziel bewegt werden. Die Stream-Objekte, mit denen wir arbeiten, bezeichnen jeweils ein Ende eines Datenstroms (Quelle oder Ziel), verfügen über interne Informationen über die Abwicklung des Datenaustauschs und sind standardmäßig mit einem Zwischenpuffer verbunden.
Anwendung In C++ stehen die folgenden Standard-Stream-Objekte zur Verfügung: char
wchar_t
Externes Gerät
cin
wcin
Die Standardeingabe, typischerweise die Tastatur
cout
wcout
Die Standardausgabe, typischerweise der Bildschirm (genauer gesagt, die Konsole).
cerr
wcerr
Die Standard-Fehlerausgabe, typischerweise die Konsole, allerdings mit der Besonderheit, daß dieser Stream nicht gepuffert ist.
clog
wclog
Die Standard-Protokollausgabe, typischerweise die Konsole.
Eigene Streams können als Instanzen der vordefinierten Stream-Klassen eingerichtet werden. Zudem besteht die Möglichkeit, von den in der Laufzeitbibliothek definierten Stream-Templates durch Spezialisierung eigene Stream-Klassen (beispielsweise für selbstdefinierte Zeichentypen) abzuleiten. Zur Manipulation der Streams und der Ein- und Ausgabe über die Streams werden die Methoden und überladenen Operatoren der Stream-Klassen verwendet.
Formatierung Formatieren läßt sich die Ausgabe mittels der folgenden Stream-Methoden
242
Streams in C++
Methode
Beschreibung
width()
Mit Hilfe der Methode width() kann man für die Ausgabe des nächsten Werts eine minimale Breite festlegen. Umfaßt die Ausgabe weniger Stellen, als width() als Argument übergeben wurde, werden Füllzeichen eingefügt. Das endgültige Format hängt dann vom ausgewählten Füllzeichen (siehe fill()) und der Ausrichtung (left, right, internal) ab.
fill()
Mit der Methode fill() können Sie ein Füllzeichen festlegen. Füllzeichen werden verwendet, wenn Sie für eine Ausgabe eine Feldbreite wählen (siehe oben), die größer ist als die eigentliche Ausgabe.
precision()
Mit der Methode precision() kann die Anzahl der Ziffernstellen bei der Ausgabe von Fließkommazahlen festgelegt werden. Standardwert sind dabei 6 Stellen. Allerdings ist dabei zu unterscheiden, ob die Fließkommazahlen im Standardmodus ausgegeben werden, oder ob zuvor eines der Format-Flags fixed oder scientific gesetzt wurde. Im Standardmodus legt precision() die Gesamtzahl der Ziffern fest. Im fixed- oder scientific-Modus legt precision() die Zahl der Nachkommastellen fest.
●
mittels Formatierungsflags, die über die Methoden setf() und unsetf() gesetzt und gelöscht werden können. Die Formatierungsflags sind als Elemente der Klasse ios_base (in nicht ganz aktuellen Compilern ios) definiert: Flag
Wirkung
boolalpha
Boolesche Werte als Zahlen oder als Worte
left
linksbündige Ausgabe
entspricht in C
Vorgabe 0
-0
right
dec
right
rechtsbündige Ausgabe
internal
Freiraum nach Vorzeichen oder Basis
dec
Dezimaldarstellung
%d, %u
oct
Oktaldarstellung
%o
hex
Hexadezimaldarstellung
%x
fixed
Feste Stellenzahl
%g, %f
scientific
Exponentialschreibweise
%e, %E
showbase
Basis anzeigen
showpoint
Dezimalpunkt anzeigen
#
0
showpos
Plus-Zeichen mit ausgeben
+
0
fixed
0
243
Sprachkonzepte und Syntax
●
Flag
Wirkung
entspricht in C
skipws
Whitespace-Zeichen bei Eingabe überlesen
1
unitbuf
Nach Formatierung Ausgabe-Stream leeren
0
uppercase
Bestimmte Kleinbuchstaben (z.B. HexZeichen) als Großbuchstaben ausgeben
%X %E %G
Vorgabe
0
mittels Manipulatoren. Dies sind Funktionen, die als Operand an Streamoperatoren übergeben werden können. Manipulator
E/A
Wirkung
boolalpha
e/a
wie setf(ios_base::boolalpha)
noboolalpha
e/a
wie unsetf(ios_base::boolalpha)
left
a
wie setf(ios_base::left, ios_base::adjustfield)
right
a
wie setf(ios_base::right, ios_base::adjustfield)
internal
a
wie setf(ios_base::internal, ios_base::adjustfield)
dec
e/a
wie setf(ios_base::dec, ios_base::basefield)
oct
e/a
wie setf(ios_base::oct, ios_base::basefield)
hex
e/a
wie setf(ios_base::hex, ios_base::basefield)
fixed
a
wie setf(ios_base::fixed, ios_base::floatfield)
scientific showbase
wie setf(ios_base::scientific, ios_base::floatfield) a
wie setf(ios_base::showbase)
noshowbase
a
wie unsetf (ios_base::showbase)
showpoint
a
wie setf(ios_base::showpoint)
noshowpoint
a
wie unsetf (ios_base::showpoint)
showpos
a
wie setf(ios_base::showpos)
noshowpos
a
wie unsetf (ios_base::showpos)
skipws
e
wie setf(ios_base::skipws)
noskipws
e
wie unsetf(ios_base::skipws)
unitbuf
a
wie setf(ios_base::unitbuf)
nounitbuf
a
wie unsetf(ios_base::unitbuf)
uppercase
a
wie setf(ios_base::uppercase)
nouppercase
wie unsetf (ios_base::uppercase)
resetiosflags (ios_base::fmtflags mask)
e/a
Flags löschen
endl
a
Neue Zeile und Stream leeren
244
Streams in C++
Manipulator
E/A
Wirkung
ends
a
Nullterrminierungszeichen ('\t') ausgeben
flush
a
Stream leeren
setbase(int base)
e/a
Basis für Integer-Werte
setfill(charT c)
e/a
wie fill(c)
setiosflags (ios_base::fmtflags mask)
e/a
Flags setzen
setprecision(int n)
e/a
wie precision(n)
setw(int n)
e/a
wie width(n)
ws
e
Whitespace-Zeichen überlesen
Fehlerbehandlung Zur Fehlerbehandlung enthält jeder Stream die folgenden Statusbits, die im Falle eines Fehlers von den Streams aus 1 gesetzt werden. Statusbit
Fehler
ios_base::goodbit
Kein Fehler aufgetreten, alles ist in Ordnung. (In Wirklichkeit kein Bit, sondern der Wert 0).
ios_base::eofbit
Beim Einlesen wurde das EOF-Zeichen erreicht (kann bei der Eingabe von Tastatur zur Not durch Strg+D oder Strg+Z simuliert werden).
ios_base::failbit
Bei der Eingabe konnte das erwartete Zeichen (beispielsweise wegen falschem Format) nicht eingelesen werden. Bei der Ausgabe konnte das gewünschte Zeichen nicht ausgegeben werden.
ios_base::badbit
Der Stream kann aus irgendeinem Grund nicht mehr korrekt arbeiten (meist ist dies der Fall, wenn es Probleme mit dem zugrundeliegenden Puffer (basic_streambuf-Objekte) gibt).
Zu den Statusbits gibt es die folgenden Methoden, mit denen der Zustand der Streams abgefragt werden kann. Methode
Beschreibung
bool good()
true, wenn kein Statusbit gesetzt wurde
bool eof()
true, wenn eofbit gesetzt wurde
bool fail()
true, wenn failbit oder badbit gesetzt wurde
bool bad()
true, wenn badbit gesetzt wurde
bool operator!()
true, wenn failbit oder badbit gesetzt wurde
iostate rdstate()
Liefert den gesamten Streamstatus zurück
245
Sprachkonzepte und Syntax
Pufferung Beschreibung Streams sind grundsätzlich gepuffert, d. h., die Eingaben oder Ausgaben erfolgen nicht direkt zum Ziel des Streams, sondern werden im Speicher zwischengepuffert. Der Sinn der Pufferung ist es, die Ein- und Ausgabe effizienter zu gestalten. Meist greift man bei der Ein- oder Ausgabe auf externe Geräte zu (Tastatur, Bildschirm, Festplatte, Laufwerk). Diese Zugriffe sind stets um ein Vielfaches langsamer als Zugriffe auf Speicherbereiche. Man kann den Datenaustausch aber beschleunigen, indem man die Daten Zeichen für Zeichen in einem Puffer im RAM zwischenspeichert und dann in möglichst großen zusammenhängenden Blöcken überträgt.
Anwendung Jeder Puffer hat eine festgelegte Größe. Ist der Puffer voll, leert er sich selbst. Darüber hinaus gibt es aber auch Möglichkeiten, einen Puffer direkt leeren zu lassen. ● ●
●
●
Durch Aufruf der Methode flush(). Der Tastaturpuffer (Streams stdin, cin) kann durch Drücken der EingabeTaste geleert werden. Der Konsolenpuffer (Streams stdout, cout, clog) wird bei Ausgabe des Newline-Zeichens geleert (cout << "\n"; oder cout << endl;). Zudem ist der Ausgabepuffer cout (stdout) mit dem Eingabepuffer cin (stdin) verbunden, so daß vor jeder erwarteten Eingabe der Ausgabepuffer geleert wird. Die Fehlerausgabe cerr (stderr) ist nicht gepuffert.
Hinter den Puffern stehen in C++ Klassen, die alle auf das Klassentemplate basic_streambuf zurückgehen. Die Verbindung zwischen einem Stream und seinem Puffer wird bei der Instanzbildung des Streams durch den Konstruktor hergestellt. Nach der Initialisierung kann der Stream über die Methode rdbuf() auf seinen Puffer zugreifen, ●
●
entweder um sich eine Referenz auf den eigenen Puffer zurückliefern zu lassen und dann diesen über seine public-Schnittstelle zu manipulieren (beispielsweise zur Anpassung der Puffergröße mit setbuf() ) oder um den Stream mit einem anderen Puffer zu verbinden (beispielsweise mit dem Puffer eines anderen Streams).
246
assert.h
Die C-Standardbibliothek Die Header-Dateien Um die C-Funktionen in Ihren Programmen aufrufen zu können, müssen Sie zuvor die Funktionsdeklarationen dem Compiler bekannt machen. Dazu dienen die Header-Dateien, in denen die Funktionen thematisch geordnet sind, und die Sie mittels der #include-Direktive in Ihre Programme aufnehmen können. Im folgenden werden die Funktionen nach Header-Dateien geordnet vorgestellt, wobei die Namen der C-Konvention verwendet wurden. Nach dem neuen C++Standard wird den Dateinamen der C-Header-Dateien jeweils ein »c« vorangestellt und die Extension weggelassen (stdlib.h wird also zu cstdlib). Der Unterschied zu den C-Headern ist dabei, daß die Elemente in den C++-Headern im Namensbereich std deklariert sind.
assert.h Beschreibung Enthält allein das Makro assert, das zum Debuggen verwendet werden kann.
Enthaltene Elemente assert
// Prüft eine Bedingung und bricht bei Nichterfüllung // das Programm ab.
Beispiel #include <stdio.h> #include int main(int argc, char **argv) { int i = 3, j = 2; double erg; // ... weitere Anweisungen assert(j != 0); // Division durch Null vermeiden erg = (double) i/j; printf("Division ergibt : %f\n",erg); return 0; }
247
Die C-Standardbibliothek
ctype.h Beschreibung Enthält die Funktionen zur Klassifizierung (is...) und Konvertierung (to...) einfacher Zeichen. Die Funktionen dieser Header-Datei verwendet man üblicherweise zur Aufbereitung oder Kontrolle von Benutzereingaben über die Tastatur. So können Sie testen, ob der Anwender wie vom Programm gewünscht eine Zahl als Ziffernfolge oder unerlaubter Weise als ausgeschriebenes Wort eingegeben hat. Oder Sie können Benutzereingaben mit Hilfe der Funktionen tolower()/toupper() in Klein- oder Großbuchstaben verwandeln.
Enthaltene Elemente isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper
// // // // // // // // // // // // //
Testet, Testet, Testet, Testet, Testet, Testet, Testet, Testet, Testet, Testet, Testet, Wandelt Wandelt
ob Zeichen eine Ziffer oder ein Buchstabe ist. ob Zeichen ein Buchstabe ist. ob Zeichen ein Kontrollzeichen ist. ob Zeichen eine Zahl ist. ob Zeichen darstellbar ist. ob Zeichen ein Kleinbuchstabe ist. ob Zeichen druckbar ist. ob Zeichen ein Interpunktionszeichen ist. ob Zeichen ein Zwischenraumzeichen ist. ob Zeichen ein Großbuchstabe ist. ob Zeichen eine Hexadezimalziffer ist. Groß- in Kleinbuchstaben um. Klein- in Großbuchstaben um.
Beispiel char zeichen, *str; ... for(i = 0; i < strlen(str); i++) str[i] = tolower(str[i]);
if(isprint(zeichen)) putchar(zeichen);
errno.h Beschreibung Enthält verschiedene Makros, die von verschiedenen Bibliotheksfunktionen zur Benachrichtigung über aufgetretene Fehler verwendet werden.
248
float.h
Enthaltene Elemente EDOM EILSEQ
ERANGE errno
// Wird im Falle eines ungültigen Funktionsarguments von den // mathematischen Funktionen in errno kopiert. // Wird im Falle eines Codierungsfehlers von Multibyte in // Wide Character oder umgekehrt von den Funktionen // mbrtowc() bzw. wcrtomb() in errno kopiert. // Wird im Falle eines ungültigen Ergebniswertes von den // mathematischen Funktionen in errno kopiert. // Wird im Fehlerfall von den mathematischen Funktionen // gesetzt.
Beispiel #include <stdio.h> #include <math.h> #include <errno.h> int main(int argc, char **argv) { int i = -3; double wurzel; errno = 0; wurzel = sqrt(i); if(errno == EDOM) puts("Domain Error bei Aufruf von sqrt()\n"); else printf("Wurzel von %d ist %f\n",i,wurzel); return 0; }
float.h Beschreibung Enthält verschiedene Makros, die zu verschiedenen implementierungsspezifischen Konstanten expandieren. Aufgeführt sind nur die Konstanten für den Datentyp float. Die Konstanten für double und long double beginnen mit den Suffixen DBL beziehungsweise LDBL. (FLT_ROUNDS gilt für alle drei Gleitkommatypen).
Enthaltene Elemente FLT_DIG
// Anzahl der signifikanten Ziffern in der Mantisse,
FLT_EPSILON
// Differenz zwischen 1 und der nächstgrößeren
FLT_MANT_DIG
// Anzahl der Ziffern in der Mantisse, wenn dargestellt
FLT_MAX FLT_MAX_10_EXP
// Größte positive Zahl. // Größte positive Zahl für den Exponenten zur Basis 10.
// wenn dargestellt zur Basis 10 // darstellbaren Zahl. // zur Basis FLT_RADIX
249
Die C-Standardbibliothek FLT_MAX_EXP
// Größte positive Zahl für den Exponenten zur
FLT_MIN FLT_MIN_10_EXP
// Kleinste positive Zahl (Wie nah kommt man an 0 heran) // Kleinste negative Zahl für den Exponenten zur
FLT_MIN_EXP
// Kleinste negative Zahl für den Exponenten zur
FLT_RADIX FLT_ROUNDS
// Basis des Exponenten // Modus, nach dem Zahlen gerundet werden:
// Basis FLT_RADIX
// Basis 10. // Basis FLT_RADIX
// // // // //
-1 0 1 2 3
unbestimmt zu Null zum nächsten Wert zu +unendlich zu -unendlich
ios646.h Beschreibung Synonyme für bestimmte Operatoren.
Enthaltene Elemente Synonym
für
and
&&
Logische UND-Verknüpfung
and_eq
&=
Bitweise UND-Verknüpfung und Zuweisung
bitand
&
Bitweise UND-Verknüpfung
bitor
|
Bitweises ODER
compl
~
Komplement
not
!
Nicht-Operator, Verneinung
not_eq
!=
vergleicht zwei arithmetische Operanden auf Ungleichheit
or
||
Logisches ODER
or_eq
|=
Bitweises ODER und Zuweisung des Ergebnisses
xor
^
Bitweises exklusives ODER
xor_eq
^=
Bitweises exklusives ODER und Zuweisung des Ergebnisses
250
limits.h
limits.h Beschreibung Enthält Konstanten, die Größe und Wertebereiche der elementaren Datentypen festlegen (die Werte sind implementierungsspezifisch und werden vom Compiler festgelegt).
Enthaltene Elemente CHAR_BIT CHAR_MIN CHAR_MAX INT_MIN INT_MAX LONG_MIN LONG_MAX MB_LEN_MAX SCHAR_MIN SCHAR_MAX SHRT_MIN SHRT_MAX UCHAR_MAX UINT_MAX ULONG_MAX USHRT_MAX
// // // // // // // // // // // // // // // //
Anzahl der Bits in kleinstem Datentyp kleinster Wert für Objekte vom Typ char größter Wert für Objekte vom Typ char kleinster Wert für Objekte vom Typ int größter Wert für Objekte vom Typ int kleinster Wert für Objekte vom Typ long int größter Wert für Objekte vom Typ long int maximale Anzahl von Bytes in Multibyte-Zeichen kleinster Wert für Objekte vom Typ signed char größter Wert für Objekte vom Typ signed char kleinster Wert für Objekte vom Typ short int größter Wert für Objekte vom Typ short int größter Wert für Objekte vom Typ unsigned char größter Wert für Objekte vom Typ unsigned int größter Wert für Objekte vom Typ unsigned long int größter Wert für Objekte vom Typ unsigned short int
locale.h Beschreibung Enthält die Funktionen localeconv() und setlocale() zum Einstellen und Abfragen landesspezifischer Eigenheiten. Rückgabetyp von localeconv() ist die Struktur lconv. Die LC-Makros können setlocale() als Argument übergeben werden, um spezielle Facetten einer Lokale auszuwählen.
Enthaltenen Elemente LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC
// // // // //
LC_TIME struct lconv localeconv setlocale
// // // //
//
Alle Kategorien landesspez. Einstellungen. Für String-Vergleiche. Für die Zeichenklassifizierung und -umwandlung. Für Währungsformate. Für Zahlenformate, beispielsweise Dezimalpunkt in Gleitkommazahlen. Für Datums- und Zeitangaben. Die Struktur lconv . Die Struktur lconv initialisieren. Landesspezifische Werte abfragen und verändern.
251
Die C-Standardbibliothek
Beispiel Aktuelle Lokale-Einstellungen abfragen: struct lconv ll; struct lconv *conv = ≪ conv = localeconv(); printf("Dezimalpunkt : %s\n", conv->decimal_point);
Lokale wechseln setlocale(LC_ALL, "de_DE");
math.h Beschreibung Deklariert die mathematischen Funktionen. Während unter C nur die double-Versionen der Funktionen verfügbar sind, sind unter C++ zusätzlich für die Datentypen float und long double überladen.
Enthaltene Elemente HUGE_VAL
acos asin atan atan2 ceil cos cosh exp fabs floor fmod frexp ldexp log log10 modf pow sin sinh sqrt tan tanh
252
// Makro, das zu einem double-Wert expandiert, der von den // math. Funktionen im Falle eines Überlaufes // zurückgeliefert wird. // Arkuskosinus berechnen. // Arkussinus berechnen. // Arkustangens von x berechnen. // Arkustangens von x/y berechnen. // Rundet auf nächste Ganzzahl auf. // Kosinus berechnen. // Kosinus hyperbolicus berechnen. // Exponentialfunktion berechnen. // Ermittelt Absolutwert. // Größte Ganzzahl ermitteln, die kleiner oder gleich dem // Argument ist. // Rest einer Gleitkomma-Division berechnen. // Exponentialwert berechnen. // Gleitkommazahl multiplizieren. // Natürlichen Logarithmus berechnen. // Logarithmus zur Basis 10 berechnen. // Argument in ganzzahligen und gebrochenen Teil zergliedern // Zahl potenzieren. // Sinus berechnen. // Sinus hyperbolicus berechnen. // Quadratwurzel berechnen. // Tangens berechnen. // Tangens hyperbolicus berechnen.
setjmp.h
Beispiel double zahl; printf("Geben Sie ein Zahl: \n\n"); scanf("%lf", &zahl); fflush(stdin); printf(" Quadrat von %lf = %lf\n",zahl,zahl*zahl); printf(" %lf^3 = %lf\n",zahl,pow(zahl,3)); printf(" Log.e von %lf = %lf\n",zahl,log(zahl)); printf(" Log.10 von %lf = %lf\n",zahl,log10(zahl)); printf(" Sinus von %lf (RAD) = %lf\n",zahl,sin(zahl)); printf(" Sinus von %lf (GRAD) = %lf\n",zahl,sin(2*3.1415*zahl/360));
setjmp.h Beschreibung Definiert den Datentyp jmp_buf, der von dem Makro setjmp() und der Funktion longjmp() benutzt wird.
Enthaltene Elemente jmp_buf
// Nimmt die Werte der aktuellen Umgebung, typischerweise
longjmp
// die Inhalte der Prozessorregister sowie Stack- und // Framezeiger auf, um einen nicht lokalen Sprung zu // ermöglichen. // Aufrufumgebung gemäßt jmp_buf-Argument wiederherstellen // nach nicht lokalem Sprung. // Aufrufumgebung in jmp_buf-Argument sichern. Umgebung // kann mittels longjmp() wiederhergestellt werden.
setjmp
Beispiel #include <stdio.h> #include <setjmp.h> #include <stdlib.h> void func(jmp_buf sprung) { puts("Sprung aus Funktion heraus durchfuehren\n"); longjmp(sprung,1); } int main(int argc, char** argv) { int wert; jmp_buf sprung; if(wert = setjmp(sprung)) { printf("Aus Sprung mit Wert %d zurueckgekehrt\n", wert); exit(wert); }
253
Die C-Standardbibliothek func(sprung); return 0; }
signal.h Beschreibung Definiert Konstanten und Funktionen, die für die Signalbearbeitung (Interrupts, Segmentation faults, etc.) erforderlich sind. Die SIG_-Konstanten sind kompatibel zum zweiten Parameter von signal(), dem ansonsten eine Signalbehandlungsfunktion übergeben wird. Die SIG-Konstanten kategorisieren die Signale und dienen als Argumente für den ersten Parameter der signal()-Funktion.
Enthaltenen Elemente SIG_DFL SIG_ERR SIG_IGN SIGABRT SIGFPE SIGILL SIGINT SIGSEGV SIGTERM raise signal
// // // // // // // // // // //
Standardverarbeitung Rückgabewert von signal(), wenn Fehler auftritt Signal ignorieren Unnormales Programmende Gleitkommafehler Unzulässige Anweisung Strg+C-Interrupt Unzulässiger Speicherzugriff (Segmentation Fault) An Programm gesendete Beendigungsanfrage Sendet ein Signal. Installiert Signalbehandlungsfunktion für die // Verarbeitung eines SIG-Signals.
Beispiel #include <stdio.h> #include <stdlib.h> #include <signal.h> void Signalbearbeitung(int par) { /* Signalbehandlung reinstallieren */ signal(SIGFPE, Signalbearbeitung); printf("Signal abgefangen!\n"); printf("Programmdaten werden zur Analyse abgespeichert\n"); exit(1); } int main(int argc, char** argv) { /* Signalbehandlung reinstallieren */ signal(SIGFPE, Signalbearbeitung); int n, i = 3265; printf("Geben Sie eine ganze Zahl ein: \n"); scanf("%d",&n); fflush(stdin);
254
stdarg.h i = i/n; printf("%d\n", i); return 0; }
stdarg.h Beschreibung Definiert die Makros, mit denen Funktionen mit einer variablen Anzahl von Argumenten verarbeitet werden können.
Enthaltene Elemente va_list va_arg va_end va_start
// // // //
Array-Typ für va_arg, va_end und va_start. Ermittelt Zeiger auf nächstes Argument in der Liste. Setzt Zeiger nach Bearbeitung der Argumente zurück. Makro, das der Funktion einen korrekten Rücksprung // ermöglicht..
stddef.h Beschreibung Definiert einige wichtige Typen und Makros, die vor allem bibliotheksintern verwendet werden.
Enthaltene Elemente NULL offsetof ptrdiff_t
size_t wchar_t
// Wert des Null-Zeigers // Ermittelt den Offset eines Elements in einer Struktur. // ganzzahliger, vorzeichenbehafteter Datentyp, der das // Ergebnis der Subtraktion zweier Zeiger // repräsentieren kann // vorzeichenloser Ganzzahltyp, der von sizeof als // Ergebnistyp verwendet wird. // Zeichentyp für umfangreichere Zeichensätze (16 bis // 32-Bit)
stdio.h Beschreibung Enthält die Funktionen zur Ein- und Ausgabe (Konsole, Tastatur, Dateien), sowie etliche verwandte Konstanten und Datentypen.
255
Die C-Standardbibliothek
Enthaltene Makros und Typdefinitionen _IOFBF _IOLBF _IONBF BUFSIZ
// // // // //
EOF FILE
// //
//
// //
FILENAME_MAX // //
FOPEN_MAX
// // // // // // // //
fpos_t
//
L_tmpnam
//
// //
für setbuf(); zeigt an, daß ein Stream gepuffert wird (0) für setbuf(); Stream zeilenweise gepuffert (1) für setbuf(); Stream nicht gepuffert (2) Das Makro BUFSIZ wird von fopen(),freopen() und setbuf() benutzt, um bei Einrichtung eines Streams automatisch die Größe des Streampuffers festzulegen. (>= 255) Negative Ganzzahlkonstante, zeigt das Ende einer Datei an Ein Datentyp, der alle Informationen aufnehmen kann, die zur Durchführung von Datei-Operationen erforderlich sind. Der Aufbau des Datentyps ist implementierungsspezifisch. Die maximale Anzahl der Zeichen, die in einem Dateinamen vorkommen dürfen. FOPEN_MAX enthält die maximale Anzahl der Datenströme, die gleichzeitig von einem Programm geöffnet werden können. Dieser Wert beträgt mindestens 8, wobei jedes Programm automatisch die Standard-Datenströme stdin, stdout und stderr erhält. Programme, die mehr als fünf weitere Datenströme benötigen, können anhand des Makros überprüfen, ob sie ihnen vom System zur Verfügung gestellt werden können. Der Type fpos_t wird von fgetpos() und fsetpos() benutzt, um die aktuelle Position in einer Datei zu spezifizieren Größe eines Arrays vom Datentyp char, das groß genug sein muß, um mit tmpnam() generierte Dateinamen aufzunehmen
// Die folgenden drei Makros, die als Argument für die // Funktion fseek() verwendet werden, legen fest von // welcher Position aus der Dateizeiger bewegt werden soll: SEEK_CUR SEEK_END SEEK_SET size_t
// Ab Dateiende (2). // Ab Dateianfang (0). // vorzeichenloser Ganzzahltyp, der von sizeof als
stderr stdin stdout TMP_MAX
// // // //
// Ab aktueller Position (1).
// Ergebnistyp verwendet wird. Standard-Fehlerstream. Standard-Eingabestream. Standard-Ausgabestream. TMP_MAX entspricht der maximalen Anzahl von // eindeutigen temporären Dateinamen, die mit der // Funktion tmpnam() erzeugt werden können. Der Wert von // TMP_MAX darf nicht kleiner sein als 25.
Enthaltene Funktionen clearerr fclose feof ferror fflush
256
// // // // //
Fehlerbedingung aus Stream entfernen. Stream schließen. Testet Stream auf Dateiende. Testet Stream auf Fehler. Leert Puffer eines Streams.
stdlib.h fgetc fgetpos fgets fopen fprintf fputc fputs fread freopen fscanf fseek fsetpos ftell fwrite getc getchar gets perror printf putc putchar puts remove rename rewind scanf setbuf setvbuf sprintf sscanf tmpfile tmpnam ungetc vfprintf vprintf vsprintf
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
Liest Zeichen von einem Stream. Ermittelt Positionszeiger eines Streams. Liest eine Zeile von einem Stream. Öffnet einen Stream. Formatierte Ausgabe an einen Stream. Schreibt ein Zeichen in einen Stream. Schreibt einen String in einen Stream. Liest Daten von einem Stream. Öffnet einen Stream erneut. Liest formatiert von Stream. Positioniert Dateizeiger neu. Positioniert Dateizeiger neu. Liest Position des Dateizeigers. Schreibt Daten unformatiert an Stream. Liest Zeichen aus Stream. Liest Zeichen aus Stream stdin. Liest String von stdin. Schreibt Fehlermeldung an stdout. Formatierte Ausgabe an stdout. Schreibt Zeichen in Stream. Schreibt Zeichen an Stream stdout. Schreibt Zeichenkette an Strean stdout. Löscht eine Datei. Benennt eine Datei um. Positioniert Dateizeiger auf Anfang des Streams. Formatierte Eingabe von stdin. Streampufferung verändern. Streampufferung und Puffergröße verändern. Formatierte Ausgabe an einen String. Formatierte Eingabe von einem String. Temporäre Datei erzeugen. Temporären Dateinamen erzeugen. Zeichen wieder zurück an Stream schicken. Formatierte Ausgabe an Stream. Formatierte Ausgabe an stdout. Formatierte Ausgabe in String.
stdlib.h Beschreibung Sammelsurium häufig benötigter Bibliotheksfunktionen, zu denen u. a. die dynamische Speicherverwaltung und die Prozeßkontrollroutinen gehören.
257
Die C-Standardbibliothek
Enthaltene Makros und Typdefinitionen div_t
// div_t ist eine Struktur, die von der Funktion div() // zurückgegeben wird. Das Strukturelement quot erhält // den Quotienten, rem den Rest der Division. // Statuscode, der zusammen mit der Bibliotheksfunktion // exit() benutzt werden kann, um anzuzeigen, daß das
EXIT_FAILURE
// Programm mit einem Fehler beendet wurde.
EXIT_SUCCESS
// Statuscode, der zusammen mit der Bibliotheksfunktion // exit() benutzt werden kann, um anzuzeigen, daß das
ldiv_t MB_CUR_MAX
// wie div_t für die Funktion ldiv(). // Dieses Makro enthält die maximale Anzahl von Bytes
NULL RAND_MAX size_t
// Wert des NULL-Zeigers. // Enthält die größte Pseudo-Zufallszahl, die von der // Funktion rand() zurückgegeben werden kann. // vorzeichenloser Ganzzahltyp, der von sizeof als
wchar_t
// Zeichentyp für umfangreichere Zeichensätze (16 bis
// Programm erfolgreich beendet wurde.
// pro Zeichen im aktuellen Zeichensatz.
// Ergebnistyp verwendet wird. // 32-Bit)
Enthaltene Funktionen abort abs atexit
// // // //
atof atoi atol bsearch calloc div exit free getenv labs ldiv
// // // // // // // // // // //
malloc mblen mbstowcs
// // //
mbtowc
//
qsort rand realloc srand
// // // //
//
//
// //
258
Beendet Programm unverzüglich. Ermittelt Absolutwert einer Integerzahl (In C++ für long int, float und long double überladen). Funktion registrieren, die beim Programmende ausgeführt werden soll. Wandelt String in Fließkommazahl um. Wandelt String in Integerzahl um. Wandelt String in long int um. Binäre Suche durchführen. Speicher allokieren und initialisieren. Ganzzahldivision durchführen (In C++ für long überladen). Programm beenden. Dynamisch angeforderten Speicher freigeben. Eintrag in Programm-Umgebung lesen. Absolutwert einer Zahl vom Typ long int ermitteln. Ganzzahldivision mit Zahlen vom Typ long int (In C++ für long überladen). Speicher anfordern. Länge eines Multibyte-Zeichens ermitteln. Zeichenkette mit Zeichen, die aus mehreren Bytes bestehen, in Wide Character-String umwandeln. Zeichen, das aus mehreren Bytes besteht, in Wide Character umwandeln QuickSort durchführen. Pseudo-Zufallszahl erzeugen. Speicherblock neu zuordnen. Pseudo-Zufallszahl erzeugen.
string.h strtod strtol strtoul system wcstombs
// // // // //
String in Gleitkommazahl (Typ double) umwandeln. String in long int umwandeln. String in unsigned long umwandeln. Programm unterbrechen und anderes Programm ausführen. Zeichenkette mit Wide Characters in Multibyte-String // umwandeln. // Wide Character in Multibyte-Zeichen umwandeln.
wctomb
string.h Beschreibung Enthält die Funktionen zur Manipulation von Zeichenketten (Strings).
Enthaltenen Elemente NULL size_t
// Wert des NULL-Zeigers. // vorzeichenloser Ganzzahltyp, der von sizeof als
memchr
// Sucht in einem Speicherbereich nach erstem Vorkommen
memcmp memcpy
// Vergleicht zwei Speicherbereiche. // Kopiert Anzahl Zeichen von einem Speicherbereich in einen
memmove
// Kopiert Zeichen von einem Speicherbereich in einen
memset strcat strchr
// Initialisiert einen Speicherbereich mit einem Zeichen. // Fügt Zeichenkette an eine andere an. // Ermittelt Zeiger auf erstes Vorkommen eines Zeichens in
strcmp strcoll strcpy strcspn
// // // //
// Ergebnistyp verwendet wird. // eines Zeichens.
// anderen. // anderen, der sich mit dem Ursprungsbereich überlappen darf
strerror strlen strncat strncmp strncpy strpbrk strrchr strspn
// String. Vergleicht zwei Zeichenketten. Vergleicht zwei Zeichenketten. Kopiert einen String in einen anderen. Sucht in Zeichenkette nach Zeichen, die in anderer // Zeichenkette nicht enthalten sind. // Übersetzt eine Fehlernummer in eine Zeichenkette. // Ermittelt die Länge einer Zeichenkette. // Fügt n Zeichen einer Zeichenkette an andere an. // Vergleicht einen String mit einem Teil eines anderen // Strings. // Kopiert n Zeichen eines Strings in einen anderen. // Sucht nach erstem Vorkommen eines beliebigen Zeichens aus // einer Zeichenkette in einer anderen Zeichenkette. // Ermittelt Zeiger auf letztes Vorkommen eines Zeichens in // einer Zeichenkette. // Ermittelt Index des ersten Zeichens in einer Zeichenkette, // das nicht zum Zeichensatz einer zweiten // Zeichenkette gehört
259
Die C-Standardbibliothek strstr
// Ermittelt erstes Vorkommen einer Zeichenkette in einer
strtok strxfrm
// Sucht nach nächstem Grundsymbol in Zeichenkette. // Wandelt String unter Berücksichtigung landesspezifischer
// anderen.
// Gegebenheiten um.
Beispiel #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char** argv) { char str[3][21] = {"String1", "String2", "String3"}; char *p_str; int bytes; // Strings aneinanderhaengen // Speicherbereich ermitteln bytes = (strlen(str[0]) + strlen(str[1]) + strlen(str[2]) + 1)*sizeof(char); // Speicher allokieren p_str = (char*) malloc(bytes); strcpy(p_str,str[0]); strcat(p_str,str[1]); strcat(p_str,str[2]); printf("String = %s enthaelt %d Bytes\n",p_str,bytes); free(p_str); return 0; }
time.h Beschreibung Enthält die Datentypen time_t und clock_t sowie die Datenstruktur tm und die allgemeinen Zeitfunktionen.
Anwendung Für Zeitnahmen geht man üblicherweise so vor, daß man sich die Startzeit und die Endzeit von der Funktion time() als Werte des Datentyps time_t zurückliefern läßt. Die Differenz läßt man sich von der Funktion difftime() errechnen. Um Zeit und/oder Datum abzufragen, kann man beispielsweise die Funktion asctime() verwenden. Für die Umwandlung des Datums in einen ausgabefähigen String nutzt man die Funktion strftime().
260
time.h
Enthaltene Makros und Typdefinitionen clock_t CLOCKS_PER_SEC
// clock_t ist der Datentyp, der von der Funktion // clock() zurückgegeben wird. // CLOCKS_PER_SEC enthält die Anzahl der Ticks pro // Sekunden. Ein Tick ist die Maßeinheit, mit der von // der Funktion clock() die Zeit gemessen wird. // Wert des NULL-Zeigers. // vorzeichenloser Ganzzahltyp, der von sizeof als // Ergebnistyp verwendet wird. // Der Typ time_t ist ein arithmetischer Typ zur // Aufnahme von Zeitangaben. // Die Struktur tm enthält alle Elemente, um Zeit- und // Datumsinformationen aufzunehmen. Das Muster wird von // den Funktionen localtime() und gmtime() verwendet.
NULL size_t time_t tm
Enthaltene Funktionen asctime
// Wandelt eine als Struktur gespeicherte Zeit in eine
clock
// Ermittelt, wieviel Ticks seit dem Start des aufrufenden
ctime difftime gmtime
// Wandelt Zahl vom Typ time_t in Zeichenkette um. // Ermittelt die Differenz zwischen zwei Zeiten. // Berechnet aus lokaler Zeit und definierter Zeitdifferenz
localtime mktime strftime time
// // // //
// Zeichenkette um. // Prozesses verbraucht wurden.
// die Greenwich Mean Time (GMT). Wandelt einen Zeitwert vom Typ time_t in die Lokalzeit um Wandelt Zeit aus Struktur in Kalenderzeit um. Formatiert Zeit nach landesspezifischen Bedingungen. Gibt aktuelle Zeit an Variable vom Typ time_t zurück.
Beispiel #include <stdio.h> #include <stdlib.h> #include int main(int argc, char** argv) { time_t start, ende; char eingabe[100]; int ergebnis; start = time(NULL); puts("Wieviel ist 7 * 23: \n"); fgets(eingabe,100,stdin); sscanf(eingabe,"%d\n",&ergebnis); ende = time(NULL); printf("Sie haben %d sec. benoetigt\n",(int) difftime(ende,start)); if(ergebnis == 161) puts("Die Loesung ist korrekt \n"); else printf("Die korrekte Antwort ist 161 (nicht %d)\n", ergebnis); return 0; }
261
Die C-Standardbibliothek
wchar.h Beschreibung Enthält die Funktionen zur Bearbeitung von Wide Character und Multibyte-Strings (inklusive Ein- und Ausgabe).
Enthaltene Makros und Typdefinitionen mbstate_t
NULL size_t tm wchar_t WCHAR_MAX WCHAR_MIN WEOF wint_t
// Typdefinition, in der Informationen für die Konvertierung // von Multibyte-Zeichen in Wide Character-Zeichen und // vice versa abgelegt werden. // Wert des NULL-Zeigers. // vorzeichenloser Ganzzahltyp; Ergebnistyp von sizeof // Struktur zum Ablegen von Zeit- und Datumsinformationen // aufzunehmen, siehe Funktionen localtime() und gmtime(). // Zeichentyp für umfangreichere Zeichensätze (16 / 32-Bit) // WCHAR_MAX enthält den größten Wert des Typs wchar_t // WCHAR_MIN enthält den kleinsten Wert des Typs wchar_t // Konstante, die das Ende einer Datei anzeigt // Integer-Typ für die Codierung von Zeichen aus erweiterten // Zeichensätzen.
Enthaltene Funktionen btowc fgetwc fgetws fputwc fputws fwide fwprintf fwscanf getwc getwchar mbrlen mbrtowc mbsinit mbsrtowcs putwc putwchar swprintf swscanf ungetwc vfwprintf vswprintf vwprintf wcrtomb
262
// Wandelt ein 1-MultiByte-Zeichen in ein // Wide Character-Zeichen um. Liest Zeichen von einem Stream. Liest eine Zeile von einem Stream. Schreibt ein Zeichen in Stream. Schreibt einen String in Stream. Kann einen Stream nachträglich für Byte- oder // Wide Character-Zeichen einrichten. // Formatierte Ausgabe an stdout. // Liest formatiert von Stream. // Liest Zeichen aus Stream. // Liest Zeichen aus Stream stdin. // Länge eines Multibyte-Zeichens ermitteln. // Multibyte-Zeichen in Wide Character umwandeln. // Ermittelt, ob sein Argument einen anfänglichen // Konvertierungsmodus anzeigt. // Multibyte-Zeichenkette in Wide Character-String umwandeln // Schreibt Zeichen in Stream. // Schreibt Zeichen an Stream stdout. // Formatierte Ausgabe in einen String. // Formatierte Eingabe von einem String. // Zeichen wieder zurück an Stream schicken. // Formatierte Ausgabe an Stream. // Formatierte Ausgabe in String. // Formatierte Ausgabe an stdout. // Wide Character in Multibyte-Zeichen umwandeln.
// // // // //
wctype.h wcscat wcschr wcscmp wcscoll wcscpy wcscspn wcsftime wcslen wcsncat wcsncmp wcsncpy wcspbrk wcsrchr wcsrtombs wcsspn
wcsstr wcstod wcstok wcstol wcstoul wcsxfrm wctob wmemchr wmemcmp wmemcpy wmemmove wmemset wprintf wscanf
// Fügt Zeichenkette an eine andere an. // Ermittelt Zeiger auf erstes Vorkommen eines Zeichens in // einem String Vergleicht zwei Zeichenketten. Vergleicht zwei Zeichenketten. Kopiert einen String in einen anderen. Sucht in Zeichenkette nach Zeichen, die in anderer // Zeichenkette nicht enthalten sind. // Formatiert Zeit nach landesspezifischen Bedingungen. // Ermittelt die Länge einer Zeichenkette. // Fügt n Zeichen einer Zeichenkette an andere an. // Vergleicht einen String mit einem Teil eines // anderen Strings // Kopiert n Zeichen eines Strings in einen anderen. // Sucht nach erstem Vorkommen eines beliebigen Zeichens aus // einer Zeichenkette in einer anderen Zeichenkette. // Ermittelt Zeiger auf letztes Vorkommen eines Zeichens in // einer Zeichenkette. // Wide Character-String in Multibyte-String umwandeln // Liefert Index des ersten Zeichens in einer Zeichenkette, // das nicht zum Zeichensatz einer anderen // Zeichenkette gehört // Ermittelt erstes Vorkommen einer Zeichenkette in einer // zweiten Zeichenkette. // String in Gleitkommazahl (Typ double) umwandeln. // Sucht nach nächstem Grundsymbol in Zeichenkette. // String in long int umwandeln. // String in unsigned long int umwandeln. // Wandelt String unter Berücksichtigung landesspezifischer // Gegebenheiten um. // Wandelt Zeichen in ein 1-MultiByte-Zeichen um // Sucht in einem Speicherbereich nach erstem Vorkommen // eines Zeichens. // Vergleicht zwei Speicherbereiche. // Kopiert Anzahl Zeichen von einem Speicherbereich in einen // anderen. // Kopiert von einem Speicherbereich in einen anderen, // der sich mit dem Ursprungsbereich überlappen darf // Initialisiert einen Speicherbereich mit einem Zeichen. // Formatierte Ausgabe an stdout. // Formatierte Eingabe von stdin.
// // // //
wctype.h Beschreibung Enthält die Funktionen zur Klassifizierung und Konvertierung von Wide Character-Zeichen, sowie einige Hilfstypen.
263
Die C-Standardbibliothek
Enthaltene Elemente wctrans_t wctype_t WEOF wint_t
// // // //
iswalnum iswalpha iswcntrl iswctype
// // // //
//
// //
iswdigit iswgraph
// //
iswlower iswprint iswpunct iswspace iswupper iswxdigit towctrans towlower towupper wctrans
// // // // // // // // // //
wctype
//
//
// //
Hilfstyp für Zeichenabbildungen. Hilfstyp für die Zeichenklassifizierung. Konstante, die das Ende einer Datei anzeigt Integer-Typ für die Codierung von Zeichen aus erweiterten Zeichensätzen Testet, ob Zeichen eine Ziffer oder ein Buchstabe ist. Testet, ob Zeichen ein Buchstabe ist. Testet, ob Zeichen ein Kontrollzeichen ist. Testet, ob Zeichen einer bestimmten Klasse angehört. Die Klasse wird durch einen Aufruf von wctype() spezifiziert. Testet, ob Zeichen eine Zahl ist. Testet, ob Zeichen druckbar ist (Leerzeichen ausgenommen). Testet, ob Zeichen ein Kleinbuchstabe ist. Testet, ob Zeichen druckbar ist. Testet, ob Zeichen ein Interpunktionszeichen ist. Testet, ob Zeichen ein Zwischenraumzeichen ist. Testet, ob Zeichen ein Großbuchstabe ist. Testet, ob Zeichen eine Hexadezimalziffer ist. Konvertiert ein Wide Character Zeichen. Wandelt Groß- in Kleinbuchstaben um. Wandelt Klein- in Großbuchstaben um. Ermittelt eine Abbildung von einem Wide CharacterZeichensatz in einen anderen (nach LC_TYPE). Wird zusammen mit iswctype() verwendet und erlaubt die Identifizierung einer Klasse anhand eines Strings.
Die Funktionen der C-Standardbibliothek abort
<stdlib.h>
Prozeß beenden
Beschreibung Die Funktion abort() bewirkt einen unnormalen Programmabbruch, was bedeutet, daß im Gegensatz zur Funktion exit() nicht sichergestellt ist, daß Dateipuffer geleert, offene Streams geschlossen und mit tmpfile() erzeugte temporäre Dateien entfernt werden. Außerdem werden die Programmbeendigungsroutinen, die mit atexit() eingerichtet worden sind, nicht aufgerufen. Dafür wird die Funktion raise() mit der Konstanten SIGABRT als Argument aufgerufen, für das man eine Signalbehandlungsroutine einrichten kann.
264
wctype.h
Parameter void abort(void);
Verwandte Funktionen assert(), atexit(), exit(), raise()
abs
<stdlib.h>
Absolutwert berechnen
Beschreibung Die Funktion abs() gibt den Betrag ihres ganzzahligen Argumentes als Funktionswert zurück. Der Betrag einer Zahl ist ihr Abstand zur Null.
Parameter int abs(int n); ●
Rückgabewert. Betrag des Arguments n.
Verwandte Funktionen fabs(), labs()
acos
<math.h>
Bogenkosinus berechnen
Beschreibung Die Funktion acos() berechnet den Bogenkosinus (Umkehrfunktion des Kosinus) für das übergebene Argument.
Parameter double acos(double zahl); ●
●
zahl. Argument, für das der Arkuskosinus berechnet wird (muß im Bereich zwischen -1.0 und 1.0 liegen) Rückgabewert. Arkuskosinus des Arguments im Bereich [0,p].
Verwandte Funktionen asin(), atan(), cos(), sin(), tan()
asctime
Uhrzeit in Zeichenkette
265
Die C-Standardbibliothek
Beschreibung Die Funktion asctime() wandelt die in der Zeitstruktur tm enthaltenen Zeitinformationen in eine Zeichenkette um. Die erzeugte Zeichenkette besitzt das folgende Format (für Lokale "C"): Mon Jul 31 23:10:07 1989\n\0
Parameter char *asctime (const struct tm *zeit); ●
Rückgabewert. Zeiger auf die erzeugte Zeichenkette.
Verwandte Funktionen ctime(), gmtime(), localtime(), setlocale()
asin
<math.h>
Bogensinus berechnen
Beschreibung Die Funktion asin() berechnet den Bogensinus (Umkehrfunktion des Sinus) für das übergebene Argument.
Parameter double asin(double zahl); ●
●
zahl. Argument, für das der Arkussinus berechnet wird (muß im Bereich zwischen -1.0 und 1.0 liegen) Rückgabewert. Arkussinus des Arguments im Bereich [-p /2, + p /2].
Verwandte Funktionen acos(), atan(), cos(), sin(), tan()
assert
Diagnosemeldung ausgeben
Beschreibung Das Makro assert überprüft den Wert eines übergebenen Ausdrucks. Liefert diese Überprüfung das Ergebnis falsch (0), gibt assert() eine Diagnosemeldung auf den Standardfehler-Stream stderr aus und ruft anschließend die Funktion abort() auf, um das Programm abzubrechen. Die Meldung von assert beinhaltet den Dateinamen und die Zeilennummer, in der das Makro aufgerufen wurde. Die Informationen werden den Konstanten __FILE__ und __LINE__ entnommen. Durch Setzen des Schalters NDEBUG (#define NDEBUG) können nachfolgende assert-Aufrufe unterdrückt werden.
266
wctype.h
Parameter void assert(int ausdruck); ●
ausdruck. Der zu überprüfende Ausdruck.
Verwandte Funktionen abort(), exit(), raise(), signal()
atan, atan2
<math.h>
Bogentangente berechnen
Beschreibung Die Funktion atan() berechnet die Bogentangente einer Zahl. atan2() berechnet die Bogentangente von zahl1/zahl2.
Parameter double atan(double zahl); double atan2(double zahl1, double zahl2); ●
Rückgabewert. atan() liefert Arcustangens von zahl im Bereich [-p /2, + p /2], atan2()liefert Arcustangens von zahl1/zahl2 im Bereich [-p , + p ].
Verwandte Funktionen acos(), asin(), cos(), sin(), tan()
atexit
<stdlib.h>
Funktion bei exit aufrufen
Beschreibung Mit der Funktion atexit() können Sie eine Funktion registrieren lassen, die beim Programmende aufgerufen werden soll. Hiermit kann die Abarbeitung von notwendigen Aufräumarbeiten bei Beendigung eines Programms eingerichtet werden. Die mit atexit() registrierten Funktionen werden aufgerufen, wenn das Programm mit exit() beendet oder von main() aus die return-Anweisung erfolgt. Wurden mehrere Funktionen durch aufeinanderfolgende Aufrufe von atexit() registriert, werden sie in der umgekehrten Reihenfolge ihrer Registrierung aufgerufen.
Parameter int atexit(void (*func)(void)); ●
Rückgabewert. Die Funktion atexit() gibt eine Null zurück, wenn die übergebene Funktion registriert werden konnte.
267
Die C-Standardbibliothek
Verwandte Funktionen abort(), exit()
atof
<stdlib.h>
Umwandlung: Zeichenkette in double
Beschreibung Die Funktion atof() (ascii to float) wandelt die Zeichenkette, auf die string zeigt, in eine Gleitkommazahl doppelter Genauigkeit um und gibt diese Zahl als Funktionswert zurück. Führende Leerzeichen und Tabulatoren werden übersprungen, die Umwandlung wird bei dem ersten Zeichen, das nicht mehr als Ziffer erkannt wird, abgebrochen.
Parameter double atof(const char *string); ●
Rückgabewert. Korrespondierender double-Wert.
Verwandte Funktionen atoi(), atol(), strtod()
atoi
<stdlib.h>
Umwandlung: String in int
Beschreibung Die Funktion atoi() (ascii to int) wandelt den übergebenen String in eine Zahl vom Datentyp int um. Führende Zwischenraumzeichen werden übersprungen, ein eventuell vorhandenes Vorzeichen jedoch berücksichtigt. Beim ersten nicht als Ziffer identifizierbaren Zeichen wird die Umwandlung abgebrochen und die bis dahin ermittelte Zahl zurückgegeben.
Parameter int atoi(const char *string); ●
Rückgabewert. Korrespondierender int-Wert oder Null.
Verwandte Funktionen atof(), atol(), strtod(), strtol()
atol Umwandlung: String in long
268
<stdlib.h>
wctype.h
Beschreibung Die Funktion atol() (ascii to long) wandelt den übergebenen String in eine Zahl vom Datentyp long um. Führende Zwischenraumzeichen werden übersprungen, ein eventuell vorhandenes Vorzeichen jedoch berücksichtigt. Beim ersten nicht als Ziffer identifizierbaren Zeichen wird die Umwandlung abgebrochen und die ermittelte Zahl zurückgegeben.
Parameter long atol(const char *string); ●
Rückgabewert. Korrespondierender long int -Wert oder Null.
Verwandte Funktionen atof(), atoi(), strtod(), strtol()
bsearch
<stdlib.h>
Binäre Suche
Beschreibung Mit der Funktion bsearch() kann eine binäre Suche in einem sortierten Datenfeld durchgeführt werden. Der bei der Suche notwendige Vergleich wird durch eine Funktion durchgeführt, deren Adresse an bsearch() übergeben wird. Diese Funktion muß zwei Elemente miteinander vergleichen und folgende Werte an bsearch() zurückgeben: < 0 0 > 0
Das erste Element ist kleiner als das zweite. Beide Elemente sind identisch. Das erste Element ist größer als das zweite.
Parameter void *bsearch (const void *suchobj, const void *basis, size_t anzahl, size_t breite, int (*vgl) (const void *elem1, const void *elem2)); ●
suchobj. Objekt, für das eine Übereinstimmung gesucht wird (Wird als erstes Argument an vgl() übergeben).
●
basis. Zeiger auf Anfang des Feldes, in dem gesucht werden soll.
●
anzahl. Anzahl der Elemente in Feld.
●
breite. Größe jedes Elements in Bytes.
●
vgl. Zeiger auf Funktion, die zwei Elemente vom Typ const void* vergleicht.
●
Rückgabewert. Ist die Suche erfolgreich, gibt die Funktion einen Zeiger auf das erste Elemente in dem Feld zurück, das dem Suchkriterium genügt. Konnte kein passendes Element gefunden werden, wird ein NULL-Zeiger zurückgegeben.
269
Die C-Standardbibliothek
Warnung Wenn das Feld noch nicht sortiert ist, kann vorher die Funktion qsort() aufgerufen werden.
Verwandte Funktionen qsort()
btowc
<wchar.h>
1-Byte-Zeichen umwandeln
Beschreibung Wandelt ein 1-MultiByte-Zeichen in ein Wide Character-Zeichen um.
Parameter wint_t btowc(int c); ●
Rückgabewert. Wide Character-Repräsentation des übergebenen Zeichens. WEOF, wenn das Zeichen kein gültiges 1-MultiByte-Zeichen im anfänglichen Konvertierungsstatus oder EOF war.
Verwandte Funktionen wctob()
calloc
<stdlib.h>
Speicher zuordnen
Beschreibung Mit calloc() kann Speicherplatz für ein Feld von anzahl Elemente der Größe groesse eingerichtet und mit Nullen initalisiert werden.
Parameter void *calloc(size_t anzahl, size_t groesse); ●
Rückgabewert. Konnte die Speicheranforderung erfüllt werden, gibt die Funktion einen Zeiger auf den allokierten Speicherbereich, anderenfalls einen NULL-Zeiger zurück.
Verwandte Funktionen malloc(), realloc()
270
wctype.h
ceil
<math.h>
Rundungsfunktion
Beschreibung Die Funktion ceil() berechnet den kleinstmöglichen ganzzahligen Wert, der größer oder gleich ihrem double-Argument ist und gibt ihn zurück.
Parameter double ceil(double zahl); ●
Rückgabewert. Aufgerundeter Wert.
Verwandte Funktionen floor(), fmod()
clearerr
<stdio.h>
Fehleranzeige zurücksetzen
Beschreibung Die Funktion setzt das Fehler- und Dateiende-Flag für einen Stream zurück.
Parameter void clearerr(FILE *stream);
Verwandte Funktionen feof(), ferror()
clock
Dauer des aufrufenden Prozesses
Beschreibung Die Funktion gibt die vom aufrufenden Prozeß verbrauchte Prozessorzeit als Wert vom Typ clock_t zurück. Der Konstanten CLOCKS_PER_SEC entspricht die Anzahl der Ticks pro Sekunde. Wenn der von clock() zurückgegebene Wert durch die Konstante CLOCKS_PER_SEC geteilt wird, erhält man die Prozessorzeit in Sekunden.
Parameter clock_t clock(void); ●
Rückgabewert. Liefert die seit einem implementierungsspezifischen Zeitpunkt vergangene Prozessorzeit. Kann die Anzahl der Ticks nicht ermittelt werden, wird der Wert -1 zurückgeliefert.
271
Die C-Standardbibliothek
Verwandte Funktionen difftime(), time()
cos, cosh
<math.h>
Kosinus berechnen
Beschreibung Die Funktionen cos() und cosh() berechnen den Kosinus bzw. den Kosinus hyperbolicus des übergebenen Arguments (Winkel in Bogenmaß) und sind für alle reellen Zahlen definiert.
Parameter double cos(double zahl); double cosh(double zahl); ●
Rückgabewert. cos() liefert den Kosinus des Arguments im Bereich [-1, 1]. cosh() liefert den Kosinus hyperbolicus des Arguments im Bereich [1, 8].
Warnung Der Kosinus hyperbolicus steigt sehr schnell an.
Verwandte Funktionen asin(), atan(), atan2(), sin(), sinh() Siehe Beispiel zu Header-Datei math.h
ctime
Zeit in Zeichenkette
Beschreibung Die Funktion ctime() wandelt die als Variable vom Typ time_t übergebene Kalenderzeit in eine Zeichenkette um. Die erzeugte Zeichenkette besitzt das lokal verwendete Format, beispielsweise: Fre Jun 30 23:10:07 1988\n\0
// für die C-Lokale
Entspricht dem Aufruf asctime(localtime(ptr_zeit));.
Parameter char * ctime(const time_t *ptr_zeit); ●
Rückgabewert. Zeiger auf die erzeugte Zeichenkette.
Verwandte Funktionen asctime(), gmtime(), localtime()
272
wctype.h
difftime
Differenz zwischen zwei Zeiten
Beschreibung Die Funktion difftime() ermittelt die Differenz zwischen zwei Zeiten vom Typ time_t und gibt das Ergebnis in Sekunden zurück.
Parameter double difftime(time_t neuezeit, time_t altezeit); ●
Rückgabewert. Differenz zwischen beiden Zeiten.
Verwandte Funktionen asctime(), ctime(), localtime(), time() Siehe Beispiel zu Header-Datei time.h
div
<stdlib.h>
Quotient und Rest zweier Ganzzahlen
Beschreibung Die Funktion div() teilt eine Ganzzahl (zahl1) durch eine zweite (zahl2) und gibt das Ergebnis als eine Variable vom Strukturtyp div_t zurück.
Parameter div_t div(int zahl1, int zahl2); ●
Rückgabewert. Die Funktion gibt eine Variable vom Typ div_t zurück, in der Quotient (.quot) und Divisionsrest (.rem) festgehalten sind.
Verwandte Funktionen ldiv()
exit
<stdlib.h>
Prozeß beenden
Beschreibung Die Funktion exit() beendet ein Programm, nachdem die mit atexit() registrierten Funktionen in der umgekehrten Folge ihrer Registrierung abgearbeitet, der Streampuffer geleert, offene Dateien geschlossen und mit tmpfile() erzeugte temporäre Dateien entfernt wurden. Das Argument status spezifiziert einen Statuscode (z. B. die ANSI-C-Konstanten EXIT_FAILURE oder EXIT_SUCCESS), der an das aufrufende Programm übergeben wird. Unter MS-DOS kann in Batchdateien dieser Wert mit IF ERRORLEVEL abgefragt werden.
273
Die C-Standardbibliothek
Parameter void exit(int status);
Verwandte Funktionen abort(), atexit(), system()
exp
<math.h>
Exponential berechnen
Beschreibung Die Funktion exp() berechnet die Exponentialfunktion ihres Arguments. Sie ist das Gegenstück der Funktion log().
Parameter double exp(double zahl); ●
Rückgabewert. Die Funktion liefert bei Erfolg den Exponentialwert (e^zahl), HUGE_VAL bei zu großem Ergebniswert.
Verwandte Funktionen log(), log10(), pow() Siehe Beispiel zu Header-Datei math.h
fabs
<math.h>
Gleitkomma-Absolutwert berechnen
Beschreibung Die Funktion fabs() berechnet den Betrag einer Gleitkommazahl und gibt ihn zurück.
Parameter double fabs(double zahl); ●
Rückgabewert. Betrag des Arguments.
Verwandte Funktionen abs(), ceil(), floor(), fmod(), labs()
fclose Stream schließen
274
<stdio.h>
wctype.h
Beschreibung Die Funktion schließt den Stream, dessen Zeiger ihr als Argument übergeben wurde. Dabei wird der mit dem Stream verbundene Puffer geleert, im Puffer befindliche ausgegebene Daten in die Datei geschrieben, im Puffer befindliche gelesene Daten gelöscht.
Parameter int fclose(FILE *stream); ●
Rückgabewert. Bei Erfolg gibt die Funktion eine Null zurück, sonst EOF.
Verwandte Funktionen fflush(), setbuf(), setvbuf()
feof
<stdio.h>
Stream auf Dateiende prüfen
Beschreibung Die Funktion feof() prüft, ob das Dateiende-Flag des Streams, dessen Zeiger übergeben wurde, gesetzt ist.
Parameter int feof(FILE *stream); ●
Rückgabewert. Wenn die gegenwärtige Position das Dateiende ist, wird ein Wert ungleich Null zurückgegeben, sonst Null.
Verwandte Funktionen ferror(), perror()
ferror
<stdio.h>
Stream auf Fehler prüfen
Beschreibung Die Funktion ferror() prüft, ob ein Schreib- oder Lesefehler für den Datenstrom stream aufgetreten ist. Entsprechende Fehler-Flags werden nicht zurückgesetzt (dies kann durch Aufruf von rewind() oder Schließen des Streams geschehen).
Parameter int ferror(FILE *stream); ●
Rückgabewert. Im Fehlerfall ein Wert ungleich Null, sonst Null.
275
Die C-Standardbibliothek
Verwandte Funktionen feof(), fopen(), perror(), rewind()
fflush
<stdio.h>
Stream entleeren
Beschreibung Diese Funktion leert den Puffer, der mit dem übergebenen Stream verbunden ist. Handelt es sich um einen Ausgabe-Stream, werden die gepufferten Daten in die zugehörige Datei geschrieben. Nach ANSI C ist das Verhalten undefiniert, wenn es sich um einen Eingabe-Stream handelt. Die meisten Compiler leeren dann den Puffer.
Parameter int fflush(FILE *stream); ●
Rückgabewert. Bei Erfolg wird eine Null zurückgegeben, ansonsten EOF.
Verwandte Funktionen fclose(), setbuf(), setvbuf()
fgetc, fgetwc
<stdio.h><wchar.h>
Zeichen aus Stream lesen
Beschreibung Die Funktion liest ein Zeichen aus dem Datenstrom, auf den stream zeigt, und rückt den Dateizeiger vor.
Parameter int fgetc(FILE *stream); wint_t fgetwc(FILE *stream); ●
Rückgabewert. Die Funktion gibt das gelesene Zeichen als int-Wert ohne Vorzeichen zurück; EOF (WEOF) kann einen Fehler oder das Ende der Datei bedeuten.
Verwandte Funktionen gets(), fputc(), ungetc()
fgetpos Stream-Positionsangabe holen
276
<stdio.h>
wctype.h
Beschreibung Die Funktion fgetpos() kopiert den Wert des Stream-Positionszeigers in die Variable, auf die pos zeigt. Der Datentyp von pos ist fpos_t, der in der Header-Datei <stdio.h> definiert ist.
Parameter int fgetpos(FILE *stream, fpos_t *pos); ●
Rückgabewert. Im Fehlerfall ein Wert ungleich Null, sonst Null.
Verwandte Funktionen fsetpos(), ftell()
fgets, fgetws
<stdio.h><wchar.h>
String aus Stream lesen
Beschreibung Die Funktion liest so lange Zeichen von stream, bis entweder anz-1 Zeichen gelesen wurden, das Zeichen für Zeilenvorschub (\n) gelesen oder das Dateiende erreicht wurde. Die Funktion terminiert den eingelesenen String automatisch durch Anhängung des Nullterminierungszeichens (\0).
Parameter char *fgets(char *zkette, int anz, FILE *stream); wchar_t *fgetws(wchar_t *zkette, int anz, FILE *stream); ●
Rückgabewert. Die Funktion gibt, wenn erfolgreich, einen Zeiger auf zkette zurück, im Fehlerfall oder bei frühzeitigem Dateiende einen NULL-Zeiger.
Verwandte Funktionen fputs(), gets(), puts()
floor
<math.h>
Abrunden
Beschreibung Die Funktion floor() berechnet den größtmöglichen ganzzahligen Wert, der kleiner/gleich dem Argument ist, und gibt ihn zurück.
Parameter double floor(double zahl); ●
Rückgabewert. Abgerundeter Wert für zahl.
277
Die C-Standardbibliothek
Verwandte Funktionen ceil()
fmod
<math.h>
Gleitkomma-Rest berechnen
Beschreibung Die Funktion fmod() teilt x durch y und gibt den Rest der Division zurück.
Parameter double fmod(double x, double y);
Rückgabewert. Gleitkommarest der Division.
●
Verwandte Funktionen ceil(), fabs(), floor(), modf()
fopen
<stdio.h>
Datei öffnen
Beschreibung Die Funktion fopen() öffnet eine Datei und initialisiert den Stream, der mit der Datei verbunden ist. Das Argument modus legt fest, in welchem Modus die Datei geöffnet werden soll. Modus
Beschreibung
r
Zum Lesen öffnen.
w
Zum Schreiben öffnen (Datei wird überschrieben oder bei Bedarf erzeugt).
a
Zum Schreiben an das Dateiende öffnen (Datei wird bei Bedarf erzeugt).
r+
Datei soll zum Lesen und Schreiben geöffnet werden und muß existieren.
w+
Leere Datei soll zum Lesen und Schreiben geöffnet werden (Datei wird überschrieben oder bei Bedarf erzeugt).
a+
Zum Lesen und Anhängen öffnen (Datei wird bei Bedarf erzeugt).
Parameter FILE *fopen(const char *datei, const char *modus); ●
Rückgabewert. Die Funktion gibt einen Zeiger auf eine Struktur vom Typ FILE zurück. Dieser Zeiger wird dann an die weiteren Funktionen, die auf dem Stream operieren sollen, als Argument übergeben. Konnte die Datei nicht geöffnet werden, wird ein NULL-Zeiger zurückgegeben.
278
wctype.h
Warnung Die Modusspezifikation kann durch Anhängen von t oder b jeweils explizit im Text- oder Binärmodus geöffnet werden (betrifft Codierung des Zeilenumbruchs). Dateistreams, die zum Lesen und Schreiben geöffnet wurden, müssen fflush() oder eine Funktion zur Positionierung des Dateizeigers (fseek(), rewind()) ausführen, bevor zwischen Lesen und Schreiben gewechselt werden kann.
Verwandte Funktionen fclose(), ferror(), freopen()
fprintf, fwprintf
<stdio.h><wchar.h>
Formatierte Ausgabe an Stream
Beschreibung Die Funktion erzeugt aus ihren Argumenten eine Zeichenkette, die in den Datenstrom, auf den stream zeigt, geschrieben wird. Die Formatierung der Zeichenkette erfolgt wie für printf() beschrieben.
Parameter int fprintf(FILE *stream, const char *format...); int fwprintf(FILE *stream, const wchar_t *format...); ●
stream. Zeiger auf FILE-Struktur des Ausgabe-Streams.
●
format. Formatzeichenkette (siehe printf()).
●
.... Variable Anzahl von Argumenten.
●
Rückgabewert. Die Funktion gibt die Anzahl der ausgegebenen Zeichen zurück. Im Fehlerfall wird ein negativer Wert zurückgeliefert (meist EOF (WEOF)).
Verwandte Funktionen fscanf(), fputs(), fwrite(), printf()
fputc, fputwc
<stdio.h><wchar.h>
Zeichen an Stream ausgeben
Beschreibung Die Funktion gibt ein einzelnes Zeichen an einen Stream aus.
Parameter int fputc(int zeichen, FILE *stream); wint_t fputwc(wchar_t zeichen, FILE *stream);
279
Die C-Standardbibliothek ●
Rückgabewert. Die Funktion gibt das geschriebene Zeichen zurück. Ist der Rückgabewert EOF (WEOF), ist ein Fehler aufgetreten.
Verwandte Funktionen fgetc(), fgets(), putc(), ungetc()
fputs, fputws
<stdio.h><wchar.h>
Zeichenkette an Stream schreiben
Beschreibung Die Funktion schreibt eine Zeichenkette an einen Stream. Das den String beendende Nullterminierungszeichen wird nicht mit ausgegeben.
Parameter int fputs(const char *zkette, FILE *stream); int fputws(const wchar_t *zkette, FILE *stream); ●
Rückgabewert. Die Funktion gibt im Erfolgsfall einen nicht-negativen Wert zurück. Ist der Rückgabewert EOF (WEOF), ist ein Fehler aufgetreten.
Verwandte Funktionen fputc(), fprintf(), putc(), putchar(), getc(), gets()
fread
<stdio.h>
Daten aus Stream lesen
Beschreibung Für Binärdateien. Die Funktion fread() liest anzahl Elemente der Größe groesse (in Bytes) aus einem Stream ein nach puffer. Mit ihr können beispielsweise in der Datei enthaltene Strukturen in ein entsprechendes Feld von Strukturen eingelesen werden.
Parameter size_t fread (void *puffer, size_t groesse, size_t anzahl, FILE *stream); ●
Rückgabewert. Anzahl der fehlerfrei gelesenen Datenelemente.
Verwandte Funktionen fopen(), fwrite(), printf(), scanf()
free Speicher freigeben
280
<stdlib.h>
wctype.h
Beschreibung Die Funktion gibt den Speicherbereich frei, auf den addr verweist und der zuvor mit einer der Funktionen malloc(), calloc() oder realloc() angefordert wurde.
Parameter void free(void *addr);
Tip Der Funktion kann auch ein Zeiger auf Null übergeben werden, in welchem Falle jedoch kein Code ausgeführt wird.
Verwandte Funktionen malloc(), calloc(), realloc() Siehe Praxisteil, Kategorie Zeiger und dynamische Speicherverwaltung, Speicherallokation mit malloc()
freopen
<stdio.h>
Dateizeiger neu zuordnen
Beschreibung Die Funktion schließt die Datei, die mit dem übergebenen Datenstrom stream verbunden ist. Anschließend wird dem Stream die durch das Argument pfad angegebene Datei mit den angegebenen Zugriffsberechtigungen (siehe fopen()) zugeordnet. Diese Funktion wird üblicherweise verwendet, um die Umleitung der Standard-Streams (stdin, stdout, stderr) in Dateien vorzunehmen.
Parameter FILE *freopen(const char *pfad, const char *modus, FILE *stream); ●
Rückgabewert. Die Funktion gibt bei Erfolg einen Zeiger zur neu geöffneten Datei zurück, bei Versagen einen NULL-Zeiger.
Verwandte Funktionen fopen(), fclose()
frexp
<math.h>
Gleitkommazahl aufteilen
Beschreibung Die Funktion frexp() zerlegt eine Gleitkommazahl in eine normalisierte Mantisse und ihren Exponenten zur Basis 2 auf. Die Mantisse ist größer/gleich 0.5 und kleiner als 1.
281
Die C-Standardbibliothek
Parameter double frexp(double zahl, int *exp); ●
Rückgabewert. Die Funktion gibt die Mantisse zurück. Der Exponent wird in die Variable geschrieben, auf die *exp zeigt.
Verwandte Funktionen exp(), ldexp()
fscanf, fwscanf
<stdio.h><wchar.h>
Formatiert aus Stream lesen
Beschreibung Die Funktion liest Daten formatiert aus dem Datenstrom stream ein. Sie benutzt das Argument format (siehe scanf()), um die eingelesenen Zeichen in die richtigen Datentypen umzuwandeln. Die eingelesenen Daten werden dann an die Speicherstellen geschrieben, die mit den weiteren Funktionsargumenten übergeben werden. Für jedes weitere Funktionsargument muß in der Formatzeichenkette ein Typbezeichner angegeben worden sein.
Parameter int fscanf(FILE *stream, const char *format,...); int fwscanf(FILE *stream, const wchar_t *format,...); ●
stream. Zeiger auf FILE-Struktur des Eingabe-Streams.
●
format. Formatzeichenkette (siehe printf()).
●
.... Variable Anzahl von Argumenten.
●
Rückgabewert. Die Funktion gibt die Anzahl der erfolgreich umgewandelten und zugewiesenen Felder zurück. Ist der Rückgabewert EOF, wurde versucht, das Dateiende einzulesen.
Verwandte Funktionen fprintf(), scanf(), sscanf()
fseek Dateizeiger positionieren
Beschreibung Die Position bewegt den Dateizeiger eines Streams zu einer bestimmten Stelle.
282
<stdio.h>
wctype.h
Parameter int fseek(FILE *stream, long offset, int urprung); ●
●
●
offset. Definiert die Entfernung in Bytes, die der Dateizeiger von der Stelle, die im Argument ursprung kodiert ist, bewegt werden soll. Für binäre Dateien ist dies die Anzahl der Zeichen, die der Dateizeiger bewegen soll (kann auch negativ sein), für Textdateien sollte der Wert Null oder von ftell() zurückgeliefert worden sein, wobei Ursprung gleich SEEK_SET sein muß. ursprung. Eine von drei Konstanten, die als Bzeug für den offset dienen: SEEK_CUR
Ab der aktuellen Position.
SEEK_END
Ab dem Ende der Datei.
SEEK_SET
Ab dem Anfang der Datei.
Rückgabewert. Die Funktion gibt eine Null zurück, wenn der Dateizeiger bewegt werden konnte, sonst einen Wert ungleich Null.
Verwandte Funktionen ftell(), rewind()
fsetpos
<stdio.h>
Positionsanzeige bestimmen
Beschreibung Mit der Funktion fsetpos() können Sie die Positionsanzeige des Streams auf einen Wert zurücksetzen, wie er beispielsweise bei einem Aufruf der Funktion fgetpos() ermittelt wurde.
Parameter int fsetpos(FILE *stream, const fpos_t *pos); ●
●
pos. Position, auf die der Positionszeiger gesetzt werden soll (abgespeicherter Rückgabewert der Funktion fgetpos() ). Rückgabewert. Die Funktion gibt im Erfolgsfall eine Null zurück, im Fehlerfall einen Wert ungleich Null.
Verwandte Funktionen fseek(), fgetpos()
ftell
<stdio.h>
Stream-Zeigerposition ermitteln
283
Die C-Standardbibliothek
Beschreibung Die Funktion ftell() gibt den aktuellen Wert des Positionszeigers zurück, der mit dem Argument stream verbunden ist.
Parameter long ftell(FILE *stream); ●
Rückgabewert. Die Funktion gibt die aktuelle Position zurück. Der Rückgabewert unterscheidet sich, je nachdem, ob es sich um eine Binär- oder eine Textdatei handelt. Für eine Binärdatei gibt die Funktion die Anzahl der Bytes vom Dateianfang bis zur aktuellen Position zurück. Für eine Textdatei ist der Wert nur zur Weitergabe an fseek() geeignet. Im Fehlerfall ist der Rückgabewert -1L.
Verwandte Funktionen fgetpos(), fseek(), rewind()
fwide
<wchar.h>
Stream-Orientierung
Beschreibung Ermittelt und ändert möglicherweise die Orientierung eines Streams. Ist modus = 0 wird die Orientierung nicht geändert, ist modus > 0 wird versucht, eine Wide Character-Orientierung herzustellen, ist modus < 0 wird versucht, eine Byte-Orientierung herzustellen
Parameter int fwide (FILE *stream, int modus); ●
Rückgabewert. Ein Wert > 0 zeigt an, daß der Stream Wide Characterorientiert ist, ein Wert < 0 zeigt an, das der Stream byteorientiert ist.
Verwandte Funktionen fgetwc(), fgetws(), fwscanf(), getwc(), fputwc(), wscanf(), fwprintf(), fputwc(), fputws(), putwc(), wprintf()
fwrite
<stdio.h>
In einen Stream schreiben
Beschreibung Für Binärdateien. Die Funktion fwrite() schreibt anzahl Datenelemente der Größe groesse (in Bytes) aus dem Speicherbereich, auf den puffer verweist, in den Datenstrom stream. Nach dem Schreibvorgang wird der Stream-Positionszeiger auf den neuen Stand gebracht.
284
wctype.h
Parameter size_t fwrite (const void *puffer, size_t groesse, size_t anzahl, FILE *stream); ●
Rückgabewert. Anzahl der tatsächlich geschriebenen Datenelemente.
Verwandte Funktionen fread(), write()
getc, getwc, getchar, getwchar
<stdio.h><wchar.h>
Ein Zeichen einlesen
Beschreibung Die Funktionen getc() und getwc() lesen ein Zeichen aus einem Stream ein, getchar() und getwchar() lesen ein Zeichen aus dem Stream stdin. Das Makro getchar() entspricht dem Aufruf getc(stdin), das Makro getwchar() dem Aufruf getwc(stdin).
Parameter int getc(FILE *stream); wint_t getwc(FILE *stream); int getchar(void); wint_t getwchar(void); ●
Rückgabewert. Die Funktionen geben das eingelesene Zeichen zurück; EOF (WEOF) wird beim Erreichen des Dateiendes oder bei Auftreten eines Fehlers zurückgeliefert.
Verwandte Funktionen fgetc(), putc(), putchar(), ungetc()
getenv
<stdlib.h>
Wert aus Umgebung holen
Beschreibung Die Funktion getenv() untersucht die betriebssystemspezifische Umgebung eines Programms. Sie sucht nach einem Eintrag, der mit dem Argument varname identisch ist, und gibt bei Erfolg einen Zeiger auf die entsprechende Umgebungsvariable zurück.
Parameter char *getenv(const char *varname); ● ●
varname. Umgebungsvariable, nach der gesucht wird. Rückgabewert. Liefert einen Zeiger auf die Definition der Umgebungsvariablen zurück, oder einen NULL-Zeiger, wenn die Variable nicht definiert ist.
285
Die C-Standardbibliothek
Verwandte Funktionen setlocale(), localeconv()
gets
<stdio.h>
Zeile von stdin lesen
Beschreibung Die Funktion gets() liest eine Zeichenkette vom Standardeingabe-Stream und speichert sie an der mit str übergebenen Adresse. Das Einlesen wird beendet, wenn die Funktion das Zeichen für Zeilenvorschub oder EOF entdeckt. Das Zeichen für Zeilenvorschub wird entfernt und durch das Nullterminierungszeichen (\0) ersetzt, mit dem das Ende des Strings markiert wird.
Parameter char *gets(char *str); ●
Rückgabewert. Zeiger auf eingelesenen String; im Fehlerfall ein NULL-Zeiger.
Warnung Beachten Sie, daß der Speicherbereich, auf den str zeigt, groß genug sein muß, um den String aufzunehmen, da die Funktion ansonsten den Speicher überschreibt.
Verwandte Funktionen fgets(), fputs(), puts()
gmtime
Zeitwert in Struktur umwandeln
Beschreibung Die Funktion gmtime() erwartet als Argument die Adresse einer Variablen vom Typ time_t. Die in dieser Variablen gespeicherte Kalenderzeit (meist erhalten durch einen Aufruf der Funktion time()), wird von der Funktion in Greenwich Mean Time (auch UTC genannt) umgewandelt und in der Struktur tm abgelegt.
Parameter struct tm *gmtime(const time_t *zeit); ●
Rückgabewert. Die Funktion gibt einen Zeiger auf eine Struktur vom Typ tm zurück, in der Datum und Zeit abgelegt sind. Im Fehlerfall gibt die Funktion einen NULL-Zeiger zurück.
Verwandte Funktionen asctime(), ctime(), localtime(), time()
286
wctype.h
is..., isw..
<wctype.h>
Zeichenklassifizierung
Beschreibung Die Funktionen aus der is...()-Gruppe überprüfen die Zugehörigkeit eines Zeichens zu bestimmten Zeichengruppen. Die entsprechenden isw..()-Funktionen stellen die gleiche Funktionalität für Wide Character-Zeichen zur Verfügung. Name isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit
Testet auf // Buchstaben und Ziffern (isalpha || isdigit) // Buchstaben (A-Z, a-z) (isupper || islower) // Steuerzeichen (0x00-0x1f oder 0x7F). // Ziffer (0-9). // Ausgebbares Zeichen (0x21-0x7E) (isprint ohne isspace) // Kleinbuchstabe (a-z). // Druckbares Zeichen (0x20-0x7E). // Interpunktionszeichen (isprint ohne isspace und isalnum) // Whitespace-Zeichen (' ', '\f', '\n', '\r', '\t', '\v'). // Großbuchstabe (A-Z). // Hexadezimalzahl (A-F, a-f, 0-9).
Parameter int is...(int zeichen); int isw...(wint_t widezeichen); ●
Rückgabewert. Die Funktionen geben einen Wert ungleich Null (true) zurück, wenn das übergebene Zeichen zu der von der Funktion überprüften Klasse von Zeichen gehört.
Verwandte Funktionen localeconv(), setlocale()
iswctype
<wctype.h>
Wide Character-Klasse feststellen
Beschreibung Testet, ob ein Zeichen einer bestimmten Klasse angehört. Die Klasse wird durch einen Aufruf von wctype spezifiziert: iswctype(wc, wctype("digit")) // = iswcdigit(wc)
Parameter int iswctype(wint_t wc, wctype_t beschreibung); ●
Rückgabewert. Liefert einen Wert ungleich Null, wenn das Wide CharacterZeichen wc die beschriebene Eigenschaft hat.
287
Die C-Standardbibliothek
Verwandte Funktionen wctype()
labs
<stdlib.h>
Absolutwert einer long-Zahl bestimmen
Beschreibung Die Funktion ermittelt den Betrag einer long-Ganzzahl und gibt ihn zurück. Der Betrag einer Zahl ist ihr Abstand zu Null.
Parameter long int labs(long int zahl); ●
Rückgabewert. Betrag des Arguments.
Verwandte Funktionen abs(), fabs()
ldexp
<math.h>
Exponential berechnen
Beschreibung ldexp berechnet den Wert von zahl*2^exp.
Parameter double ldexp(double zahl, int exp); ●
Rückgabewert. Liefert den Wert von zahl*2^exp; HUGE_VAL bei Überlauf.
Verwandte Funktionen log(), log10(), pow()
ldiv
<stdlib.h>
Quotient und Rest zweier long-Zahlen
Beschreibung Die Funktion ldiv() teilt eine long-Ganzzahl durch eine andere und gibt das Ergebnis in Form einer Struktur vom Typ ldiv_t zurück:
Parameter ldiv_t ldiv(long int zahl1, long int zahl2);
288
wctype.h
●
Rückgabewert. Die Funktion gibt eine Struktur vom Typ ldiv_t zurück, in der sich der Quotient (.quot) und der Divisionsrest (.rem) befinden.
Verwandte Funktionen div()
localeconv
Lokale Einstellung numerischer Formate
Beschreibung Über die Funktion localeconv() können lokale Einstellungen zur Formatierung numerischer Werte, insbesondere der Darstellung von Währungsangaben, abgefragt werden. Die Einstellungen werden in einer lconv-Struktur abgelegt.
Parameter struct lconv *localeconv(void); ●
Rückgabewert. Zeiger auf die gefüllte Struktur lconv.
Verwandte Funktionen setlocale() Siehe Beispiel zu Header-Datei locale.h
localtime
Uhrzeit auf Ortszeit einstellen
Beschreibung Die Funktion wandelt die Kalenderzeit zeit in eine Struktur vom Typ tm um. Hierbei wird, im Gegensatz zu der Funktion gmtime(), die Ortszeit berücksichtigt.
Parameter struct tm *localtime(const time_t *zeit); ●
Rückgabewert. Zeiger auf eine Struktur vom Typ tm oder einen NULL-Zeiger, falls zeit nicht interpretiert werden konnte.
Verwandte Funktionen asctime(), gmtime(), time()
log, log10
<math.h>
Logarithmus berechnen
289
Die C-Standardbibliothek
Beschreibung Die Funktion log() berechnet den natürlichen Logarithmus ihres Arguments zur Basis e, log10() den Logarithmus ihres Arguments zur Basis 10.
Parameter double log(double x); double log10(double x); ●
Rückgabewert. Die Funktionen geben den jeweiligen Logarithmus von x zurück. Da der Logarithmus nur für positive Werte größer Null definiert ist, wird bei Werten von x < 0 EDOM in errno geschrieben; bei 0 als Argument wird HUGE_VAL zurückgeliefert.
Verwandte Funktionen exp(), pow() Siehe Beispiel zu Header-Datei math.h
longjmp
<setjmp.h>
Programmzustand wiederherstellen
Beschreibung Mit der Funktion longjmp() wird die Umgebung wiederhergestellt, die beim Aufruf von setjmp() im Array umg gespeichert wurde. Die Ausführung wird danach nicht an der Stelle fortgeführt, an der longjmp() aufgerufen wird, sondern dort, wo setjmp() aufgerufen wurde.
Parameter void longjmp(jmp_buf umg, int wert);
Warnung Die Funktion, in der der vorangehende Aufruf von setjmp() erfolgte, muß zum Zeitpunkt des Sprungs durch longjmp() aktiv, d. h. auf dem Stack abgelegt sein.
Verwandte Funktionen setjmp() Siehe Beispiel zu Header-Datei setjmp.h
malloc Speicherblock anfordern
290
<stdlib.h>
wctype.h
Beschreibung Die Funktion malloc() fordert einen Speicherblock von der übergebenen Größe an. Der von der Funktion zurückgegebene Zeiger ist vom Typ *void und muß explizit in den gewünschten Typ umgewandelt werden.
Parameter void *malloc(size_t groesse); ●
Rückgabewert. void-Zeiger auf den allokierten Speicherbereich oder NULLZeiger, wenn Speicher nicht allokiert werden konnte.
Verwandte Funktionen calloc(), free(), realloc()
mblen, mbrlen
<stdlib.h><wchar.h>
Länge eines Multibyte-Zeichens
Beschreibung Die Funktion mblen() ermittelt die Länge eines Multibyte-Zeichens. Die Funktion mbrlen() ermittelt, nach wieviel Byte das Multibyte-Zeichen vollständig ist. Es werden maximal n Bytes geprüft.
Parameter int mblen(const char *mb, size_t n); size_t mbrlen(const char *mb, size_t n, mbstate_t *ps); ●
Rückgabewert. Die Funktion mblen() liefert die Anzahl der Bytes des Multibyte-Zeichens mb zurück. Handelt es sich um kein gültiges Multibyte-Zeichen, wird ein negativer Wert zurückgegeben. Die Funktion mbrlen() liefert die Anzahl der Bytes bis zum Abschluß des Multibyte-Zeichens mb zurück. Handelt es sich um kein gültiges Multibyte-Zeichen oder tritt ein Fehler auf, wird ein negativer Wert zurückgegeben.
Verwandte Funktionen mbstowcs(), mbtowc(), mbrtowc()
mbsinit
<wchar.h>
Konvertierungsstatus abfragen
Beschreibung Ermittelt, ob das mbstate_t-Objekt, auf das der Parameter verweist, einen anfänglichen Konvertierungsstatus beschreibt.
291
Die C-Standardbibliothek
Parameter int mbsinit(const mbstate_t *ps); ●
Rückgabewert. Ein Wert ungleich Null zeigt an, daß ein NULL-Zeiger oder ein Zeiger auf einen anfänglichen Konvertierungsstatus übergeben wurde; ansonsten wird Null zurückgeliefert.
Verwandte Funktionen mbstowcs()
mbstowcs,mbsrtowcs
<stdlib.h><wchar.h>
Konvertiert Multibyte-String
Beschreibung Die Funktion mbstowcs() konvertiert den übergebenen Multibyte-String mb_string, der im anfänglichen Shift-Status beginnt (der Zustand, in dem die Zeichen aus dem C-Zeichensatz 1:1 codiert sind), in ein Feld von Elementen des Typs wchar_t. Die Funktion mbsrtowcs() konvertiert einen Multibyte-String, der im durch ps gegebenen Konvertierungsmodus ist. Es werden maximal n Wide Character-Zeichen eingelesen.
Parameter size_t mbstowcs(wchar_t *wc, const char *mb_str, size_t n); size_t mbsrtowcs(wchar_t *wc,const char *mb_str,size_t n,mbstate_t *ps); ●
Rückgabewert. Liefert die Anzahl der umgewandelten Elemente zurück (ohne mögliches Nullterminierungszeichen (\0)). Treten ungültige Multibyte-Sequenzen auf, wird ein negativer Wert zurückgegeben.
Verwandte Funktionen mbstowcs(), mbtowc(), mbrtowc()
mbtowc,mbrtowc
<stdlib.h><wchar.h>
Konvertiert Multibyte-Zeichen zu wchar_t
Beschreibung Beide Funktionen konvertieren das übergebene Multibyte-Zeichen mb_char in eine Wide Character-Repräsentation und speichern das Ergebnis in wc. Die Funktion mbtowc() bestimmt dabei die Anzahl der Bytes in dem Multibyte-Zeichen, die Funktion mbrtowc() ermittelt, nach wieviel Byte das Multibyte-Zeichen vollständig ist. Es werden maximal n Bytes konvertiert.
Parameter int mbtowc(wchar_t *wc, const char *mb_char, size_t n); size_t mbrtowc(wchar_t *wc,const char *mb, size_t n, mbstate_t *ps);
292
wctype.h
●
Rückgabewert. Die Funktion mbtowc() liefert die Anzahl der Bytes des Multibyte-Zeichens mb zurück. Handelt es sich um kein gültiges Multibyte-Zeichen, wird ein negativer Wert zurückgegeben. Die Funktion mbrtowc() liefert die Anzahl der Bytes bis zum Abschluß des Multibyte-Zeichens mb zurück. Handelt es sich um kein gültiges Multibyte-Zeichen oder tritt ein Fehler auf, wird ein negativer Wert zurückgegeben.
Verwandte Funktionen mbstowcs(), mblen(), mbrtowc()
memchr, wmemchr
<string.h><wchar.h>
Zeichen suchen
Beschreibung Die Funktion untersucht einen Speicherbereich daraufhin, ob das übergebene Zeichen vorkommt. Die Suche schreitet so lange voran, bis entweder das Zeichen gefunden oder anzahl Zeichen untersucht worden sind.
Parameter C: void *memchr(const void *puffer, int zeichen, size_t anzahl); wchar_t *wmemchr(const wchar_t *puffer, wchar_t zeichen, size_t anzahl); C++: const void *memchr(const void *puffer, int zeichen, size_t anzahl); const wchar_t *wmemchr(const wchar_t *puffer, wchar_t zeichen, size_t anzahl); void *memchr(void *puffer, int zeichen, size_t anzahl); wchar_t *wmemchr(wchar_t *puffer, wchar_t zeichen, size_t anzahl); ●
puffer. Anfangsadresse des zu durchsuchenden Speicherbereichs.
●
zeichen. Das zu suchende Zeichen.
●
anzahl. Maximal zu überprüfende Zeichen.
●
Rückgabewert. Zeiger auf das gefundene Zeichen oder NULL-Zeiger.
Verwandte Funktionen memcmp(), memset(), memcpy()
memcmp, wmemcmp
<string.h><wchar.h>
Zeichen in Speicher vergleichen
293
Die C-Standardbibliothek
Beschreibung Die Funktion vergleicht zeichenweise den Inhalt von puf1 mit dem von puf2. Der Vergleich wird so lange durchgeführt, bis entweder ein Unterschied entdeckt oder die mit anzahl übergebene Zeichenzahl überprüft wurde.
Parameter int memcmp(const void *puf1, const void *puf2, size_t anzahl); int wmemcmp(const wchar_t *puf1, const wchar_t *puf2, size_t anzahl);
Rückgabewert. Die Funktion hat drei mögliche Rückgabewerte:
●
Verwandte Funktionen strcmp(), strncmp()
memcpy, wmemcpy
<string.h><wchar.h>
Zeichen zu Zwischenspeicher kopieren
Beschreibung Die Funktion kopiert Zeichen von einem Speicherbereich in einen anderen. Es werden anzahl Zeichen kopiert. (Beide Speicherbereiche dürfen sich nicht überlappen.)
Parameter void *memcpy(void *ziel, const void *quelle, size_t anzahl); wchar_t *wmemcpy(wchar_t *ziel, const wchar_t *quelle, size_t anzahl); ●
Rückgabewert. Zeiger auf ziel.
Verwandte Funktionen memchr(), memmove(), memset()
memmove, wmemmove
<string.h><wchar.h>
Zwischenspeicher verlegen
Beschreibung Diese Funktion kopiert den Inhalt eines Speicherbereichs in einen anderen (beide Speicherbereiche dürfen sich überlappen). Es wird die als Argument übergebene Anzahl von Bytes kopiert.
294
wctype.h
Parameter void *memmove(void *ziel, const void *quelle, size_t anzahl); wchar_t *wmemmove(wchar_t *ziel, const wchar_t *, size_t anzahl); ●
Rückgabewert. Zeiger auf ziel.
Verwandte Funktionen memchr(), memchr(), memset()
memset, wmemset
<string.h><wchar.h>
Zwischenspeicher beschreiben
Beschreibung Die Funktion initialisiert einen Speicherbereich mit einem bestimmten Zeichen. Die Anzahl der zu initialisierenden Speichereinheiten wird über das Argument anzahl festgelegt. void *memset(void *puffer, int zeichen, size_t anzahl); wchar_t *wmemset(wchar_t *puffer, int zeichen, size_t anzahl); ●
Rückgabewert. Zeiger auf puffer.
Verwandte Funktionen memchr(), memcmp(), memmove()
mktime
Konvertiert Zeit von tm zu time_t
Beschreibung Die Funktion mktime() konvertiert eine Zeitangabe, wie sie in der Struktur tm abgelegt ist, in den Typ time_t.
Parameter time_t mktime(struct tm *zeit); ●
Rückgabewert. Die Funktion liefert die konvertierte Zeitangabe vom Typ time_t zurück. Konnte die Zeitangabe nicht konvertiert werden, wird -1 zurückgegeben.
Verwandte Funktionen time(), gmtime(), localtime()
modf
<math.h>
Gleitkommawert aufteilen
295
Die C-Standardbibliothek
Beschreibung Die Funktion modf() teilt eine Gleitkommazahl wert in einen ganzzahligen Vorkomma-Anteil und einen Nachkomma-Anteil auf.
Parameter double modf(double wert, double *p_int); ●
Rückgabewert. Der Nachkomma-Anteil wird zurückgegeben. Der Ganzzahlteil wird an der Stelle gespeichert, auf die p_int zeigt.
Verwandte Funktionen fmod(), frexp(), ldexp()
perror
<stdio.h>
Fehlermeldung ausgeben
Beschreibung Die Funktion perror() erzeugt eine Fehlermeldung, in dem sie den übergebenen String und die Fehlermeldung, die dem derzeitigen Wert der Variablen errno entspricht, zusammenfügt und dann an den Datenstream stderr ausgibt.
Parameter void perror(const char *zkette);
Verwandte Funktionen ferror(), clearerror(), strerror()
pow
<math.h>
Potenz ermitteln
Beschreibung Die Funktion berechnet x ^ y.
Parameter double pow(double x, double y); double pow(double x, int y); ●
Rückgabewert. Bei erfolgreicher Ausführung liefert die Funktion die Potenz von x hoch y zurück. Ist x < 0 und y keine ganzzahlige Zahl wird EDOM in errno gschrieben; bei zu großem Ergebniswert wird HUGE_VAL zurückgeliefert
296
wctype.h
Verwandte Funktionen sqrt()
printf, wprintf
<stdio.h><wchar.h>
Formatierte Ausgabe an stdout
Beschreibung Die Funktion gibt formatierte Daten an den Datenstream stdout aus. Zur Ausgabe wird eine Formatzeichenkette untersucht. Normaler Text und Escape-Sequenzen in diesem String werden einfach ausgegeben. Findet printf() (wprintf()) in der Formatzeichenkette das Zeichen % mit einem gültigen Formattyp und optionalen zusätzlichen Angaben, wird das zugehörige wahlfreie Argument von printf() (wprintf()) in den angegebenen Typ umgewandelt und in die Zeichenkette eingefügt. Das allgemeine Format für die Typangabe sieht folgendermaßen aus: % [Flag] [Breite] [Genauigkeit] [Typvorsatz] Formattyp
Die einzelnen Elemente der Typangabe und ihre Wirkung: Formattyp c s
d, i u o x X f e,E g,G p n %
Datentyp // Einzelnes Zeichen. // Nullzeichenterminierte Zeichenkette. // Für wprintf() wird im Falle des Typvorsatzes l (siehe // unten) die Wide Character-Zeichenkette in // Multibyte-Zeichen umgewandelt. // Dezimale Ganzzahl. // Dezimale Ganzzahl ohne Vorzeichen. // Oktale Ganzzahl ohne Vorzeichen. // Hexadezimale Ganzzahl, ohne Vorzeichen und ohne Präfix // (0x), Ausgabe mit Kleinbuchstaben. // Hexadezimale Ganzzahl, ohne Vorzeichen und ohne Präfix // (0X), Ausgabe mit Großbuchstaben. // Gleitkommazahl, dezimale Schreibweise. // Gleitkommazahl, Exponential-Schreibweise: // [-]d.dddde±xx bzw. [-]d.ddddE±xx // Gleitkommazahl, die kürzeste Darstellung wird gewählt // (f oder e). // Ausgabe als Zeiger. // Gibt die Anzahl der bis dahin ausgegebenen Zeichen aus. // Keine Konvertierung, das Zeichen % wird ausgegeben.
Folgende Zeichen können Sie als Flag verwenden, um das Formatelement zu erweitern: Flags -
Wirkung // Die Ausgabe erfolgt linksbündig. Wenn dieses Zeichen nicht // angegeben wird, erfolgt die Ausgabe automatisch // rechtsbündig, d.h., es werden Leerzeichen vorangestellt.
297
Die C-Standardbibliothek +
// // // // // Leerzeichen // // 0 // # // // ●
●
●
●
Bei numerischen Variablen erfolgt die Ausgabe mit Angabe eines Vorzeichens, d.h., bei positiven Zahlen wird der Ausgabe ein Pluszeichen vorangestellt. Die Ausgabe des Minuszeichens bei negativen Werten bleibt von diesem Flag unbeeinflußt. Durch Angabe dieses Zeichens erreichen Sie bei positiven Zahlen, daß ihnen ein Leerzeichen vorangestellt wird. Füllt Feldbreite mit Nullen auf. Die Wirkung des Formatzeichens # ist abhängig vom für das Argument festgelegten Formattyp.
Wenn als Formattyp 0 (oktale Ganzzahl) angegeben wurde, wird der ausgegebenen Variablen eine Null vorangestellt, sofern der Wert der Variablen nicht 0 beträgt. Bei den Formattypen für hexadezimale Ganzzahlen (x und X) wird der Ausgabe 0x bzw. 0X vorangestellt. Bei den Formattypen e, E und f (Gleitkommazahlen) wird durch # die Ausgabe eines Dezimalpunktes erzwungen. Die Wirkung bei den Formattypen g und G ist der bei e und E identisch.
Breite In diesem Feld kann die minimale Anzahl der auszugebenden Zeichen festgelegt werden. Wenn die Zahl der auszugebenden Zeichen kleiner ist, als der in [Breite] angegebene Wert, werden (je nachdem ob das – Flag gesetzt wurde) links oder rechts Leerzeichen ausgegeben, bis der in [Breite] angegebene Wert erreicht ist. Ist der Zahl eine Null vorangestellt, werden anstelle der Leerzeichen Nullen hinzugefügt. Genauigkeit Der Angabe der Genauigkeit, mit der die Anzahl der auszugebenden Dezimalstellen festgelegt wird, muß ein Punkt vorangestellt werden. So können die Parameter Breite und Genauigkeit voneinander unterschieden werden. Die Wirkung des Wertes für die Genauigkeit ist vom auszugebenden Datentyp abhängig. Genauigkeit Zeichen d,i,u,o,x,X e,E,f
g,G c s
298
Wirkung // Der Wert für Genauigkeit gibt die Minimalzahl der // Ziffern an, die ausgegeben werden. // Gibt die Anzahl der Ziffern an, die nach dem // Dezimalpunkt ausgegeben werden. Die letzte Zahl wird // gerundet. // Die durch Genauigkeit angegebene Zahl von signifikanten // Stellen wird ausgegeben. // Keine Wirkung. Das Zeichen wird ausgegeben. // Der Wert gibt die Maximalzahl der Zeichen an, die // ausgegeben werden.
wctype.h
Vor dem Formattyp können verschiedene Typvorsätze stehen, die Angaben über die Größe des Parameters machen. Typvorsatz h
Bedeutung // Wenn als Formattyp d, i, o, x oder X angegeben wird, ist // der Parameter vom Typ short int. Beim Formattyp u wird // mit dem Typvorsatz h eine Variable vom Typ short unsigned // int erwartet. // Bei den Formattypen d, i, o, x oder X wird ein Parameter // vom Typ long int, bei u vom Typ long unsigned int // erwartet. // Bei den Formattypen e, E, f, g, und G wird eine Variable // des Typs double anstelle von float erwartet. // Für wprintf() wird für einen c-Parameter ein // wint_t- statt eines int-Arguments und für einen // s-Parameter ein wchar_t-Zeiger statt eines char-Zeigers // erwartet. Zudem wird die automatische Konvertierung von // Wide Character in Multibyte eingeschaltet. // Bei den Formattypen e, E, f, g, und G wird eine Variable // des Typs long double erwartet. // Für wprintf() wird für einen s-Parameter ein // wchar_t-Zeiger statt eines char-Zeigers als Argument // erwartet.
l
L
Die nachstehenden Escape-Sequenzen werden von der Funktion printf() (wprintf()) wie folgt umgewandelt: Sequenz \a \v \b \' \f \" \n \\ \r \ddd \t \xdd
Wirkung // Signalton. // Vertikaltabulator. // Rückschritt. // Apostroph. // Seitenvorschub. // Anführungszeichen. // Neue Zeile. // Schrägstrich links. // Wagenrücklauf. // ASCII-Zeichen in Oktal-Schreibweise. // Horizontaltabulator. // ASCII-Zeichen in Hexadezimal-Schreibweise.
Parameter int printf(const char *format,...); int wprintf(const wchar_t *format,...); ●
Rückgabewert. Anzahl der ausgegebenen Zeichen.
Verwandte Funktionen sprintf(), vsprintf() Siehe Praxisteil, Kategorie Ein- und Ausgabe, Formatierte Ein- und Ausgabe in C
299
Die C-Standardbibliothek
putc, putwc
<stdio.h><wchar.h>
Zeichen in Stream schreiben
Beschreibung Die Funktion schreibt ein einzelnes Zeichen in einen Stream.
Parameter int putc(int zeichen, FILE *stream); wint_t putwc(wchar_t zeichen, FILE *stream); ●
Rückgabewert. Die Funktion gibt das geschriebene Zeichen zurück. Der Rückgabewert EOF (WEOF) deutet auf einen Fehler oder das Ende der Datei hin.
Verwandte Funktionen putchar(), printf(), puts()
putchar, putwchar
<stdio.h><wchar.h>
Zeichenausgabe an stdout
Beschreibung Die Funktion schreibt ein einzelnes Zeichen an den Datenstream stdout. Es entspricht somit dem Aufruf von putc(zeichen,stdout), bzw. putwc(zeichen,stdout).
Parameter int putchar(int zeichen); wint_t putwchar(wchar_t zeichen); ●
Rückgabewert. Die Funktion gibt das ausgegebene Zeichen zurück. Im Fehlerfall ist der Rückgabewert EOF (WEOF).
Verwandte Funktionen putc(), printf(), puts()
puts
<stdio.h>
Zeichenkette an stdout schreiben
Beschreibung Die Funktion puts() schreibt eine Zeichenkette an den Datenstream stdout. Das Nullterminierungszeichen (\0) des Strings wird durch das Zeilenvorschubzeichen (\n) ersetzt.
Parameter int puts(const char *zkette);
300
wctype.h
●
Rückgabewert. Bei fehlerfreier Ausführung wird ein nicht-negativer Wert zurückgeliefert, ansonsten EOF.
Verwandte Funktionen fputs(), gets()
qsort
<stdlib.h>
Quicksort ausführen
Beschreibung Mit der Funktion qsort() kann ein Datenfeld sortiert werden. Die Funktion verwendet dazu den QuickSort-Algorithmus. Der beim Sortieren notwendige Vergleich wird durch eine Funktion durchgeführt, deren Adresse an qsort() übergeben wird. Diese Funktion muß zwei Elemente miteinander vergleichen und folgende Werte an qsort() zurückgeben: negativ
Das erste Element ist kleiner als das zweite.
Null
Beide Elemente sind identisch.
positiv
Das erste Element ist größer als das zweite.
Parameter void qsort (void *basis, size_t anzahl, size_t laenge, int (*vgl)(const void *elem1, const void *elem2)); ●
basis. Zeiger auf Anfang des Arrays.
●
anzahl. Anzahl der Array-Elemente.
●
laenge. Größe jedes Elementes in Bytes.
●
vgl. Zeiger auf Funktion, die zwei Elemente vom Typ const void* vergleicht.
Verwandte Funktionen bsearch()
raise
<signal.h>
Signal an ausführendes Programm
Beschreibung Die Funktion raise() sendet ein Signal an das aufrufende Programm. Über die Funktion signal() ist es möglich, eigene Routinen einzurichten, die nach der Auslösung eines Signals aufgerufen werden. Für das zu erzeugende Signal (und somit für das Argument sig) können die symbolischen Konstanten SIG... (siehe Header signal.h) verwendet werden.
Parameter int raise(int sig);
301
Die C-Standardbibliothek ●
Rückgabewert. Null, bei erfolgreicher Ausführung.
Verwandte Funktionen abort(), signal()
rand
<stdlib.h>
Pseudo-Zufallszahl generieren
Beschreibung Die Funktion rand() erzeugt eine Pseudo-Zufallszahl. Die erzeugte Zahl liegt im Bereich von 0 bis RAND_MAX. Die Funktion generiert immer die gleiche Folge von Zufallszahlen, es sei denn, Sie ändern den Anfangswert mit der Funktion srand(). Der Aufruf von rand() ohne den vorherigen Aufruf von srand() hat die gleiche Wirkung, wie wenn vorher srand() mit dem Argument 1 aufgerufen worden ist.
Parameter int rand(void); ●
Rückgabewert. Erzeugte Pseudo-Zufallszahl.
Verwandte Funktionen srand()
realloc
<stdlib.h>
Speicherblock neu zuordnen
Beschreibung Mit der Funktion realloc() kann die Größe eines zuvor mit malloc() oder calloc() angeforderten Speicherblocks angepaßt werden. Der Inhalt des alten Speicherblocks bleibt dabei erhalten, seine Adresse kann sich jedoch ändern.
Parameter void *realloc(void *puffer, size_t groesse); ●
puffer. Zeiger auf den zu vergrößernden oder verkleinernden Speicherblock.
●
groesse. Neue Größe für den Speicherblock.
●
Rückgabewert. Die Funktion gibt einen Zeiger zum neu zugeordneten Speicherblock zurück oder einen NULL-Zeiger, wenn der Speicher nicht eingerichtet werden konnte.
Verwandte Funktionen calloc(), free(), malloc()
302
wctype.h
remove
<stdio.h>
Datei löschen
Beschreibung Die Funktion remove() löscht die Datei (sofern Schreibrecht besteht), deren Name ihr übergeben wurde. Die Datei sollte dazu geschlossen sein.
Parameter int remove(const char *dateiname); ●
Rückgabewert. Die Funktion gibt eine Null zurück, wenn die Datei gelöscht wurde, sonst einen Wert ungleich 0.
Verwandte Funktionen rename(), tmpfile(), tmpnam()
rename
<stdio.h>
Datei umbenennen
Beschreibung Die Funktion rename() ändert einen Dateinamen von altname in neuname. Beide Argumente müssen einen gültigen Pfadnamen beinhalten.
Parameter int rename(const char *altname, const char *neuname); ●
Rückgabewert. Die Funktion gibt eine Null zurück, wenn die Datei umbenannt werden konnte, sonst einen Wert ungleich 0.
Verwandte Funktionen remove(), tmpfile(), tmpnam()
rewind
<stdio.h>
Streampositionszeiger zum Anfang
Beschreibung Die Funktion rewind() setzt die Dateipositionsanzeige auf den Anfang der Datei und löscht eventuell gesetzte Fehlerflags. Die Funktion entspricht damit den Aufrufen: fseek(stream, 0L, SEEK_SET); clearerr(stream);
Parameter void rewind(FILE *stream);
303
Die C-Standardbibliothek
Verwandte Funktionen fseek(), ftell()
scanf, wscanf
<stdio.h><wchar.h>
Formatierte Daten von stdin lesen
Beschreibung Die Funktion liest formatierte Daten vom Datenstream stdin. Sie benutzt das Argument format, um die eingelesenen Zeichen in die richtigen Datentypen umzuwandeln. Die eingelesenen Daten werden dann an die Speicherstellen geschrieben, die mit den weiteren Funktionsargumenten übergeben werden. Für jedes weitere Funktionsargument muß in der Formatzeichenkette ein Typbezeichner angegeben worden sein. Typbezeichner besitzen folgendes allgemeine Format, wobei die Angaben in eckigen Klammern optional sind: %[*][Breite][Größe]Typ ●
●
●
●
●
%: Markiert den Anfang eines Typbezeichners. Das Prozentzeichen und der Typ sind die Elemente, aus denen jeder Typbezeichner mindestens zusammengesetzt sein muß. *: Steht nach dem Prozentzeichen ein Stern (*), wird die Zuweisung des nächsten Eingabefeldes unterdrückt. Breite: Die Breite ist eine positive Dezimal-Ganzzahl, mit der die maximal zu lesenden Zeichen angegeben werden. Größe: Dieses Feld im Typbezeichner kann verwendet werden, um die Größe der Variablen anzugeben. Typ:
Formattyp c s
d i u o x f e,E g,G p
304
Datentyp // Einzelnes Zeichen. // Zeichenkette. // Ist der Typvorsatz l gesetzt (siehe oben), werden // eingelesene Multibyte-Zeichenfolge in Wide Characters // umgewandelt. // Dezimale Ganzzahl. // Dezimale, oktale (0) oder hexadezimale (0x, 0X) Ganzzahl. // Dezimale Ganzzahl ohne Vorzeichen. // Oktale Ganzzahl ohne Vorzeichen. // Hexadezimale Ganzzahl, ohne Vorzeichen und ohne Präfix // (0x), Ausgabe der Kleinbuchstaben. // Gleitkommazahl, dezimale Schreibweise. // Gleitkommazahl, Exponential-Schreibweise: // [-]m.dddde±xx bzw. [-]m.ddddE±xx // Gleitkommazahl, die kürzeste Darstellung wird gewählt // (f oder e). // Zeiger.
wctype.h n [...]
[^...]
%
// // // // // // // // // // // //
Gibt die Anzahl der bis dahin eingelesenen Zeichen aus. Entspricht der längsten Zeichenkette, die nur aus den in den Klammern spezifizierten Zeichen besteht. Ist der Typvorsatz l gesetzt (siehe oben), werden eingelesene Multibyte-Zeichenfolge in Wide Characters umgewandelt. Entspricht der längsten Zeichenkette, die keines der in den Klammern spezifizierten Zeichen enthält. Ist der Typvorsatz l gesetzt (siehe oben), werden eingelesene Multibyte-Zeichenfolge in Wide Characters umgewandelt. Keine Konvertierung, das Zeichen % wird eingelesen.
Den Typangaben können zur näheren Spezifizierung folgende Typvorsätze vorangestellt werden: Typvorsatz h l
L
Bedeutung // Wenn als Formattyp d, i, n, o, u, x angegeben wird, ist // das Argument vom Typ short int. // Wenn als Formattyp d, i, n, o, u, x angegeben wird, ist // das Argument vom Typ long int. // Bei den Formattypen e, E, f, g, und G wird ein Argument // des Typs double anstelle von float erwartet. // Für wscanf(): Bei den Formattypen c, s und [ ] wird ein // Argument des Typs wchar_t erwartet und die automatische // Konvertierung von Multibyte zu Wide Character // eingeschaltet. // Bei den Formattypen e, E, f, g, und G wird ein Argument // des Typs long double erwartet.
Parameter int scanf(const char *format, ...); int wscanf(const wchar_t *format, ...); ●
Rückgabewert. Die Funktion gibt die Anzahl der umgewandelten und eingelesenen Felder zurück (kann auch 0 sein) oder EOF (WEOF), wenn versucht wurde, das Ende der Zeichenkette zu lesen, oder ein Fehler aufgetreten ist.
Verwandte Funktionen fscanf(), printf(), sscanf(), vsprintf()
setbuf
<stdio.h>
Stream-Puffer
Beschreibung Mit der Funktion setbuf() kann für die Zwischenspeicherung eines Streams ein eigener Puffer eingerichtet werden, der den automatisch zugeordneten Puffer ersetzt. Dazu muß das Funkti-
305
Die C-Standardbibliothek
onsargument puffer auf einen Speicherbereich zeigen, der mindestens BUFSIZ Bytes groß ist. Ist das Argument ein NULL-Zeiger, wird keine Zwischenspeicherung durchgeführt.
Parameter void setbuf(FILE *stream, char *puffer);
Verwandte Funktionen setvbuf()
setjmp
<setjmp.h>
Programmstatus speichern
Beschreibung Die Funktion setjmp() dient zur Vorbereitung eines nicht lokalen Sprungs (aus einer Funktion heraus). Alle wichtigen Informationen zum späteren Rücksprung an die aktuelle Stelle werden in den Puffer umg gespeichet.
Parameter int setjmp(jmp_buf umg); ●
Rückgabewert. Nach dem Sichern der Umgebung ist der Rückgabewert 0. Nach einem späteren longjmp()-Aufruf liefert die Funktion einen Wert ungleich 0, um eine Endlosschleife zu vermeiden:
Verwandte Funktionen longjmp() Siehe Beispiel zu Header-Datei setjmp.h
setlocale
Ändern einer Locale-Einstellung
Beschreibung Mit dieser Funktion kann die lokale Einstellung zu dem verwendeten Zeichensatz, den Währungsangaben, etc. abgefragt oder gewechselt werden. Dem ersten Parameter können die LC...Konstanten (siehe Header-Datei locale.h) übergeben werden, um auszuwählen, welche Kategorien von den Änderungen betroffen sein sollen. Der zweite Parameter zeigt auf den Namen der lokalen Einstellung, die für die angegebenen Kategorien eingerichtet werden soll (beispielsweise "C" für die minimale Umgebung oder "" für die Systemumgebung). Wird an den Parameter lokale ein NULL-Zeiger übergeben, wird ein String für die Kategorie der aktuellen lokalen Einstellungen zurückgeliefert.
306
wctype.h
Parameter char *setlocale(int kategorie, char *lokale); ●
● ●
kategorie. Eine der LC-Konstanten aus locale.h zur Auswahl eines Teilgebiets der lokalen Einstellungen. lokale. Name der Lokale. Rückgabewert. Bei Erfolg gibt die Funktion einen Zeiger auf den String mit den Kategorien und Einstellungen zurück. Im Fehlerfall wird ein NULL-Zeiger zurückgeliefert, und die Locale-Einstellung bleibt unverändert.
Verwandte Funktionen localeconv()
setvbuf
<stdio.h>
Stream-Pufferung
Beschreibung Mit der Funktion setvbuf() kann die Zwischenspeicherung für einen Stream festgelegt werden. Gegenüber der Funktion setbuf() haben Sie hier mehr Einstellmöglichkeiten.
Parameter int setvbuf(FILE *stream, char *buffer, int buf_typ, size_t buf_groesse); ● ●
●
●
stream. Stream, für den Puffer gesetzt werden soll. buffer. Nullzeiger (der Puffer wird von setvbuf eingerichtet) oder Zeiger auf selbst allokierten Puffer. buf_typ. Kann den Wert einer von drei symbolischen Konstanten annehmen, die in der Datei <stdio.h> definiert sind. Wenn das Argument buf_typ den Wert _IONBF besitzt, werden die Optionen buffer und buf_groesse ignoriert und die Flags des Streams auf diesen Modus eingestellt. Bei den beiden anderen Werten wird zuerst der Wert des Arguments buffer überprüft. Steht dort ein NULL-Zeiger, allokiert die Funktion einen Zwischenspeicher von der Größe, die in buf_groesse angegeben ist. Anderenfalls sollte buffer auf einen Speicherbereich zeigen, der von Ihnen in buf_groesse Bytes allokiert wurde. _IOFBF
Zeigt an, daß ein Stream voll gepuffert werden soll. Das bedeutet, daß so lange Daten im Zwischenspeicher abgelegt werden, bis er voll ist.
_IOLBF
Stream wird zeilenweise gepuffert.
_IONBF
Stream wird nicht gepuffert.
buf_groesse. Größe des Puffers.
307
Die C-Standardbibliothek ●
Rückgabewert. Bei Erfolg gibt die Funktion eine Null zurück. Im Fehlerfall oder bei ungültigen Parametern ist der Rückgabewert ungleich Null.
Warnung Die Funktion muß aufgerufen werden, nachdem der Stream mit einer geöffneten Datei verbunden wurde und bevor irgendwelche Operationen auf dem Stream ausgeführt wurden.
Verwandte Funktionen setbuf()
signal
<signal.h>
Unterbrechungssignal-Handhabung
Beschreibung Mit der Funktion signal() können Bearbeitungsroutinen festgelegt werden, die immer dann aufgerufen werden, wenn ein bestimmtes Signal auftritt.
Parameter void (*signal(int sig, void(*funkt)(int)))(int); ●
●
●
sig. Die Funktion erhält als erstes Argument eine SIG..-Konstante (siehe Header-Datei signal.h), die das Signal beschreibt, dessen Behandlung geändert werden soll. funkt. Das zweite Argument ist ein Zeiger auf eine Funktion, die keinen Wert zurückgibt, jedoch die Nummer des Signals erhält. Ruft diese Bearbeitungsfunktion irgendeine Funktion der C-Laufzeitbibliothek außer signal() auf, ist das Verhalten implementierungsspezifisch. Statt eines Funktionszeigers können auch folgende Konstanten benutzt werden: SIG_DFL
Bezeichnet die Standardbearbeitung.
SIG_IGN
Signal ignorieren. Das Unterbrechungssignal wird ignoriert. Diese Konstante sollte nicht gemeinsam mit SIGFPE eingesetzt werden, da dies das Fließkomma-Arithmetik-Paket unbrauchbar macht.
SIG_ERR
Wird benutzt, um eine Fehlerbedingung bei der Ausführung anzuzeigen.
Rückgabewert. Im Erfolgsfall gibt die Funktion die Adresse der vorherigen Behandlungsroutine für die Signale des angegebenen Typs zurück. Im Fehlerfall wird der Wert der Konstanten SIG_ERR zurückgegeben und die globale Variable errno auf einen positiven Wert gesetzt.
308
wctype.h
Verwandte Funktionen raise() Siehe Beispiel zu Header-Datei signal.h
sin, sinh
<math.h>
Sinus berechnen
Beschreibung Die Funktion sin() berechnet den Sinus, sinh() den Sinus hyperbolicus ihres Arguments.
Parameter double sin(double x); double sinh(double x); ●
Rückgabewert. sin() liefert den Sinus des Arguments im Bereich [-1, 1]. sinh() liefert den Sinus hyperbolicus des Arguments im Bereich [-8 , 8 ].
Warnung Der Sinus hyperbolicus steigt und fällt sehr schnell.
Verwandte Funktionen asin(), cos(), cosh() Siehe Beispiel zu Header-Datei math.h
sprintf, swprintf
<stdio.h><wchar.h>
Formatierte Daten in String schreiben
Beschreibung Die Funktion nimmt eine formatierte Ausgabe in eine Zeichenkette vor. Die Formatzeichenkette hat den für printf() beschriebenen Aufbau.
Parameter int sprintf(char *zkette, const char *format, ...); int swprintf(wchar_t *zkette, size_t n, const wchar_t *format, ...); ●
zkette. Zeiger auf String, in den Ausgabe erfolgen soll.
●
n. Maximale Anzahl an Zeichen, die geschrieben werden (nur für swprintf()).
●
format. Formatzeichenkette (siehe printf()).
●
.... Variable Anzahl von Argumenten.
●
Rückgabewert. Die Funktion gibt die Anzahl der ausgegebenen Zeichen zurück, ohne die abschließende Null. Im Fehlerfall wird EOF (WEOF) zurückgeliefert.
309
Die C-Standardbibliothek
Verwandte Funktionen printf(), sscanf(), vprintf(), vsprintf()
sqrt
<math.h>
Quadratwurzel berechnen
Beschreibung Die Funktion sqrt() berechnet die Quadratwurzel ihres Arguments und gibt diesen Wert zurück.
Parameter double sqrt(double zahl); ●
Rückgabewert. Quadratwurzel des Arguments. Da die Wurzel nur für positive Werte größer Null definiert ist, wird bei Werten von x < 0 EDOM in errno geschrieben.
Verwandte Funktionen pow()
srand
<stdlib.h>
Zufallszahlengenerator initialisieren
Beschreibung Die Funktion srand() verwendet ihr Argument, um den Startpunkt einer Reihe von Zufallszahlen festzulegen, die dann von rand() zurückgegeben werden. Wird die Funktion zweimal mit dem gleichen Wert aufgerufen, erzeugt sie die gleiche Folge von Zufallszahlen.
Parameter void srand(unsigned int zahl);
Verwandte Funktionen rand()
sscanf, swscanf
<stdio.h><wchar.h>
Formatierte Daten aus String einlesen
Beschreibung Die Funktion liest formatierte Daten aus einem String ein. Sie verwendet die Formatzeichenkette, um die eingelesenen Daten in die richtigen Typen umzuwandeln. Die weiteren wahlfreien Argumente müssen Adressen sein, an die die eingelesenen Daten geschrieben werden sollen.
310
wctype.h
Die Formatzeichenkette kann das gleiche Muster enthalten, wie unter scanf() beschrieben.
Parameter int sscanf(const char *str, const char *format, ...); int swscanf(const wchar_t *str, const wchar_t *format, ...); ●
puffer. Zeiger auf String, aus dem die Daten ausgelesen werden.
●
format. Formatierungsstring (siehe scanf()).
●
Rückgabewert. Die Funktion gibt die Anzahl der umgewandelten und eingelesenen Felder zurück oder EOF (WEOF), wenn versucht wurde, das Ende der Zeichenkette zu lesen.
Verwandte Funktionen fscanf(), scanf(), sprintf()
strcat, wcscat
<string.h><wchar.h>
Zeichenkette anhängen
Beschreibung Die Funktion kopiert alle Zeichen des Strings, auf den quelle zeigt (inklusive des den String beendenden \0-Zeichens), an das Ende der Zeichenkette, auf die ziel zeigt. Das Terminationszeichen des ersten Strings wird überschrieben.
Parameter char *strcat(char * ziel, const char * quelle); wchar_t *wcscat(wchar_t * ziel, const wchar_t * quelle); ●
Rückgabewert. Zeiger auf die verbundene Zeichenkette.
Verwandte Funktionen strncat(), strcpy(), strspn()
Siehe Praxisteil, Kategorie Zeichenketten, Programmieren mit C-Strings
strchr, wcschr
<string.h><wchar.h>
Zeichen in String finden
Beschreibung Die Funktion sucht nach dem ersten Vorkommen eines Zeichens in einer Zeichenkette. Das Nullterminierungszeichen am Ende der Zeichenkette ist in die Suche eingeschlossen.
311
Die C-Standardbibliothek
Parameter C: char *strchr(const char *zkette, int zeichen); wchar_t *wcschr(const wchar_t *zkette, wchar_t zeichen); C++: const char *strchr(const char *zkette, int zeichen); const wchar_T *wcschr(const wchar_t *zkette, wchar_t zeichen); char *strchr(char *zkette, int zeichen); wchar_t *wcschr(wchar_t *zkette, wchar_t zeichen); ●
Rückgabewert. Die Funktion gibt einen Zeiger auf zeichen zurück, wenn es gefunden wurde, anderenfalls einen NULL-Zeiger.
Verwandte Funktionen strrchr(), strcspn(), strpbrk(), strspn()
strcmp, wcscmp
<string.h><wchar.h>
Zeichenketten vergleichen
Beschreibung Die Funktion vergleicht jedes Zeichen von zkette1 mit dem Zeichen an der korrespondierenden Position von zkette2. Der Vergleich wird so lange fortgeführt, bis entweder zwei unterschiedliche Zeichen gefunden oder das Ende der Zeichenkette erreicht wurde.
Parameter int strcmp(const char *zkette1, const char *zkette2); int wcscmp(const wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Die Funktion hat drei mögliche Rückgabewerte, in denen das Ergebnis des Vergleichs kodiert ist: < 0
zkette1 < zkette2
0
zkette1 == zkette2
> 0
zkette1 > zkette2
Verwandte Funktionen strncmp(), strcoll()
strcoll, wcscoll
<string.h><wchar.h>
Zeichenkette kopieren
Beschreibung Die Funktion vergleicht den String zkette1 mit dem String zkette2 nach den lokalen Einstellungen der Kategorie LC_COLLATE.
312
wctype.h
Parameter int strcoll(const char *zkette1, const char *zkette2); int wcscoll(const wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Die Funktion hat drei mögliche Rückgabewerte, in denen das Ergebnis des Vergleichs kodiert ist: < 0
zkette1 < zkette2
0
zkette1 == zkette2
> 0
zkette1 > zkette2
Verwandte Funktionen strcmp()
strcpy, wcscpy
<string.h><wchar.h>
Zeichenkette kopieren
Beschreibung Die Funktion kopiert alle Zeichen der Zeichenkette quelle (inklusive des beendenden Nullterminierungszeichens) in den Speicherbereich, auf den ziel zeigt. Das Verhalten der Funktion ist nicht definiert, wenn der Speicherbereich, auf den ziel zeigt, vom Speicherbereich überlappt wird, auf den quelle zeigt.
Parameter char *strcpy(char *ziel, const char *quelle); wchar_t *wcscpy(wchar_t *ziel, const wchar_t *quelle); ●
Rückgabewert. Zeiger auf die ziel-Zeichenkette.
Verwandte Funktionen strncpy()
strcspn, wcscspn
<string.h><wchar.h>
Präfix in String
Beschreibung Die Funktion bestimmt die Länge des vorangehenden Teil-Strings von zkette1, der keine Zeichen aus zkette2 enthält.
Parameter size_t strcspn(const char *zkette1,const char *zkette2); size_t wcscspn(const wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Länge des gefundenen Präfix von zkette1.
313
Die C-Standardbibliothek
Verwandte Funktionen strspn(), strncmp()
strerror
<string.h>
System-Fehlermeldung holen
Beschreibung Die Funktion strerror() dient der Unterstützung beim Generieren von Fehlermeldungen. Üblicherweise erhält sie als Argument die globale Variable errno, die von einigen Funktionen der Laufzeitbibliothek gesetzt wird, wenn bei der Abarbeitung der entsprechenden Funktion ein Fehler auftrat. Die Funktion strerror() schreibt die zu dem Wert von fehler gehörende Fehlermeldung in einen statischen Puffer und liefert einen Zeiger auf diesen Puffer zurück. Eine Ausgabe der Fehlermeldung erfolgt mit dieser Funktion nicht.
Parameter char *strerror(int fehler); ●
Rückgabewert. Zeiger auf die Fehlermeldung, die dem Argument fehler entspricht.
Verwandte Funktionen perror()
strftime, wcsftime
<wchar.h>
Formatiert die Uhrzeit
Beschreibung Die Funktion übernimmt die Zeitangabe aus der Variablen zeit der Struktur tm, konvertiert sie entsprechend der in dem Parameter format enthaltenen Formatierungsanweisungen und der lokalen Einstellung (LC_TIME) und gibt sie in die Zeichenkette str aus. Folgende Formatierungsangaben sind für den Parameter format zulässig: %% %a %A %b %B %c %d %H %I %j %m %M %p
314
// // // // // // // // // // // // //
Prozentzeichen %. Abgekürzter Name des Wochentags. Voller Name des Wochentags. Abgekürzter Name des Monats. Voller Name des Monats. Datum und Zeit. Tag des Monats (01 – 31). Stunde (00 -23). Stunde (01 – 12). Tag des Jahres (001 – 366). Monat (01 – 12). Minute (00 – 59). AM oder PM.
wctype.h %S %U %w %W %x %X %y %Y %Z
// // // // // // // // //
Sekunde (00 – 59). Wochennummer (00 – 53), Woche beginnt mit Sonntag. Wochentag (0 – 6), Sonntag == 0. Wochennummer (00 – 53), Woche beginnt mit Montag. Datum. Uhrzeit. Jahr ohne Jahrhundertangabe (00 – 99). Jahr mit Jahrhundertangabe. Name der Zeitzone.
Parameter size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *zeit); size_t wcsftime(wchar_t *str, size_t maxsize, const wchar_t *format, const struct tm *zeit); ●
● ●
● ●
str. Ausgabe-String, in den die umformatierte Datums/Zeitangabe geschrieben wird.. maxsize. Maximale Anzahl von Zeichen, die in str geschrieben werden dürfen. format. Formatierungs-String, der aus oben aufgeführten Formatierungsanweisungen bestehen kann. zeit. Umzuformatierende Datum/Zeitangabe. Rückgabewert. Die Funktion liefert die Anzahl, der nach str geschriebenen Zeichen zurück. Werden mehr Zeichen benötigt, als durch maxsize freigestellt, wird Null zurückgeliefert. Die konvertierte Zeitangabe wird über den Parameter str zur Verfügung gestellt.
Verwandte Funktionen localtime(), time(), gmtime(), setlocale()
strlen, wcslen
<string.h><wchar.h>
Länge einer Zeichenkette ermitteln
Beschreibung Die Funktion ermittelt die Länge der übergebenen Zeichenkette. Das den String beendende Nullterminierungszeichen wird nicht mitgezählt.
Parameter size_t strlen(const char *zkette); size_t wcslen(const wchar_t *zkette); ●
Rückgabewert. Länge der Zeichenkette (ohne Nullterminierungszeichen).
315
Die C-Standardbibliothek
Verwandte Funktionen strcpy(), sizeof-Operator
strncat, wcsncat
<string.h><wchar.h>
n Zeichen an String anhängen
Beschreibung Die Funktion kopiert anzahl Zeichen des Strings, auf den quelle zeigt, an das Ende des Strings, auf den ziel zeigt. Das Kopieren wird beendet, wenn entweder anzahl Zeichen übertragen oder das Ende von ziel erreicht wurde.
Parameter char *strncat(char * ziel, const char *quelle, size_t anzahl); wchar_t *wcsncat(wchar_t * ziel, const wchar_t *quelle, size_t anzahl); ●
Rückgabewert. Zeiger auf ziel.
Verwandte Funktionen strcat(), strncpy()
strncmp, wcsncmp
<string.h><wchar.h>
n Zeichen in Strings vergleichen
Beschreibung Die Funktion vergleicht die ersten anzahl Zeichen der Strings zkette1 und zkette2. Der Vergleich endet, wenn entweder ein Nullterminierungszeichen eingelesen wird oder ein Zeichenpaar gefunden wurde, das nicht identisch ist.
Parameter int strncmp (const char *zkette1, const char *zkette2, size_t anzahl); int wcsncmp (const wchar_t *zkette1, const wchar_t *zkette2, size_t anzahl); ●
Rückgabewert. Die Funktion hat drei mögliche Rückgabewerte, in denen das Ergebnis des Vergleichs kodiert ist: < 0
zkette1 < zkette2
0
zkette1 == zkette2
> 0
zkette1 > zkette2
Verwandte Funktionen strcmp(), strcoll()
316
wctype.h
strncpy, wcsncpy
<string.h><wchar.h>
n Zeichen aus String kopieren
Beschreibung Die Funktion kopiert anzahl Zeichen aus dem Speicherbereich, auf den quelle zeigt, in den Speicherbereich, auf den ziel zeigt. Wenn quelle weniger als anzahl Zeichen besitzt, wird so lange das Nullterminierungszeichen kopiert, bis anzahl Zeichen geschrieben wurde. Der Speicherbereich, auf den ziel zeigt, muß anzahl Zeichen groß sein. Anderenfalls kann es passieren, daß durch die Funktion andere Daten oder Code überschrieben werden.
Parameter char *strncpy(char *ziel, const char *quelle, size_t anzahl); wchar_t *wcsncpy(wchar_t *ziel, const wchar_t *quelle, size_t anzahl); ●
Rückgabewert. Zeiger auf ziel.
Verwandte Funktionen strcat(), strncat(), strspn()
strpbrk, wcspbrk
<string.h><wchar.h>
Zeichen in String suchen
Beschreibung Die Funktion überprüft die Zeichenkette zkette1 daraufhin, ob in ihr ein beliebiges Zeichen aus der Zeichenkette zkette2 vorkommt.
Parameter C: char *strpbrk(const char *zkette1, const char *zkette2); wchar_t *wcspbrk(const wchar_t *zkette1, const wchar_t *zkette2); C++: const char *strpbrk(const char *zkette1, const char *zkette2); const wchar_t *wcspbrk(const wchar_t *zkette1, const wchar_t *zkette2); char *strpbrk(char *zkette1, const char *zkette2); wchar_t *wcspbrk(wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Die Funktion gibt einen Zeiger zum ersten entsprechenden Zeichen in zkette1 oder einen NULL-Zeiger zurück, wenn kein identisches Zeichen gefunden wird.
Verwandte Funktionen strchr(), strrchr(), strstr()
317
Die C-Standardbibliothek
strrchr, wcsrchr
<string.h>
Suche: letztes Vorkommen von Zeichen
Beschreibung Die Funktion tastet die übergebene Zeichenkette daraufhin ab, ob in ihr ein bestimmtes Zeichen vorkommt. Das abschließende Nullterminierungszeichen ist in die Suche eingeschlossen und kann ebenfalls das Zeichen sein, nach dem gesucht wird.
Parameter C: char *strrchr(const char *zkette, int zeichen); wchar_t *wcsrchr(const wchar_t *zkette, wchar_t zeichen); C++: const char *strrchr(const char *zkette, int zeichen); const wchar_T *wcsrchr(const wchar_t *zkette, wchar_t zeichen); char *strrchr(char *zkette, int zeichen); wchar_t *wcsrchr(wchar_t *zkette, wchar_t zeichen); ●
Rückgabewert. Die Funktion gibt einen Zeiger zum letzten (d. h. am weitesten rechts liegenden) Vorkommen von zeichen in zkette zurück, wenn das Zeichen gefunden wurde. Anderenfalls wird ein NULL-Zeiger zurückgegeben.
Verwandte Funktionen strcspn(), strpbrk(), strspn()
strspn, wcsspn
<string.h><wchar.h>
Erstes fremdes Zeichen suchen
Beschreibung Die Funktion untersucht, bis zu welcher Position zkette1 ausschließlich aus Zeichen besteht, die in zkette2 enthalten sind.
Parameter size_t strspn(const char *zkette1, const char *zkette2); size_t wcsspn(const wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Länge des Präfix bis zum ersten ungleichen Zeichen.
Verwandte Funktionen strcspn(), strpbrk(), strrchr()
strstr, wcsstr Zeichenkette in anderer finden
318
<string.h><wchar.h>
wctype.h
Beschreibung Die Funktion sucht nach zkette2 in zkette1.
Parameter C: char *strstr(const char *zkette1,const char *zkette2); wchar_t *strstr(const wchar_t *zkette1, const wchar_t *zkette2); C++: const char *strstr(const char *zkette1, const char *zkette2); const wchar_t *wcsstr(const wchar_t *zkette1, const wchar_t *zkette2); char *strstr(const char *zkette1, const char *zkette2); wchar_t *wcsstr(const wchar_t *zkette1, const wchar_t *zkette2); ●
Rückgabewert. Zeiger zum ersten Vorkommen von zkette2 in zkette1 zurück, bzw. einen NULL-Zeiger, wenn zkette2 nicht gefunden wurde.
Verwandte Funktionen memchr(), strchr(), strcspn(), strrchr()
strtod, wcstod
<stdlib.h><wchar.h>
Umwandlung: String in double
Beschreibung Die Funktion wandelt einen String zkette in eine Gleitkommazahl doppelter Genauigkeit (Typ double) um. Die Umwandlung wird beendet, nachdem das erste Zeichen, das nicht mehr als Bestandteil der Zahl erkannt wird, oder **endzgr erreicht wurde.
Parameter double strtod(const char *zkette, char **endzgr); double wcstod(const wchar_t *zkette, wchar_t **endzgr); ●
Rückgabewert. Die Funktion gibt den umgewandelten Wert zurück.
Verwandte Funktionen strtol(), strtoul()
strtok, wcstok
<string.h><wchar.h>
Suche nächstes Grundsymbol in String
Beschreibung Die Funktion ermittelt einzelne Grundsymbole in einer Zeichenkette. Um die Grundsymbole zu finden, sucht sie nach den in zkette2 enthaltenen Trennzeichen.
319
Die C-Standardbibliothek
Ab dem zweiten Aufruf kann für zkette1 das Argument NULL übergeben werden. Die Funktion sucht dann nach dem nächsten Grundsymbol ab der Stelle, die beim ersten Aufruf intern gespeichert wurde. Durch wiederholte Aufrufe der Funktion kann die Zeichenkette zkette1 in eine Reihe Teil-Strings aufgesplittet werden, die jeweils durch Zeichen aus zkette2 abgeschlossen werden. Die Wide Character-Version benötigt einen zusätzlichen Zeiger auf wchar_t, um Hilfsinformationen ablegen zu können. char *strtok(char *zkette1, const char *zkette2); wchar_t *wcstok(wchar_t *zkette1, const wchar_t *zkette2, wchar_t ** zeiger); ●
Rückgabewert. Zeiger auf das gefundene Grundsymbol oder NULL-Zeiger, wenn keine Token mehr vorhanden sind.
Verwandte Funktionen strpbrk(), strcspn(), strspn(), strstr()
strtol, wcstol
<stdlib.h><wchar.h>
Umwandlung: String in long
Beschreibung Die Funktion wandelt eine Zeichenkette in einen Wert vom Typ long um. Die Umwandlung wird beendet, nachdem das erste Zeichen, das nicht mehr als Bestandteil der Zahl erkannt wird, oder **endzgr erreicht wurde.
Parameter long strtol(const char *zkette, char **endzgr, int basis); long int wcstol(const wchar_t *zkette, wchar_t **endzgr, int basis); ●
Rückgabewert. Gibt den umgewandelten Wert zurück.
Verwandte Funktionen strtod(), strtoul(), sprintf(), atof()
strtoul, wcstoul
<stdlib.h><wchar.h>
Umwandlung: String in unsigned long
Beschreibung Die Funktion wandelt eine Zeichenkette in einen Wert vom Typ unsigned long um. Die Umwandlung wird beendet, nachdem das erste Zeichen, das nicht mehr als Bestandteil der Zahl erkannt werden konnte, oder **endzgr erreicht wurde.
320
wctype.h
Parameter unsigned long int strtoul (const char *zkette, char **endzgr, int basis); unsigned long int wcstoul (const wchar_t *zk,wchar_t **endzgr,int basis); ●
Rückgabewert. Bei Erfolg der umgewandelten Wert, anderenfalls Null.
Verwandte Funktionen strtod(), strtol()
strxfrm, wcsxfrm
<string.h><wchar.h>
Umwandlung nach lokaler Einstellung
Beschreibung Die Funktion wandelt maximal anz Zeichen der Zeichenkette quelle um und schreibt das Ergebnis nach ziel. Bei erfolgreicher Transformation liefern die Aufrufe: strcmp(ziel, x)
und strcoll(quelle, x)
danach das gleiche Ergebnis.
Parameter size_t strxfrm (char *ziel, char *quelle, size_t anz); size_t wcsxfrm (wchar_T *ziel, wchar_t *quelle, size_t anz); ●
Rückgabewert. Anzahl der transformierten Zeichen.
Verwandte Funktionen strcmp(), strcoll()
system
<stdlib.h>
Betriebssystem-Befehl ausführen
Beschreibung Mit der Funktion system() kann von einem C-Programm aus ein anderes Programm ausgeführt werden. Der momentan laufende Prozeß wird unterbrochen, und der übergebene Systembefehl an den Befehlsinterpreter, falls vorhanden, weitergeleitet.
Parameter int system(const char *prgname); ●
Rückgabewert. Implementierungsspezifisch.
321
Die C-Standardbibliothek
tan, tanh
<math.h>
Tangente berechnen
Beschreibung Die Funktion tan() berechnet den Tangens und tanh() den Tangens hyperbolicus ihres Arguments.
Parameter double tan(double x); double tanh(double x); ●
Rückgabewert. tan() liefert den Tangens des Arguments im Bereich [ -8, 8 ]. tanh() liefert den Tangens hyperbolicus des Arguments im Bereich [-1, 1].
Warnung Der Tangens nähert sich in der Umgebung der Vielfachen von p /2 den Werten -8 und 8.
Verwandte Funktionen acos(), asin(), atan(), atan2(), sinh() Siehe Beispiel zu Header-Datei math.h
time
System-Uhrzeit holen
Beschreibung Die Funktion time() ermittelt die aktuelle Kalenderzeit. Die Kalenderzeit ist die Anzahl der Sekunden, die seit dem 1. Januar 1970, 00:00:00 Uhr, vergangen sind. Wenn das Argument p_zeit kein NULL-Zeiger ist, wird die Kalenderzeit auch an die Adresse geschrieben, auf die p_zeit zeigt.
Parameter time_t time(time_t *p_zeit); ●
Rückgabewert. Anzahl der seit dem 1.1.1970, 00:00:00 Uhr, verstrichenen Sekunden.
Verwandte Funktionen asctime(), ctime(), gmtime(), localtime() Siehe Beispiel zu Header-Datei time.h
tmpfile Vorübergehende Datei anlegen
322
<stdio.h>
wctype.h
Beschreibung Die Funktion tmpfile() erzeugt eine temporäre Datei. Die Datei wird im Modus wb+ geöffnet und automatisch entfernt, wenn die Datei geschlossen wird oder das Programm endet.
Parameter FILE *tmpfile(void); ●
Rückgabewert. Zeiger auf eine FILE-Struktur. Konnte die Datei nicht geöffnet werden, wird ein NULL-Zeiger zurückgegeben.
Verwandte Funktionen tmpnam()
tmpnam
<stdio.h>
Vorübergehenden Dateinamen schaffen
Beschreibung Die Funktion tmpnam() erzeugt einen eindeutigen Namen für eine temporäre Datei. Erhält die Funktion als Argument einen NULL-Zeiger, wird der Name in einen internen statischen Puffer geschrieben, der von weiteren Aufrufen der Funktion überschrieben wird. Ansonsten zeigt zkette auf einen Speicherbereich, in dem der Name abgelegt werden soll. Der für den Dateinamen vorgesehene Speicherbereich muß mindestens L_tmpnam Bytes groß sein. Sie sollten also diese Konstante bei der Definition eines Arrays auf char als Größenangabe verwenden.
Parameter char *tmpnam(char *zkette); ●
Rückgabewert. Zeiger auf den temporären Namen.
Verwandte Funktionen tmpfile()
tolower, towlower
<wctype.h>
Umwandlung: Groß- in Kleinbuchstaben
Beschreibung Die Funktion wandelt das übergebene Zeichen in einen Kleinbuchstaben um. Die Funktion tolower() überprüft, ob der übergebene Buchstabe ein Großbuchstabe ist, zu dem ein Kleinbuchstabe existiert, und nimmt nur dann die Umwandlung vor.
323
Die C-Standardbibliothek
Parameter int tolower(int zeichen); wint_t towlower(wchar_t zeichen); ●
Rückgabewert. Die Funktion gibt das umgewandelte Zeichen zurück. Wird ein Kleinbuchstabe übergeben, wird dieser unverändert zurückgeliefert.
Verwandte Funktionen toupper(), towupper()
toupper, towupper
<wctype.h>
Umwandlung: Klein- in Großbuchstaben
Beschreibung Die Funktion wandelt das übergebene Zeichen in einen Großbuchstaben um. Die Funktion toupper() überprüft, ob der übergebene Buchstabe ein Großbuchstabe ist, zu dem ein Kleinbuchstabe existiert, und nimmt nur dann die Umwandlung vor.
Parameter int toupper(int zeichen); wint_t towupper(wchar_t zeichen); ●
Rückgabewert. Die Funktion gibt das umgewandelte Zeichen zurück. Wird ein Großbuchstabe übergeben, wird dieser unverändert zurückgeliefert.
Verwandte Funktionen tolower(), towlower()
towctrans
<wctype.h>
Wide Character-Abbildung
Beschreibung Konvertiert ein Wide Character-Zeichen gemäß der durch die Parameterbeschreibung gegebenen Abbildung. Argumente für die Parameterbeschreibung können mit Hilfe der Funktion wctrans() erzeugt werden.
Parameter wint_t towctrans(wint_t wc, wctrans_t beschreibung); ●
Rückgabewert. Wert, auf den das Zeichen abgebildet wurde.
Verwandte Funktionen wctrans()
324
wctype.h
ungetc, ungetwc
<stdio.h><wchar.h>
Zeichen zurück an Stream schicken
Beschreibung Die Funktion schreibt das eingelesene Zeichen zurück an den Stream. Die Funktion kann nicht beliebig viele Male hintereinander ausgeführt werden. Bei gepufferter Ein- und Ausgabe wird das Zeichen in den Puffer geschrieben und geht entsprechend bei Operationen, die den Puffer löschen (fseek(), rewind(), fflush(), fsetpos()), verloren.
Parameter int ungetc(int zeichen, FILE *stream); wint_t ungetwc(wchar_t zeichen, FILE *stream); ●
Rückgabewert. Die Funktion gibt das Argument zeichen zurück, wenn erfolgreich, sonst EOF (WEOF).
Verwandte Funktionen getc(), getchar()
va_...
<stdarg.h>
Variable Anzahl von Argumenten
Beschreibung Die Makros va_arg(), va_end() und va_start() dienen zur Implementierung von Funktionen, die mit einer variablen Anzahl von Argumenten aufgerufen werden, wie beispielsweise printf() oder scanf().
Parameter type va_arg(va_list arg_zgr, type); void va_end(va_list arg_zgr); void va_start(va_list arg_zgr, letzter_param);
Verwandte Funktionen vfprintf(), vprintf(), vsprintf()
v-,vf-,vs- (w)printf
<stdio.h><wchar.h>
Formatierte Daten ausgeben
Beschreibung Diese sechs Funktionen geben formatierte Daten aus. Sie sind mit den Funktionen fprintf(), printf() und sprintf() nahezu identisch. Die variable Anzahl von Argumenten wird nicht als Liste, sondern als Zeiger auf eine Liste von Argumenten übergeben.
325
Die C-Standardbibliothek
Die drei Funktionen unterscheiden sich durch das Ziel ihrer Ausgabe. vfprintf() schreibt an einen beliebigen Stream, vprintf() an den Stream stdout und vsprintf() in einen Speicherbereich.
Parameter int int int int int int ●
vfprintf(FILE *stream, const char *format, va_list argzgr); vfwprintf(FILE *stream, const wchar_t *format, va_list argzgr); vprintf(const char *format, va_list argzgr); vwprintf(const wchar_t *format, va_list argzgr); vsprintf(char *puffer, const char *format, va_list argzgr); vswprintf(wchar_t *puffer, size_t n, const wchar_t *format, va_list argzgr);
Rückgabewert. Die drei Funktionen geben die Anzahl der geschriebenen Zeichen ohne das »\0«-Zeichen zurück. Im Fehlerfall wird ein negativer Wert zurückgeliefert.
Verwandte Funktionen fprintf(), printf(), sprintf(), va_arg(), va_end(), va_start()
wcstombs, wcsrtombs
<stdlib.h><wchar.h>
wchar_t zu Multibyte
Beschreibung Beide Funktionen konvertieren die übergebene Folge von wchar_t-Zeichen in einen MultibyteString. Die Konvertierung bricht ab, wenn n Bytes konvertiert wurden oder ein Nullterminierungszeichen oder ein ungültiges Multibyte-Zeichen aufgetreten ist. Werden genau n Bytes ohne abschließendes Nullterminierungszeichen konvertiert, ist der String nicht nullterminiert. Die Konvertierung berücksichtigt die lokale Einstellung der Kategorie LC_TYPE. Die Funktion wcstomb() erzeugt eine Folge von Multibyte-Zeichen im anfänglichen Shift-Status (Zustand in dem die Zeichen aus dem C-Zeichensatz 1:1 codiert sind). Die Funktion wcsrtomb() erzeugt eine Folge von Multibyte-Zeichen im durch den mbstate_t-Argument gegebenen Konvertierungsstatus.
Parameter size_t wcstombs(char *mb_str, const wchar_t *wc, size_t n); size_t wcsrtombs(char *mb_str,const wchar_t *wc,size_t n,mbstate_t *ps); ●
Rückgabewert. Anzahl der geänderten Bytes (ohne Nullterminierungszeichen), bzw. -1 im Fehlerfall.
Verwandte Funktionen wctomb(), wcrtomb()
326
wctype.h
wctob
<wchar.h>
1-Byte-Repräsentation abfragen
Beschreibung Bestimmt, ob es sich bei dem Zeichen c um ein Zeichen eines erweiterten Zeichensatzes handelt, dessen Multibyte-Darstellung im anfänglichen Shift-Status (Zustand in dem die Zeichen aus dem C-Zeichensatz 1:1 codiert sind) ein einzelnes Byte ist.
Parameter int wctob(wint_t c); ●
Rückgabewert. 1-Byte-Repräsentation des übergebenen Zeichens. EOF, wenn das Zeichen nicht mit einem MultiByte-Zeichen korrespondiert, das im anfänglichen Konvertierungsstatus nur aus einem Byte besteht.
Verwandte Funktionen btowc()
wctomb, wcrtomb
<stdlib.h><wchar.h>
wchar_t zu Multi-Byte
Beschreibung Beide Funktionen konvertieren das übergebene wchar_t-Zeichen in ein Multibyte-Zeichen. Die Konvertierung bricht nach spätestens MB_CUR_MAX Zeichen ab. Die Konvertierung berücksichtigt die lokale Einstellung der Kategorie LC_TYPE.
Parameter int wctomb(char *mb_ch, const wchar_t *wc); size_t wcrtomb(char *mb_ch, const wchar_t *wc, mbstate_t *ps); ●
Rückgabewert. Anzahl der geänderten Bytes (ohne Nullterminierungszeichen), bzw. -1 im Fehlerfall.
Verwandte Funktionen wcstombs()
wctrans
<wctype.h>
Wide Character-Abbildung kennzeichnen
Beschreibung Erzeugt einen Wert des Typ wctrans_t, der die durch den Parameter eigenschaft beschriebene Abbildung von einem Wide Character-Zeichensatz in einen anderen (nach LC_TYPE) identifiziert. Wird zusammen mit towctrans() benutzt.
327
Die C-Standardbibliothek
Parameter wctrans_t wctrans(const char *eigenschaft); ●
Rückgabewert. Wert, der eine gemäß LC_TYPE gültige Wide Character-Abbildung repräsentiert und als zweites Argument an towctrans() übergeben werden kann.
Verwandte Funktionen towctrans()
wctype
<wctype.h>
Wide Character-Klasse kennzeichnen
Beschreibung Erzeugt einen wctype_t-Wert, der die Klasse von Wide Characters bezeichnet, die als Argument übergeben wurde. Wird zusammen mit iswctype() verwendet und erlaubt die Identifizierung einer Klasse anhand eines Strings.
Parameter wctype_t wctype(const char *eigenschaft); ●
Rückgabewert. Wert, der eine Wide Character-Klasse repräsentiert und als zweites Argument an iswctype() übergeben werden kann.
Verwandte Funktionen iswctype()
328
Die C++-Standardbibliothek Übersicht über die Standardbibliothek Die in der C++-Standardbibliothek definierten Klassen und Funktionen können in verschiedene Gruppen aufgeteilt werden: ●
●
●
●
●
●
●
●
●
Die Container-Klassen implementieren die gängigsten Modelle zur Behandlung und Speicherung von Daten. Die verschiedenen Modelle erlauben es, Datenobjekte nach bestimmten Kriterien abzulegen, beispielsweise als einfache Menge (set) oder als Keller (stack). Iteratoren dienen dazu, auf bequeme und einheitliche Weise auf die Elemente in Containern zugreifen zu können. Algorithmen. In der Header-Datei sind eine Reihe von praktischen Funktionen implementiert, die mit Hilfe von Iteratoren die Elemente in Containern manipulieren. Die Stream-Klassen bilden eine kleine Klassenhierarchie zur Regelung des gepufferten oder ungepufferten Datenflusses zwischen Festspeicher, Arbeitsspeicher und Bildschirm. Die String-Klassen bieten eine komfortablere und weniger fehleranfällige Alternative zum Umgang mit Zeichenketten. Die Klasse locale und die zugehörigen Facettenklassen dienen zur Anpassung an lokale Besonderheiten. Die mathematischen Klassen umfassen vor allem die beiden Klassen valarray (besonders für die Verwendung auf Parallelrechnern ausgelegt) und complex (zur Behandlung komplexer Zahlen). Die Diagnoseklassen ermöglichen Typidentifikationen zur Laufzeit (wichtig bei Zeigern auf Klasseninstanzen) und objektorientierte Fehlerbehandlung. Die restlichen Utility-Klassen unterstützen beispielsweise die dynamische Speicherallokation und die Programmierung mit Funktionsobjekten.
329
Die C++-Standardbibliothek
Die Header-Dateien Ebenso wie in C viele wichtige und häufig gebräuchliche Funktionen fester Bestandteil der C-Laufzeitbibliothek sind, umfaßt C++ standardmäßig eine Reihe nützlicher Klassen und Klassenhierarchien. Diese Klassen sind ebenfalls Teil der Laufzeitbibliothek. Um auf diese Klassen zugreifen zu können, müssen wiederum die entsprechenden Header-Dateien mittels #include-Anweisungen eingebunden werden. Die C++-Klassen erweitern zum einen die Laufzeitbibliothek um neue Funktionalitäten und Einsatzbereiche, zum anderen werden bestehende Konzepte objektorientiert implementiert. Dies gilt insbesondere für die Stream-Klassen, die zur Regelung des Datenflusses zwischen Arbeitsspeicher, Bildschirm und Festplatte dienen und damit die Funktionen aus der Header-Datei stdio.h ersetzen. Dies bedeutet aber nicht, daß die alten C-Funktionen nicht mehr verfügbar wären oder man nicht beide Systeme mischen könnte. Ob Sie sich der C++-Klassen oder der C-Funktionen bedienen, oder beide Konzepte nebeneinander in einem Programm verwenden, ist gänzlich Ihnen überlassen. Alles andere würde auch der Kompatibilität von C und C++ widersprechen. Teilweise wird die Mischung sogar absichtlich unterstützt, indem Standard C-Funktionen überladen werden, um zusammen mit den Klassen der Laufzeitbibliothek verwendet werden zu können. Die Elemente der C++-Laufzeitbibliothek sind in dem Namensbereich std eingeschlossen. Üblicherweise läßt man daher auf die #include-Anweisungen für die Header-Dateien die Anweisung using namespace std;
folgen.
Warnung Noch werden aber nicht alle Elemente der C++Bibliothek von allen Compilern unterstützt. Manche Compiler unterstützen keine Namensbereiche oder haben nicht alle Klassen der Laufzeitbibliothek im Namensbereich std deklariert. Manche Compiler bieten Teile der Laufzeitbibliothek in den alten C-Headern an, beispielsweise iostream.h statt iostream.
algorithm Beschreibung Enthält eine ganze Reihe von Funktionentemplates, die die gängigsten Algorithmen zur Anwendung auf Container-Elementen implementieren (Kopieren, Verschieben, Sortieren,
330
algorithm
Suchen, etc.). Unter Containern sind hier allerdings nicht nur die in der C++-Laufzeitbibliothek definierten Container zu verstehen. Algorithmen können in gleicher Weise auch auf selbst definierte Container oder auch simple Arrays angwendet werden Voraussetzung ist lediglich, daß für den Zugriff auf die Elemente im Container passende Iteratoren zur Verfügung stehen und die Elemente geeignet sind, von den Algorithmen bearbeitet zu werden.
Enthaltene Funktionentemplates adjacent_find() binary_search() copy() copy_backward() count() count_if() equal() equal_range() fill() fill_n()
// // // // // // // // // // // find() // find_end() // find_first_of() // find_if() // for_each() // generate() // generate_n() // // includes() // inplace_merge() // iter_swap() // lexicographical_compare() // lower_bound() // make_heap() // max() // max_element() // merge() // min() // min_element() // mismatch() // // next_permutation() // nth_element() // partial_sort() // partial_sort_copy() // partition() // pop_heap() // prev_permutation() // push_heap() // random_shuffle() // remove() //
Benachbarte Vorkommen aufspüren Element in sortierter Sequenz suchen Sequenz kopieren (beginnt mit erstem Element) Sequenz kopieren (beginnt mit letztem Element) Elemente in Sequenz zählen Bestimmte Elemente in Sequenz zählen Elemente in Sequenz paarweise vergleichen Einheitliche Sequenz suchen Elemente in Sequenz einen Wert zuweisen Den ersten n Elementen in Sequenz einen Wert zuweisen Wert in Sequenz suchen Letztes Vorkommen einer Teilsequenz suchen Element in Sequenz suchen Bestimmtes Element suchen Funktion auf Elemente anwenden Sequenz einen berechneten Wert zuweisen Ersten Elementen einer Sequenz einen berechneten Wert zuweisen Sequenz enthalten in anderer Sequenz Zwei Sequenzen vermengen Zwei Elemente tauschen zwei Sequenz lexikographisch vergleichen Element in sortierte Sequenz einfügen Sequenz in Heap umwandeln Maximum zweier Werte bestimmen Maximum einer Sequenz bestimmen Zwei Sequenzen vermengen Minimum zweier Werte bestimmen Minimum einer Sequenz bestimmen Ersten Elemente zweier Sequenzen, die nicht gleich sind, aufspüren Nächste Permutation erzeugen ntes Element einsortieren Teilsequenz sortieren Teilsequenz sortiert kopieren Ausgewählte Elemente nach vorne kopieren Element von Heap entfernen Vorangehende Permutation erzeugen Element in Heap einfügen Elemente in Sequenz neu verteilen Elemente mit bestimmtem Wert entfernen
331
Die C++-Standardbibliothek remove_copy()
// // remove_copy_if() // remove_if() // replace() // replace_copy() // // replace_copy_if() // // replace_if() // reverse() // reverse_copy() // rotate() // rotate_copy() // search() // search_n() // set_difference() // // set_intersection() // set_symmetric_difference() // // set_union() // sort() // sort_heap() // stable_partition() // // stable_sort() // // swap() // swap_ranges() // transform() // unique() // unique_copy() // upper_bound() //
Nur Elemente kopieren, die ungleich einem bestimmten Wert sind Ausgewählte Elemente kopieren Bestimmte Elemente entfernen Elemente mit bestimmtem Wert ersetzen Sequenz kopieren, bestimmte kopierte Elemente durch Wert ersetzen Sequenz kopieren, bestimmte kopierte Elemente durch Wert ersetzen Bestimmte Elemente ersetzen Reihenfolge der Elemente umkehren Sequenz in umgekehrter Reihenfolge kopieren Elemente rotieren Sequenz in rotierter Reihenfolge kopieren Teilsequenz suchen Teilsequenz mit identischen Werten suchen Differenz zweier sortierter Sequenzen (für Elemente aus A, aber nicht B) Schnitt zweier sortierter Sequenzen Differenz zweier sortierter Sequenzen (für Elemente entweder in A oder B) Vereinigung zweier sortierter Sequenzen Sequenz sortieren Heap sortieren Ausgewählte Elemente unter Beibehaltung ihrer relativen Ordnung nach vorne kopieren Sequenz sortieren (Reihenfolge gleicher Elemente bleibt erhalten) Zwei Elemente tauschen Elemente zweier Sequenzen tauschen Elementen einer Sequenz neue Werte zuweisen Duplikate aus Sequenz entfernen Sequenz ohne Duplikate kopieren Element in sortierte Sequenz einfügen
bitset Beschreibung Definiert ein Klassentemplate und einige überladene Operatoren zur Verwaltung und Bearbeitung von Bitfolgen feststehender Größe.
Enthaltene Klassentemplates bitset
// Für Bit-Folgen feststehender Größe
Überladene Operatoren &, |, ^, <<, >>
332
complex
Beispiel #include #include using namespace std; int main(int argc, char **argv) { enum optionen {op1, op2, op3, op4}; bitset<4> options("0100"); // Bits initialisieren options.set(op1); options.set(op3,0);
// Letztes Bit setzen // 3. Bit von rechts auf Null setzen
cout << "Optionen: " << options << endl; for(int loop = op1; loop <= op4; loop++) if(options[loop]) cout << loop << "-te Option gesetzt" << endl; else cout << loop << "-te Option nicht gesetzt" << endl; return 0; }
complex Beschreibung Definiert das Klassentemplate complex einschließlich Spezialisierungen des Klassentemplates für die Datentypen float, double und long double, für die Programmierung mit komplexen Zahlen. Zudem sind etliche Funktionentemplates und überladene Operatoren für die Verwendung mit complex-Objekten definiert.
Enthaltene Klassentemplates complex complex<double> complex complex
// // // //
Klasse für komplexe Spezialisierung für Spezialisierung für Spezialisierung für
Zahlen. Datentyp double. Datentyp float. Datentyp long double.
Enthaltene Funktionentemplates abs() arg() conj() cos() cosh() exp() imag()
// // // // // // //
Betrag berechnen. Argument ermitteln. Konjugiert Komplexe bestimmen. Kosinus berechnen. Kosinus hyperbolicus berechnen. Exponentialfunktion berechnen. Imaginärteil bestimmen.
333
Die C++-Standardbibliothek log() log10() norm() polar() pow() real() sin() sinh() sqrt() tan() tanh()
// // // // // // // // // // //
Natürlichen Logarithmus berechnen. Logarithmus zur Basis 10 berechnen. Norm bestimmen. Komplexe Zahl aus Polarform bestimmen. Zahl potenzieren. Realteil bestimmen. Sinus berechnen. Sinus hyperbolicus berechnen. Quadratwurzel berechnen. Tangens berechnen. Tangens hyperbolicus berechnen.
Überladene Operatoren +, –, *, /, +, –, ==, !=, <<, >>
Beispiel #include using namespace std; complex<double> zahl(32,6, -5.1);
deque Beschreibung Definiert das Klassentemplate deque, das einen Container zur Verwaltung von Objekten beliebiger Datentypen implementiert. Zusätzlich sind Funktionen (swap()) und Operatoren zur Anwendung auf deque-Objekte definiert.
Enthaltene Templates deque
// Datenstruktur, die für Einfügen/Löschen am Anfang
swap()
// und Ende optimiert ist und schnellen direkten Zugriff // auf beliebige Positionen zuläßt. // Zum Vertauschen der Elemente zweier deque-Container.
Überladene Operatoren ==, !=, <, >, >=, <=
Beispiel #include <deque> using namespace std; class demo;
334
exception deque container(5); // deque-Container für int-Objekte deque<demo> container(5); // deque-Container für demo-Objekte
exception Beschreibung Enthält verschiedene Klassen, Typdefinitionen und Funktionen, die mit der Exception-Behandlung in C++-Programmen in Zusammenhang stehen.
Enthaltene Klassen und Typdefinitionen exception
// Basisklasse, der in der Laufzeitbibliothek
// definierten Exception-Klassen. // Für Exception-Objekte, die von // der Funktion unexpected() // ausgelöst werden. typedef void (*terminate_handler)() // Typ des Funktionsarguments zu // set_terminate() typedef void (*unexpected_handler)() // Typ des Funktionsarguments zu // set_unexpected()
bad_exception
Enthaltene Funktionen terminate() set_terminate() unexpected()
// Ruft Funktion zum Programmabbruch auf. // Richtet eine Funktion zum Programmabbruch ein. // Ruft Funktion zur Behandlung unerwarteter
set_unexpected()
// Richtet Funktion zur Behandlung unerwarteter
uncaught_exception()
// Liefert true, wenn eine noch nicht abgefangene
// Exceptions auf. // Exceptions ein. // Exception ausgelöst wurde.
Verweise Siehe Kategorie Sprachkonzepte, Exception-Behandlung
fstream Beschreibung Enthält Klassentemplates für Dateistreams und die dazugehörigen Puffer. Für die Zeichentypen char und wchar_t sind bereits entsprechende Klassen aus den Templates instanziiert, die man direkt in Programmen verwenden kann.
335
Die C++-Standardbibliothek
Enthaltene Klassentemplates basic_fstream
// Stellt HighLevel-Routinen zur Ein- und Ausgabe von
basic_ifstream
// Stellt HighLevel-Routinen zum Einlesen aus einer
basic_ofstream
// Stellt HighLevel-Routinen zum Ausgeben in eine
basic_filebuf
// Von basic_streambuf abgeleiteter Puffer, der die
// und in Dateien zur Verfügung. // Datei zur Verfügung. // Datei zur Verfügung. // Ein- und Ausgabe mit einer Datei verknüpft.
Enthaltene Klassen fstream wfstream ifstream wifstream ofstream wofstream filebuf wfilebuf
// // // // // // // //
typedef typedef typedef typedef typedef typedef typedef typedef
basic_fstream fstream basic_fstream<wchar_t> wfstream basic_ifstream ifstream basic_ifstream<wchar_t> wifstream basic_ofstream ofstream basic_ofstream<wchar_t> wofstream basic_filebuf<wchar_t> filebuf basic_filebuf<wchar_t> wfilebuf
functional Beschreibung Funktionsobjekte sind Klasseninstanzen, für die in den vielen Fällen lediglich der ()-Operator definiert ist. Der Operator erlaubt es, Objekte dieser Klassen wie Funktionsaufrufe einzusetzen, da die Anwendung des Operators auf eine Instanz der Klasse syntaktisch genauso aussieht wie ein Funktionsaufruf. In der STL werden Funktionsobjekte häufig als Parameter zu den Algorithmen eingesetzt. Zur weiteren Unterstützung der Programmierung mit Funktionsobjekten sind in der HeaderDatei noch etliche Hilfsdatentypen, Klassen und Funktionen definiert.
Enthaltene Funktionsobjekte divides equal_to greater greater_equal less less_equal logical_and logical_not logical_or minus modulus
336
// // // // // // // // // // //
Funktionsobjekt zur Division. Funktionsobjekt zum Test auf Gleichheit. Funktionsobjekt, äquivalent zu >. Funktionsobjekt, äquivalent zu >=. Funktionsobjekt, äquivalent zu <. Funktionsobjekt, äquivalent zu <=. Funktionsobjekt, äquivalent zu &&. Funktionsobjekt, äquivalent zu !. Funktionsobjekt, äquivalent zu ||. Funktionsobjekt zur Subtraktion. Funktionsobjekt zur Modulo-Berechnung // (Rest der Division).
functional multiplies negate not_equal_to plus
// // // //
Funktionsobjekt Funktionsobjekt Funktionsobjekt Funktionsobjekt
zur zur zum zur
Multiplikation. Negation. Test auf Ungleichheit. Addition.
Enthaltene Hilfselemente binary_function
// Basisklasse für Funktionsobjekte
binary_negate
// Hilfsklasse zur Negation einer Aussage
bind1st()
// Funktion, die das erste Argument eines
// mit zwei Argumenten. // mit zwei Argumenten. // Funktionsobjekts für zwei Argumente mit // einem konstanten Wert verbindet. bind2nd() // Funktion, die das zweite Argument eines // Funktionsobjekts für zwei Argumente mit // einem konstanten Wert verbindet. binder1st // Hilfsklasse zur Bindung eines Wertes an das // erste Argument eines Funktionsobjektes. binder2nd // Hilfsklasse zur Bindung eines Wertes an das // zweite Argument eines Funktionsobjektes. mem_fun_t // Hilfsklasse zur Übergabe von Methoden ohne // Argument an Funktionsobjekt-Parameter. mem_fun1_t // Hilfsklasse zur Übergabe von Methoden mit // einem Argument an Funktionsobjekt-Parameter. mem_fun() // Hilfsfunktion zur Übergabe von Methoden ohne // Argument an einen Funktionsobjekt-Parameter mem_fun1() // Hilfsfunktion zur Übergabe von Methoden mit // einem Argument an einen Funktionsobjekt-Parameter mem_fun_ref_t // Hilfsklasse zur Übergabe von Methoden ohne // Argument an Funktionsobjekt-Parameter mem_fun1_ref_t // Hilfsklasse zur Übergabe von Methoden mit // einem Argument an Funktionsobjekt-Parameter. mem_fun_ref() // Hilfsfunktion zur Übergabe von Methoden ohne // Argument an einen Funktionsobjekt-Parameter. mem_fun1_ref() // Hilfsfunktion zur Übergabe von Methoden mit einem // Argument an einen Funktionsobjekt-Parameter. not1() // Negiert eine unäre Aussage (Funktionsobjekt // mit Rückgabetyp bool). not2() // Negiert eine binäre Aussage (Funktionsobjekt // mit Rückgabetyp bool). pointer_to_binary_function // Zur Erzeugung von Funktionsobjekten aus // Zeigern auf Funktionen mit zwei Argumenten pointer_to_unary_function // Zur Erzeugung von Funktionsobjekten aus // Zeigern auf Funktionen mit einem Argument ptr_fun() // Hilfsfunktion, die es ermöglicht eine Funktion an // einen Binder oder einen Negator zu übergeben. unary_function // Basisklasse für Funktionsobjekte mit // einem Argument unary_negate // Hilfsklasse zur Negation einer Aussage mit // einem Argument
337
Die C++-Standardbibliothek
Beispiel // Verwendung eines Funktionsobjekts // Werte sortieren; stable_sort(container.begin(), container.end(), less()); // Verwendung eines Binders // ersten Wert groesser 500 suchen iter i = find_if(container.begin(), container.end(), bind2nd(greater(),500));
iomanip Beschreibung Enthält Manipulatoren zur Konfiguration von Streams (werden wie Daten an Streams geschickt).
Enthaltene Funktionen resetiosflags setbase setfill setiosflags setprecision setw
// // // // // //
Formatierungsflag löschen. Basis des Zahlensystems festlegen. Füllzeichen festlegen. Formatierungsflag setzen. Genauigkeit festlegen. Feldbreite festlegen.
Beispiel #include using namespace std; cout cout cout cout
<< << << <<
setw(7); 1.23; setw(7); "Ha!" << endl;
// Ausgabe:
1.23
Ha!
Verweise Siehe Kategorie Ein- und Ausgabe, Streams in C++
ios Beschreibung Enthält Typdefinitionen und Basisklassen für die Stream-Klassentemplates sowie eine Reihe von Manipulatoren zur Konfiguration von Stream-Eingaben und -Ausgaben.
338
ios
Enthaltene Klassen und Typdefinitionen basic_ios ios_base
// Basisklasse der Stream-Klassen. // Klasse, die Aufzählungstypen, Bitfelder und verwandte
fpos streamoff streamsize
// Funktionen für die Streamkonfiguration und -diagnose // enthält. Dient als Basisklasse zu basic_ios // Hilfsklasse für Streamstatus und Dateipositionierung. // Hilfstyp für Streams. // Integer-Typ, der als Rückgabetyp der Schreib- und // Lesemethoden verwendet wird (soweit diese die Anzahl // gelesener oder geschriebener Zeichen zurückliefern).
Enthaltene Manipulatoren boolalpha dec hex fixed internal left noboolalpha noshowbase noshowpoint noshowpos noskipws nouppercase oct right scientific showbase showpoint showpos skipws uppercase
// // // // // // // // // // // // // // // // // // // //
bool-Typ berücksichtigen. Dezimaldarstellung. Hexadezimaldarstellung. Feste Stellenzahl. Bestimmte Stellen mit Füllzeichen. Linksbündige Ausgabe. bool-Typ nicht berücksichtigen. Basis des Zahlensystems nicht anzeigen. Gleitkommazahlen ohne Dezimalpunkt. Positive Zahlen ohne Vorzeichen. Whitespace miteinlesen. Keine Konvertierung in Großbuchstaben. Oktaldarstellung. Rechstbündige Ausgabe. Darstellung mit Mantisse und Exponent. Basis des Zahlensystems anzeigen. Gleitkommazahlen mit Dezimalpunkt. Positive Zahlen mit Vorzeichen. white space nicht einlesen. Bestimmte Zeichen als Großbuchstaben.
Beispiel cout.width(10); cout << 1234.5 << endl; cout.width(10); cout << left << 1234.5 << endl;
Ausgabe: 1234.5 1234.5
Verweise Siehe Kategorie Ein- und Ausgabe, Streams in C++
339
Die C++-Standardbibliothek
iosfwd Beschreibung Enthält die folgenden Typdefinitionen (Instanziierungen von Stream-Klassen aus den Templates der Stream-Bibliothek) und die dafür erforderlichen Vorwärtsdeklarationen der Templates.
Enthaltene Klassendefinitionen typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef
basic_ios ios; basic_ios<wchar_t> wios; basic_streambuf streambuf; basic_istream istream; basic_ostream ostream; basic_iostream iostream; basic_stringbuf stringbuf; basic_istringstream istringstream; basic_ostringstream ostringstream; basic_stringstream stringstream; basic_filebuf filebuf; basic_ifstream ifstream; basic_ofstream ofstream; basic_fstream fstream; basic_streambuf<wchar_t> wstreambuf; basic_istream<wchar_t> wistream; basic_ostream<wchar_t> wostream; basic_iostream<wchar_t> wiostream; basic_stringbuf<wchar_t> wstringbuf; basic_istringstream<wchar_t> wistringstream; basic_ostringstream<wchar_t> wostringstream; basic_stringstream<wchar_t> wstringstream; basic_filebuf<wchar_t> wfilebuf; basic_ifstream<wchar_t> wifstream; basic_ofstream<wchar_t> wofstream; basic_fstream<wchar_t> wfstream; fpos::state_type> streampos; fpos::state_type> wstreampos;
iostream Beschreibung Definiert eine Reihe von Stream-Objekten, die bei Start jedes Programmes automatisch eingerichtet werden und zur Verfügung stehen.
Definitierte Standard-Stream-Objekte istream cin ostream cout
340
// StandardEingabe-Stream für char // StandardAusgabe-Stream für char
istream ostream cerr ostream clog wistream wcin wostream wcout; wostream wcerr wostream wclog
// // // // // //
Standardfehlerausgabe für char Standardprotokollausgabe für char StandardEingabe-Stream für wchar_t StandardAusgabe-Stream für wchar_t Standardfehlerausgabe für wchar_t Standardprotokollausgabe für wchar_t
Beispiel #include <string> #include int main(int argc, char **argv) { string str; int wert; cout << "Geben Sie einen String und einen int-Wert ein" << endl; cin >> str >> wert; cout << left; // Manipulator cout << setw(10) << "String =" << setw(10) << str << endl; cout << setw(10) << "Wert =" << setw(10) << wert << endl; return 0; }
Verweise Siehe Kategorie Ein- und Ausgabe, Streams in C++
istream Beschreibung Enthält das Klassentemplate basic_istream zur Unterstützung von Leseoperationen auf Streams, sowie zwei Instanziierungen des Templates für die Datentypen char und wchar_t, die man in Programmen direkt verwenden kann.
Enthaltene Templates basic_istream
// Stellt HighLevel-Routinen zum Einlesen aus
basic_iostream
// Stream zu Lesen und Schreiben
ws
// Manipulator zum Überlesen von Whitespace
// einem Stream zur Verfügung. // einem Stream zur Verfügung.
Enthaltene Klassen istream wistream
// Spezialisierung für Datentyp char // Spezialisierung für Datentyp wchar_t
341
Die C++-Standardbibliothek
Beispiel filebuf datei; datei.open("Daten.dat", ios_base::in | ios_base::out); istream dat_ein(&datei); ostream dat_aus(&datei);
iterator Beschreibung Iteratoren dienen dazu, Elemente von Containern zu dereferenzieren. Ihr Einsatz ähnelt der Indizierung von Arrays. Die meisten der Funktionen aus arbeiten mit Iteratoren. In der Header-Datei sind einige Iteratoren (in Form von Templates) vordefiniert. Des weiteren enthält die Datei verschiedene Strukturen für die Definition eigener Iteratoren und Hilfsfunktionen für die Programmierung mit Iteratoren.
Hilfsfunktionen advance() distance() back_inserter() front_inserter() inserter()
// // // // //
Iterator verrücken Distanz zwischen zwei Iteratoren ermitteln Liefert einen Back-Inserter für einen Container Liefert einen Front-Inserter für einen Container Liefert einen Inserter für einen Container
Iteratoren-Templates back_insert_iterator
// Iterator zum Einfügen am Ende einer
front_insert_iterator
// Iterator zum Einfügen am Anfang einer
insert_iterator
// Iterator zum Einfügen an beliebiger Stelle in
istream_iterator istreambuf_iterator ostream_iterator ostreambuf_iterator reverse_iterator
// // // // //
// Datenstruktur. // Datenstruktur. // eine Datenstruktur. Iterator zum Lesen aus einen Stream. Iterator zum Lesen aus einen Streampuffer. Iterator zum Schreiben in einen Stream. Iterator zum Schreiben in einen Streampuffer. Random Access-Iterator, der die Datenstruktur // in umgekehrter Richtung durchläuft.
Hilfsstrukturen bidirectional_iterator_tag
// Vordefinierte Struktur als Hilfe zur
forward_iterator_tag
// Vordefinierte Struktur als Hilfe zur
input_iterator_tag
// Vordefinierte Struktur als Hilfe zur
iterator
// Basisklasse zur Definition von Iteratoren
// Erstellung eigener Iteratoren. // Erstellung eigener Iteratoren. // Erstellung eigener Iteratoren.
342
limits iterator_traits
// Struktur mit Charakteristika
output_iterator_tag
// Vordefinierte Struktur als Hilfe zur
random_access_iterator_tag
// Vordefinierte Struktur als Hilfe zur
// von Iteratoren // Erstellung eigener Iteratoren. // Erstellung eigener Iteratoren
Überladene Operatoren Für reverse_iterator: Für istream_iterator: Für istreambuf_iterator:
// ==, <, !=, >, >=, <=, –, + // ==, != // ==, !=
Beispiel deque container(5); typedef deque::iterator iter; ... for(iter i = container.begin(); i != container.end(); i++) cout << *i << endl; }
{
limits Beschreibung Enthält das Klassentemplate numeric_limits und seine Spezialisierungen für die verschiedenen Datentypen, die der Programmierer zum Abfragen implementierungsspezifischer Charakteristika der elementaren Datentypen nutzen kann.
Enthaltene Elemente float_round_style
// Aufzählungstyp mit folgenden Werten: // // // // //
numeric_limits
// // // // // // // // //
round_indeterminate (-1) round_toward_zero (0) round_to_nearest (1) round_toward_infinity(2) round_toward_neg_infinity(3) Template-Klasse mit Informationen zur implementierungsspezifischen Darstellung der numerischen Datentypen. Mit Spezialisierungen für: bool char, signed char, unsigned char, wchar_t short, int, long unsigned short, unsigned int, unsigned long float, double, long double
343
Die C++-Standardbibliothek
Beispiel #include using namespace std; ... cout << "Int: " << setw(14) << numeric_limits::min() << setw(14) << numeric_limits::max() << endl;
list Beschreibung Definiert das Klassentemplate list, das einen Container zur Verwaltung von Objekten beliebiger Datentypen implementiert. Zusätzlich sind Funktionen (swap()) und Operatoren zur Anwendung auf list-Objekte definiert.
Die enthaltenen Funktionen und Klassen list
// Datenstruktur, mit schnellem Einfügen/Löschen
swap()
// Zum Vertauschen der Elemente zweier list-Container.
// auf allen Positionen, kein direkter Zugriff
Überladene Operatoren ==, !=, <, >, >=, <=
Beispiel #include <list> using namespace std; class demo; list container(5); list<demo> container(5);
// list-Container für int-Objekte // list-Container für demo-Objekte
locale Beschreibung In bestimmten Anwendungen ist es notwendig, lokale Einstellungen zu berücksichtigen (Darstellungen von Zeichen oder Datumsangaben oder die Darstellung numerischer Werte). Die einzelnen Bereiche werden als Kategorien bezeichnet, die wiederum in Facetten (Spezialisierungen der entsprechenden Template-Klassen) untergliedert werden. Mit Hilfe der Klasse locale und ihrer Methoden können verschiedene lokale Einstellungen und ihre Kategorien oder Facetten erstellt und ausgewählt werden.
344
locale
Enthaltene Klassen locale codecvt
// Klasse zur Verwaltung lokaler Einstellungen. // Kategorie ctype. Unterstützt die Konvertierung
codecvt_base codecvt_byname
// Definiert Aufzählungstyp für Ergebniswerte. // Auswahl spezifischer Implementierungen anhand des
collate
// Klasse zur gleichnamigen Kategorie. Unterstützt den
collate_byname
// Auswahl spezifischer Implementierungen anhand des
ctype
// Klasse zur gleichnamigen Kategorie. Unterstützt die
// zwischen Codierungsformen.
// Namens der lokalen Einstellung. // Vergleich und die Aufspaltung von Zeichenketten. // Namens der lokalen Einstellung. // Zeichenerkennung sowie das Einlesen und // Formatieren von Zeichenketten. ctype // Spezialisierung für den Datentyp char. ctype_base // Definiert Bitfeld zur Zeichenklassifizierung. ctype_byname // Auswahl spezifischer Implementierungen anhand des // Namens der lokalen Einstellung. ctype_byname // Spezialisierung für den Datentyp char. messages // Klasse zur gleichnamigen Kategorie. Unterstützt das // Herauslesen von strings aus Botschaftskatalogen. messages_base // Definiert den Datentyp catalog. messages_byname // Auswahl spezifischer Implementierungen anhand des // Namens der lokalen Einstellung. money_base // Definiert Aufzählunstyp part zur Formatierung von // Währungsangaben und den Rückgabetyp pattern. moneypunct // Kategorie monetary. Unterstützt die Punktsetzung // in Währungsangaben. moneypunct_byname // Auswahl spezifischer Implementierungen anhand des // Namens der lokalen Einstellung. money_get // Kategorie monetary. Unterstützt das Einlesen // von Währungsangaben. money_put // Kategorie monetary. Unterstützt die formatierte // Ausgabe von Währungsangaben. numpunct // Kategorie numeric. Unterstützt die Punktsetzung // in numerischen Werten. numpunct_byname // Auswahl spezifischer Implementierungen anhand des // Namens der lokalen Einstellung. num_get // Kategorie numeric. Unterstützt das Einlesen // numerischer Werte. num_put // Kategorie numeric. Unterstützt die Formatierung // bei der Ausgabe numerischer Werte. time_base // Aufzählungstyp zur Abfolge von Datumsangaben. time_get // Kategorie time. Unterstützt das Einlesen von // Zeitangaben in eine Variable des Typs tm. time_get_byname // Auswahl spezifischer Implementierungen anhand // des Namens der lokalen Einstellung. time_put // Kategorie time. Unterstützt die Ausgabe von // Zeitangaben. time_put_byname // Auswahl spezifischer Implementierungen anhand // des Namens der lokalen Einstellung.
345
Die C++-Standardbibliothek
Funktionentemplates has_facet() use_facet() is... tolower() toupper()
// Testet, ob eine bestimmte Facette verfügbar ist // Auswahl einer Facette zu einer locale-Einstellung. // Methoden zur Zeichenklassifizierung (siehe C-Header // ctype.h) // Umwandlung in Kleinbuchstaben. // Umwandlung in Großbuchstaben.
map Beschreibung Definiert die Klassentemplates map und multimap, die Container zur Verwaltung von Objekten beliebiger Datentypen implementieren. Zusätzlich sind Funktionen (swap()) und Operatoren zur Anwendung auf map- und multimap-Objekte definiert.
Die enthaltenen Funktionen und Klassen map
// Datenstruktur, die mit Hilfe von eindeutigen Schlüsseln
multimap
// Datenstruktur, die mit Hilfe von nicht unbedingt
swap()
// Zum Vertauschen der Elemente zweier map- oder // multimap-Container.
// auf ihre Elemente zugreift (ideales Hashing). // eindeutigen Schlüsseln auf ihre Elemente zugreift.
Überladene Operatoren ==, <, !=, > , >=,
<=
Beispiel #include <map> #include <string> using namespace std; class demo; map container; map container;
346
// map-Container für mit // map-Container für mit
string-Objekte int-Schlüssel demo-Objekte int-Schlüssel
memory
memory Beschreibung Enthält verschiedene Klassen- und Funktionenstemplates zur Speicherverwaltung. Die meisten dieser Elemente sind für die Implementierung eigener Container und Algorithmen gedacht.
Enthaltene Klassentemplates allocator allocator auto_ptr
// Klasse zur Implementierung von Speichermanagern. // Spezialisierung für void // Zeigeklasse zur automatischen Freigabe // dynamischen Speichers.
raw_storage_iterator // Zur Ablage in nicht initialisierten // Speicherbereichen.
Enthaltene Funktionentemplates get_temporary_buffer() return_temporary_buffer() uninitialized_copy() uninitialized_fill() uninitialized_fill_n()
// // // // //
Temporären Speicher anfordern. Auf temporären Speicher zugreifen Nicht-initialisierten Speicher füllen Nicht-initialisierten Speicher füllen Nicht-initialisierten Speicher füllen
Für allocator überladene Operatoren ==, !=
Verweise Siehe Praxisteil, Kategorie Zeiger und dynamische Speicherverwaltung, auto_ptr
new Beschreibung Enthält die überladenen Operatorfunktionen für new und delete, sowie verschiedene Hilfselemente.
Überladene Operatoren new new[] delete delete[]
// // // //
Speicher Speicher Speicher Speicher
reservieren für Feld reservieren freigeben für Feld freigeben
347
Die C++-Standardbibliothek
Enthaltene Hilfselemente bad_alloc
// Für Exception-Objekte, die bei gescheiterten
nothrow_t nothrow set_new_handler()
// Strukturtyp zur Übergabe an new-Operator. // Hilfsstruktur als Argument für new // Fehlerbehandlungsroutine für new registrieren.
// Speicherreservierungen ausgelöst werden.
Verweise Siehe Kategorie Operatoren, Die Operatoren new und delete
numeric Beschreibung Enthält verschiedene Funktionentemplates zur Ausführung numerischer Algorithmen auf Container-Elementen.
Die enthaltenen Funktionen und Klassen accumulate() adjacent_difference() inner_product() partial_sum()
// // // //
Summe berechnen Differenz zuweisen Skalarprodukt berechnen Summe berechnen
Beispiel #include #include #include using namespace std; vector container; int main() { for(int i = 0; i < 7; i++) container.push_back(i); cout << "Summe der Werte: " << accumulate(container.begin(), container.end(), 0) << endl; return 0; }
348
ostream
ostream Beschreibung Enthält das Klassentemplate basic_ostream zur Unterstützung von Schreiboperationen auf Streams, zwei Instanziierungen des Templates für die Datentypen char und wchar_t, die man in Programmen direkt verwenden kann und verschiedene Manipulatoren.
Enthaltene Templates basic_ostream
// Stellt HighLevel-Routinen zum Schreiben in
endl ends flush
// Manipulator: Zeilenende // Manipulator: Stringende // Streampuffer leeren
// einem Stream zur Verfügung.
Enthaltene Klassen ostream wostream
// Spezialisierung für Datentyp char // Spezialisierung für Datentyp wchar_t
Beispiel filebuf datei; datei.open("Daten.dat",ios_base::in istream dat_ein(&datei); ostream dat_aus(&datei);
// Datei zum Lesen und Schreiben | ios_base::out); // öffnen // Datei mit Eingabe-Stream verbinden // Datei mit Ausgabe-Stream verbinden
Verweise Siehe Kategorie Ein- und Ausgabe, Streampuffer teilen
queue Beschreibung Definiert die Klassentemplates queue und priority_queue, die Container zur Verwaltung von Objekten beliebiger Datentypen implementieren. Zusätzlich sind Operatoren zur Anwendung auf queue-Objekte definiert.
Die enthaltenen Funktionen und Klassen queue
priority_queue
// Warteschlange. Datenstruktur, die das Einfügen von // Elementen nur am Ende und das Entfernen von // Elementen nur am Anfang zuläßt. // Datenstruktur, die das Einfügen neuer Elemente // erlaubt und bei Pop-Zugriffen stets das Element // höchster Priortät (beispielsweise das größte // Element) löscht.
349
Die C++-Standardbibliothek
Überladene Operatoren ==, <, !=, > , >=,
<=
Beispiel #include using namespace std; class demo; queue container(5); // queue-Container für int-Objekte queue<demo> container(5); // queue-Container für demo-Objekte
set Beschreibung Definiert die Klassentemplates set und multiset, die Container zur Verwaltung von Objekten beliebiger Datentypen implementieren. Zusätzlich sind Funktionen (swap()) und Operatoren zur Anwendung auf set- und multiset-Objekte definiert.
Die enthaltenen Funktionen und Klassen set
multiset
swap()
// Datenstruktur, in der Elemente automatisch nach ihren // Werten geordnet werden. Ermöglicht schnelles Auffinden // der Elemente. // Datenstruktur, die mit nicht unbedingt eindeutigen // Werten arbeitet. Ermöglicht schnelles Auffinden der // Elemente. // Zum Vertauschen der Elemente zweier set-Container.
Überladene Operatoren ==, <, !=, > , >=,
<=
Beispiel #include <set> using namespace std; class demo; // set-Container für int-Objekte, sortiert mit < -Operator set container(5); // set-Container für demo-Objekte, sortiert nach greater<demo> set<demo, greater<demo> > container(5);
350
sstream
sstream Beschreibung Enthält Klassentemplates, die die Verbindung zwischen Strings und Streams herstellen, sowie Instanziierungen der Templates für die Zeichentypen char und wchar_t. Stringstreams können wie die C-Funktionen sprintf() und sscanf() eingesetzt werden.
Enthaltene Klassentemplates basic_stringstream
// Stellt HighLevel-Routinen zum Einlesen und // Schreiben aus und in basic_string-Objekten zur
basic_istringstream
// Stellt HighLevel-Routinen zum Einlesen aus // basic_string-Objekten zur Verfügung. // Stellt HighLevel-Routinen zum Schreiben in // basic_string-Objekten zur Verfügung. // Von basic_streambuf abgeleitete Pufferklasse // für Objekte der Klasse basic_string.
// Verfügung.
basic_ostringstream basic_stringbuf
Enthaltene Klassen stringstream wstringstream istringstream wistringstream ostringstream wostringstream stringbuf wstringbuf
// // // // // // // //
typedef typedef typedef typedef typedef typedef typedef typedef
basic_stringstream stringstream basic_stringstream<wchar_t> wstringstream basic_istringstream istringstream basic_istringstream<wchar_t> wistringstream basic_ostringstream ostringstream basic_ostringstream<wchar_t> wostringstream basic_stringbuf stringbuf basic_stringbuf<wchar_t> wstringbuf
stack Beschreibung Definiert das Klassentemplate stack, das einen Container zur Verwaltung von Objekten beliebiger Datentypen implementiert. Zusätzlich sind Operatoren zur Anwendung auf stack-Objekte definiert.
Die enthaltenen Funktionen und Klassen stack
// Keller. Datenstruktur, die Einfügen und Löschen jeweils // nur am oberen Ende erlaubt.
Überladene Operatoren ==, !=, <, >, >=, <=
351
Die C++-Standardbibliothek
Beispiel #include <stack> using namespace std; class demo; stack container(5); stack<demo> container(5);
// stack-Container für int-Objekte // stack-Container für demo-Objekte
stdexcept Beschreibung Definiert eine Reihe von Exceptions für Fehlermeldungen in C++-Programmen.
Enthaltene Exception-Klassen domain_error
// Für Exception-Objekte, die bei Auftreten // unzulässiger Werte ausgelöst werden.
invalid_argument // Für Exception-Objekte, die bei Übergabe ungültiger // Argumente ausgelöst werden.
length_error
// Für Exception-Objekte, die ausgelöst werden, wenn
// die zulässige Größe von Datenstrukturen oder // -typen überschritten wird. logic_error // Für Exception-Objekte, die logische Fehler betreffen. out_of_range // Für Exception-Objekte, die ausgelöst werden, wenn ein // Argument nicht im vorgesehenen Bereich liegt. overflow_error // Für Exception-Objekte, die bei arithmetischen // Überläufen ausgelöst werden. range_error // Für Exception-Objekte, die bei Verletzung von // Gültigkeitsbereichen ausgelöst werden. runtime_error // Für Exception-Objekte, die Laufzeitfehler betreffen. underflow_error // Für Exception-Objekte, die bei arithmetischen // Unterläufen ausgelöst werden.
streambuf Beschreibung Enthält das Template für die Streampuffer-Klassen, sowie zwei Instanziierungen des Templates für die Datentypen char und wchar_t. Die Stream-Klassen verwenden Instanzen der Pufferklassen für die eigentlichen Schreib- und Leseoperationen (dabei müssen die Ein- und Ausgaben nicht wirklich gepuffert sein; der interne Puffer der Pufferklassen kann auch auf NULL gesetzt sein).
352
string
Enthaltene Klassentemplates basic_streambuf
// Basisklasse für gepufferte LowLevel-Operationen.
Enthaltene Klassen streambuf wstreambuf
// typedef basic_streambuf streambuf // typedef basic_streambuf<wchar_t> wstreambuf
Verweise Siehe Kategorie Ein- und Ausgabe, Pufferung
string Beschreibung Enthält das Klassentemplate basic_string, das alle wichtigen Methoden für die Programmierung mit Strings bereitstellt, Instanziierungen des Templates für die Zeichentypen char und wchar_t, sowie Operatoren und Funktionen für die Arbeit mit Strings.
Enthaltene Elemente basic_string char_traits
// Klassentemplate zur String-Behandlung. // Klasse, die die Charkteristika eines // Zeichentyps beschreibt
char_traits // Spezialisierung für Datentyp char char_traits<wchar_t> // Spezialisierung für Datentyp wchar_t getline() // Zeile einlesen swap() // Zum Vertauschen der Zeichen zweier Strings string // Spezialisierung von basic_string für Datentyp char wstring // Spezialisierung von basic_string für Datentyp wchar_t
Für basic_string überladene Operatoren +, ==, !=, <, >, <=, >=
Für basic_istream überladene Operatoren >>, <<
Verweise Siehe Kategorie Zeichenketten, Programmieren mit der Klasse string
353
Die C++-Standardbibliothek
typeinfo Beschreibung Enthält die Klasse type_info, mit deren Hilfe RTTI-Informationen in C++-Programmen zur Verfügung gestellt werden, sowie zwei Exception-Klassen für die Fehlerbehandlung.
Enthaltene Klassen type_info
// Verwaltet Informationen (beschreibender String,
bad_cast
// Für Exception-Objekte, die bei gescheiterten
bad_typeid
// Für Exception-Objekte, die ausgelöst werden, um einen
// Prioritätswert) zu Datentypen. // dynamic_cast-Operationen ausgelöst werden. // Null-Zeiger in typeid-Ausdrücken anzuzeigen.
Verweise Siehe Kategorie Vererbung und Polymorphie, RTTI – Laufzeittypidentifizierung
utility Beschreibung Enthält Templates, die die Arbeit mit Wertepaaren erleichtern und innerhalb der C++-Laufzeitbibliothek verwendet werden.
Enthaltene Templates pair
// Template-Klasse für Strukturen mit zwei Elementen // unterschiedlicher Datentypen.
make_pair() // Funktion, die aus ihren zwei Argumenten ein pair erzeugt.
Überladene Operatoren ==, <, !=, >, >=, <=
valarray Beschreibung Definiert die Datenstruktur valarray, die ihre Elemente wie in einem Array verwaltet (der Speicher wird allerdings dynamisch verwaltet). Die Klasse valarray erlaubt eine Vielzahl von Operationen, die jeweils auf sämtlichen Elementen des Arrays ausgeführt werden, sowie die Möglichkeit, mit Hilfe der Hilfsklassen Teilmengen zu bilden.
354
valarray
Enthaltene Klassentemplates und Klassen valarray
// Eindimensionales dynamisches Array mit einer Vielzahl
gslice gslice_array
indirect_array
mask_array
slice slice_array
// von Operationen, die gleichzeitig auf allen Elementen // ausgeführt werden // Hilfsklasse zur Definition einer mehrdimensionalen // Teilmenge aus einem eindimensionalen valarray-Objekt. // Hilfsklasse zur Manipulation einer mehrdimensionalen // Teilmenge aus einem eindimensionalen valarray-Objekt. // Die Teilmenge wird mittels gslice erzeugt. // Hilfsklasse zur Manipulation einer Teilmenge // eines valarray-Objekts. Die Teilmenge wird // mittels eines Index-Arrays erzeugt. // Hilfsklasse zur Manipulation einer Teilmenge eines // valarray-Objekts. Die Teilmenge wird mittels einer // Maske erzeugt. // Hilfsklasse zur Definition einer Teilmenge konstanter // Schrittweite aus einem valarray-Objekt. // Hilfsklasse zur Manipulation einer Teilmenge // konstanter Schrittweite aus einem valarray-Objekt. // Die Teilmenge wird mittels slice erzeugt.
Für valarray überladene Funktionen abs
atan2
log
pow
tan
acos
cos
log10
sin
tanh
asin
cosh
min
sinh
atan
exp
max
sqrt
Für valarray überladene Operatoren *, /,
%,
+, –, ^, &, <<, >>,
&&,
==, !=, <, >, <=, >=
Beispiel #include #include <string> #include using namespace std; int main() { valarray va(20); for(int i = 0; i < 20; i++) { va[i] = i; cout << va[i] << endl; } valarray teil(5); valarray<size_t> size(2);
355
Die C++-Standardbibliothek size[0] = 3; size[1] = 4; valarray<size_t> stride(2); stride[0] = 5; stride[1] = 1; teil = va[gslice(2,size,stride)]; cout << "\nTeilarray\n"; for(int i = 0; i < size[0]*size[1]; i++) { cout << teil[i] << endl; } return 0; }
vector Beschreibung Definiert das Klassentemplate vector, das einen Container zur Verwaltung von Objekten beliebiger Datentypen implementiert. Zusätzlich sind Funktionen (swap()) und Operatoren zur Anwendung auf vector-Objekte definiert.
Die enthaltenen Funktionen und Klassen vector
vector
swap()
// Standard-Datenstruktur, die für Einfügen/Löschen am // Ende optimiert ist und schnellen direkten Zugriff // auf beliebige Positionen zuläßt. // Vordefinierte Spezialisierung des Klassentemplates // vector, die eine optimierte Speicherverwaltung // garantiert. // Zum Vertauschen der Elemente zweier vector-Container.
Überladene Operatoren ==, !=, <, >, >=, <=
Beispiel #include using namespace std; class demo; vector container(5); vector<demo> container(5);
356
// vector-Container für int-Objekte // vector-Container für demo-Objekte
Allgemeine Eigenschaften
Die Container-Klassen Allgemeine Eigenschaften Beschreibung Die Container-Klassen sollen dem Programmierer die Verwaltung größerer Datenmengen in seinen Programmen erleichtern. Sie stellen somit eine komfortable Alternative zu den Arrays und dynamischen Feldern dar und ersparen dem Programmierer die Implementierung eigener höherer Datenstrukturen. ●
● ●
●
●
●
●
Container können für Objekte elementarer oder selbst definierter Klassentypen eingerichtet werden. Voraussetzung ist allerdings, daß Kopierkonstruktor und Zuweisungsoperator des Klassentyps nicht durch private- oder protectedDeklaration unzugänglich gemacht wurden. Weiterhin müssen für bestimmte Aktionen auf den Elementen eines Containers die entsprechenden Operatoren für den Klassentyp der Objekte überladen sein (beispielsweise die Vergleichsoperatoren). In einem Container können nur Objekte eines Datentyps gespeichert werden. Der Container ermöglicht den Zugriff sowie das Einfügen und Löschen von Objekten. Das Löschen des Containers impliziert auch das Löschen der in ihm enthaltenen Objekte (handelt es sich dabei um Zeiger, werden natürlich nur die Zeiger und nicht die Objekte, auf die die Zeiger verweisen, gelöscht). Der Container definiert nur die Methoden zur Verwaltung der Objekte nicht zu deren Bearbeitung. Letzterem Zweck dienen die Algorithmen. Die verschiedenen Containertypen sind als Templates definiert, die für den Datentyp der aufzunehmenden Objekte spezialisiert werden müssen. Die Größe eines Containers (sprich die Anzahl der Objekte, die er aufnehmen kann) ist nicht statisch festgelegt. Zu diesem Zweck implementiert jeder Container eine eigene dynamische Speicherverwaltung. Die einzelnen Containertypen unterscheiden sich in der Art und Weise, wie sie die Daten verwalten (sortiert – unsortiert; geordnet – ungeordnet), welche Möglichkeiten des Zugriffs sie gestatten (Zugriff am Anfang oder Ende der Datenstruktur, indizierter Zugriff, etc.), welche Möglichkeiten des Zugriffs besonders effizient sind.
Grundeigenschaften von Containern Obwohl die Klassentemplates der verschiedenen Container nicht auf ein gemeinsames Basisklassentemplate zurückgehen, gibt es eine Reihe von Eigenschaften, die allen Containertypen gemeinsam sind.
357
Die C++-Standardbibliothek
Allgemeine Container-Eigenschaften Bei dieser und den folgenden Auflistungen bezeichnet X einen beliebigen Containertyp, a und b sind Container, i und j Iteratoren. Eigenschaft
Beschreibung
X()
Erzeugt einen anonymen Container der Größe 0
Xa X a(b)
Erzeugt den Container a der Größe 0
X a = b;
Erzeugt a als Kopie von b
(&a)->~X()
Destruktor wird auf alle Elemente angewendet
a.begin()
Liefert Iterator auf erstes Element in Container
a.end()
Liefert Iterator hinter letztes Element
a.size()
Liefert die aktuelle Anzahl an Elementen
a.empty()
Gibt an, ob der Container leer ist
a.swap(b)
Vertauscht die Elemente der Container a und b
a == b
true, wenn beide Container gleich viele Elemente enthalten, die paarweise gleich sind
a != b
!(a == b)
a
Lexikographischer Vergleich von a und b
a>b
Lexikographischer Vergleich von a und b
a <= b
!(a > b)
a >= b
!(a < b)
Sequentielle und assoziative Container Jeder Containertyp hat seine eigenen Spezialitäten und Qualitäten. Unabhängig von diesen individuellen Besonderheiten lassen sich die Container in zwei Gruppen einteilen, die sich in der Anordnung der in ihnen verwahrten Objekte unterscheiden: sequentielle und assoziative Container. Sequentielle Container ●
●
●
●
In einem sequentiellen Container werden die Elemente in linearer Folge angeordnet (beispielsweise als Array, Liste oder eine Kombination aus beidem). Die Anordnung richtet sich nach dem Zeitpunkt, bzw. dem Ort des Einfügens der einzelnen Elemente. Die lineare Anordnung erlaubt Operationen wie das Einfügen an einer bestimmten Position oder das Löschen eines bestimmten Bereichs von Elementen. Sequentielle Container arbeiten mit Forward-Iteratoren oder höheren.
Sequentielle Container sind vector, list, deque, queue, priority_queue und stack.
358
Allgemeine Eigenschaften
Eigenschaft
Beschreibung
X(n,t) X a(n,t)
Erzeugt Container mit n Kopien des Werts t
X(i,j) X a(i,j)
Erzeugt Container als Kopie der Folge [i,j)
a.insert(p, )
Fügt vor eine Position p ein; entweder einen Wert t, n Kopien eines Werts t oder eine Kopie der Folge [i,j)
a.erase(p)
Löscht das Element an der Position p
a.erase(i,j)
Löscht die Elemente im Bereich [i,j)
a.clear()
Löscht alle Elemente im Container
(mit i, ohne j)
Die folgenden Operationen werden nur von den sequentiellen Containern unterstützt, für die sie in konstanter Zeit implementiert werden können: Operation
Beschreibung
für die Container
a.front()
*a.begin()
vector, list, deque
a.back()
*--a.end()
vector, list, deque
a.push_front(t)
a.insert(a.begin(),t)
list, deque
a.push_back(t)
a.insert(a.end(),t)
vector, list, deque
a.pop_front()
a.erase(a.begin())
list, deque
a.pop_back()
a.erase(--a.end())
vector, list, deque
a[n]
*(a.begin() + n)
vector, deque
a.at(n)
*(a.begin() + n)
vector, deque
Entsprechend der internen Speicherverwaltung lassen sich – wie der Tabelle zu entnehmen ist – nicht alle dieser Operationen für alle sequentiellen Container implementieren. Für einen vector-Container, der einem dynamischen Array sehr nahe kommt, würde beispielsweise das Einfügen am Containeranfang (push_front()) erfordern, daß zuerst alle Elemente im Container, vom Containerende angefangen, um eine Position nach hinten gerückt werden. Da dies nur in linearer und nicht in konstanter Zeit zu schaffen ist, wird diese Operation von vector nicht unterstützt. Assoziative Container ●
●
●
In einem assoziativen Container werden die Elemente sortiert verwahrt (die zugrundeliegende Datenstruktur kann dabei ein binärer Baum oder eine Hashing-Tabelle sein). Die Sortierung richtet sich nach den Schlüsseln (keys) der Elemente und nach einem bestimmten Sortierkriterium, dem Vergleichsobjekt (Compare). Für set und multiset fallen Wert und Schlüssel zusammen, d. h., der Wert ist gleichzeitig auch der Schlüssel, nach dem sortiert wird.
359
Die C++-Standardbibliothek ●
●
Für map und multimap wird jedem Element ein eigener Schlüssel zugewiesen, und Element und Schlüssel werden im Container als Paar verwahrt. Die Sortierung erlaubt das schnelle Auffinden von Elementen nach ihrem Wert oder einem zugehörigen Schlüssel.
●
Assoziative Container arbeiten mit bidirektionalen Iteratoren.
●
Assoziative Container sind set, multiset, map und multimap. Eigenschaft
Beschreibung
X(c) X a(c) X(i,j,c) X a(i,j,c)
Pendants zu den Konstruktoren der sequentiellen Container, die als zusätzliches Argument ein Compare-Objekt als Vergleichskriterium übernehmen. Ohne Compare-Objekt aufgerufen, wird das Standardvergleichsobjekt des Containers (siehe Template-Definition) verwendet.
a.insert(... )
Verschiedene überladene Versionen zum Einfügen von Elementen in den Container
a.erase(... )
Verschiedene überladene Versionen zum Löschen von Elementen aus dem Container
a.clear()
Löscht alle Elemente im Container
a.find(k)
Liefert einen Iterator auf das erste Element mit Schlüssel k
a.count(k)
Liefert die Anzahl der Elemente mit Schlüssel k
a.lower_bound(k)
Liefert einen Iterator auf das erste Element, dessen Schlüssel nicht kleiner als k ist
a.upper_bound(k)
Liefert einen Iterator auf das erste Element, dessen Schlüssel größer als k ist
a.equal_range(k)
Liefert die Iteratoren von lower_bound() und upper_bound() als Paar (siehe make_pair() )
bitset Flags verwalten (aus Header bitset)
Beschreibung Die Klasse bitset dient zur Verwaltung einer bestimmten Zahl von Elementen (Bits), die nur Werte von Null oder Eins annehmen.
Anwendung Instanzen der Klasse bitset können zur übersichtlichen Verwaltung von Diagnoseund Schaltervariablen (Flags) herangezogen werden. Gegenüber dem Einsatz der von C stammenden Bitoperatoren oder der Bitfelder bieten die für bitset zur Verfügung stehenden Methoden und Operatoren mehr Komfort.
360
bitset
Die Anzahl der Bits im Bitset ist unveränderlich und wird durch den Platzhalter N bei der Spezialisierung des Templates festgelegt. Zur Verwaltung einer variablen Zahl von Flags kann man auf den Container vector zurückgreifen. bitset<4> options; // Bits initialisieren
Die einzelnen Bits im Bitset können direkt bei der Erzeugung des Bitsets mit den Werten aus einem Integer oder einem String initialisiert werden. bitset<4> options("0100");
Die Funktionen to_ulong() und to_string() wandeln in umgekehrter Weise ein Bitset in einen Integer-Wert oder ein String-Objekt um. Die Funktionen set(), reset(), flip() können zum Setzen aller Bits oder eines bestimmten Bits verwendet werden. Zur Abfrage einzelner Bits dient die Funktion test(). Zur Manipulation einzelner Bits ist zudem der Index-Operator [ ] implementiert. Die Operatoren &=, |= und ^= verwenden ein zweites Bitset zur Manipulation ihrer Instanz.
Warnung Die Klasse bitset fällt aus dem Rahmen der anderen Container (unterstützt keine Iteratoren, verfügt nicht über die Grundeigenschaften, etc.).
Referenz template<size_t N> class bitset { public: class reference { friend class bitset; reference(); public: ~reference(); reference& operator=(bool x); // für b[i] = x; reference& operator=(const reference&); // für b[i] = b[j]; bool operator~() const; // dreht das Bit um operator bool() const; // für x = b[i]; reference& flip(); // für b[i].flip(); }; // Konstruktoren: bitset(); bitset(unsigned long val); template explicit bitset( const basic_string& str, typename basic_string::size_type pos = 0, typename basic_string::size_type n = basic_string::npos); // Operatoren: bitset& operator&=(const bitset& rhs); bitset& operator|=(const bitset& rhs); bitset& operator^=(const bitset& rhs);
361
Die C++-Standardbibliothek bitset& operator<<=(size_t pos); bitset& operator>>=(size_t pos); bitset& set(); bitset& set(size_t pos, int val = true); bitset& reset(); bitset& reset(size_t pos); bitset operator~() const; bitset& flip(); bitset& flip(size_t pos); reference operator[](size_t pos); // for b[i]; unsigned long to_ulong() const; template basic_string to_string() const; size_t count() const; size_t size() const; bool operator==(const bitset& rhs) const; bool operator!=(const bitset& rhs) const; bool test(size_t pos) const; bool any() const; bool none() const; bitset operator<<(size_t pos) const; bitset operator>>(size_t pos) const; };
Verweise Siehe Header-Datei bitset
deque Sequentieller Container (aus Header deque)
Beschreibung Die Abkürzung deque steht für double ended queue. Dahinter verbirgt sich eine Datenstruktur, die üblicherweise auf einer Verkettung von Array-Blöcken basiert und ihre Elemente in einer sequentiellen Folge anordnet. Die Klasse deque gleicht der Klasse vector und bietet wie diese schnellen direkten Zugriff auf einzelne Elemente ([]-Operator, Methode at()) sowie Einfügen und Löschen von Elementen an beliebigen Positionen (insert(), erase()). Anders als der Zugriff auf bestehende Elemente ist das Einfügen oder Löschen von Elementen allerdings recht zeitaufwendig, da diese Operationen meist mit dem internen Umkopieren der Elemente im Container verbunden ist. Lediglich am Anfang oder Ende der Datenstruktur kann in konstanter Zeit eingefügt oder gelöscht werden.
362
deque
Im Verleich zu vector ist der Zugriff auf beliebige Positionen etwas langsamer, dafür ist die Speicherverwaltung meist günstiger. Im Vergleich zu einem list-Container ist das Einfügen und Löschen an mittleren Positionen zeitaufwendiger, dafür erlaubt deque den bequemen indizierten Zugriff auf bestimmte Positionen (zum Lesen oder Schreiben). Iteratoren für deque-Container sollten mindestens der forward_iterator-Kategorie angehören, verschiedene Funktionen verlangen aber höhere Iteratorenkategorien. Einfüge- und Löschoperationen in der Mitte des Containers führen stets zur Ungültigkeit bestehender Iteratoren und Referenzen auf Container-Elemente.
Anwendung Hinter deque steht ein Klassentemplate, aus dem man zuerst eine Container-Klasse erzeugt. Dabei übergibt man dem ersten Template-Parameter T den Datentyp der zu verwaltenden Objekte. deque container;
Die überladenen Formen der Konstruktoren erlauben die Einrichtung leerer Container oder Container, die mehrere Kopien eines Elements bzw. die Kopie einer Sequenz von Elementen aus einem anderen Container enthalten. Neue Elemente können mittels insert(), push_front(), push_back(), resize() oder assign() eingefügt werden, wobei letztere Funktion den Container zuvor leert. Bestehende Elemente können mittels erase(), pop_front(), pop_back(), resize() oder clear() gelöscht werden. Zugriff auf einzelne Elemente kann mit Hilfe zurückgelieferter Iteratoren geschehen (begin(), end(), rbegin(), rend() sowie insert() ) oder über zurückgegebene Referenzen (Operator [], at(), front(), back() ). Schließlich ist es möglich, über die globalen Funktionen der Header-Datei Container zu manipulieren, zu sortieren, in ihnen zu suchen, etc.
Referenz template > class deque { public: typedef typename Allocator::reference typedef typename Allocator::const_reference typedef implementation defined typedef implementation defined typedef typename Allocator::size_type typedef typename Allocator::difference_type typedef T typedef Allocator typedef typename Allocator::pointer
reference; const_reference; iterator; const_iterator; size_type; difference_type; value_type; allocator_type; pointer;
363
Die C++-Standardbibliothek typedef typename Allocator::const_pointer const_pointer; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; // Konstruktoren und Zuweisung: explicit deque(const Allocator& = Allocator()); explicit deque(size_type n, const T& value = T(), const Allocator& = Allocator()); template deque(InputIterator first, InputIterator last, const Allocator& = Allocator()); deque(const deque& x); ~deque(); deque& operator=(const deque& x); template void assign(InputIterator first, InputIterator last); template void assign(Size n, const T& t = T()); allocator_type get_allocator() const; // Iteratoren: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; // Größe: size_type size() const; size_type max_size() const; void resize(size_type sz, T c = T()); bool empty() const; // Zugriff auf Elemente: reference operator[](size_type n); const_reference operator[](size_type n) const; reference at(size_type n); const_reference at(size_type n) const; reference front(); const_reference front() const; reference back(); const_reference back() const; // Einfügen, Löschen: void push_front(const T& x); void push_back(const T& x); iterator insert(iterator position, const T& x = T()); void insert(iterator position, size_type n, const T& x); template void insert(iterator position,InputIterator first, InputIterator last); void pop_front();
364
list void pop_back(); iterator erase(iterator position); iterator erase(iterator first, iterator last); void swap(deque&); void clear(); };
list Sequentieller Container (aus Header list)
Beschreibung Das Klassentemplate list repräsentiert wie deque und vector einen sequentiellen Container mit direktem Zugriff auf das erste und letzte Element sowie Einfügen und Löschen von Elementen an beliebigen Positionen. Das Einfügen und Löschen von Elementen an beliebigen Positionen innerhalb des Containers erfolgt für list schneller als für deque oder vector, da diese Operationen nicht mit dem Verschieben der nachfolgenden Elemente verbunden ist. Dafür fehlt list der indizierte Zugriff auf Elemente an einer bestimmten Position. Iteratoren für list-Container sollten mindestens der forward_iterator-Kategorie angehören, verschiedene Funktionen verlangen aber höhere Iteratorenkategorien. Einfüge- und Löschoperationen beeinträchtigen nicht die Gültigkeit bestehender Iteratoren und Referenzen auf Container-Elemente.
Anwendung Hinter list steht ein Klassentemplate, aus dem man zuerst eine Container-Klasse erzeugt. Dabei übergibt man dem ersten Template-Parameter T den Datentyp der zu verwaltenden Objekte. list container;
Die überladenen Formen der Konstruktoren erlauben die Einrichtung leerer Container oder Container, die mehrere Kopien eines Elementes bzw. die Kopie einer Sequenz von Elementen aus einem anderen Container enthalten. Neue Elemente können mittels insert(), push_front(), push_back(), resize(), assign() oder merge() eingefügt werden. Bestehende Elemente können mittels erase(), pop_front(), pop_back(), resize(), clear(), unique() oder remove(), remove_if() gelöscht werden. Der Zugriff auf einzelne Elemente kann mit Hilfe zurückgelieferter Iteratoren geschehen (begin(), end(), rbegin(), rend() sowie insert()) oder über zurückgegebene Referenzen (front(), back()).
365
Die C++-Standardbibliothek
Mittels der Funktion sort() können die Elemente sortiert werden, wobei die Art der Sortierung durch den <-Operator oder eine selbstdefinierte Vergleichsfunktion bestimmt werden kann. Mittels reverse() kann die Reihenfolge umgekehrt werden. Schließlich ist es möglich, über die globalen Funktionen der Header-Datei Container zu manipulieren, zu sortieren, in ihnen zu suchen, etc.
Referenz template > class list { public: typedef typename Allocator::reference reference; typedef typename Allocator::const_reference const_reference; typedef implementation defined iterator; typedef implementation defined const_iterator; typedef typename Allocator::size_type size_type; typedef typename Allocator::difference_type difference_type; typedef T value_type; typedef Allocator allocator_type; typedef typename Allocator::pointer pointer; typedef typename Allocator::const_pointer const_pointer; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; // Konstruktoren und Zuweisung: explicit list(const Allocator& = Allocator()); explicit list(size_type n, const T& value = T(), const Allocator& = Allocator()); template list(InputIterator first, InputIterator last, const Allocator& = Allocator()); list(const list& x); ~list(); list& operator=(const list& x); template void assign(InputIterator first, InputIterator last); template void assign(Size n, const T& t = T()); allocator_type get_allocator() const; // Iteratoren: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; // Größe: bool empty() const;
366
map und multimap size_type size() const; size_type max_size() const; void resize(size_type sz, T c = T()); // Zugriff auf Elemente: reference front(); const_reference front() const; reference back(); const_reference back() const; // Einfügen und Löschen: void push_front(const T& x); void pop_front(); void push_back(const T& x); void pop_back(); iterator insert(iterator position, const T& x = T()); void insert(iterator position, size_type n, const T& x); template void insert(iterator position, InputIterator first, InputIterator last); iterator erase(iterator position); iterator erase(iterator position, iterator last); void swap(list&); void clear(); // Operatoren: void splice(iterator position, list& x); void splice(iterator position, list& x, iterator i); void splice(iterator position, list& x, iterator first, iterator last); void remove(const T& value); template void remove_if(Predicate pred); void unique(); template void unique(BinaryPredicate binary_pred); void merge(list& x); template void merge(list& x, Compare comp); void sort(); template void sort(Compare comp); void reverse(); };
map und multimap Assoziativer Container (aus Header map)
Beschreibung Die Container map und multimap benutzen Schlüssel, um einen schnellen Zugriff auf ihre Elemente zu gewähren. Die Idee dabei ist, daß den zu verwaltenden Objekten sogenannte Schlüs-
367
Die C++-Standardbibliothek
sel beigeordnet werden (man kennt dies beispielsweise von Arrays, bei denen die einzelnen Elemente mit Indizes verbunden sind). Ähnlich den Indizes eines Arrays soll aus den Schlüsseln auf die Lokation des Objekts geschlossen werden können. In den Containern werden die Elemente nach ihren Schlüsseln sortiert abgelegt. Die Sortierung erlaubt ein schnelles Auffinden von Elementen nach ihren Schlüsseln. Der Wert eines Schlüssels eines eingefügten Elements darf nicht geändert werden, da dies die Sortierung zerstören würde. Iteratoren auf map und multimap müssen von der Kategorie bidirectional sein. Während die Klasse map nur eindeutige Schlüssel erlaubt, können in multimap mehrere Schlüssel bezüglich der Vergleichsfunktion identisch sein. Dies bedeutet, daß ein Schlüssel auf mehrere Objekte verweist, die dann noch extra durchsucht werden müssen, um ein bestimmtes Objekt herauszufinden. Dies bedeutet meist verlängerte Laufzeiten bei geringerem Speicherbedarf. Die Klasse multimap verfügt über keinen Indexoperator [].
Anwendung Hinter map und multimap stehen Klassentemplates, aus denen man zuerst ContainerKlassen erzeugen muß. Dabei übergibt man dem Template-Parameter Key den Datentyp der Schlüssel und dem Template-Parameter T den Datentyp der zu verwaltenden Objekte. map container;
Zusätzlich kann man als dritten Template-Parameter ein Funktionsobjekt übergeben, das festlegt, wie die Elemente im Container anhand der Schlüssel sortiert werden sollen. map > container;
Die überladenen Formen der Konstruktoren erlauben die Einrichtung leerer Container ebenso wie von Container, die eine Sequenz von Elementen aus einem anderen Container erhalten. Die Objekte zur Allokation und zum Vergleich können selbst definiert werden oder aus der Vorgabe übernommen werden. Neue Elemente können mittels insert() eingefügt werden. Bestehende Elemente können mittels erase() oder clear() gelöscht werden. Zugriff auf einzelne Elemente kann mit Hilfe zurückgelieferter Iteratoren geschehen (begin(), end(), rbegin(), rend(), find(), lower_bound(), upper_bound() sowie insert()). Schließlich ist es möglich, über die globalen Funktionen der Header-Datei Container zu manipulieren, zu sortieren, in ihnen zu suchen, etc.
368
map und multimap
Referenz map
<map>
template , class Allocator = allocator > class map { public: typedef Key key_type; typedef T mapped_type; typedef pair value_type; typedef Compare key_compare; typedef Allocator allocator_type; typedef typename Allocator::reference reference; typedef typename Allocator::const_reference const_reference; typedef implementation defined iterator; typedef implementation defined const_iterator; typedef typename Allocator::size_type size_type; typedef typename Allocator::difference_type difference_type; typedef Allocator::pointer pointer; typedef Allocator::const_pointer const_pointer; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; class value_compare : public binary_function { friend class map; protected: Compare comp; value_compare(Compare c) : comp(c) {} public: bool operator()(const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; // Konstruktoren und Zuweisung: explicit map(const Compare& comp = Compare(), const Allocator& = Allocator()); template map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); map(const map& x); ~map(); map& operator=(const map& x); // Iteratoren: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const;
369
Die C++-Standardbibliothek reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; // Groesse: bool empty() const; size_type size() const; size_type max_size() const; // Zugriff auf Elemente: reference operator[](const key_type& x); // Einfügen und Löschen: pair insert(const value_type& x); iterator insert(iterator position, const value_type& x); template void insert(InputIterator first, InputIterator last); void erase(iterator position); size_type erase(const key_type& x); void erase(iterator first, iterator last); void swap(map&); void clear(); // Sonstiges: key_compare key_comp() const; value_compare value_comp() const; iterator find(const key_type& x); const_iterator find(const key_type& x) const; size_type count(const key_type& x) const; iterator lower_bound(const key_type& x); const_iterator lower_bound(const key_type& x) const; iterator upper_bound(const key_type& x); const_iterator upper_bound(const key_type& x) const; pair equal_range(const key_type& x); pair equal_range(const key_type& x) const; };
multimap template , class Allocator = allocator > class multimap { public: typedef Key key_type; typedef T mapped_type; typedef pair value_type; typedef Compare key_compare; typedef Allocator allocator_type; typedef typename Allocator::reference reference; typedef typename Allocator::const_reference const_reference; typedef implementation defined iterator; typedef implementation defined const_iterator; typedef typename Allocator::size_type size_type;
370
<map>
map und multimap typedef typedef typedef typedef typedef
typename Allocator::difference_type difference_type; typename Allocator::pointer pointer; typename Allocator::const_pointer const_pointer; std::reverse_iterator reverse_iterator; std::reverse_iterator const_reverse_iterator; class value_compare : public binary_function { friend class multimap; protected: Compare comp; value_compare(Compare c) : comp(c) {} public: bool operator()(const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; // Konstruktoren und Zuweisung: explicit multimap(const Compare& comp = Compare(), const Allocator& = Allocator()); template multimap(InputIterator first, InputIterator last, const Compare& comp=Compare(),const Allocator&=Allocator()); multimap(const multimap& x); ~multimap(); multimap& operator=(const multimap& x); allocator_type get_allocator() const; // Iteratoren: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; // Groesse: bool empty() const; size_type size() const; size_type max_size() const; // Einfügen und Löschen: iterator insert(const value_type& x); iterator insert(iterator position, const value_type& x); template