Grundlagen der Programmierung in C++ datadidact Bestellnummer 98067 Verlag für Bildungsmedien im Datenverarbeitungsunterricht An den Tongruben 29 76764 Rheinzabern Information und pädagogische Beratung: Tel: (0 72 72) 77 48 77 Fax: (0 72 72) 77 48 78 Druckzentrum und Verkauf: Tel: (07 21) 9 20 47 32 Fax: (07 21) 9 20 47 16 EMail: [email protected] C++ Programmierung 1 Grundlagen der Programmierung in C++ Inhaltsverzeichnis 1.1 Wie erstelle ich eigene Software? 3 PROGRAMMIERSPRACHEN ....................................................................................................................................................................3 GRUNDLAGEN DES PROGRAMMIERENS.................................................................................................................................................5 DER BEGRIFF “ALGORITHMUS”...........................................................................................................................................................6 BESCHREIBUNGSMÖGLICHKEITEN FÜR ALGORITHMEN .........................................................................................................................7 1.2 Aufbau eines Programmes in C++ 9 VOM QUELLTEXT ZUM LAUFFÄHIGEN PROGRAMM ...............................................................................................................................9 PROGRAMMAUFBAU ..........................................................................................................................................................................10 KOMMENTARE ...................................................................................................................................................................................11 DATENTYPEN .....................................................................................................................................................................................11 VARIABLEN .......................................................................................................................................................................................12 KONSTANTEN.....................................................................................................................................................................................12 ANWEISUNGEN ..................................................................................................................................................................................13 WERTZUWEISUNG, RECHENOPERATIONEN .........................................................................................................................................14 IMPLIZITE UND EXPLIZITE TYPKONVERTIERUNG .................................................................................................................................15 INKREMENT- UND DEKREMENTOPERATOREN .....................................................................................................................................15 1.3 Folge (Sequenz) 17 DARSTELLUNG DER FOLGE IM STRUKTOGRAMM ................................................................................................................................17 BENUTZERFREUNDLICHKEIT ..............................................................................................................................................................17 ANFANGSPROBLEME UND DEREN LÖSUNGEN .....................................................................................................................................20 1.4 Auswahl (Selektion) 22 EINSEITIGE AUSWAHL........................................................................................................................................................................22 LOGISCHE VERGLEICHSOPERATOREN .................................................................................................................................................23 PROBLEME UND DEREN LÖSUNG BEI EINSEITIGER AUSWAHL..............................................................................................................25 ZWEISEITIGE AUSWAHL .....................................................................................................................................................................26 MEHRSEITIGE AUSWAHL....................................................................................................................................................................29 PROBLEME UND DEREN LÖSUNG BEI ZWEI- UND MEHRSEITIGER AUSWAHL ........................................................................................33 1.5 Wiederholung (Repetition) 34 SCHLEIFE MIT ENDABFRAGE ..............................................................................................................................................................35 PROBLEME UND DEREN LÖSUNG ........................................................................................................................................................37 SCHLEIFE MIT ANFANGSABFRAGE ......................................................................................................................................................38 PROBLEME UND DEREN LÖSUNGEN ....................................................................................................................................................41 DIE ZÄHLSCHLEIFE ............................................................................................................................................................................42 ZÄHLSCHLEIFE MIT VARIABLER SCHRITTWEITE ..................................................................................................................................43 ZÄHLSCHLEIFE MIT LOKAL DEKLARIERTER LAUFVARIABLE ................................................................................................................44 ANWEISUNGEN FÜR SCHLEIFENABBRUCH UND -WIEDERHOLUNG .......................................................................................................48 1.6 Modul (Unterprogramm, Funktion) 49 FUNKTIONEN OHNE PARAMETER ........................................................................................................................................................51 FUNKTIONEN MIT WERTEPARAMETERN..............................................................................................................................................53 FUNKTIONEN MIT REFERENZPARAMETERN .........................................................................................................................................54 FUNKTIONEN MIT RÜCKGABETYP.......................................................................................................................................................56 VERSCHACHTELTER FUNKTIONSAUFRUF ............................................................................................................................................59 1.7 Sprachspezifische Besonderheiten bei Funktionen 60 GÜLTIGKEITSBEREICH UND SICHTBARKEIT VON BEZEICHNERN ..........................................................................................................60 FUNKTIONSDEFINITION UND FUNKTIONSDEKLARATION ......................................................................................................................61 INLINE-FUNKTIONEN ..........................................................................................................................................................................62 INITIALISIERUNG VON LOKALEN VARIABLEN, STATISCHE LOKALE VARIABLEN ..................................................................................62 KONSTANTE WERT- UND REFERENZPARAMETER ...............................................................................................................................63 REFERENZ AUF DEN FUNKTIONSNAMEN .............................................................................................................................................64 ÜBERLADENE FUNKTIONSNAMEN ......................................................................................................................................................65 DEFAULT-ARGUMENTE ......................................................................................................................................................................66 SCHABLONEN (TEMPLATES) FÜR FUNKTIONEN...................................................................................................................................68 Programmieren mit C++ datadidact 1-2 2000-06 1 Grundlagen der Programmierung in C++ 1 C++ Programmierung Grundlagen der Programmierung in C++ C++ ist eine moderne Programmiersprache, an deren Beispiel sich grundsätzliche Eigenschaften und Vorgehensweisen anderer Programmiersprachen erlernen lassen. Die Codierung der Algorithmen kann daher sinnvoll in C++ erfolgen. Das Hauptaugenmerk soll aber hauptsächlich auf der Herleitung der Algorithmen liegen, nicht auf der Verwendung einer speziellen Programmiersprache. 1.1 Wie erstelle ich eigene Software? Um spezielle Problemlösungen zu erstellen bzw. Unterstützung durch den Computer zu erfahren, bedient sich der Anwender spezifischer Softwareprogramme, die im Falle der Nutzung von DOS-kompatiblen Rechnern in großer Fülle auf dem Markt angeboten werden. Hier sind oft Programme notwendig, die auf die eventuell erforderlichen Bedürfnisse des Benutzers zugeschnitten werden. Man spricht in diesem Fall von Programmanpassungen über die Programmier- bzw. Softwareschnittstelle. Bei Computersystemen in höheren Größenordnungen wird die Software für den Benutzer auf der Basis des dort bestehenden Betriebssystems speziell geschrieben, so dass den betrieblichen Anforderungen schon von Anbeginn Rechnung getragen wird. Man kann Software also auch unterscheiden in fertiggestellte Programme und in Programmiersprachen, mit denen sie erstellt werden können. Lerninhalte L1 ! " # $ Programmiersprachen Grundlagen des Programmierens Der Begriff “Algorithmus” Lerninhalte Beschreibungsmöglichkeiten für Algorithmen Programmiersprachen Programme, die für einen Computer geschrieben werden, bedienen sich des Befehlsvorrats, den der Mikroprozessor im Computer „verstehen“ kann. Der Prozessor vom Typ 8080 z. B. versteht 243 verschiedene Befehle wie „addiere“, „subtrahiere“, „hole aus Speicherzelle“ usw. Diese Befehle werden dem Prozessor allerdings in binär codierter Form eingegeben. Der Befehl „addiere“ lautet beispielsweise verschlüsselt 10000110. Da diese Verschlüsselung oder „Sprache“ den Prozessor direkt zur Ausführung von Operationen veranlasst, nennt man sie auch Maschinensprache. Allerdings ist diese Form der Sprache zum Programmieren sehr ungünstig; man müsste in diesem Fall nämlich 243 verschiedene 8-stellige Zahlen kennen und die entsprechende Funktion zuordnen. Es lag nahe, anstelle der Zahlenkombination Abkürzungen zu verwenden, die von ihrer Namensgebung einen Rückschluss auf die Funktion zulassen (sogenannte Mnemonics). Aus dem Zahlencode wurde ein Buchstabencode und die Umsetzung übernimmt ein entsprechendes Programm, der sogenannte Assembler. ADD z. B. steht für addiere, SUB steht für subtrahiere. Diese Beschreibungsart nennt man Assemblersprache. Die Assemblersprache ist sehr maschinennah, aber immer noch bedienerunfreundlich. Insbesondere weist jeder Mikroprozessortyp eine andere Assemblersprache auf. Die Folge davon war die Entwicklung von höheren Programmiersprachen. Diese beinhalten nun Wörter aus der (englischen bzw. amerikanischen) Umgangssprache, so dass der Abstraktionsgrad nicht mehr so hoch und die Eingabe von Befehlen wesentlich komfortabler ist. Nach Abschluss einer Programmerstellung muss dieses lediglich mit einem Übersetzungsprogramm (Compiler oder Interpreter) in die jeweilige Maschinensprache übertragen werden, damit die Befehlsmenge für den Prozessor „verständlich“ ist und dieser sie ausführen kann. Damit ist auch gewährleistet, dass die gleichen Programme auf verschiedenen Mikroprozessorsystemen abgearbeitet werden können. Wichtige Hochsprachen sind im folgenden kurz genannt. 1. FORTRAN (Formula Translator, entstanden 1954) 2000-06 Programmieren mit C++ datadidact 1-3 C++ Programmierung 1 Grundlagen der Programmierung in C++ ist die meistverbreitete Hochsprache zur Lösung mathematisch-naturwissenschaftlicher Probleme. Die Sprache entspricht nicht mehr den modernen Erkenntnissen der strukturierten Programmierung, dennoch wird sie wegen der großen Menge geschriebener Software noch lange verwendet werden. 2. COBOL (Common Business Oriented Language entstanden 1959) ist die verbreitetste Programmiersprache für kommerzielle Problemstellungen. Bezüglich Sprachsyntax und Menge der programmierten Software gilt ähnliches wie bei FORTRAN. 3. ALGOL (Algorithmic Language, entstanden etwa 1960) war eine Spezialsprache für mathematisch-wissenschaftliche Probleme. ALGOL wird in der heutigen Programmentwicklung nicht mehr praktisch verwendet. Die knappe, einheitliche und geschlossene Sprachdefinition diente jedoch als Vorbild für die moderneren strukturierten Hochsprachen. 4. BASIC (Beginners All Purpose Symbolic Instruction Code) ist auf Mikrocomputern mit am weitesten verbreitet. Die stärkste Syntax-Verwandtschaft besteht zu FORTRAN. BASIC ist jedoch wenig standardisiert und ursprünglich unstrukturiert. Nach dem Aufkommen von PASCAL erschienen immer mehr BASIC-Dialekte, die auch eine Unterstützung der strukturierten Programmierung aufweisen. 5. PASCAL (benannt nach Blaise Pascal, einem Wissenschaftler des 17. Jahrhunderts, entstanden 1972) Die Sprache ist an ALGOL angelehnt, wurde unter didaktischen Gesichtspunkten entwickelt und zwingt zu strukturierter Programmierung, daher wird sie zu Lehrzwecken sehr gerne eingesetzt. Im Bereich der kommerziellen Programmierung hat sie nicht viel Anklang gefunden. Die Implementation TURBO-PASCAL bzw. BORLAND-PASCAL ist auf IBM-kompatiblen Rechnern zu einem Quasi-Standard geworden. In der grafischen Variante zur Windows-Programmierung ist ein PASCAL-orientiertes Entwicklungssystem unter dem Namen DELPHI bekannt. 6. C (entwickelt aus der Sprache „B“, etwa 1969), C++ (ab 1979) C ist die in der professionellen Softwareentwicklung am häufigsten verwendete Sprache. Obwohl ursprünglich für die Systemprogrammierung entworfen, lassen sich mit ihr auch gut strukturierte Programme für beliebige Anwendungsbereiche entwickeln. Die Spracherweiterung C++ ist mittlerweile der „Quasistandard“ in der professionellen Softwareentwicklung. Ihre Syntax dient als Grundlage für viele neueren Programmiersprachen-Entwicklungen. 7. JAVA (ab 1994) Die Sprache wurde zur herstellerunabhängigen Programmierung von Internet-Anwendungen entwickelt und baut auf C++ auf. 8. ADA (benannt nach Lady Ada Augusta; veröffentlicht 1980) wurde vom US-Verteidigungsministerium als Universalsprache für alle Programmentwicklungen herausgebracht. Sie ist daher streng standardisiert und unterstützt parallel ablaufende Prozesse, hat sich jedoch in der professionellen Softwareentwicklung nicht stark verbreitet. 9. MODULA 2 (entstanden 1976) und OBERON MODULA 2 wurde von PASCAL-Erfinder Niklaus Wirth, Professor an der Eidgenössischen Technischen Hochschule in Zürich als Nachfolgesprachen von PASCAL entwickelt. Sie ist besser strukturiert und unterstützt modulares Programmieren. Als objektorientierte Nachfolgesprache entwickelten Wirth und seine Mitarbeiter die Sprache OBERON. Programmieren mit C++ datadidact 1-4 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung 10.Weitere Hochsprachen sind z.B. LISP und PROLOG für Anwendungen der sogenannten Künstlichen Intelligenz, PL/1 als Großrechner-Sprache, FORTH für Steuerungsanwendungen, LOGO als „kindgerechte Programmiersprache“, ELAN, PEARL, PERL, SIMULA, APL usw. Die bestehenden Sprachen werden ständig fortentwickelt, neue Konzepte fließen in die alten Sprachen mit ein, wie z. B. die objektorientierte Programmierung. Als kurzes Beispiel sollen in einer tabellarischen Übersicht die Befehle zur Bildschirmausgabe in verschiedenen Hochsprachen dienen. FORTRAN: BASIC: PASCAL: MODULA-2: C: C++: L2 FORMAT('Hallo Welt!') PRINT "Hallo, Welt!" WRITELN('Hallo, Welt!'); Write('Hallo, Welt!');WriteLn; printf("Hallo, Welt!\n"); cout << "Hallo, Welt!" << endl; Grundlagen des Programmierens Die Praxis der Grundlagen der Programmierung wird in der Programmiersprache C++ behandelt. In C++ lässt sich strukturiert programmieren, außerdem ist C++ weiter verbreitet als die klassischen Lehrsprachen Pascal oder gar Modula-2. Die Grundlagen, die hier erarbeitet werden lassen sich mühelos auf andere Programmiersprachen anwenden. Auf viele Spezialitäten von C++, die sich in anderen Sprachen nicht wiederfinden, wird im Grundlagenteil nicht eingegangen. Alleine die Kenntnis der Befehle einer speziellen Hochsprache genügt nicht, um Programme erstellen zu können. Die Kodierung einer Vorschrift zur Problemlösung (Programmerstellung) steht vielmehr am Ende eines länger dauernden Prozesses, der im folgenden skizziert ist. Mensch Problemstellung Umgangssprache, mehrdeutig, komplex Struktursprache, eindeutig, detailliert Algorithmus Assemblersprache Hochsprache Assembler Compiler/Interpreter Programmiersprache eindeutiger Formalismus genormt prozessorunabhängig Sprachenübersetzer Maschinensprache, prozessorabhängig Maschinensprache Maschine Mikroprozessor Von der Problemstellung bis zur Bearbeitung durch den Mikroprozessor 2000-06 datadidact 1-5 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Ausgangspunkt der Programmierung ist eine Problemstellung, die meist umgangssprachlich vorgegeben ist. Daher ist sie oft mehrdeutig, unvollständig oder unklar; diese Mehrdeutigkeiten, Unvollständigkeiten und Unklarheiten müssen im Gespräch mit dem Problemsteller geklärt werden oder vom Programmierer in eigenem Ermessen beseitigt werden. Der erste Ansatz zur Lösung besteht darin, einen Algorithmus zur Lösung des Problems zu entwerfen, der möglichst unabhängig von der verwendeten Programmiersprache ist. Dieser Entwurf wird zunächst grob sein und muss verfeinert werden; dies kann in mehreren Stufen geschehen (Prinzip der schrittweisen Verfeinerung). Ein wichtiger Ansatzpunkt für den Grobentwurf des Algorithmus' ist das EVA-Prinzip. Es ist demnach wichtig zu erkennen, wo in der Problemstellung sich die Eingabedaten befinden, wo der Verarbeitungsvorgang stattfindet und welches die interessanten Ausgabedaten sind (Problemanalyse). Ist der Algorithmus fixiert, kann er in eine Programmiersprache übertragen werden. Dabei werden üblicherweise syntaktische Fehler gemacht (Schreibfehler), die sofort vom Compiler erkannt werden. Semantische Fehler (auch logische Fehler genannt) sind Fehler in der Bedeutung; es wird etwas anderes programmiert, als beabsichtigt. Diese Fehler sind schwer zu finden und müssen durch mehrfachen Test des Programms entdeckt und eliminiert werden. Am Ende dieser Entwicklung steht ein Programm, welches das gestellte Problem fehlerfrei löst. L3 Der Begriff “Algorithmus” Losgelöst von der gewählten höheren Programmiersprache muss zunächst eine genaue Beschreibung bzw. Definition des Problems, das gelöst werden soll, auf allgemeine Weise erreicht werden. Dazu bedarf es einer exakten Analyse, die Aufschluss über einzugebende Daten und die Festlegung der auszugebenden Daten beinhaltet. Diese Analyse muss am Ende auch kleinste Schrittfolgen aufzeigen, die die Lösung der Aufgabenstellung beschreiben. Ein Algorithmus ist eine endliche Folge von eindeutig bestimmten, ausführbaren Anweisungen zur Lösung einer Klasse von Problemen. In einem Algorithmus werden also Anweisungen aneinandergereiht, die unser Problem lösen. Dabei sind folgende Bedingungen zu beachten. 1. Die Anweisungsfolge muss ein Ende haben, sie muss terminieren. Ohne Ende liefert die Anweisungsfolge nämlich kein Ergebnis. 2. Die einzelnen Anweisungen müssen eindeutig bestimmt und ausführbar sein. „Dividiere a durch b“ erscheint uns als sinnvolle Anweisung; im Falle b=0 ist diese Anweisung aber gar nicht mehr ausführbar, und ein Computer reagiert auf solche Anweisungen dann mit Fehlermeldungen und Programmabbrüchen. 3. Die Anweisungsfolge löst eine Klasse von Problemen, also nicht nur das aktuelle Problem mit den aktuellen Werten, sondern mindestens mit allgemeinen Werten oder sogar noch eine ähnliche Problemstellung. Wenn z.B. das Ergebnis der Addition 4+5 gefragt ist, soll der Algorithmus a und b als Eingabe nehmen und die Summe a+b ausgeben. Eventuell kann darüber nachgedacht werden, nicht nur die Summe der Zahlen, sondern auch noch Differenz, Produkt und Quotient mit auszugeben. Im täglichen Leben begegnen uns viele Dinge, die wir unbewusst mehr oder weniger komplex ausführen und Programmieren mit C++ datadidact 1-6 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung die sich, würden wir sie exakt beschreiben, als Alltagsalgorithmen darstellen. Beispiel: Bedienung eines Getränkeautomaten Umgangssprachlich: Ich werfe Geld hinein, er liefert mir mein Getränk Eingaben in den Algorithmus: Verarbeitung: Ausgabe aus dem Algorithmus: Eine Verfeinerung des Algorithmus ist möglich, indem man als weitere Eingabe die Geldmenge im Geldbeutel betrachtet und nach dem Prüfen der Kosten testet, ob das Geld reicht. Nur in diesem Fall ist es nämlich sinnvoll, Geld einzuwerfen. L4 Beschreibungsmöglichkeiten für Algorithmen Um einen Algorithmus letztlich in eine höhere Programmiersprache übertragen zu können, ist die rein verbale Form wenig geeignet. Daher wird der Algorithmus zunächst graphisch dargestellt. Die graphische Form kann dann in eine Pseudo-Programmiersprache oder in eine konkrete Programmiersprache übertragen werden. Als graphische Darstellungsform werden der Programmablaufplan (PAP) und das Struktogramm verwendet. Programmablaufplan (PAP). Der PAP leitet nicht zum strukturierten Programmieren an, daher wird er nicht näher besprochen. 2000-06 datadidact 1-7 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Ausschnitt aus einem Programmablaufplan Struktogramm Struktogramme wurden 1973 von Nassi/Shneidermann entwickelt. Der Aufbau eines Struktogramms ist selbst bei komplexen Algorithmen sehr übersichtlich, was ein einfaches Lesen zur Folge hat. Hinzu kommt, dass in etwa zeitgleich die Programmiersprachen PASCAL und C entwickelt wurden, die die gleichen Programmstrukturen verwenden. Ein Algorithmus, der mittels Struktogramm dargestellt wird, lässt sich deshalb recht leicht in die Programmiersprachen PASCAL oder C übertragen. Anhand eines Beispiels wird der Aufbau eines Struktogramms vorgestellt. Problemstellung: Klassensprecherwahl mit zwei Kandidaten. Auszählen der Stimmen bei der Klassensprecherwahl; Es gibt nur zwei Kandidaten; alle Stimmzettel befinden sich in der Wahlurne und weisen einen der beiden Namen auf. Struktogramm Das gezeigte Struktogramm enthält die elementaren Grundstrukturen Folge, Auswahl und Schleife. Diese Programmieren mit C++ 2000-06 datadidact 1-8 1 Grundlagen der Programmierung in C++ C++ Programmierung werden in einem der nächsten Abschnitte genauer erläutert. Pseudo-Programmiersprache Als Pseudo-Programmiersprache wird eine Sprache bezeichnet, die sich an eine bestehende Programmiersprache anlehnt; bei der Formulierung des Algorithmus werden aber nicht so strenge Richtlinien angelegt wie bei der konkreten Programmiersprache. Beispiel: ALGORITHMUS Klassensprecherwahl-Stimmauszaehlung SOLANGE Stimmzettel in der Wahlurne TUE BEGINN Stimmzettel entnehmen Stimmzettel lesen WENN Name = A-Mann DANN Strich bei A-Mann SONST Strich bei B-Frau ENDE Striche zaehlen WENN Anzahl der Striche A-Mann = Anzahl der Striche B-Frau DANN Losentscheid SONST WENN Anzahl Striche A-Mann > Anzahl Striche B-Frau DANN A-Mann gewaehlt SONST B-Frau gewaehlt 1.2 Aufbau eines Programms in C++ Lerninhalte L1 ! Vom Quelltext zum lauffähigen Programm " Programmaufbau # Kommentare $ Datentypen %& Variablen ' Konstanten ( Anweisungen ) Wertzuweisung, Rechenoperationen * Implizite und explizite Typkonvertierung + Inkrement- und Dekrementoperatoren Lerninhalte Vom Quelltext zum lauffähigen Programm Wir arbeiten mit der integrierten Entwicklungsumgebung von Borland C++ unter Windows. Weltweit existieren ca. 10 Entwicklungssysteme für C++. Seit dem Jahr 1998 ist die Sprache nach ISO standardisiert, so dass alle Programmbeispiele mit jedem C++-System ausgeführt werden können. Im Entwicklungssystem werden die dem Menschen verständlichen C++-Quelltexte editiert (verändert), zudem werden die jeweiligen Programme compiliert (übersetzt) und gelinkt (verknüpft, verbunden mit Bibliotheken). Bei größeren Programmierprojekten arbeitet man immer mit mehreren CPP-Dateien – ihre Anzahl kann sehr groß werden, wenn viele Programmierer an dem Projekt beteiligt sind. Der Compiler erzeugt aus den Quelltexten *.CPP und *.H sogenannte Objektdateien (*.OBJ). Der Linker bindet diese Objektdateien mit 2000-06 datadidact 1-9 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Bibliotheksdateien (*.LIB) zu einem ablauffähigen EXE-Programm. Bibliotheksdateien enthalten vorgefertigte Sammlungen von Programmmodulen. ERSTPROG.CPP main() IOSTREAM.H Compiler X.CPP Y.H usw. ... Compiler Compiler ERSTPROG.OBJ X.OBJ *.OBJ *.LIB Linker ERSTPROG.EXE L2 Programmaufbau Das folgende Beispielprogramm in C++ hat die Struktur der Sequenz. Diese Struktur wird weiter unten erläutert. Auch das EVA-Prinzip ist gut zu erkennen. // Programm Erstprog.cpp // Einbinden einer Header-Datei // iostream.h ist notwendig, um Ein-/Ausgabe nutzen zu koennen #include <iostream.h> /* Definitionsteil */ // main() enthaelt das Hauptprogramm zwischen den geschweiften Klammern {} void main() { // Deklaration der Variablen float a,b,c; /* Anweisungsteil */ // Startmeldung cout << "Programm zum Addieren von zwei Zahlen." << endl; // Eingabeaufforderungen cout << "Geben Sie die erste Zahl ein: "; cin >> a; cout << "Geben Sie die zweite Zahl ein: "; cin >> b; // Berechnung der Summe, Ergebnis in c ablegen c=a+b; // Ergebnis ausgeben cout << "Die Summe von " << a << " und " << b << " ist " << c << "." << endl; } An diesem Beispiel soll der Aufbau eines Programms in C++ erläutert werden. Ein Programm in C++ besteht zunächst aus 2 Teilen. Programmieren mit C++ datadidact 1-10 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung I. Einbinden evtl. benötigter „Headerdateien“ (Vorspanndateien). Durch diese werden neue Befehle in C++ zugänglich. Es gibt eine Reihe von Standard-Headerdateien, die bei jeder C++-Implementierung mitgeliefert werden. Eine davon ist die Datei iostream.h, durch die Befehle zur Eingabe und Ausgabe verfügbar werden. II. Im Definitionsteil werden benötigte Variablen und Funktionen definiert. Im hier dargestellten einfachsten Fall wird nur die Funktion main() definiert, die das Hauptprogramm enthält. Funktionen und damit auch das Hauptprogramm main bestehen wiederum aus zwei Teilen. A. Im Variablendeklarationsteil werden die benötigten Variablen und Konstanten eingeführt (deklariert). B. Im Anweisungsteil wird der eigentliche Algorithmus in der Sprache C++ formuliert. Grobanalyse des Beispielprogramms Erstprog.cpp Im Deklarationsteil werden drei Variablen mit den Namen a, b und c deklariert. Diese können bestimmte Datentypen („Datentypen“ siehe einen der nächsten Abschnitte) speichern, hier „float“, also Fließkommazahlen. Im Anweisungsteil werden verschiedene Anweisungen zur Ausgabe (cout <<) und Eingabe (cin >>) verwendet. Auch eine Berechnungsvorschrift (c=a+b) ist zu finden. L3 Kommentare Um den Überblick über ein größeres Programm zu behalten ist es wichtig, Programmteile optisch voneinander trennen zu können und bestimmte Anmerkungen anbringen zu können. Dies geschieht durch Kommentare, die vom restlichen Text auf zwei Arten abgetrennt werden können. Durch // wird ein Kommentar eingeleitet, der bis zum Ende der Zeile geht. Kommentare können aber auch in /* ... */ eingeschlossen werden. Der Text in Kommentarklammern unterliegt keinen Einschränkungen und ist absolut beliebig. L4 Datentypen C++ kennt verschiedene Datentypen, die jeweils einen bestimmten Platzbedarf und verschiedene Wertebereiche haben. Typname Bereich Platzbedarf in Bytes Bool true, false 1 Char -128 bis 127 bzw. Zeichen nach ASCII-Tabelle, 1 z.B. ’A’ oder ’@’ unsigned char 0 bis 255 bzw. Zeichen nach ASCII-Tabelle, z.B. 1 ’A’ oder ’@’ int -32768 bis +32767 2 long -2 147 483 648 bis +2 147 483 647 4 unsigned int 0 bis 65535 2 unsigned long 0 bis 4.294.967.295 4 float 4 ±3,4*10-38 bis ±3,4*10+38 2000-06 datadidact 1-11 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ ±1,7*10-308 bis ±1,7*10+308 ±3,4*10-4932 bis ±1,1*10+4932 double long double 8 10 // Deklaration von Variablen float a,a2; // reelle Zahlen char b; // Zeichen unsigned long l; // lange vorzeichenlose Ganzzahl long double x; // sehr genaue relle Zahl mit großem Zahlenbereich Darüber hinaus können bei Bedarf weitere Typen definiert werden, z.B. Aufzählungstypen oder Verbunde. // Definition neuer Typen enum Wochentage {Mo,Di,Mi,Do,Fr,Sa,So}; // Aufzählungstyp struct Datum { int Tag,Monat,Jahr } ; // Verbund // darauf aufbauende Deklaration von Variablen Wochentage t; // Wert von Mo bis So Datum d; // Variable zusammengesetzt aus 3 Integer-Zahlen L5 Variablen In den Variablen eines Programms werden die zu verarbeitenden Daten, Ergebnisse und Zwischenergebnisse gespeichert. Variablen werden ebenso wie Typen über ihre symbolischen Namen angesprochen. Namen müssen mit einem Buchstaben beginnen und dürfen ansonsten Buchstaben, Ziffern und den Unterstrich „_“ enthalten; Groß- und Kleinschreibung wird beachtet, name, NaMe und NAME meinen also 3 verschiedene Variablen. Leerzeichen können in Variablennamen nicht vorkommen. Variablen können nur Daten eines bestimmten Datentyps speichern. Dieser Typ wird im Deklarationsteil festgelegt; er kann im Lauf des Programms nicht verändert werden. Variablen bekommen einen Wert zugewiesen, indem man schreibt Variablenname=Ausdruck; a=1.2345; // Variable a erhält den Wert 1.2345 a2=a*5+7; // Variable a2 erhält den Wert, der sich aus der Formel berechnet b=’z’; // Variable b speichert das Zeichen ’z’ Konstante ASCII-Zeichen werden dabei in einfachen Apostrophen „’“ eingeschlossen. Bei reellen Zahlen (float oder double) wird ein Dezimal-Punkt, kein Dezimal-Komma verwendet. L6 Konstanten Konstanten können wie Variablen angesprochen werden, verändern aber während des Programmlaufs ihren Wert nicht. Wird dies trotzdem versucht, reagiert der Compiler mit einer Fehlermeldung. KonstantenBezeichner werden üblicherweise in BLOCKSCHRIFT deklariert. Beispiele: Programmieren mit C++ datadidact 1-12 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung const double PI=3.1415; // Kreiszahl PI als double-Konstante const float E=2.7172; // Eulersche Zahl const unsigned int ZWEIUNDVIERZIG=42; // ob das sinnvoll ist? Zeichen und Zeichenketten (auch Literale genannt) werden in Apostrophen eingeschlossen. Einzelne Zeichen werden dabei in einfachen Apostrophen „’“und Zeichenketten werden in doppelten Apostrophen „"“ eingeschlossen; letzteres wird häufig bei Ausgaben verwendet (cout << "Hallo"). Bei reellen float- oder double-Zahlen wird ein Dezimal-Punkt, kein Dezimal-Komma verwendet. Bestimmte ASCII-Zeichen sind nicht direkt ausgebbar; wie lasse ich z. B. den doppelten Apostrophen ausgeben, wo der doch als Begrenzung für die auszugebende Zeichenkette dient? Deshalb werden diese Zeichen über besondere sogenannte Escape-Sequenzen dargestellt, die alle mit dem Backslash „\“ beginnen: Zeichen \t \v \r \n \a \b \\ \’ \" \xzz, \xZZ L7 Bedeutung Tabulator Zeilensprung Zeilenrücklauf neue Zeile Signalton Backspace, löscht das Zeichen links von der Eingabemarke der Backslash selbst ’ einfacher Apostroph " doppelter Apostroph Zeichen aus Sedezimalziffern, z.B. \x40 ist der ’@’ Anweisungen Über Anweisungen wird der Ablauf des Programms gesteuert. Mit der Anweisung cout << ... << ... ; beispielsweise werden Zeichen, Zeichenketten und Zahlen auf dem Bildschirm ausgegeben. Mit cin >> ... >> ... ; werden Daten von der Tastatur in Variablen eingelesen. Anweisungen werden immer mit einem Strichpunkt ; abgeschlossen. Mehrere Anweisungen werden mit den geschweiften Klammern {...} zu einer Anweisungs-Gruppe zusammengefasst. AnweisungA; { Anweisung1; Anweisung2; Anweisung3; } AnweisungC; Die Anweisungsgruppen werden bei den weiteren Strukturen benötigt; aber auch das Hauptprogramm main besteht aus einer Anweisungsgruppe. C++ stellt selbst bereits viele Anweisungen zur Verfügung. Eine besondere Art der Anweisung ist die Wertzuweisung, die meist mit der Ausführung von Rechenoperationen verbunden ist. cout<< und cin>> sind genaugenommen keine Anweisungen, sondern Methodenaufrufe von Objekten, die durch überladene Operatoren << und >> realisiert sind. Dabei soll das Objekt cout den Monitor repräsentieren, auf den die Daten ausgegeben werden, weswegen der Pfeil << in diese Richtung zeigt. Das Objekt cin soll die Tastatur repräsentieren, aus der Daten in Variable fließen, deswegen zeigt der Pfeil >> von cin aus in Richtung Variable. 2000-06 datadidact 1-13 Programmieren mit C++ C++ Programmierung L8 1 Grundlagen der Programmierung in C++ Wertzuweisung, Rechenoperationen Die Wertzuweisung an eine Variable erfolgt über den Operator „=“, z.B. a=5; ergebnis=x*faktor+abstand-12; Das erste Beispiel weist der Variablen a den Wert 5 zu. Im zweiten Beispiel wird eine Formel berechnet, das Ergebnis der Berechnung wird der Variablen mit Namen ergebnis zugewiesen. Der Zuweisungsoperator „=“ muss wie ein Pfeil nach links („←“) verstanden werden. Rechts vom „=“ kann ein beliebig komplexer mathematischer oder logischer Ausdruck stehen, links muss immer ein einzelner Variablenname stehen, in dem das Ergebnis des Ausdrucks gespeichert werden kann (L-Wert, Linkswert oder Lvalue genannt). Daher muss eine Wertzuweisung immer von rechts nach links gelesen werden - sie ist in der Wirkung nicht identisch mit einer mathematischen Gleichung! // Beispiele für Wertzuweisungen: int a; a = 3; // Der Wert 3 wird in der Variablen a gespeichert a = a + 5; // 3 wird aus a entnommen, 5 wird addiert, das Ergebnis 8 wird // wieder in a gespeichert : wäre als Gleichung falsch! a + 5 = a // Nicht kompilierbar - Fehlermeldung „Lvalue required“ C++ bietet die Grundrechenarten Addition, Subtraktion, Multiplikation und Division an. Addition, Subtraktion, Multiplikation und Division werden durch die Operatoren +, -, * und / ausgeführt; der Ergebnistyp richtet sich dabei immer nach dem größten beteiligten Operandentyp. int+int ergibt z.B. int, aber long+float ergibt float. Ergänzend zur Ganzzahl-Division gibt es noch den Operator %, der den Rest der ganzzahligen Division errechnet. C++ beachtet die Punkt-vor-Strich-Regel, so dass Multiplikation und Division vor Addition und Subtraktion ausgeführt werden. Auch runde Klammern (...) können wie gewohnt eingesetzt werden. Formel 17+5 12*1.5 12/4.0 12/4 17/3 17/3.0 17%3 Ergebnis 22 18.0 3.0 3 5 5.66666666667 2 Ergebnistyp (int) (float) (float) (int) (int) (float) (int) Bemerkung int*float 17:3=5 Rest 2 Bei komplizierteren mathematischen Formeln müssen evtl. Klammern gesetzt werden, da z.B. Bruchstriche in C++ nicht gesetzt werden können. Beispiele: x= a+b − e : x=(a+b)/(c+d)-e; ist richtig, falsch wäre x=a+b/c+d-e; c+d y= a 2 − bc − 7 : y=(a*a-b/c)/(d/(e+f)+g)-7 d g e+ f + Programmieren mit C++ datadidact 1-14 2000-06 1 Grundlagen der Programmierung in C++ L9 C++ Programmierung Implizite und explizite Typkonvertierung Was passiert, wenn bei einer Zuweisung die Formel einen anderen Typ hat, als die Variable, der der Wert zugewiesen werden soll? Beispiel: long a,e; float b; e=a/b; Das Ergebnis der Formel a/b ist vom Typ float, weil b vom Typ float ist. Aber e ist eine long-Variable. Daher muss der Ergebnistyp dem Variablentyp von e angepasst werden. Solche Anpassungen können implizit vorgenommen werden, wie in diesem Beispiel, oder sie können auch explizit vorgegeben werden. Bei geeignetem Programmzusammenhang erfolgt die Typumwandlung automatisch (implizit). Zusätzlich kann der Programmierer explizite Typ-Konvertierungen (engl. type casting) vornehmen. Beispiele: int i,j; bool a,b; char z; a i j b = = = = true; int(a); 7; bool(j); j i b z i = = = = = 0; a; j; ’Ç’; z; L10 // keine Konvertierung // explizit: int(true) ist 1, daher ist i gleich 1 // keine Konvertierung // explizit: bool(7) ist true, daher ist b gleich true // Jede Ganzzahl <> 0 ergibt als bool-Wert true // keine Konvertierung // implizit: i wird auf 1 gesetzt // implizit: b wird auf 0 gesetzt // keine Konvertierung; Zeichen mit ASCII-Wert 128 // implizit: Aufpassen: i auf -128 gesetzt, da i ein // signed int ist Inkrement- und Dekrementoperatoren C++ erlaubt spezielle Schreibweisen für Operationen, die sich auf eine einzige Variable beziehen. Soll der Wert einer Variablen erhöht (inkrementiert) oder vermindert (dekrementiert) werden, kann man folgende Schreibwiesen anwenden: int i=3,a=6; i+=15; // i i-=17*a; // i ++i; // i i++; // i --i; // i i--; // i i*=17; // i i/=2*a; // i i%=a; // i 2000-06 wird wird wird wird wird wird wird wird wird um 15 erhöht um 17*a vermindert um 1 erhöht. Inkrement-Operator (Pre-Inkrement) um 1 erhöht. Inkrement-Operator (Post-Inkrement) um 1 vermindert. Dekrement-Operator (Pre-Dekrement) um 1 vermindert. Dekrement-Operator (Post-Dekrement) mit 17 multipliziert durch 2*a dividiert der Divisionsrest von i/a zugewiesen datadidact 1-15 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Der Unterschied, ob man die Operatoren ++ bzw. – vor oder nach der Variablen platziert, ist erst wichtig, wenn die Operatoren innerhalb von Ausdrücken verwendet werden: ++i: Zuerst wird i inkrementiert, dann wird mit diesem i im Ausdruck weitergerechnet (analog für --i) i++: Zuerst wird der Ausdruck mit dem momentanen Wert von i berechnet, danach wird i inkrementiert (analog für i--) Beispiele: int i=10, j=5, k; k = i*j++; // jetzt hat k hat den Wert 50, j hat den Wert 6 int i=10, j=5, k; k = i*++j; // jetzt hat k den Wert 60, j hat den Wert 6. Testen Sie dazu das folgende Programmbeispiel KurzOp.cpp. // PROGRAMM KurzOp.cpp // Inkrement-, Dekrement- und Zuweisungsoperationen testen. #include <iostream.h> void main() { int i, j; cout << "Ganze Zahl eingeben: "; cin >> j; i = 1000 * ++j; cout << "Inkrement-Operator vor der Variablen ergibt: " << i << endl; j--; // Erhöhung zurücknehmen i = 1000 * j++; cout << "Inkrement-Operator nach der Variablen ergibt: " << i << endl; j = 5; i += j; cout << "Addition um 5 ergibt: " << i << endl; i -= j; cout << "Subtraktion von 5 ergibt: " << i << endl; i *= j; cout << "Multiplikation mit 5 ergibt: " << i << endl; } Ganze Zahl eingeben: 3 Inkrement-Operator vor der Variablen ergibt: 4000 Inkrement-Operator nach der Variablen ergibt: 3000 Addition um 5 ergibt: 3005 Subtraktion von 5 ergibt: 3000 Multiplikation mit 5 ergibt: 15000 Programmieren mit C++ datadidact 1-16 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung 1.3 Folge (Sequenz) Lerninhalte L1 ! " # Darstellung der Folge im Struktogramm Lerninhalte Benutzerfreundlichkeit Anfangsprobleme und deren Lösungen Darstellung der Folge im Struktogramm Sollen für die Lösung einer Aufgabe einfach nur mehrere Anweisungen hintereinander ausgeführt werden, können sie im Algorithmus in der gleichen Reihenfolge beschrieben werden. Das Beispielprogramm Erstprog.cpp entspricht folgendem (ausführlichem) Struktogramm. L2 Benutzerfreundlichkeit Kommt es einem nur auf das Wesentliche an, kann man dieses Struktogramm auch wie folgt verkürzen. Die Verkürzungen, die hier möglich waren, beziehen sich auf das Thema „benutzerfreundliche Programmierung“. Wenn im zweiten Struktogramm steht „E: a,b“, ist damit die benutzerfreundliche Eingabe von a und b gemeint, die die entsprechenden Ausgaben von Eingabeaufforderungen beinhalten. Durch diese Verkürzungen lenken im Struktogramm die benutzerfreundlichen Teile nicht von den wesentlichen Teilen der Programmierung ab. 2000-06 datadidact 1-17 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Es ist dennoch wichtig, dass sichergestellt ist, dass vor jeder Eingabe eine Ausgabe erfolgt, die dem Benutzer genau beschreibt, was er denn nun eingeben soll. Grundlegende Aufgabe ErstProg2.cpp Verändern Sie das erste Beispielprogramm, so dass es die Differenz der beiden eingegebenen Zahlen ausgibt. Grundlegende Aufgabe Benzinv.cpp Schreiben Sie ein Programm, das nach Eingabe der gefahrenen Kilometer und der Menge des verbrauchten Kraftstoffes den Durchschnittsverbrauch auf 100km berechnet und ausgibt. Berechnung des Durchschnittsverbrauchs verbrauchter Kraftstoff in l: 32.3 gefahrene Strecke in km: 423.1 Der Durchschnittsverbrauch ist 7.63 l/100km. Programmende. Grundlegende Aufgabe Ganzteil.cpp Das ganzzahlige Ergebnis und der Rest der Division von zwei einzugebenden ganzen Zahlen sollen mit einem Programm berechnet werden. Ganzzahlige Division mit Rest Dividend: 23 Divisor: 4 Das Ergebnis der Division 23:4 ist 5 Rest 3 Programmende Grundlegende Aufgabe ParallelR.cpp Der Gesamtwiderstand zweier parallelgeschalteter Widerstände soll nach der Eingabe der beiden Widerstandswerte berechnet werden. Rg = R1 ⋅ R2 R1 + R2 Widerstands-Parallelschaltung R1 in Ohm: 12000 R2 in Ohm: 10000 Der Widerstand der Gesamtschaltung ist 5454.545 Ohm Programmende Grundlegende Aufgabe DraWi.cpp Nach Eingabe des Durchmessers d und der anderen Größen soll der Drahtwiderstand R eines Kupferdrahtes nach folgender Formel berechnet und ausgegeben werden. l m R = ρ mit ρ = 56 A Ω ⋅ mm 2 ρ (rho) ist der spezifische Widerstand von Kupfer, l ist die Länge des Drahtes, A ist die Querschnittsfläche des Drahtes. Verwenden Sie RHO als Konstante in Ihrem Programm. Programmieren mit C++ datadidact 1-18 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung Drahtwiderstand in Ohm Durchmesser in mm: 1.5 30 Laenge in m: Bei 1.5 mm Durchmesser betraegt der Drahtwiderstand 0.303152 Ohm Grundlegende Aufgabe WarmWi.cpp Nach der numerischen Eingabe der entsprechenden Größen soll der Temperaturunterschied ∆T und der Wert des Warmwiderstandes RW ausgegeben werden. RW = RK (1 + α ⋅ ∆T ) Dabei ist RK der Kaltwiderstand in Ω , RW der Warmwiderstand in Ω , α ist der Temperaturkoeffizient in 1/K, ∆T der Temperaturunterschied in °C. Berechnung des Warmwiderstands Kaltwiderstand in Ohm Temperaturkoeffizient in 1/K Temperaturdifferenz in Grad Der Warmwiderstand betraegt Grundlegende Aufgabe : 100 : 0.0039 C : 60 123.4 Ohm Pythagor.cpp Entwickeln Sie ein Struktogramm und C++ Programm, welches für ein rechtwinkliges Dreieck bei Eingabe beliebiger Katheten a und b die Hypothenuse c und die beiden Winkel α und β berechnet und ausgibt. Hinweise: c = a2 + b2 , α Grad α Bogen b , α Bogen = arctan( ) = o a 2π 360 Sie benötigen die zusätzliche Bibliothek math.h. Die mathematische Funktion sqrt() für die Berechnung der Quadratwurzel, atan() für die Berechnung des Arcustangens und die Konstante M_PI für die Kreiszahl π finden Sie dort unter Anwendung der Online-Hilfe. Verwenden Sie in Ihrem Programm alpha, alpha_bogen, beta und beta_bogen als Variablennamen, wenn benötigt. Berechnung RECHTWINKLIGES D R E I E C K Kathete a in mm: 356 Kathete b in mm: 435 Die Hypothenuse c ist c = 562.104 mm. Winkel alpha = 50.7034 Grad, Winkel beta = 39.2966 Grad. 2000-06 datadidact 1-19 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Dreieck1.cpp Ergänzen Sie den Programmtext und erklären Sie den Namen Dreieck1: //Programm Dreieck1.cpp; ... int zahl1,zahl2,.....; cout<< "Geben Sie zwei Zahlen ein:" << endl; cout "Zahl1: "; cin >> zahl1; cout "Zahl2: "; cin >> zahl2; ..................; ..................; ..................; cout<< "Die Zahlen sind jetzt: " << zahl1 << " " << zahl2; Geben Sie zwei Zahlen ein: Zahl1: 15 Zahl2: 24 Die Zahlen sind jetzt: Zahl1=24 Zahl2=15 L3 Anfangsprobleme und deren Lösungen Bei den ersten Übungen entstehen normalerweise Probleme, von denen hier eine Auswahl mitsamt Lösungen angegeben sind. 1. Vergessene Strichpunkte (Syntaxfehler) Einzelne Anweisungen werden stets durch Strichpunkt getrennt. Wurde dieser hinter einer Anweisung vergessen, wird beim Übersetzen meist ein Fehler bei der folgenden Anweisung angemahnt. Beispiele (die , zeigt die echte Fehlerstelle, die - die vom Compiler angezeigte Fehlerstelle an): „Statement missing ;“ ist eine sehr klare Fehlermeldung in diesem Fall. Der Fehler wird aber leider hinter der Fehlerstelle angezeigt. cout << "Hallo", cin ->> a; „Declaration syntax error“ kann seine Ursache auch in einem fehlenden Strichpunkt nach einer Variablendeklaration haben. int a,b, cout -<< "Hallo"; Falsch geschriebene Worte (Syntaxfehler) „Undefined Symbol ‘XXX’“ bedeutet, dass der Compiler mit dem Wort XXX nichts anfangen kann. Eine mögliche Ursache ist ein Schreibfehler, wie im folgenden Fall mit dem Schlüsselwort Cout als XXX: ,Cout << -"Hallo"; Eine weitere Ursache ist eine falsche Variablendeklaration, wie im folgenden Fall mit Summe als XXX: float ,summe; cout << Summe;- Programmieren mit C++ datadidact 1-20 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung 2. Ausgabe verschwindet sofort nach Programmende Um die Programmausgabe nach dem Ablauf des Programms weiter anzuzeigen, hält man durch das Setzen einer Unterbrechung (Breakpoint) nach dem letzten Programmbefehl das Programm an dieser Stelle an. In der Borland-C++-Entwicklungsumgebung kann man dazu den Cursor auf die letzte geschweifte Klammer im Programm setzen und dann die Taste F4 betätigen (Das bedeutet: Führe das Programm aus bis zur Cursorposition im Quelltext). Durch Mausklick auf das Programmsymbol in der Taskleiste holt man den Programmbildschirm wieder in den Vordergrund. Das Bildschirmfenster bleibt auch offen, wenn man als letzten Befehl das Programm auf eine Eingabe warten lässt. Dazu ist der folgende Befehl getch() geeignet: #include <iostream.h> #include <conio.h> ... void main() { ... getch(); // erfordert #include <conio.h>, Programmende nach Tastendruck } 3. Merkwürdiges Zahlenformat bei der Ausgabe Fließkommazahlen werden manchmal mit einer unerwünscht hohen Genauigkeit oder in wissenschaftlicher Notation (+1E+20 für 1020) angegeben. Auch die genaue Ausgabebreite liegt nicht fest, was bei Tabellen wichtig ist. In C++ kann man mit folgenden Anweisungen die Anzahl der Vor- und Nachkommastellen genau festlegen. cout.setf(ios::fixed); // muss einmal am Anfang des Programmes stehen cout.width(20); // die Gesamtbreite der Ausgabe wird bestimmt cout.precision(3); // Anzahl der Nachkommastellen cout << f1 << f2; // Vorsicht! Die Formatierung mit width() gilt genau einmal, nämlich für f1, f2 wird wieder mit variabler Gesamtbreite ausgegeben. Die Anweisung cout.setf(ios::fixed); legt fest, dass das Programm bei Ausgaben auf den Bildschirm nicht mit Fließkommadarstellung, sondern mit Festkommadarstellung arbeiten will. Deswegen legt dann die Anweisung cout.precision(3); fest, mit wie vielen festen Nachkommastellen gearbeitet werden soll. Diese beiden Einstellungen haben ständige Gültigkeit, man kann damit am Anfang eines Programms eine gewisse Ausgabegenauigkeit festlegen. Soll auch die Anzahl der Stellen vor dem Komma festliegen, muss mit cout.width(V+N+1); die Ausgabebreite der gesamten Zahl gesetzt werden. V ist die Anzahl der gewünschten Vorkommastellen, N die der Nachkommastellen. Leider muss die Ausgabebreite vor der Ausgabe jeder Variablen neu eingestellt werden. 4. Semantische (logische) Fehler Im Gegensatz zu syntaktischen Fehlern können semantische Fehler nicht vom Compiler festgestellt werden, da semantische Fehler durch einen falschen Sinn der Anweisungen entstehen, nicht durch feststellbar falsche Schreibung. summe=a,-b; // Eigentlich soll nicht die Differenz berechnet werden cout << summe; Dieses Programm wird übersetzt und ausgeführt, liefert aber wegen eines falschen Rechenzeichens ein falsches Ergebnis. Es ist syntaktisch korrekt, aber semantisch falsch. 2000-06 datadidact 1-21 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ 1.4 Auswahl (Selektion) In Abhängigkeit von einer Bedingung werden Anweisungen ausgeführt oder weggelassen. Diese Bedingungen sind in der Regel an Operatoren wie > (größer als), < (kleiner als), == (gleich) und != (ungleich) geknüpft. ! Einseitige Auswahl " Logische Vergleichsoperatoren # Probleme und deren Lösung bei einseitiger Auswahl $ Zweiseitige Auswahl %& Mehrseitige Auswahl ' Probleme und deren Lösung bei zwei- und mehrseitiger Auswahl Lerninhalte L1 Lerninhalte Einseitige Auswahl Bei der einseitigen Auswahl wird eine Anweisung oder eine Gruppe von Anweisungen nur dann ausgeführt, wenn eine bestimmte Bedingung erfüllt ist. Die Syntax lautet in C++: if (Bedingung) Anweisung; Beispiel: if (SparkontoGuthaben >= AbbuchungsBetrag) SparkontoGuthaben -= AbbuchungsBetrag; Sollen mehrere Anweisungen in Abhängigkeit der Bedingung ausgeführt werden, muss diese Anweisungsgruppe mit {...} geklammert werden. if (SparkontoGuthaben>=UmbuchungsBetrag) { SparkontoGuthaben -= UmbuchungsBetrag; GirokontoGuthaben += UmbuchungsBetrag; } Die angegebene Bedingung ist entweder wahr oder falsch, sie nimmt also die booleschen Werte true oder Programmieren mit C++ datadidact 1-22 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung false an. Boolesche Werte können mit den Operatoren „&&“ für UND bzw. „||“ für ODER verknüpft werden. A false false true true b false true false true a && b false false false true a false false true true b false true false true a || b false true true true a&&b ist also nur dann wahr, wenn sowohl a als auch b wahr sind. Bei a||b genügt es, wenn mindestens eines der beiden a oder b wahr ist. Vergleichsoperationen werden vorrangig vor boolschen Operationen behandelt. Welchen Datentyp hat im folgenden Beispiel die Variable GuterKunde? if (GirokontoGuthaben >= AbbuchungsBetrag || GirokontoGuthaben+Dispositionskredit >= Abbuchungsbetrag || GuterKunde) GirokontoGuthaben -= AbbuchungsBetrag; L2 Logische Vergleichsoperatoren Die folgenden Operatoren für logische Vergleiche sind definiert: Operator < <= > >= = = != Aufgabe kleiner als kleiner oder gleich größer als größer oder gleich gleich ungleich Beispiel if (i < 1) if (i <= 1) if (i > 1) if (i >= 1) if (i = = 1) if (i != 1) Das Ergebnis eines Vergleichs ist immer ein Ausdruck vom Typ bool, also ein Wahrheitswert true oder false. Insbesondere darf der Operator „= =“ nicht verwechselt werden mit dem Zuweisungsoperator „=“ – die Wirkung ist eine vollkommen andere. if (i=1) cout << “Logischer Fehler!“; // Die Variable i wird auf 1 gesetzt. Dann ergibt sich: // Der Ausdruck hinter if ist immer true, da ungleich 0 Der Compiler warnt hier übrigens mit der Meldung „Possibly incorrect assignment“. 2000-06 datadidact 1-23 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Bestell.cpp Eine Firma liefert bei einem Bestellwert ab 200,-DM porto- und verpackungsfrei. Für Aufträge unter 200.-DM beträgt die Versandpauschale 5,50DM. Ein Programm soll den Rechnungsbetrag in Abhängigkeit vom Bestellwert ausgeben. Berechnung des Rechnungsbetrages Bestellwert in DM: 150 Rechnungsbetrag: 155.5 DM Programmende. Berechnung des Rechnungsbetrages Bestellwert in DM: 250 Rechnungsbetrag: 250 DM Programmende. Grundlegende Aufgabe Versand1.cpp Ein Versandhaus berechnet bei Nettobeträgen unter 150,-DM Verpackungskosten von 5,-DM. Bei Beträgen ab 150,-DM erhält der Kunde 2% Rabatt. Schreiben Sie Struktogramm und Programm! Rechnung Versandhaus Geben Sie den Nettobetrag in DM ein: 89 Rabatt: 0 Der Rechnungsbetrag betraegt: 94 DM. Darin enthalten sind der Rabatt von 0 DM und 5 DM Verpackungskosten. Rechnung Versandhaus Geben Sie den Nettobetrag in DM ein: 345 Rabatt: 0.02 Der Rechnungsbetrag betraegt: 338.1 DM. Darin enthalten sind der Rabatt von 6.9 DM und 0 DM Verpackungskosten. Grundlegende Aufgabe Dreieck.cpp Ein Dreieck lässt sich aus den Seiten a, b und c konstruieren, wenn die Dreiecksungleichungen a+b>c, a+c>b, b+c>a gelten. Schreiben Sie ein Programm, das bei einzugebenden Seiten überprüft, ob sich das Dreieck konstruieren lässt. Konstruierbarkeit eines Dreiecks pruefen Seite a: 12.5 Seite b: 12.5 Seite c: 12.5 Das Dreieck ist konstruierbar. Programmende. Konstruierbarkeit eines Dreiecks pruefen Seite a: 12.5 Seite b: 10 Seite c: 25 Das Dreieck ist nicht konstruierbar. Programmende. Weiterführende Aufgabe LinGl.cpp Die Gleichung ax+b=0 soll für einzulesende Werte a und b vom Computer gelöst werden. Denken Sie daran, dass sowohl a als auch b den Wert 0 annehmen können. Programmieren mit C++ datadidact 1-24 2000-06 1 Grundlagen der Programmierung in C++ L3 C++ Programmierung Probleme und deren Lösung bei einseitiger Auswahl Dieser Abschnitt weist auf einige typische Fehler bei der einfachen Auswahl hin. 1. Strichpunkt nach if (semantischer Fehler) Die Auswirkung dieses Fehlers ist meist, dass Programmteile immer ausgeführt werden, obwohl sie nur unter bestimmten Bedingungen ausgeführt werden sollen. Der „bedingte Teil“ des untenstehenden Programmfragmentes wird immer ausgeführt, weil für den Compiler die bedingte Anweisung die leere Anweisung vor dem Strichpunkt ist. Der Block in {} wird daher ohne Bedingung ausgeführt. if (D<0);, { D=-D; // Vorzeichen umdrehen cout << "Vorzeichen umgedreht." << endl; } 2. Vergessene geschweifte Klammern (semantischer Fehler) Die Auswirkung dieses Fehlers ist meist, dass Programmteile immer ausgeführt werden, obwohl sie nur unter bestimmten Bedingungen ausgeführt werden sollen. Der „bedingte Teil“ des untenstehenden Programmfragmentes soll der eingerückte Teil sein. Da der Compiler Einrückungen aber nicht beachtet, wird nur die erste Anweisung nach dem if() bedingt ausgeführt, die Ausgabeanweisung dagegen immer. if (D<0), D=-D; // Vorzeichen umdrehen cout << "Vorzeichen umgedreht." << endl; , Richtig wäre: if (D<0) { D=-D; // Vorzeichen umdrehen cout << "Vorzeichen umgedreht." << endl; } 3. Zuweisung statt Vergleich (semantischer Fehler) Verwechselt man den Vergleichsoperator == mit dem Zuweisungsoperator =, erhält man vom Compiler eine Warnung „Possibly incorrect assignment“. In der folgenden Zeile ist der Bedingungsausdruck niemals wahr, obwohl nach Ausführen der Anweisung garantiert D=0 ist; die Ausgabe wird nie getätigt. if (D=,0) cout << "D ist 0!" << endl; D=0 ist eine Zuweisung an D, wonach D den Wert 0 erhält. Der Wert des gesamten Ausdrucks ist auch 0, was logisch „falsch“ entspricht. Damit wird die Ausgabeanweisung nie ausgeführt. richtig wäre hier: if (D==0) cout << "D ist 0!" << endl; 2000-06 datadidact 1-25 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung L4 Zweiseitige Auswahl Bei der zweiseitigen Auswahl wird in Abhängigkeit davon, ob eine Bedingung erfüllt ist oder nicht, jeweils eine bestimmte Anweisung oder Anweisungsgruppe ausgeführt. Die Syntax lautet in C++: if (Bedingung) Anweisung1; else Anweisung2; Man kann die zweiseitige Auswahl also als eine Erweiterung der einseitigen Auswahl um eine Alternative betrachten. Beachten Sie: die else-Anweisung bezieht sich immer auf das letzte, nicht durch else abgeschlossene if im gleichen Block. Beispiel: if (SparkontoGuthaben >= AbbuchungsBetrag) SparkontoGuthaben -= AbbuchungsBetrag; else AbbuchungsBetrag=0; if (SparkontoGuthaben >= UmbuchungsBetrag) { SparkontoGuthaben -= UmbuchungsBetrag; GirokontoGuthaben += UmbuchungsBetrag; } else { UmbuchungsBetrag = SparkontoGuthaben; SparkontoGuthaben = 0; GirokontoGuthaben += UmbuchungsBetrag; } Sollen mehrere Anweisungen in Abhängigkeit der Bedingung ausgeführt werden, muss diese Anweisungsgruppe wieder mit geschweiften Klammern { } gruppiert werden. Programmieren mit C++ datadidact 1-26 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung Zweiseite Auswahl mit Bedingungsoperator: Falls es nur darum geht, eine bedingte Zuweisung zu treffen, kann man eine ganz rasche zweiseitige Auswahl mit dem Bedingungsoperator „?“ treffen: Ergebnis = Bedingung ? AusdruckBeiWahr : AusdruckBeiFalsch; Zuerst wird die Bedingung ausgewertet. Ist sie wahr, dann ist das Ergebnis gleich AusdruckBeiWahr, ansonsten AusdruckBeiFalsch. Beispiel: int Max; Max = a>b ? a:b; // if (a>b) Max = a; else Max = b; Im Struktogramm muss man dies ausführlich darstellen. Grundlegende Aufgabe BenzinV2.cpp Schreiben Sie ein Programm, das nach Eingabe der gefahrenen Kilometer und der Menge des verbrauchten Kraftstoffes den Durchschnittsverbrauch auf 100km berechnet und ausgibt. Die Berechnung darf aber nur erfolgen, wenn die gefahrenen Kilometer ungleich null sind, ansonsten soll eine (sinnvolle) Fehlermeldung ausgegeben werden. Berechnung des Durchschnittsverbrauchs verbrauchter Kraftstoff in l: 32.3 gefahrene Strecke in km: 0 Ungueltige Strecke eingegeben. Programmende. Grundlegende Aufgabe Bestell2.cpp Eine Firma liefert bei einem Bestellwert ab 200,-DM porto- und verpackungsfrei. Für Aufträge unter 200.-DM beträgt die Versandpauschale 5,50DM. Ein Programm soll den Rechnungsbetrag in Abhängigkeit vom Bestellwert ausgeben. Berechnung des Rechnungsbetrages Bestellwert in DM: 150 Rechnungsbetrag: 155.5 DM Programmende. Berechnung des Rechnungsbetrages Bestellwert in DM: 250 Rechnungsbetrag: 250 DM Programmende. Grundlegende Aufgabe Versand2.cpp Schreiben Sie das Programm Versand1.cpp so um, dass eine zweiseitige Auswahlstruktur verwendet wird. Grundlegende Aufgabe Bestell3.cpp Die Elektronikfirma Kleinkram erhebt für Bestellungen unter 100,-DM einen Porto- und Verpackungsanteil 2000-06 datadidact 1-27 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung von 5,50DM, von 100,-DM bis 200,-DM einen Betrag von 3,-DM, ab 200,-DM werden keine Spesen berechnet. Ein Programm soll bei gegebener Auftragssumme den Rechnungsbetrag ausgeben. Berechnung des Rechnungsbetrages Auftragssumme in DM: 150 Rechnungsbetrag: 153.00 DM Programmende. Grundlegende Aufgabe Widerstand.cpp Nach Eingabe von zwei Widerstandswerten wird wahlweise der Gesamtwiderstand zweier parallel oder in Reihe geschalteter Widerstände berechnet. R ⋅R Bei Reihenschaltung: Rg = R1 + R2 Bei Parallelschaltung: R g = 1 2 R1 + R2 Widerstands-Schaltung (P)arallel- oder (R)eihenschaltung? P R1 in Ohm: 12000 R2 in Ohm: 10000 Der Widerstand der Parallelschaltung ist 5454.545 Ohm Programmende Grundlegende Aufgabe KinderG.cpp Eine Familie in Kautschukistan erhält nach folgender einkommensabhängiger Tabelle Kindergeld. Ein Programm ist gesucht, das nach Eingabe der Kinderzahl und des Einkommens das zu zahlende Kindergeld berechnet. Einkommen für das 1. Kind für das 2. Kind für das 3. Kind ab dem 4. Kind < 45.000,- Talente 70 Talente 130 Talente 220 Talente 240 Talente Weiterführende Aufgabe ab 45.000,- Talente 70 Talente 70 Talente 140 Talente 140 Talente Geraden.cpp Der Schnittpunkt von 2 Geraden ist gesucht. Gerade 1 wird durch die Punkte A(ax,ay) und B(bx,by), Gerade 2 durch die Punkte C(cx,cy) und D(dx,dy) angegeben. Denken Sie daran, dass die Geraden auch parallel sein können, dies muss besonders beachtet werden. Es gilt: (bx - ax) y = (by - ay) x + (ay bx - ax by) und (dx - cx) y = (dy - cy) x + (cy dx - cx dy) Die Geraden sind parallel, wenn gilt: (by - ay) (dx - cx) - (dy - cy) (bx - ax) = 0 Weiterführende Aufgabe Resistor.cpp Schreiben Sie ein Struktogramm und Programm, in dem wahlweise der Gesamtwiderstand für eine Reihenoder Parallelschaltung von drei Widerständen berechnet werden soll. Weiterführende Aufgabe Skonto.cpp Schreiben Sie das Programm nach folgendem Struktogramm: Programmieren mit C++ datadidact 1-28 2000-06 1 Grundlagen der Programmierung in C++ Weiterführende Aufgabe C++ Programmierung Ungerade.cpp Wie lautet die Auswahlstruktur des Programms Ungerade.cpp?// Programm Ungerade.cpp ... int zahl; cout << "Eine Zahl eingeben: "; cin >> zahl; ... Gerade oder ungerade? Eine Zahl eingeben: 66 66 ist eine GERADE Zahl. Gerade oder ungerade? Eine Zahl eingeben: 7 7 ist eine UNGERADE Zahl. L5 Mehrseitige Auswahl Es gibt zwei Formen der mehrseitigen Auswahl, die sich darin unterscheiden, wie man sie programmieren kann. Die erste Art ist sehr speziell und beruht darauf, dass die einzelnen Fälle sich durch feste Einzelwerte bestimmen lassen. Die mehrseitige Auswahl stützt ihre Wahl auf einen abzählbaren Datentyp, den Selektor, der mehr als zwei Werte annehmen kann, z.B. int oder char. In Abhängigkeit des aktuellen Wertes wird dann eine von vielen Möglichkeiten ausgewählt und ausgeführt. Die Syntax lautet in C++: switch(c) { case m1: case m2: case m3: case m4: case m5: case m6: default: } Anweisung1; break; Anweisung2; break; Anweisung34; break; Anweisung56; break; AnweisungS; Der default-Teil kann auch weggelassen werden. 2000-06 datadidact 1-29 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Beispiel: switch (wochentag) { case Montag: case Dienstag: case Mittwoch: case Freitag: case Donnerstag: default: } cout << "Werktag"; break; cout << "Langer Werktag"; break; cout << "Wochenende"; Auch mehrere Anweisungen in Abhängigkeit der Bedingung sind möglich, dabei kommt es aber auf das Beenden der Anweisungsfolge mit break an, die Anweisungen müssen nicht geklammert werden. switch (wochentag) { case Montag: case Dienstag: case Mittwoch: case Freitag: case Donnerstag: default: cout << "Werktag"; Werktag=true; break; cout << "Langer Werktag" Werktag=true; break; cout << "Wochenende"; Werktag=false; } Mehrseitige Auswahl mit verschachtelten if()-else()-Anweisungen Falls der Selektor kein abzählbarer Datentyp ist oder die Bedingungen für die einzelnen Fälle komplizierter sind, muss auf eine verschachtelte zweiseitige Auswahl zurückgegriffen werden. Dabei taucht die breakAnweisung aber nicht mehr auf, sondern mehrere Anweisungen werden wie bei der zweiseitigen Auswahl mit geschweiften Klammern geklammert. Programmieren mit C++ datadidact 1-30 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung Beispiel: // a ist vom Typ float und damit nicht abzaehlbar if (a<1.7) b=1; else if (a<2.5) { b=2; cout << "Fall 2" << endl; } else if (a<4.0) b=3; else b=4; // komplexe Bedingungen if (a<5 && c>7) b=1; else if (a<5 && c<=7) b=2; else if (a>=5 && c>7) { b=3; cout << "Fall 3" << endl; } else b=4; Merke: Bei verschachtelter zweiseitiger Auswahl gehört das letzte else immer zum direkt vohergehenden if. if (Bedingung1) Anweis1; else if (Bedingung2) Anweis2; else Anweis3; // ist das gleiche wie: if (Bedingung1) Anweis1; else { if (Bedingung2) Anweis2; else Anweis3; } Grundlegende Aufgabe MonName.cpp Schreiben Sie ein Programm, das die Monatsnamen „Januar“ bis „Dezember“ ausgibt, wenn eine der Zahlen von 1 bis 12 eingegeben wird. Ausgabe des Monatsnamens Nummer des Monats (1..12): 4 Der 4. Monat heisst April. Programmende. Ausgabe des Monatsnamens Nummer des Monats (1..12): 15 Es gibt nur 12 Monate! Programmende. 2000-06 datadidact 1-31 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Brief.cpp Briefe werden nach ihrem Gewicht frankiert. Es gelten folgende Portobeträge: Sendungen schwerer als 1kg werden als Päckchen oder Paket verschickt. Schreiben Sie ein Programm, das nach Eingabe des Gewichtes das Porto ausgibt. Portoberechnung Gewicht des Briefes in g: 75.6 Kosten: 2.40 Talente Programmende Portoberechnung Gewicht des Briefes in g: 1500 Das wird ein Paket! Programmende Grundlegende Aufgabe urlaub1.cpp Entwickeln Sie Struktogramm und Programm, welches für die Eingabe von Alter und Betriebsjahren Auskunft über die Urlaubstage bei folgender Regelung gibt: 30 Tage (Alter unter 18), 28 Tage (von 18 bis einschl. 40 Jahre) oder 31 Tage Urlaub (ab 40 Jahren). Bei 10 oder mehr Jahren Betriebszugehörigkeit erhält man einen und bei 25 oder mehr Jahren zwei Zusatztage zum Urlaub hinzu. Grundlegende Aufgabe Funktion.cpp Ergänzen Sie den Quelltext und lassen Sie den Funktionswert sowie den x-Wert und den y-Wert ausgeben für die Funktion, die folgendermaßen definiert ist: 3 x − 2 für x < 1 y= y = 2 x + 1 für x ≥ 1 // Programm funktion.cpp ... float x,y; cout << "x-wert? "; cin >> x; if ... Weiterführende Aufgabe QuadGl.cpp Die quadratische Gleichung ax2 + bx + c = 0 ist zu lösen. Denken Sie daran, dass die Lösungen imaginär sein können. Hinweis: Wurzeln werden in C++ mit der Funktion sqrt(x) berechnet, die in der Headerdatei math.h deklariert wird. Weiterführende Aufgabe Volumen.cpp Es sollen die Volumina und Oberflächen von Zylinder, Würfel, Quader, Kreiskegel oder Kugel berechnet werden. Erstellen Sie ein Programm, das die Eingabe der Körperform und der notwendigen Maße erlaubt und danach die Werte berechnet. Hinweis: Die Kreiszahl π ist in C++ in der Headerdatei math.h als Konstante M_PI vereinbart. Programmieren mit C++ datadidact 1-32 2000-06 1 Grundlagen der Programmierung in C++ Weiterführende Aufgabe C++ Programmierung Rechnen1.cpp Schreiben Sie Struktogramm und C++- Programm, welches nach Eingabe von zwei Zahlen und eines Operators (+, -, *, /) das Ergebnis berechnet. Die Eingabe eines falschen Rechenzeichens soll eine eindeutige Reaktion hervorrufen. Weiterführende Aufgabe Noten1.cpp Bei Facharbeiterprüfungen werden die erbrachten theoretischen Leistungen in Prozent berechnet. Die Prozentangaben werden dann Notenbezeichnungen zugeordnet. Einzugeben ist der erreichte Prozentsatz. Liegt eine Eingabe außerhalb des zulässigen Prozentbereiches, so soll eine Fehlermeldung auf dem Bildschirm erscheinen. Schreiben Sie Struktogramm und Programm. Es gelten die Zuordnungen: 0-29 ungenügend, 30-49 mangelhaft, 50-66 ausreichend, 67-82 befriedigend, 83-92 gut, 93-100 sehr gut. Weiterführende Aufgabe Noten2.cpp Das Programm Noten1.cpp ist so abzuwandeln, dass auch beliebig zu erreichende Punktzahlen umgerechnet werden können. L6 Probleme und deren Lösung bei zwei- und mehrseitiger Auswahl Bei Verwendung der switch-Anweisung werden leicht einige spezielle Fehler gemacht. Auch bei verschachtelten if()-else()-Konstruktionen wird oft Unbeabsichtigtes programmiert. 1. Selektor nicht abzählbar (Syntaxfehler) Tritt folgende Fehlermeldung beim Übersetzen auf: „Switch selection expression must be of integral type“, wurde versucht, in der SwitchAnweisung einen nicht abzählbaren Datentypen zu verwenden, wie z.B. float. Verwenden Sie als Typen in der switch-Anweisung nur char, short, int oder long (unsigned oder signed) oder selbstdefinierte enum-Typen. Abhilfe 1 (Datentyp ändern): Abhilfe 2 (kein switch verwenden): float, i; cin >> i; switch (i){ case 1:... case 2:... ... } int i; cin >> i; switch (i) { case 1:... case 2:... ... } float i; cin >> i; if (i>0.5 && i<=1.5) {...} else if (i>1.5 && i<=2.5) {...} else ... 2000-06 datadidact 1-33 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Vergessene break-Anweisung (semantischer Fehler) Dieser Fehler äußert sich darin, dass plötzlich die Aktionen mehrerer switch-Fälle nacheinander ausgeführt werden. Bei der Eingabe von 4, 5, 6 usw. wird korrekt „was anderes“ ausgegeben. Bei der Eingabe 1 dagegen wird „Eins Zwei Drei was anderes“ ausgegeben, weil hinter der Ausgabeanweisung cout << "Eins "; kein abschließendes break; folgt, ebenso hinter den folgenden Ausgabeanweisungen. Abhilfe: int i; cin >> i; switch (i) { case 1: cout case 2: cout case 3: cout default: cout } cout << endl; int i; cin >> i; switch (i) { case 1: case 2: case 3: default: } cout << endl; cout cout cout cout << << << << << << << << "Eins ";, "Zwei ";, "Drei ";, "was anderes"; "Eins "; break; "Zwei "; break; "Drei "; break; "was anderes"; 2. Falsche if()-else()-Konstruktionen (semantischer Fehler) Diese äußern sich in wirren, unbeabsichtigten Reaktionen des Programms, genauer lassen sich die Folgen dieses Fehlers leider nicht beschreiben. Beachten Sie im folgenden Programmfragment die Einrückungen, die symbolisieren, was der Programmierer programmieren will. if (D>0) if (E>0) cout << "D und E sind beide positiv.";, else if (E<=0) cout << "D und E sind beide nicht positiv."; Was der Programmierer tatsächlich programmiert hat, ist aber folgendes: if (D>0) if (E>0) cout << "D und E sind beide positiv."; else if (E<=0) cout << "D und E sind beide nicht positiv."; Begründung: Eine else-Anweisung bezieht sich immer auf das zuletzt noch nicht durch else abgeschlossene if. Abhilfe: if (D>0) { if (E>0) cout << "D und E sind beide positiv."; } else { // diese geschweifte Klammer ist nicht notwendig, aber konsequent if (E<=0) cout << "D und E sind beide nicht positiv."; } // diese geschweifte Klammer ist nicht notwendig, aber konsequent Dadurch muss die else-Anweisung sich auf die im Block vorhergehende if-Anweisung beziehen. 1.5 Wiederholung (Repetition) In Abhängigkeit einer Bedingung werden Anweisungen wiederholt. Dabei ist zu unterscheiden zwischen Bedingungen, die bereits am Schleifenanfang abgefragt werden und solchen, die erst beim Beenden der Schleife geprüft werden. Programmieren mit C++ datadidact 1-34 2000-06 1 Grundlagen der Programmierung in C++ Lerninhalte L1 C++ Programmierung ! Schleife mit Endabfrage " Probleme und deren Lösung # Schleife mit Anfangsabfrage $ Probleme und deren Lösungen %& Die Zählschleife ' Zählschleife mit variabler Schrittweite ( Zählschleife mit lokal deklarierter Laufvariable ) Anweisungen für Schleifenabbruch Lerninhalte Schleife mit Endabfrage Die Schleife mit Endabfrage wird mindestens einmal durchlaufen, sie wird auch als fußgesteuerte Schleife bezeichnet. Die Schleife wird solange wiederholt, wie die Bedingung wahr ist. Die Syntax in C++ lautet: do Anweisung while Bedingung; Das folgende Beispiel wartet auf das Drücken einer Taste J oder N: cout << "Ja oder Nein? "; do { cin >> c; if (c!='J' && c!='j' && c!='N' && c!='n') cout << (char)7; } while (c!='J' && c!='j' && c!='N' && c!='n'); Grundlegende Aufgabe BenzinV3.cpp Erweitern Sie das Programm BenzinV2.cpp, so dass so oft Kraftstoffverbrauch und Strecke eingegeben werden können, bis der Benutzer abbricht. Berechnung des Durchschnittsverbrauchs verbrauchter Kraftstoff in l: 32.3 gefahrene Strecke in km: 352 Der Durchschnittsverbrauch ist 9.18l/100km. Noch eine Berechnung? J verbrauchter Kraftstoff in l: 45.7 gefahrene Strecke in km: 515 Der Durchschnittsverbrauch ist 8.87l/100km. Noch eine Berechnung? N Programmende. Grundlegende Aufgabe Teiler.cpp Lassen Sie alle Teiler einer einzulesenden ganzen Zahl z ausgeben. Prüfen Sie dabei bei jeder Zahl i zwischen 1 und z, ob der Divisionsrest von z/i gleich 0 ist. Beispiel: 8 hat die Teiler 1, 2, 4, 8. 2000-06 datadidact 1-35 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Mittel1.cpp Schreiben Sie ein Programm, welches solange den (gleitenden) Mittelwert von einzugebenden Daten ermittelt, bis das Programm mit „N“ (Nein) abgebrochen wird. Die Anzahl der Daten ist vor dem Programmaufruf unbekannt. Es ist dazu nötig, die Summe der eingegebenen Zahlen und die Anzahl der eingegebenen Zahlen mitzuführen. Mittelwertberechnung 1. Zahl: 15 Mittelwert: 15. Eine weitere Zahl? J 2. Zahl: 17.5 Mittelwert: 16.25. Eine weitere Zahl? J 3. Zahl: -20.5 Mittelwert: 4. Eine weitere Zahl? N Programmende. Grundlegende Aufgabe Bremsen.cpp Bei einem Bremsvorgang wird ein Auto mit 4,3m/sec2 gebremst. Ein Programm soll die Bremswege bis zu einem einzugebenden maximalen Bremsweg in Tabellenform ausgeben, wobei die Geschwindigkeit von 5km/h beginnend in Schritten von 5km/h zu steigern ist. Gleichzeitig soll die Bremszeit berechnet und ausgegeben werden. Hinweis: s = v2 / (2 a); t = v / a Grundlegende Aufgabe Werttab1.cpp Mit der do-while-Schleife soll eine formatierte Wertetabelle der Funktionsgleichung y = 0,1x³ - 2x² + 5 im Bereich -10 <= x <= 10 erstellt werden. Lassen Sie die Anfangs- und Endwerte von x sowie eine beliebige Schrittweite eingeben. Weiterführende Aufgabe Suppe.cpp Eine Schüssel Suppe kühlt pro Minute um 19% der Differenz aus Suppentemperatur und Umgebungstemperatur ab. Es soll die Temperatur der Suppe nach jeder Minute angegeben werden, wenn man als Anfangstemperatur 90oC und als Umgebungstemperatur 20oC annimmt. Schreiben Sie das Programm so, dass Suppen- und Umgebungstemperatur eingegeben werden können. Weiterführende Aufgabe Zins.cpp Ein Kapital k wird mit Zinseszins verzinst. Lassen Sie ein Tabelle ausgeben, die den Betrag ausgibt, der am Ende jeden Jahres auf dem Konto steht. Der Zinsfuß p, das Kapital k und der zu erreichende Kontostand bmax sollen einzugeben sein. Hinweis: b = k ( 1 + p / 100)n Weiterführende Aufgabe Binaer1.cpp Schreiben sie Struktogramm und Programm mit der do-while Schleife zur Umwandlung einer gnzen positiven Dezimalzahl in eine Dualzahl. Die Dualzahl soll in Tetraden (Vierergruppen) ausgeben werden. Umwandlung einer Dezimalzahl in eine Binärzahl Geben Sie die Dezimalzahl ein: 230 Die gesuchte Dualzahl lautet: 1110 0110 Programmieren mit C++ datadidact 1-36 2000-06 1 Grundlagen der Programmierung in C++ Weiterführende Aufgabe C++ Programmierung Zahlrate.cpp (Mini-Projekt) Eine vom Rechner vorgegebene Zufallszahl soll durch Zahleneingabe erraten werden. Nach Eingabe einer Ober- und Untergrenze erzeugt der Computer Zufallszahlen in diesem Bereich. Nach Eingabe eines Zahlentipps erhält der Spieler eine Angabe, ob seine Zahl zu groß, zu klein oder richtig ist. Bei richtiger Zahl wird die Anzahl der Versuche ausgegeben. Erweiterungen: 1. Neues Spiel auf Anfrage 2. Abfrage auf sinnvolle Eingaben 3. Abbruch bei mehr als der maximalen Versuchsanzahl 4. Mehrere Spieler mit Punktekonto L2 Probleme und deren Lösung 1. Vergessene geschweifte Klammern (syntaktischer Fehler) Wenn der Fehler „do statement must have while“ erscheint, und man hat dem do aber ein passendes while() zugedacht, dann sind meist fehlende (oder falsch gesetzte) geschweifte Klammern um den Schleifenkörper der Fehler. cin >> e; i=0; s=0; do, s+=i; // Summieren cout - << i++ << " " << endl; ,while (i<=e); 2. Endlosschleife (semantischer Fehler) Auswirkung dieses Fehlers ist, dass eine Endlosschleife entsteht, so dass es den Eindruck macht, das Programm „hänge“. Die Ausgabe des untenstehenden Programmfragmentes wird immer einmal ausgeführt, wenn ein negatives e eingegeben wird, ansonsten entsteht eine Endlosschleife. Bei negativem e wird die Schleife einmal mit Ausgabe ausgeführt. Dann ist i>e und die Schleife wird beendet. Bei nichtnegativem e ist i<=e sofort erfüllt, und da sich weder i noch e in der Schleife ändern, entsteht eine Endlosschleife. cin >> e; i=0; s=0; do { s+=i; // Summieren cout << i , << " " << endl; } while (i<=e); Hier wurde also vergessen, i in der Schleife zu erhöhen, so dass es sich dem Endwert e nähert. Eine mögliche Lösung: do { s+=i; // Summieren cout << i++ << " " << endl; } while (i<=e); 2000-06 datadidact 1-37 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung L3 Schleife mit Anfangsabfrage Bei der Schleife mit Anfangsabfrage, auch kopfgesteuerte Schleife genannt, kann es vorkommen, dass der Schleifenkörper nie durchlaufen wird, weil die Schleifenbedingung schon zu Anfang nicht zutrifft. Solange die Bedingung wahr ist, wird die Schleife durchlaufen. Die Syntax in C++ lautet: while (Bedingung) Anweisung; Beispiel: cout << "Ja oder Nein? "; cin >> c; while (c!='j' && c!='n') { cout << (char)7; // (char)7=ASCII-Zeichen 7=BELL; erzeugt Warnton cin >>c; } Programmieren mit C++ datadidact 1-38 2000-06 1 Grundlagen der Programmierung in C++ Grundlegende Aufgabe C++ Programmierung Primzahl.cpp Lassen Sie prüfen, ob eine long-Zahl Primzahl ist. Dividieren Sie die zu prüfende Zahl durch i=2, 3, usw. bis i2>x ist und prüfen Sie, ob die Division x/i einen Rest läßt. Grundlegende Aufgabe SumX.cpp Lassen Sie den Rechner die Summe der folgenden Zahlen bis zu einem Grenzwert g berechnen und geben Sie die Anzahl der benötigten Glieder aus. (SumA.cpp) 1 + 2 + 3 + 4 + ...+ n +... (SumB.cpp) 1 - 2 + 3 - 4 + 5 ... + (2n-1) - 2n ... (SumC.cpp) 1 + ½ + 1/3 + ...+ 1/n +... (SumD.cpp) 1 - ½ + 1/3 ... + 1/(2n-1) - 1/(2n) ... (SumE.cpp) 1 + 4 + 9 + 16 + 25 +...+ n2 +... (SumF.cpp) 1 + 2 + 4 + 7 + 11 + 16 + ... + xn + (xn+n) + ... Berechnung der Summe 1+2+3+4+... bis zu einem Grenzwert Grenzwert: 16 Nach 6 Gliedern ist 16 erreicht. Die Summe ist 21. Berechnung der Summe 1+2+3+4+... bis zu einem Grenzwert Grenzwert: 28 Nach 7 Gliedern ist 28 erreicht. Die Summe ist 28. Grundlegende Aufgabe arithm1.cpp Es sollen durch ein C++Programm alle Glieder einer arithmetischen Reihe erster Ordnung zwischen einem Anfangsglied und einem Endglied ausgegeben werden. Die Differenz zwischen den Gliedern der Reihe soll als variable Größe eingelesen werden. Arithmetische Reihe. Anfangsglied : 1 Endglied : 101 Differenz : 2 Reihe: 1, 3, 5, 7, ..., 101 Grundlegende Aufgabe Werttab2.cpp Im Programm Werttab1.cpp wird mit einer fußgesteuerten Schleife eine formatierte Wertetabelle der Funktionsgleichung y = 0,1x³ - 2x² + 5 im Bereich -10 <= x <= 10 erstellt. Ändern Sie Werttab1.cpp so ab, dass eine kopfgesteuerte Scheife benutzt wird. Lassen Sie die Anfangs- und Endwerte von x sowie eine beliebige Schrittweite eingeben. 2000-06 datadidact 1-39 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Euklid1.cpp (Euklid1.cpp) Euklid kam auf die Idee, den größten gemeinsamen Teiler (ggT) z. B. von 322 und 63 so zu berechnen: 322=5*63+7, dann 63=9*7+0 und dann zu beenden, da Rest =0. Ergänzen Sie das Programm: // Programm euklid1.cpp ggT zweier Zahlen ... int a,b,rest; cout << "Zwei Zahlen eingeben: "; cin >> a >> b; //mit Leerstelle eingeben while ... rest = a % b; ... Weiterführende Aufgabe ProdAdd.cpp (ProdAdd.cpp) Das Produkt zweier ganzer Zahlen ist mit Hilfe der Addition zu berechnen. Beachten Sie, dass die Zahlen auch 0 oder negativ sein können. Berechnung des Produktes ueber Addition Multiplikator: 3 Multiplikand : 5 Das Produkt ist 15. Berechnung des Produktes ueber Addition Multiplikator 1: 0 Multiplikator 2: 5 Das Produkt ist 0. Weiterführende Aufgabe QuotSub.cpp (QuotSub.cpp) Der ganzzahlige Quotient a/b zweier ganzer Zahlen a und b und der entstehende Rest sollen über die Subtraktion berechnet werden. Überlegen Sie, welche Fehlerquellen es hier geben kann und berücksichtigen Sie diese im Programm. Beispiel: 25 / 8 = 3 Rest 1 Programmieren mit C++ datadidact 1-40 2000-06 1 Grundlagen der Programmierung in C++ L4 C++ Programmierung Probleme und deren Lösungen Die hier geschilderten Probleme ähneln den bei der einfachen Auswahl aufgezeigten. 1. Strichpunkt nach while (semantischer Fehler) Auswirkungen dieses Fehlers sind meist, dass • der Schleifenkörper nur einmal ausgeführt wird, obwohl er öfter ausgeführt werden sollte. • eine Endlosschleife entsteht, so dass es den Eindruck macht, das Programm „hänge“. Der „Schleifenkörper“ des untenstehenden Programmfragmentes wird immer einmal ausgeführt, wenn ein negatives e eingegeben wird, ansonsten entsteht eine Endlosschleife. Für den Compiler ist der Schleifenkörper die leere Anweisung vor dem Strichpunkt. Der Block in {} wird daher nicht als Schleife, sondern als Sequenz nach der Schleife ausgeführt. Bei negativem e wird dann die Schleife nie ausgeführt, weil sofort i>e ist. Bei nichtnegativem e ist i<=e sofort erfüllt, und da sich weder i noch e in der leeren Schleife ändern, entsteht eine Endlosschleife. cin >> e; i=0; s=0; while (i<=e);, { s+=i; // Summieren cout << i++ << " " << endl; } 2. Vergessene geschweifte Klammern (semantischer Fehler) Auswirkungen dieses Fehlers sind meist, dass • der Schleifenkörper nur einmal ausgeführt wird, obwohl er öfter ausgeführt werden sollte. • eine Endlosschleife entsteht, so dass es den Eindruck macht, das Programm „hänge“. Die Ausgabe des untenstehenden Programmfragmentes wird immer einmal ausgeführt, wenn ein negatives e eingegeben wird, ansonsten entsteht eine Endlosschleife. Für den Compiler ist der Schleifenkörper die Anweisung s+=i. Die Ausgabe wird daher nicht in der Schleife, sondern als Sequenz hinter der Schleife ausgeführt. Bei negativem e wird dann die Schleife nie ausgeführt und stattdessen sofort die Ausgabe, weil sofort i>e ist. Bei nichtnegativem e ist i<=e sofort erfüllt, und da sich weder i noch e in der Schleife ändern, entsteht eine Endlosschleife. cin >> e; i=0; s=0; while (i<=e);, s+=i; // Summieren cout << i++ << " " << endl; , 2000-06 datadidact 1-41 Programmieren mit C++ C++ Programmierung L5 1 Grundlagen der Programmierung in C++ Die Zählschleife Die Zählschleife ist eine Struktur, bei der von Anfang an feststeht, wie viele Wiederholungen ausgeführt werden. Dabei wird noch ein Zähler (genannt Laufvariable) mitgeführt. Im Beispiel ändert sich die Laufvariable von Anfangswert bis Endwert. Die Syntax in C++ lautet: for (Laufvariable=Anfangswert; Laufvariable<=Endwert; Laufvariable++) Anweisung; Im einfachsten Fall wird die Laufvariable bei jedem Schleifendurchgang um 1 erhöht (inkrementiert). Man spricht dann von einer Zählschleife mit der Schrittweite 1 Beispiele: // gibt 20 Sterne in einer Zeile aus for (i=1;i<=20;++i) cout << '*'; cout << endl; // addiert 5 einzugebende Werte auf cout << "5 Werte eingeben." << endl; summe=0; for (z=1; z<=5; z++) { cout << z << "-ter Wert: "; cin >> x; summe+=x; } Programmieren mit C++ datadidact 1-42 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung Jede Zählschleife kann durch eine Schleife mit Anfangsabfrage ersetzt werden: for (Laufvariable=Anfangswert; Laufvariable<=Endwert; Laufvariable++) Anweisung; // ist das gleiche wie: Laufvariable=Anfangswert; while (Laufvariable<=Endwert) { Anweisung; Laufvariable++; } L6 Zählschleife mit variabler Schrittweite Ersetzt man die Inkrementierung der Laufvariablen Laufvariable++ in den obigen Ausdrücken durch einen anderen Inkrementierungsausdruck (z.B. Laufvariable+=5), dann ergibt sich eine Zählschleife mit variabler Schrittweite. Laufvariable=Anfangswert; while (Laufvariable<=Endwert) { Anweisung; Laufvariable+=Weite; } // ist das gleiche wie: for (Laufvariable=Anfangswert; Laufvariable<=Endwert; Laufvariable+=Weite) Anweisung; Die folgende Anweisung bildet z.B. eine Schleife mit der Schrittweite 3 und setzt die Laufvariable i nacheinander auf die Werte 1, 4, 7, 10: for (i=1; i<=10; i+=3) cout << "Wert der Laufvariable: " << i << endl; 2000-06 datadidact 1-43 Programmieren mit C++ C++ Programmierung L7 1 Grundlagen der Programmierung in C++ Zählschleife mit lokal deklarierter Laufvariable Meist ist es empfehlenswert, die Laufvariable von Zählschleife direkt im Schleifenkopf zu deklarieren. Dann ist die Laufvariable nur innerhalb des Schleifenrumpfs gültig und es gibt keine Wechselwirkungen mit anderen Variablen gleichen Namens. Syntax: for (typ Laufvar=Anfangsw; Laufvar<=Endwert; Laufvar+=Weite) Anweisung; Für das folgende Codefragment int i = 0; // übergeordnet deklarierte Variable i for (int i; i<=x; i++) // lokal deklarierte Variable i { // Anweisungen des Schleifenrumpfs cout << i; // ändert sich bei jedem Schleifendurchlauf bis zu x+1 } cout << i; // Das übergeordnete i behält seinen Wert ist die übergeordnet deklarierte Variable i im gesamten Programm gültig, das den Schleifenrumpf umgibt. Im Schleifenrumpf selbst gilt die lokal deklarierte Laufvariable i gleichen Namens – die übergeordnete Variable i wird durch die Anweisungen im Schleifenrumpf nicht verändert. Beispiel 1: // Programm laufvar1.cpp // Schleife mit global deklarierter Laufvariable #include <iostream.h> main() { int i; i = 5; cout << "Anfangswert von i : " << i << endl; for (i=1; i<=10; i++) cout << i << " "; cout << "\nWeiterarbeiten mit i : " << i; // schlecht, da verändert auf 11 } Beispiel 2: /Programm laufvar2.cpp // Schleife mit lokal deklarierter Laufvariable #include <iostream.h> main() { int i; i = 5; cout << "Anfangswert von i : " << i << endl;; for (int i=1; i<=10; i++) cout << i << " "; // hier i neu lokal deklariert cout << "\nWeiterarbeiten mit i : " << i; // i wird mit 5 weiterverarbeitet } Programmieren mit C++ datadidact 1-44 2000-06 1 Grundlagen der Programmierung in C++ Grundlegende Aufgabe C++ Programmierung Ueb0x.cpp Erstellen Sie mittels Zählschleifen die folgenden Programme: a) (Ueb01.cpp) Die Variable x soll von 1 bis 10 laufen und immer um 1 erhöht werden. Dabei soll der aktuelle Zählerstand ausgegeben werden. b) (Ueb02.cpp) Die Variable x soll von 30 bis 10 laufen und immer um 2 erniedrigt werden. Dabei soll der aktuelle Zählerstand ausgegeben werden. c) (Ueb03.cpp) Die Variable x soll von einem Anfangswert bis zu einem Endwert laufen und immer um 0,5 erhöht werden. Dabei soll der aktuelle Zählerstand ausgegeben werden. d) (Ueb04.cpp) Erstellen Sie eine äußere Zählschleife mit der Variablen k, die von 1 bis 10 zählt und eine innere Schleife i, die von 1 bis 3 zählt. Ueb4.cpp 1 123 2 123 ... 10 123 Grundlegende Aufgabe ProdAdd2.cpp Das Produkt zweier ganzer Zahlen ist mit Hilfe der Addition zu berechnen. Beachten Sie, dass die Zahlen auch 0 oder negativ sein können. Grundlegende Aufgabe Einmal1.cpp Ergänzen Sie das folgende Programm, welches das kleine Einmaleins in tabellarischer Form erzeugt: ... int x,y; for (x=1; x<10; x++) // äußere Schleife { for (y ... ) // innere Schleife cout << x << "*" << y << "=" << x*y << endl; cout << endl; } ... Grundlegende Aufgabe Ungerade.cpp Lassen Sie den Computer die ungerade Zahlen zwischen zwei einzugebenden Werten ausdrucken. Grundlegende Aufgabe Rechnen.cpp Es sollen die Quadratzahlen, die Kubikzahlen und die Kehrwerte der Zahlen zwischen zwei einzugebenden Grenzen ausgegeben werden. 2000-06 datadidact 1-45 Programmieren mit C++ C++ Programmierung Grundlegende Aufgabe 1 Grundlagen der Programmierung in C++ SumNx.cpp Schreiben Sie Programme, die nach Eingabe von n die folgenden Summen berechnen: a) b) c) d) (SumNa.cpp) 12 + 22 + 32 + 42 + ... + n2 (SumNb.cpp) 12 + 32 + 52 + ... + (2 n - 1)2 (SumNc.cpp) 22 + 42 + 62 + ... + (2 n)2 (SumNd.cpp) 1 + 1 / 2 + 1 / 3 + ... + 1 / n Grundlegende Aufgabe Primzahl2.cpp Verändern Sie Primzahl.cpp, so dass es alle Primzahlen ab 2 bis zu einer einzugebenden Obergrenze berechnet. Weiterführende Aufgabe N3und5.cpp Der Rechner soll alle natürlichen Zahlen zwischen einzugebenden Grenzen ausgeben, die sowohl durch 3 als auch durch 5 teilbar sind. Weiterführende Aufgabe N3oder5.cpp Der Rechner soll alle natürlichen Zahlen zwischen einzugebenden Grenzen ausgeben, die durch 3 oder durch 5 teilbar sind. Weiterführende Aufgabe N4und7.cpp Der Rechner soll alle natürlichen Zahlen zwischen einzugebenden Grenzen ausgeben, die sowohl durch 4 als auch durch 7 teilbar sind. Weiterführende Aufgabe N4oder7.cpp Der Rechner soll alle natürlichen Zahlen zwischen einzugebenden Grenzen ausgeben, die durch 4 oder durch 7 teilbar sind. Weiterführende Aufgabe Kapital.cpp Ein Kapital k wird n Jahre lang mit Zinseszins verzinst. Lassen Sie ein Tabelle ausgeben, die den Betrag ausgibt, der am Ende jeden Jahres auf dem Konto steht. Der Zinsfuß p, das Kapital k und die Anzahl der Jahre n sollen einzugeben sein. (b = k ( 1 + p / 100)n) Programmieren mit C++ datadidact 1-46 2000-06 1 Grundlagen der Programmierung in C++ Weiterführende Aufgabe C++ Programmierung Reis.cpp Der indische König Schehram verlangte, dass Sessa, der Erfinder des Schachspiels, sich eine Belohnung wählen solle. Dieser erbat sich die Summe Weizenkörner, die sich ergibt, wenn für das 1. Feld des Schachbrettes 1 Korn, für das 2. Feld 2 Körner für das 3. Feld 4 Körner usw. gerechnet werden. Schreiben Sie ein Programm, das für alle 64 Felder die Gesamtsumme der Körner berechnet. Geben Sie auch das Gewicht der Körner an, wenn 200 Körner 1g wiegen. Wie viele Eisenbahnwaggons bräuchte man zum Transport des Reises und wie lange wäre der Zug, wenn ein Eisenbahnwaggon 30t fasst und 15m lang ist? Weiterführende Aufgabe FiFolge1.cpp Leiten Sie aus folgendem Ausführungsbeispiel zum Programm FiFolge1.cpp das Bildungsgesetz der FIBONACCI-Folge ab und entwickeln Sie das Programm mit Zählschleife. Verwenden Sie die Variablen f1 und f2 als Startwerte, f3 für das jeweils nächste Folgeglied. Wieviele Folgeglieder der Fibonacci-Folge sollen berechnet werden? 14 Geben Sie zwei Startwerte ein: 2 3 5 8 13 21 34 55 89 144 233 377 610 2584 Weiterführende Aufgabe 987 1597 DesignX.cpp Erstellen Sie die folgenden Programme mit geschachtelten for-Schleifen und den Zählervariablen z und s: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Ende design1.cpp Ende design2.cpp 2000-06 = = = = = = = = = = = = = = = = = = = = = = = = = Ende design3.cpp datadidact 1-47 Programmieren mit C++ C++ Programmierung L8 1 Grundlagen der Programmierung in C++ Anweisungen für Schleifenabbruch und -wiederholung Zwei zusätzliche Anweisungen können in einigen Fällen die Übersicht bei der Programmierung von Schleifen erhöhen, aber auch stark beeinträchtigen. Die Anweisungen sollten daher sparsam und nach reiflicher Überlegung eingesetzt werden (möglichst nur einmal pro Schleife). Bei Vertretern der „reinen Lehre“ der strukturierten Programmierung gelten derartige Anweisungen als „unstrukturiert“. • break; Die Schleife wird sofort verlassen (beendet). • continue; Die Schleifenbedingung wird sofort erneut abgefragt. Die Anweisungen break und continue können bei allen Schleifenformen eingesetzt werden. Für break existiert eine eigene Darstellung im Struktogramm, die rechts dargestellt ist. // Verwendung von break: do { cout << "Ja oder Nein? "; cin >> c; if (c==’J’ || c==’j’ || c==’N’ || c==’n’) { break; } cout << "Ungültige Eingabe!" << endl; } while (true); // Verwendung von continue: bool ok; do { cout << "Ja oder Nein? "; cin >> c; ok = (c=='J' || c=='j' || c=='N' || c=='n'); if (!ok) continue; // Dann nochmal Ja oder Nein abfragen // Hier würde nun ein längerer Verarbeitungsteil folgen, // der nur abgearbeitet wird, wenn Ja oder Nein eingegeben wird. } while (false); Für die Verwendung der continue-Anweisung gibt es keine gesonderte Struktogrammdarstellung. Man kann jedoch die folgende Ersetzung verwenden: Programmieren mit C++ 2000-06 datadidact 1-48 1 Grundlagen der Programmierung in C++ // Schleife mit continue while (Bedingung1) { Anweisung1; if (Bedingung2) continue; Anweisung2; Anweisung3; // usw. } C++ Programmierung // entspricht Schleife mit Auswahl while (Bedingung1) { Anweisung1; if (!Bedingung2) { Anweisung2; Anweisung3; // usw. } } Entwerfen Sie die folgenden Programme jeweils ohne Verwendung von break oder continue und dann verändern Sie sie, so dass break oder continue zum Einsatz kommen. Grundlegende Aufgabe AddBel.cpp Schreiben Sie ein Programm, das eine beliebige Anzahl von einzugebenden Zahlen addiert, bis die Zahl 0 eingegeben wird. Addition von Zahlen Nach der letzten Zahl 0 eingeben 1. Zahl: 15.2 2. Zahl: 17.1 3. Zahl: 20 4. Zahl: 0 Die Summe der 3 eingegebenen Zahlen ist 52.3. Programmende. Grundlegende Aufgabe DivBel.cpp Schreiben Sie ein Programm, das eine beliebige Anzahl von einzugebenden Zahlenpaaren dividiert, bis als erste Zahl 0 eingegeben wird. Wird als zweite Zahl 0 eingegeben, soll die Eingabe wiederholt werden. Division von Zahlen Nach der letzten Zahl 0 eingeben 1. Zahl: 15.2 2. Zahl: 17.1 15.2/17.1 ist 0.8888888888889. 1. Zahl: 20 2. Zahl: 0 Durch 0 kann nicht dividiert werden. 1. Zahl: 0 Programmende. 1.6 Modul (Unterprogramm, Funktion) Wenn die zu bearbeitenden Algorithmen so groß werden, dass man sie durch ein übersichtliches Struktogramm nicht mehr darstellen kann, muss man die Aufgabenstellung modularisieren. Das bedeutet, ein Problem wird in Teilprobleme zerlegt, die einzeln für sich gelöst werden. Jede strukturierte Programmiersprache besitzt Sprachelemente zur Modularisierung. In C++ heißen die Module Funktionen. Bislang enthielten unsere C++-Programme schon ein einziges Modul, nämlich das Hauptprogramm main(). Bereits an void main() erkennen wir, dass eine Funktion einen Typ hat, einen Bezeichner und einen Anweisungsblock, der zwischen geschweiften Klammern „{“ und „}“ eingeschlossen wird. Allgemein wird eine Funktion deklariert als Rückgabetyp Funktionsname (Parameterliste) 2000-06 datadidact 1-49 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Also z.B. void bool int float Ausgabe(float f) IstPrimzahl(int n) Quadrat(int n) Kegelvolumen(float r,h) Als Rückgabetyp von Funktionen können beliebige Datentypen eingesetzt werden. Möchte man eine Funktion ohne Rückgabetyp verwenden, benutzt man dafür das Schlüsselwort void. Diesen Spezialfall der typlosen Funktion bezeichnet man in anderen Programmiersprachen, z.B. Pascal, als Prozedur. Wir werden in den nächsten Kapiteln zunächst nur auf Funktionen ohne Rückgabewert eingehen, danach aber besprechen, wie Funktionen mit Rückgabewert programmiert werden. Der Rückgabetyp der Funktion main() war bislang void, das heißt, die Funktion main() gibt nach ihrem Ablauf keinen Wert an das Betriebssystem zurück. void main() { /* Anweisungen */ } // Kein Rückgabewert an das // Betriebssystem. Wird main() vollständig ohne Rückgabetyp angegeben, so stellt dies eine Abkürzung von int main() dar. Der von main zurückgegebene int-Wert ist der Status-Code des Programms. (Der Programmstatus kann in BatchDateien mit dem Befehl IF ERRORLEVEL abgefragt werden.) int main() { // oder nur main() !! /* Anweisungen */ return(3); // Status-Code 3 wird zurückgegeben. } Programmieren mit C++ datadidact 1-50 2000-06 1 Grundlagen der Programmierung in C++ ! Funktionen ohne Parameter " Funktionen mit Werteparametern # Funktionen mit Referenzparametern $ Funktionen mit Rückgabetyp %& Verschachtelter Funktionsaufruf Lerninhalte L1 C++ Programmierung Lerninhalte Funktionen ohne Parameter Oftmals finden sich in Programmen mehrmals ähnliche Aufgabenstellungen, die fast identische QuellcodeBlöcke erfordern. Mit dem folgenden Programmbeispiel MAXIMUM0.CPP wird dies demonstriert. Der Anweisungsblock zur Ermittlung des Maximums von zwei Zahlen ist im Programm zweimal enthalten. /* PROGRAMM Maximum0.cpp Demonstriert die Notwendigkeit zur Modularisierung */ #include <iostream.h> int a,b,x,y; void main() { cout << "Zwei Zahlen: "; cin >> a >> b; if (a > b) cout << "Maximum = " << a; else cout << "Maximum = " << b; cout << endl; // Bestimmung des Maximums cout << "Zwei andere Zahlen: "; cin >> x >> y; if (x > y) cout << "Maximum = " << x; // statt a hier x, statt b hier y else cout << "Maximum = " << y; // ansonsten identischer Funktionsblock cout << endl; cout << "Programmende."; } Zwei Zahlen: 1000 2000 Maximum = 2000 Zwei andere Zahlen: 5 9 Maximum = 9 Programmende. Im folgenden Programm MAXIMUM1.cpp ist der Funktionsblock zur Ermittlung des Maximums in ein Modul ausgelagert. Module (Funktionen) können mehrmals in Programmen ausgeführt werden können, obwohl man sie nur einmal deklarieren und definieren muss. Die Funktion Max() wird hier zweimal aufgerufen, um jeweils das Maximum von zwei Zahlen anzugeben. 2000-06 datadidact 1-51 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung /* PROGRAMM Maximum1.cpp Maximum von zwei Zahlen. Typlose Funktion ohne Parameter */ #include <iostream.h> int a,b,x,y; void Max() // Deklaration der Funktion { if (a > b) cout << "Maximum = " << a; else cout << "Maximum = " << b; cout << endl; } // Definition der Funktion void main() { cout << "Zwei Zahlen: "; cin >> a >> b; Max(); // Erster Funktionsaufruf cout << "Zwei andere Zahlen: "; cin >> x >> y; a = x; b = y; Max(); // Zweiter Funktionsaufruf cout << "Programmende."; } Die Bildschirmausgabe von Maximum1.cpp ist identisch mit der von Maximum0.cpp. Grundlegende Aufgabe Benzin2.cpp Schreiben Sie das früher bereits behandelte Programm BENZINV.CPP zu BENZIN2.CPP um. Im Hauptprogramm sollen nacheinander drei typlose Funktionen aufgerufen werden: main() { /* ... */ Eingabe(); Verarbeitung(); Ausgabe(); /* ... */ } Grundlegende Aufgabe Functst1.cpp Schreiben Sie ein Programm, das zur Eingabe zweier Zahlen auffordert und danach deren Produkt ausgibt. Vor jeder einzelnen Ein- und Ausgabe soll mittels einer Funktion void loesche_bild() der Bildschirm gelöscht werden, indem ein 25facher Zeilenvorschub ausgegeben wird. Alle Ein- und Ausgaben erfolgen demzufolge in der untersten Bildschirmzeile des Ausgabefensters. Grundlegende Aufgabe Functst2.cpp Ergänzen Sie das Programm FUNCTST1.cpp durch eine Funktion mit dem Namen pause(), die nach jedem einzelnen Schritt den Programmablauf unterbricht und erst nach Druck auf RETURN wieder fortfährt. (Anweisungsfolge cout << endl; cin.get(); ) Programmieren mit C++ datadidact 1-52 2000-06 1 Grundlagen der Programmierung in C++ L2 C++ Programmierung Funktionen mit Werteparametern Das Programm MAXIMUM1 arbeitet an einer Stelle sehr umständlich: Da innerhalb der Funktionsdefinition von Max() die Variablen a und b verglichen werden, müssen vor dem zweiten Funktionsaufruf die neuen Variablen x und y explizit „von Hand“ auf a und b zugewiesen werden. Hier wünscht man sich eine implizite Variablenzuweisung, die so arbeitet, dass in der Funktionsdefinition „Platzhaltervariablen“ angegeben werden können, in die dann beim Funktionsaufruf automatisch die tatsächlich benötigten Variablen eingesetzt werden. Das folgende Programm MAXIMUM2 erledigt diese „automatische“ Wertübergabe mittels Werteparametern. Die Parameter x und y werden innerhalb der Funktion Max() als „Platzhalter“ verarbeitet. Sie sind formale Parameter. Beim Funktionsaufruf werden an die Stelle der formalen Parameter x und y die aktuellen Parameter a und b bzw. c und d eingesetzt. Statt dem Begriff aktueller Parameter benutzt man auch die Bezeichung Argument. /* PROGRAMM Maximum2.cpp Maximum von zwei Zahlen. Typlose Funktion mit Werteparametern */ #include <iostream.h> int a,b,c,d; void Max(int x, int y) // Funktion mit formalen Werteparametern x und y { if (x > y) cout << "Maximum = " << x; else cout << "Maximum = " << y; cout << endl; } void main() { cout << "Zwei Zahlen: "; cin >> a >> b; Max(a,b); // Erster Funktionsaufruf cout << "Zwei andere Zahlen: "; cin >> c >> d; Max(c,d); // Zweiter Funktionsaufruf cout << "Programmende."; } Die Bildschirmausgabe von Maximum2.cpp ist identisch mit der von Maximum0.cpp . Globale und lokale Variablen Die Variablen a, b, c und d sind im globalen Programmteil als globale Variablen deklariert worden. Globale Variablen sind in allen Modulen bekannt, die ein Programm verwendet. Die Eingabeparameter x und y jedoch sind als lokale Variablen nur innerhalb der Funktion Max() gültig und bekannt. Globale Variablen sollen nach Möglichkeit nicht verwendet werden, da ihr Einsatz leicht zu unübersichtlichen, schwer wartbaren Programmen führt. Man sollte sie lediglich einsetzen, wenn es unbedingt notwendig ist. 2000-06 datadidact 1-53 Programmieren mit C++ C++ Programmierung 1 Grundlagen der Programmierung in C++ Werteparameter Ein Werteparameter kann innerhalb einer Funktion zwar verändert werden, der geänderte Wert kann jedoch nicht an den aktuellen Parameter des Hauptprogrammes zurückgegeben werden. Deshalb nennt man Werteparameter auch Eingabeparameter. Sie werden in das Modul nur hineingegeben, das Modul kann sie nicht (geändert) wieder ausgeben. Grundlegende Aufgabe Benzin3.cpp Schreiben Sie das Programm BENZINV.CPP um zu BENZIN3.CPP. Bei gleicher Bildschirmausgabe soll die folgende Funktion aufgerufen werden - innerhalb der Funktion sollen keinerlei Tastatureingaben oder Bildschirmausgaben getätigt werden: void DVerbrauch(float Verbrauch, float Strecke); Grundlegende Aufgabe Paramet1.cpp Das Programm soll mit dem Aufruf einer Funktion void vorschub(x) eine feste Anzahl ( x = 3, 5, 1) und eine variable Anzahl von Leerzeilen erzeugen. Die Anzahl x der variablen Leerzeilen wird über die Tastatur eingegeben. Grundlegende Aufgabe Paramet2.cpp Mit einer Funktion linie sollen Linien variabler Länge gezeichnet werden. An die Funktion sollen das zu druckende Zeichen und die Zeichenanzahl übergeben werden. L3 Funktionen mit Referenzparametern In den bisherigen Programmbeispielen war innerhalb des Moduls Max() eine Bildschirmausgabe enthalten. Das ist unschön, denn Programmodule sollen möglichst universell verwendbar sein. So wünscht man sich eine Funktion Max(), die den Maximalwert nur an das aufrufende Programm zurückgibt, anstatt ihn sofort auf dem Bildschirm auszugeben. Das aufrufende Programm kann dann den in der Funktion ermittelten Wert beliebig verwerten - ihn z.B. in arithmetischen Ausdrücken weiter verrechnen oder mit einem speziellen Format selbst auf dem Bildschirm ausgeben, usw. Das Zurückgeben von Werten, die in einem Modul geändert wurden, an das rufende Programm kann man mit Referenzparametern erreichen, auch Variablenparameter oder Ein-Ausgabeparameter genannt: Programmieren mit C++ datadidact 1-54 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung /* PROGRAMM Maximum3.cpp Maximum von zwei Zahlen. Typlose Funktion mit Wert- und Referenzparameter */ #include <iostream.h> int a,b,Maximum; void Max(int x, int y, int& erg) // Funktion mit Wertparametern x und y und Referenzparameter erg { erg = x > y ? x:y ; } // Kurzform von { if (x > y) erg = x; else erg = y; } void main() { int i; for (i=0;i<2;i++) { if (!i) cout << "Zwei Zahlen: "; else cout << "Zwei andere Zahlen: "; cin >> a >> b; Max(a,b,Maximum); cout << "Maximum = " << Maximum << endl; } cout << "Programmende."; } Man erkennt, dass ein Referenzparameter durch das Symbol & direkt hinter der Typbezeichnung gekennzeichnet wird. Ein formaler Referenzparameter in einer Funktion zeigt immer auf die gleiche Speicheradresse wie das Argument (aktueller Parameter) im rufenden Programm. Daher verändert sich das Argument schon während des Ablaufs der Funktion. Grundlegende Aufgabe Erhoehen.cpp Das folgende Programm Erhoehen.cpp ruft zweimal nacheinander eine Funktion namens Erhoehe auf. Innerhalb der Funktion wird der übergebene Referenzparameter um 10 erhöht und das Ergebnis wird an das Hauptprogramm zurückgegeben. Lassen Sie das Programm ablaufen und studieren Sie seine Wirkungsweise. Anmerkung: a ist global deklariert, damit man die Erhöhung von a im Debugger beobachten kann. 2000-06 datadidact 1-55 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung /* PROGRAMM Erhoehen.cpp Erhoehen einer Zahl um 10. Typlose Funktion mit Referenzparameter */ #include <iostream.h> float a; void Erhoehe(float& par) { par += 10; } void main() { int i; for (i=0;i<2;i++) { if (!i) cout << "Eine Zahl eingeben: "; else cout << "Noch eine Zahl eingeben: "; cin >> a; Erhoehe(a); cout << "Die um 10 erhoehte Zahl lautet " << a << endl; } cout << "Programmende."; } Eine Zahl eingeben: 3.4 Die um 10 erhöhte Zahl lautet 13.4 Noch eine Zahl eingeben: 5.6 Die um 10 erhöhte Zahl lautet 15.6 Programmende. Grundlegende Aufgabe Erhoehen2.cpp Machen Sie den Referenzparameter par in Erhoehen.cpp zum Werteparameter, lassen Sie das Programm ablaufen und studieren Sie die Wirkung. Begründen Sie das Programmverhalten. Grundlegende Aufgabe Tausche.cpp Schreiben Sie ein Programme TAUSCHE.CPP, in dem zwei Zahlen a und b getauscht werden. Verwenden Sie darin die folgende Funktion. Auch hier sollen innerhalb der Funktion weder Tastatureingaben noch Bildschirmausgaben stehen. void Tausch(/* Zwei Argumente */); Grundlegende Aufgabe 2func.cpp Schreiben Sie ein Unterprogramm namens Berechne(...), welches für einen Fließkomma-Parameter x sowohl den Wert der Funktion f(x)=x2-15 als auch g(x)=ln(x2-15)+7 berechnet. Überlegen Sie zunächst, wieviele Parameter diese Funktion benötigt und welcher Art sie sind. Grundlegende Aufgabe Sort3f.cpp (sort3f.cpp) Entwickeln Sie ein Programm, das ein Unterprogramm sort3(...) mit 3 float-Parametern verwendet. Nach dem Aufruf von sort3(a,b,c) soll a<=b<=c gelten, also a, b und c sollen dadurch in aufsteigender Reihenfolge sortiert werden. Entwickeln Sie auch ein Struktogramm dieses Unterprogrammes L4 Funktionen mit Rückgabetyp Programmieren mit C++ datadidact 1-56 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung Eine Funktion in C++ kann beliebig viele Wertparameter und/oder Referenzparameter mit sich tragen. Sehr oft hat eine Funktion jedoch den Zweck, genau einen einzigen, ganz bestimmten Wert zu berechnen. Dann gibt man der Funktion einen Rückgabetyp, dadurch wird der zu berechnende Wert im Funktionsnamen zurückgegeben. Diese Verwendung einer Funktion ist der Normalfall in C++: /* PROGRAMM Maximum4.cpp Maximum von zwei Zahlen mit Integerfunktion */ #include <iostream.h> int a,b,Erg; // Erg kann auch eingespart werden, siehe unten int Max(int x, int y) // Kein Referenzparameter mehr erforderlich { if (x > y) return x; else return y; } void main() { for (int i=0;i<2;i++) // i gleichzeit deklariert und verwendet! { if (!i) cout << "Zwei Zahlen: "; else cout << "Zwei andere Zahlen: "; cin >> a >> b; Erg = Max(a,b); cout << "Maximum = " << Erg << endl; // noch kürzer ohne Erg statt der oberen Zeile: // cout << "Maximum = " << Max(a,b) << endl; } cout << "Programmende."; } Man beachte die elegante Schreibweise, die mit typisierten Funktionen möglich ist: Da der Name der Funktion sowohl das Unterprogramm als auch die zurückgegebene Variable repräsentiert, kann der Funktionsaufruf syntaktisch genauso verwendet werden wie eine Variable. Die Anweisung return bewirkt Zweierlei: • Der Rückgabewert der Funktion wird mit dem angegebenen Wert initialisiert. • Die Funktion wird danach (sofort, ohne Ausführung eventuell in der Funktion folgender Befehle!) verlassen. Wenn man Funktionen benötigt, die mehr als einen Rückgabewert an das rufende Programm erfordern, kann man zusätzlich noch Referenzparameter verwenden oder besser, man verwendet für alle Rückgabewerte dann Referenzparameter und verwendet eine Funktion ohne Rückgabewert. 2000-06 datadidact 1-57 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe PotFunk.cpp Schreiben Sie ein Programm mit einer C++-Funktion, die die folgende mathematische Funktion berechnet, wobei das Funktionsergebnis vom Typ float ist. Der Wert von x soll als Parameter an die Funktion übergeben werden. f(x) = 3x3 + x2 + 5x - 22 Eine Zahl eingeben: 1 Das Funktionsergebnis lautet -13 Noch eine Zahl eingeben: 2 Das Funktionsergebnis lautet 16 Programmende. Grundlegende Aufgabe Euklid2.cpp Schreiben Sie das Programm Euklid1.cpp so um, dass der ggT von zwei Zahlen mit Hilfe einer Funktion namens ggT(...) berechnet wird. Grundlegende Aufgabe AddBruch.cpp Der Computer soll zwei Brüche addieren, die in Form von Zähler und Nenner eingegeben werden. Die Summe soll gekürzt ausgegeben werden. Erstellen Sie ein Struktogramm und schreiben Sie das Programm unter Verwendung der Funktionen int kuerze(zahl,zaehler,nenner) und int ggT(...) (ggT = größter gemeinsamer Teiler). Addition zweier Brueche Geben Sie den Zaehler des 1. Bruchs ein: Geben Sie den Nenner des 1. Bruchs ein: Geben Sie den Zaehler des 2. Bruchs ein: Geben Sie den Nenner des 2. Bruchs ein: 4 6 2 3 ggT ist: 6 ggT ist: 6 Ergebnis: 4 / 3 Grundlegende Aufgabe Kapitel.cpp (Kapital.cpp) Mit Hilfe der Funktionen Potenz(float basis; long exponent) und Kapital(float anfangskapital; float zinssatz; long jahre) soll ein Hauptprogramm bei einzugebendem Anfangskapital, Zinssatz und Laufzeit die Zinsen berechnen. Grundlegende Aufgabe MittelwertF.cpp Schreiben Sie eine Funktion double Mittelwert(double a), die bei jedem Aufruf den Mittelwert aus allen bisher beim Aufruf übergebenen Werten von a und dem aktuellen Wert von a berechnet und als Ergebnis zurückgibt. Dazu verwenden Sie innerhalb von Mittelwert statische Variablen für Summe und Anzahl der erfolgten Funktionsaufrufe. Programmieren mit C++ datadidact 1-58 2000-06 1 Grundlagen der Programmierung in C++ L5 C++ Programmierung Verschachtelter Funktionsaufruf Das nachfolgende Programm MAXIMUM5 demonstriert, wie man typisierte Funktionen geschachtelt aufruft. Damit können komplexe Aufgabenstellungen mit kompaktem Programmcode erledigt werden: /* PROGRAMM Maximum5.cpp Geschachtelte Aufrufe einer Integerfunktion */ #include <iostream.h> int a,b,c; int Max(int x, int y) { if (x > y) return x; else return y; } void main() { cout << "Drei Zahlen: "; cin >> a >> b >> c; cout << "Maximum = " << Max(Max(a,b),c) << endl; cout << "Programmende."; } Drei Zahlen: 4 8 6 Maximum = 8 Programmende. Grundlegende Aufgabe KapitalR.cpp Mit Hilfe der Funktionen Potenz(float basis; long exponent) und Kapital(float anfangskapital; float zinssatz; long jahre) soll ein Hauptprogramm bei einzugebendem Anfangskapital, Zinssatz und Laufzeit die Zinsen berechnen. Grundlegende Aufgabe RekSum.cpp Entwickeln Sie ein Programm, welches ein Unterprogramm namens Summe(long a; long b) verwendet, um rekursiv die Summe von a und b zu berechnen. Dazu verwenden Sie folgenden Algorithmus, der für positives b gültig ist: a wenn b = 1 summe(a, b) = a + summe(a, b − 1) sonst Grundlegende Aufgabe RekProd.cpp Entwickeln Sie ein Programm, welches ein Unterprogramm namens Produkt(long a; long b) verwendet, um rekursiv das Produkt von a und b zu berechnen. Dazu verwenden Sie folgenden Algorithmus, der für positives b gültig ist: a wenn b = 1 produkt(a, b) = a * produkt(a, b − 1) sonst 2000-06 datadidact 1-59 Programmieren mit C++ C++ Programmierung Grundlegende Aufgabe 1 Grundlagen der Programmierung in C++ Euklid2R.cpp Ändern Sie Euklid2.cpp so ab, dass der ggT rekursiv berechnet wird. a wenn b = 0 ggT( a, b) = sonst ggT(b, a%b) 1.7 Sprachspezifische Besonderheiten bei Funktionen ! Gültigkeitsbereich und Sichtbarkeit von Bezeichnern " Funktionsdefinition und Funktionsdeklaration # Inline-Funktionen $ Initialisierung von lokalen Variablen, statische Variablen %& Konstante Wert- und Refernzparameter ' Referenz auf den Funktionsnamen ( Überladene Funktionsnamen ) Default-Argumente * Schablonen (Templates) für Funktionen Lerninhalte Lerninhalte Die im folgenden erläuterten Eigenschaften sind typisch für die Sprache C++. Bei anderen höheren Programmiersprachen sind teilweise ähnliche Konzepte definiert. L1 Gültigkeitsbereich und Sichtbarkeit von Bezeichnern An der Lösung von TAUSCHE.CPP ist zu ersehen, dass es außer globalen Variablen und (formalen) Parametern auch noch echte lokale Variablen innerhalb von Funktionen gibt: void Tausch( /* ... */) { int platz; // echte lokale Variable, ist nur in Funktion Tausch gültig /* ... */ } Das wirft die Frage auf, in welchen Programmteilen Variablen und andere Bezeichner gültig oder sichtbar sind. Allgemein gilt: • Beliebige Namen sind nur nach der Deklaration und innerhalb des Blocks gültig, in dem sie deklariert wurden. Ein Block wird eingeschlossen durch geschweifte Klammern { und }. • Bezeichner im aktuellen Block verdecken Bezeichner in übergeordneten Blöcken gleichen Namens – diese sind dann im aktuellen Block unsichtbar. In diesem Fall kann man globale Variablen trotzdem ansprechen, indem man einen doppelten Doppelpunkt :: voranstellt Beispiel: Programmieren mit C++ datadidact 1-60 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung /* Programm gueltig.cpp */ #include<iostream.h> int n; float a, b, i; // a und b sind global - überall im Programm gültig double testfunc(int x, double& y) { char buchst; /* x, y, buchst sind nur in diesem Block gültig */ y = a; // das geht! c = x; // ergibt Compilerfehler, da c hier ungültig } // Hier Compilerwarnung "Function should return a value" // da return-Anweisung fehlt. Trotzdem wird Programm compiliert void main() { int c; // nur innerhalb des main()-Blocks gültig for (int i=0;i<5;i++) // i wird hier deklariert !! { // i ist nur innerhalb dieses Blockes gültig !! // Das globale i wird hier verdeckt. cout << ::i; // Damit ist das globale i trotzdem sichtbar } testfunc(n,b); // Trotz Typ double kann die Funktion // ohne Rückgabewert aufgerufen werden } L2 Funktionsdefinition und Funktionsdeklaration Wir unterscheiden in C++ Funktionsdeklaration und Funktionsdefinition. Eine Funktionsdeklaration erfordert lediglich den Funktionsnamen, den Rückgabetyp sowie Typ und Anzahl der übergebenen Argumente - die Namen der Argumente müssen bei einer Deklaration nicht angegeben werden! Eine Funktionsdeklaration wird auch als Funktionsprototyp bezeichnet. Demgegenüber muss eine Funktionsdefinition zusätzlich noch die Parameternamen und alle Anweisungen des Funktionsrumpfes enthalten. Beispiel: a) Deklaration (Prototyp): void AddiereWas(int, int); b) Definition: void AddiereWas(int p, int q) { p += 10; q +=20; } Zusätzliche Funktionsdeklarationen werden dann benötigt, wenn die Funktionsdefinition in einer anderen Quelldatei steht als der Funktionsaufruf. Unter Umständen kann der eigentliche Funktionsrumpf bereits compiliert in einer Bibliothek vorliegen. Dann macht man in der Quelldatei, die den Funktionsaufruf enthält, dem Compiler die Funktion lediglich durch Deklaration bekannt. extern void AddiereWas(int,int); // Funktion in anderer Quelldatei definiert Bei größeren Programmprojekten ist es üblich, alle benötigten Funktionsdeklarationen in sog. Header-Dateien (*.h) aufzulisten. Die Header.Dateien werden dann in allen benötigen cpp-Quelldateien mit der Direktive #include eingebunden. 2000-06 datadidact 1-61 Programmieren mit C++ C++ Programmierung L3 1 Grundlagen der Programmierung in C++ Inline-Funktionen Eine Funktion kann als inline definiert werden, z.B. mit inline void AddiereWas(int p, int q); In diesem Fall versucht der Compiler, den gesamten Objektcode der Funktion bei jedem Funktionsaufruf immer wieder in die ablauffähige Datei einzusetzen. Das Zielprogramm wird dadurch größer, der Programmablauf wird jedoch schneller. L4 Initialisierung von lokalen Variablen, statische lokale Variablen Eine lokale Variable wird initialisiert (mit einem Anfangswert belegt), wenn der Programmablauf die Variablendefinition erreicht. Standardmäßig passiert das bei jedem Aufruf der Funktion. Jeder Funktionsaufruf verwaltet seine eigene Kopien von Wertparametern und lokalen Variablen. Demgegenüber ist zu beachten: Lokale, nichtstatische Variablen und dynamische Datenstrukturen (auf dem sog. Heap, werden in späteren Kapiteln noch besprochen) werden nicht mit Nullwert initialisiert, wenn sie nicht definiert werden - sie bleiben dann undefiniert. Man kann eine lokale Variable jedoch auch als static deklarieren. Dann wird im gesamten Programm bei allen Funktionsaufrufen ein einziges statisches Speicherobjekt für diese Variable benutzt. In diesem Fall wird die lokale Variable nur beim erstmaligen Erreichen der Variablendefinition initialisiert. Eine statische lokale Variable erzeugt für eine Funktion ein „Gedächtnis“, ohne dass man eine globale Variable verwenden muss, die von anderen Funktionen benutzt und verfälscht werden könnte. /* Programm StatBsp.cpp Demonstration von statischen lokalen Variablen */ #include <iostream.h> void staticfkt(int a) { while (a--) { static int n = 0; // Variable einmal mit 0 initialisert int x = 0; // bei jedem Funktionseintritt initialisiert cout << " n == " << n++ << ", x == " << x++ << endl; } } int main() { staticfkt(3); } n == 0, x == 0 n == 1, x == 0 n == 2, x == 0 Programmieren mit C++ datadidact 1-62 2000-06 1 Grundlagen der Programmierung in C++ L5 C++ Programmierung Konstante Wert- und Referenzparameter Es kommt öfter vor, dass bei Funktionen Parameter übergeben werden, bei denen der Ersteller der Funktion schon von vornherein weiß, dass die Parameterwerte innerhalb der Funktion nicht geändert werden dürfen. In solchen Fällen werden die Parameter als konstant deklariert. Wenn die Werte von konstanten Parametern innerhalb von Funktionen geändert werden, kann das Programm nicht kompiliert werden - der Compiler liefert eine entsprechende Fehlermeldung. char Grossbuchstabe(const char buchst) // Konstanter Wertparameter { /* ... Hier darf die Variable buchst nicht geändert werden */ } char Grossbuchstabe(const char& buchst) // Konstanter Referenzparameter { /* .. Hier gilt das Gleiche. Damit stellt man sicher, dass sowohl der formale Parameter buchst als auch der jeweilige aktuelle Parameter beim Funktionsaufruf ihren Wert behalten. */ } Auf den ersten Blick erscheint es sinnlos, konstante Referenzparameter zu definieren. Normalerweise ist es ja gerade die Aufgabe eines Referenzparameters, Werte geändert aus einer Funktion zurückzugeben. Man muss jedoch bedenken, dass beim Funktionsaufruf für Wertparameter grundsätzlich eine lokale Kopie der Parametervariablen erzeugt wird. Das kostet Speicherplatz und Rechenzeit. Bei Referenzparametern wird der Funktion lediglich ein Verweis auf den aktuellen Parameter übergeben (auch Zeiger oder Adresse genannt siehe spätere Kapitel), das ist ressourcenschonender und schneller. Konstante Referenzparameter verwendet man also, wenn man Ressourcen und Rechenzeit sparen und trotzdem sicherstellen will, dass sich ein Parameter nicht ändern kann. Grundlegende Aufgabe Maximum6.cpp Kopieren Sie Progamm Maximum3.cpp nach Maximum6.cpp. Machen Sie in der Funktion Max von Maximum6.cpp den Referenzparameter erg konstant. Kompilieren Sie das Programm und beobachten Sie die Reaktion des Compilers. Kompilieren Sie nochmals, nachdem Sie den Wertparameter x ebenfalls konstant gemacht haben und ihn innerhalb von Max inkrementiert (d.h. um 1 erhöht) haben. 2000-06 datadidact 1-63 Programmieren mit C++ C++ Programmierung L6 1 Grundlagen der Programmierung in C++ Referenz auf den Funktionsnamen Möchte man zusätzlich noch vermeiden, dass während der Abarbeitung einer Funktion für ihren Rückgabewert zusätzlicher Speicherplatz reserviert wird, kann man unter bestimmten Bedingungen auch den Funktionsnamen als Referenz definieren: /* Programm Minvar1.cpp liefert das Minimum von drei Werten, Demonstration einer Referenz auf den Funktionsnamen */ #include <iostream.h> int& Min(int& x, int& y, int& z) { if (x<y) { if (x<z) return x; } else if (y<z) return y; return z; /* Die Rückgabe-Variablen x, y, z müssen alle als Referenzparameter int& übergeben werden. Hätte man x, y und z als Wertparameter int deklariert, wäre ein Compilerfehler entstanden: "Attempting to return a reference to local variable 'z'" */ } int main() { int a=5, b=3, c=2; cout << "Minimum von a = " << a << ", b = " << b << ", c = " << c << " : " << Min(a,b,c); } Im Beispiel Minvar1.cpp wird für den Rückgabewert im Funktionsnamen Min keine zusätzlicher Speicherplatz reserviert. Stattdessen wird als Min ein Verweis auf die Variable zurückgeliefert, die nach dem Schlüsselwort return aufgelistet ist - je nach dem Funktionsergebnis ist das einer der Referenzparameter x, y oder z, die den Argumenten a, b oder c beim Funktionsaufruf entsprechen. Eine Referenz auf den Funktionsnamen kann nur compiliert werden, wenn jede Rückgabevariable (also jeder Variablenname, der nach return aufgelistet ist), einen Speicherplatz außerhalb der Funktionsdefinition hat die Rückgabevariable muss entweder als Referenzparameter an die Funktion übergeben werden oder sie muss eine globale Variable sein. Rückgabevariablen, die als Wertparameter übergeben wurden oder lokal in der Funktion definierte Variablen sind, führen zu Compilerfehlern! Die Referenz auf den Funktionsnamen hat noch einen anderen Effekt: die Funktion ist dann ein L-Wert, d.h. es kann ihr ein Wert zugeweisen werden! Im obigen Beispiel könnte man auch schreiben: Min(a,b,c)=17; Dies würde im konkreten Fall bewirken, dass c den Wert 17 zugewiesen bekommt. Wenn man genau weiß, dass innerhalb der Funktion kein Variablenwert (hier x, y oder z) geändert wird, kann man auch vollständig konstante Referenzparameter verwenden. Der Compiler meldet dann einen Fehler, wenn der Programmierer trotzdem unabsichtlich diese als konstant definierten Werte verändern will. Damit muss im vorliegenden Beispiel auch der Rückgabewert eine Referenz auf einen konstanten Wert sein, was bewirkt, dass die Funktion dann kein L-Wert mehr ist. Programmieren mit C++ datadidact 1-64 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung /* Programm Minvar2.cpp liefert das Minimum von drei Werten, Demonstration einer konstanten Referenz auf den Funktionsnamen */ #include <iostream.h> const int& Min(const int& x, const int& y, const int& z) { if (x<y) { if (x<z) return x; } else if (y<z) return y; return z; } int main() { int a=5, b=3, c=2; cout << "Minimum von a = " << a << ", b = " << b << ", c = " << c << " : " << Min(a,b,c); } L7 Überladene Funktionsnamen Manchmal schreibt man Funktionen, die die gleiche oder ähnliche Aufgaben für verschiedene Typen ausführen sollen. Dann kann man die Funktion mehrmals mit gleichem Namen definieren. int Max(int x, int y) // Kein Referenzparameter mehr erforderlich { cout << "Das ist die int-Version: " << endl; if (x > y) return x; else return y; } float Max(float x, float y) // Kein Referenzparameter mehr erforderlich { cout << "Das ist die float-Version: " << endl; if (x > y) return x; else return y; } Die Funktionen können bei jeder einzelnen Definition eine vollkommen unterschiedliche Anzahl von Argumenten und auch unterschiedliche Typen von Argumenten und Rückgabewerten besitzen. In Wirklichkeit existieren unterschiedliche Funktionen, die lediglich den gleichen Namen tragen. Welche der einzelnen Funktionen beim Aufruf tatsächlich zur Ausführung gelangt, wird aufgrund der Typen der Aktualparameter entschieden. Bei dieser Auswahl spielt der Typ des Rückgabewertes einer Funktion keine Rolle. Grundlegende Aufgabe Maximum7.cpp Kopieren Sie das Programm Maximum4.cpp nach Maximum7.cpp und fügen Sie in Maximum7.cpp zwei zusätzliche globale float-Variablen c und d ein sowie eine zweite Funktion Max wie oben beschrieben. Lassen Sie das Programm ablaufen, geben Sie ganzzahlige und reelle Werte ein und beobachten Sie die Reaktion des Programmes. 2000-06 datadidact 1-65 Programmieren mit C++ 1 Grundlagen der Programmierung in C++ C++ Programmierung Grundlegende Aufgabe Erhoehe2.cpp Entwickeln Sie ein Programm, welches überladene Funktionen Erhoehe(...) verwendet, um long- oder doubleParameter zu erhöhen. Dabei sollen folgende Aufrufe möglich sein, die alle auch im Hauptprogramm stehen sollen: long i; double x; Erhoehe(i,7); // i+=7; Erhoehe(x,1.5); // x+=1.5; Grundlegende Aufgabe PotFunc2.cpp (PotFunc2.cpp) Entwickeln Sie ein Programm, welches überladene Funktionen Potenz(...) verwendet, um xy zu berechnen. float Potenz(float x, int y) soll mit Hilfe einer Schleife berechnet werden, float Potenz(float x, float y) soll mit Hilfe der Formel x y = e y⋅ln x berechnet werden. Dabei können Sie die Funktionen exp() und log() aus der Bibliothek math.h verwenden. L8 Default-Argumente Eine allgemein gehaltene Funktion benötigt oft mehr Parameter, als für alle Funktionsaufrufe nötig ist - einige spezielle Parameter werden nur bei ganz bestimmten Funktionsaufrufen benötigt. Möchte man nicht auf das (relativ aufwendige) Konzept des Überladens zurückgreifen, kann man für Funktionen stattdessen DefaultArgumente angeben. Ein Default-Argument wird bei der Funktionsdeklaration typgeprüft und mit dem Default-Wert initialisiert, wenn kein Aktualparameter vorhanden ist. Beispiel: /* PROGRAMM Maximum8.cpp Maximum von zwei Zahlen, Funktion mit Default-Parameter */ #include <iostream.h> int a,b; int Max(int x, int y =5) // Vergleich mit Default-Wert 5 { if (x > y) return x; else return y; } void main() { cout << "Zwei Zahlen: "; cin >> a >> b; cout << "Maximum = " << Max(a,b) << endl; cout << "Nur eine Zahl (Vergleich mit 5): "; cin >> a; cout << "Maximum = " << Max(a) << endl; // Zweiter Aktualparameter fehlt! cout << "Programmende."; } Zwei Zahlen: 3 8 Maximum = 8 Nur eine Zahl (Vergleich mit 5): 4 Maximum = 5 Programmende. Dieses Konzept bietet einen weiteren Vorteil bei der Programmwartung. Stellt es sich heraus, dass in einer Programmieren mit C++ datadidact 1-66 2000-06 1 Grundlagen der Programmierung in C++ C++ Programmierung bereits entworfenen und vielfältig eingesetzten Funktion manchmal weitere Parameter notwendig sind, fügt man diese neuen Parameter mit Default-Argumenten hinten in der Parameterliste an, so dass alle bisher getätigten Funktionsaufrufe gültig bleiben und funktionieren. Grundlegende Aufgabe Erhoehe3.cpp Entwickeln Sie ein Programm, welches überladene Funktionen Erhoehe(...) verwendet, um long- oder doubleParameter zu erhöhen. Dabei sollen folgende Aufrufe möglich sein, die alle auch im Hauptprogramm stehen sollen: long i; double x; Erhoehe(i); // Erhoehe(i,7); // Erhoehe(x); // Erhoehe(x,1.5); // 2000-06 i++; i+=7; x++; x+=1.5; datadidact 1-67 Programmieren mit C++ C++ Programmierung L9 1 Grundlagen der Programmierung in C++ Schablonen (Templates) für Funktionen Die dritte (und wohl eleganteste) Möglichkeit, Funktionen mit verschiedenen Typen aufzurufen, besteht darin, eine Schablone (engl. Template) zu definieren. Eine Schablone ist eine Definition, bei der die Datentypen noch nicht festgelegt werden. Es wird ein Platzhalter eingefügt, der später durch den tatsächlich benötigten Datentyp ersetzt wird. Die Syntax zur Deklaration einer Schablone lautet: template <class TypBezeichner> Funktionsdefinition Beispiel: /* PROGRAM Maximum9.cpp Maximum von zwei Zahlen mit Funktionsschablone */ #include <iostream.h> char float a,b; c,d; template<class TIrgendwas> TIrgendwas Max(TIrgendwas x, TIrgendwas y) { if (x > y) return x; else return y; } void main() { cout << "Zwei Buchstaben: "; cin >> a >> b; cout << "Maximum = " << Max(a,b) << endl; cout << endl << "Zwei Dezimalzahlen: "; cin >> c >> d; cout << "Maximum = " << Max(c,d) << endl; cout << "Programmende."; } Innerhalb von main() stellt der Compiler anhand des Funktionsaufrufs fest, für welchen Datentyp die Funktion benötigt wird und bildet die Definition mit Hilfe der Schablone. Für jeden bei Aufrufen benötigten Datentyp wird vom Compiler aus der Schablone eine Funktion erzeugt. Grundlegende Aufgabe Erhoehe4.cpp Verändern Sie Erhoehe3.cpp, so dass es Funktions-Schablonen benutzt. Hinweis: Sie müssen jetzt beim Aufruf Erhoehe(i,7L); schreiben, damit die 7 nicht als kurzer Integer, sondern als langer Integer interpretiert wird. Grundlegende Aufgabe PotFunc3.cpp Warum ist es im Gegensatz zu Erhoehe2.cpp nicht sinnvoll, PotFunc2.cpp umzuschreiben, so dass es Funktions-Schablonen benutzt? Programmieren mit C++ datadidact 1-68 2000-06