Elementare Syntax von C++: Ein- und Ausgaben über die Konsole

Für die ersten Programme bietet es sich an, Ausgaben und Einagben über die Konsole zu machen. Die zugehörigen Befehle in C++ werden hier erläutert. Zugleich werden die Programmbeispiele verwendet, um zahlreiche weiterreichende Konzepte zu erklären: Präprozessor-Direktive, Kommentare, Namensräume, die besondere Bedeutung der main()-Methode, der Rückgabewert einer Funktion, Quelltextformatierung.

Einordnung des Artikels

Das erste Programm: HelloWorld.cpp

Der Quelltext des ersten Programmes, das bereits in Vorgehensweise und Vorbereitungen zum Erlernen von C++ compiliert und ausgeführt wurde, lautet:

#include <iostream>

int main(){

    std::cout << "Hello World!";
    return 0;
}

Zur Erklärung:

1. Zeile 1:

Mit # werden sogenannte Präprozessor-Direktiven eingeleitet; diese Anweisungen werden schon vor dem Kompilieren ausgeführt. Hier dient sie zum Einbinden einer Datei, nämlich iostream. Die Abkürzung io steht - wie so oft - für input output. In iostream werden Objekte definiert, um verschiedene Ein- und Ausgabe-Geräte anzusprechen. Hier wird es nur für die Ausgabe einer Meldung auf der Konsole verwendet (Befehl in Zeile 5). Fehlt die Direktive, ergibt sich beim Kompilieren ein Fehler in Zeile 5. Ebenso ergibt sich ein Fehler beim Kompilieren, wenn die Direktive nicht schon vor den anderen Befehlen steht.

Verwundern mag vielleicht, dass die Pfadangabe zu iostream eine relative Pfadangabe ist, aber sich im workspace bisher keine Datei namens iostream befindet. Dadurch dass iostream in spitzen Klammern steht, wird die Datei zunächst im Installationsverzeichnis <MINGW-home> des Compilers gesucht. Unter

<MINGW-home>\lib\gcc\mingw32\5.3.0\include\c++

finden Sie tatsächlich eine Datei namens iostream. Öffnen Sie diese Datei mit einem Text-Editor. Die wichtigste Information steht sofort in der ersten Zeile:

// Standard iostream objects -*- C++ -*-

und später werden diese Objekte definiert:

//@{
  extern istream cin;       /// Linked to standard input
  extern ostream cout;      /// Linked to standard output
  extern ostream cerr;      /// Linked to standard error (unbuffered)
  extern ostream clog;      /// Linked to standard error (buffered)

#ifdef _GLIBCXX_USE_WCHAR_T
  extern wistream wcin;     /// Linked to standard input
  extern wostream wcout;    /// Linked to standard output
  extern wostream wcerr;    /// Linked to standard error (unbuffered)
  extern wostream wclog;    /// Linked to standard error (buffered)
#endif
  //@}

2. Zeile 1:

Bisher wurde erklärt, was in Zeile 1 steht, wichtig ist auch, was dort nicht steht. Am Beispiel der inkludierten Datei iostream sieht man die Konvention: In der ersten Zeile steht ein kurzer Satz, der den Zweck der Datei erklärt. Wenn Sie später an einem großen Projekt arbeiten, werden Sie dankbar sein, wenn die Dateinamen schon auf den Zweck schließen lassen und wenn er sofort beschrieben wird. Noch besser ist es den Dateinamen (eventuell sogar den gesamten Pfad) zuerst anzugeben. Hier könnten die ersten beiden Zeilen etwa lauten:

/* HelloWorld.cpp
Ausgabe über die Konsole */

Damit dies nicht als Befehl interpretiert wird, müssen Kommentar-Zeichen verwendet werden. Welche es davon gibt, wird später erklärt.

Tip:

Gestalten Sie den Kopf einer Datei nach folgenden Regeln:

3. Zeile 2:

Leerzeile zur besseren Strukturierung des Quelltextes. Leerzeilen und andere Formatierungshilfen (wie Leerzeichen oder Einrückungen) werden vom Compiler wie ein Leerzeichen gelesen; sie dienen somit nur der leichteren Lesbarkeit des Quelltextes.

4. Zeile 3: Die Methode main() des Programmes.

5. Zeile 5:

Endlich die zentrale Anweisung des HelloWorld-Programms. Man muss den Befehl in Verbindung mit der Präprozessor-Direktive aus Zeile 1 lesen - andernfalls ist er nicht verständlich.

Mit std wird der Namensraum der Standard Bibliothek bezeichnet; er wird auch kurz Standard-Namensraum genannt. Das eigenartige :: (Sie kennen aus dem Namen Code::Blocks) ist der Bereichsoperator (scope operator, genauer scope resolution operator).

Mit der Konstruktion

std::cout

wird somit ein Objekt aus der Standard-Bibliothek angesprochen, nämlich cout (console output) aus der inkludierten Datei iostream; dieses Objekt erlaubt, dass auf dem Standard-Ausgabe-Gerät, der Konsole, etwas ausgegeben wird. Dazu wird der Operator << verwendet, der den string (string = Zeichenkette) Hello World! an die Konsole übergibt.

6. Zeile 6:

Die Methode main() muss einen int-Wert zurückgeben, hier wird 0 zurückgegeben. Hier wird folgende Konvention verwendet: Erfolgt die Ausführung der main-Methode fehlerfrei, gibt man 0 zurück; ergeben sich während der Ausführung Fehler, gibt man - je nach Art des Fehlers - einen anderen Wert zurück. Damit lässt sich dann steuern, was im Fehlerfall zu geschehen hat.

Verwendete Konzepte in HelloWorld.cpp

Kommentare

Wie oben beschrieben, sollte jedes Programm mit einem Kommentar beginnen. Es gibt mehrere Arten von Kommentaren, deren Bezeichnung allerdings nicht einheitlich ist:

1. Sogenannte inline-Kommentare:

Sie werden mit // eingeleitet; alles in der Zeile vor dem // ist Quelltext, alles danach ein Kommentar. Ein Zeilenumbruch beendet den Kommentar.

Beispiel:

std::cout << "Hello World!";    // Konsolen-Ausgabe
// inline-Kommentare sollten verwendet werden, um nach einer Anweisung eine kurze Erklärung zu liefern
// oder um die folgende Anweisung zu beschreiben, siehe unten

// Rückgabewert int
return 0;

// Für längere Texte sollten inline-Kommentare nicht verwendet werden

2. Block- oder Stream-Kommentare: Kommentare über mehrere Zeilen

Sie beginnen mit /* und enden mit */ .

Beispiel:

/* Jedes Programm sollte mit einem Kommentar beginnen, 
der kurz die Aufgabe des Programmes beschreibt;
hilfreich kann es auch sein, den Dateinamen oder sogar den vollständigen Pfad der Datei anzugeben.
Hier: 

HelloWorld.cpp

Ausgaben über die Konsole */
#include <iostream>
...

Warnung:

Block-Kommentare dürfen nicht verschachtelt werden. Dies führt zu einem Compiler-Fehler.

3. Box- oder Doxygen-Kommentare:

Es gibt noch eine weitere Version der Block-Kommentare, die sogar leichter erkennbar ist als der Block-Kommentar, da jede Zeile mit einem * beginnt.

Beispiel:

/* Jedes Programm sollte mit einem Kommentar beginnen, 
* der kurz die Aufgabe des Programmes beschreibt;
* hilfreich kann es auch sein, den Dateinamen oder sogar den vollständigen Pfad der Datei anzugeben.
* Hier: HelloWorld.cpp
* Ausgaben über die Konsole 
*/

Der wesentliche Unterschied zum Block-Kommentar: Es gibt Programme, die dazu eingesetzt werden, automatisch aus Quelltexten Dokumentationen zu erzeugen. Diese Programme (Doxygen für C++, javadoc für Java) lesen nur die Doxygen-Kommentare und nehmen deren Inhalte in die Dokumentation auf.

Tip:

Verwenden Sie in Kommentaren niemals Angaben über Programm-Zeilen. Sie werden sehen, dass diese sich sehr schnell verändern.

Aufgabe:

Code::Blocks bietet unter

Edit -> Comment
Edit -> Uncomment
. . .

mehrere Unterstützungen zum Setzen und Entfernen von Kommentaren an. Machen Sie sich damit vertraut!

Die Methode main()

1. Bedeutung der geschweiften Klammern:

Die Befehle, die durch den Aufruf der Methode main() abgearbeitet werden sollen, müssen in geschweiften Klammern stehen. Der Inhalt der geschweiften Klammern ist der Körper (body) der Methode main():

int main(){
    // body: do something
    return 0;
}

Empfehlenswert ist die Formatierung, die im Quelltext zu sehen ist: Der Körper ist klar zu unterscheiden von der Signatur int main() (siehe unten); man erkennt sofort, wo die Methode main() zu Ende ist.

2. Bedeutung der runden Klammern:

In den runden Klammern stehen üblicherweise die Eingabewerte einer Methode; die main()-Methode hat keine, also bleiben die Klammern leer. Falsch wäre es, auf die Klammern deswegen zu verzichten: Man könnte dann eine Methode nicht mehr von einer Variable unterscheiden.

3. return-Statement und Signatur:

Die Methode main() endet mit dem Befehl

return 0;

Das Schlüsselwort return bedeutet, dass die Methode hier ein definiertes Ende hat und das Programm kehrt dorthin zurück, von wo aus die Methode aufgerufen wurde. Wegen der bevorzugten Bedeutung von main() ist damit die Programmausführung zu Ende.

Die Methode main() muss bei der Rückkehr (zum Aufruf) einen Wert vom Datentyp int mitbringen, hier ist es immer der Wert 0. Der Wert 0 bedeutet - gemäß Konvention -, dass die Methode fehlerfrei ausgeführt wurde. Der Programmierer kann hier also für den Fall eines Fehlers entsprechende Informationen weitergeben - mehr dazu später.

Der Rückgabewert 0 ist eine ganze Zahl, in der C++-Welt ist das der Datentyp int (integer); dies ist im Einklang mit der Signatur der Methode main():

int main()

Dass int vor main() steht, bedeutet nämlich, dass ein Wert vom Typ int zurückgegeben werden muss.

Weitere Bestandteile der Signatur werden noch folgen.

4. Projekt, Programm und .cpp-Datei

Die Funktion main() hat die Besonderheit, dass sie automatisch aufgerufen wird, wenn das Programm ausgeführt wird - dabei ist mit dem Programm die zugehörige .exe-Datei gemeint. Dies wirft die Frage auf, wie die drei Begriffe Projekt, Programm und .cpp-Datei zusammenhängen.

A) Projekt:

Mit einem Projekt ist immer eine Einheit gemeint, die unter der Kontrolle der Entwicklungsumgebung steht; beim Beispiel HelloWorld wurde gezeigt, dass im Ordner

<workspace>\HelloWorld

eine Datei für die Projektverwaltung angelegt wird (HelloWorld.cbp) und dass Ordner für den Maschinencode angelegt werden. Dass ein Projekt meist ein Programm enthält, ist Zufall - aber was ist eigentlich ein Programm?

B) Programm:

Mit Programm meint man oft zweierlei:

1. Das Programm wird meist mit dem Projekt gleichgesetzt, wenn man an die Quelltexte denkt, die man selber schreiben muss und weniger an die Projektverwaltung durch die Entwicklungsumgebung. In diesem Sinn besteht ein Programm aus sämtlichen Quelltexten, die in höherer Programmiersprache geschrieben sind. Ein Programm besteht dann im Allgemeinen aus mehreren Dateien, die nicht nur die Endung .cpp besitzen können. Später werden auch .h-Dateien hinzukommen. (Es wird auch noch weitere Resourcen wie Text-Dateien geben.) Aber in einem Programm muss genau eine Funktion main() vorkommen. Gäbe es mehrere Funktionen main(), wäre nicht eindeutig definiert, was bei der Ausführung des Programmes geschehen soll. Gibt es keine Funktion main(), kann das Programm nicht ausgeführt werden. Compiliert wird niemals eine einzelne .cpp-Datei, sondern immer das gesamte Programm. Der Compiler und Linker sorgen dafür, dass alle Quelltexte, die zu einem Programm gehören, übersetzt und in einer .exe-Datei zusammengefasst werden.

2. Streng genommen ist das Programm diese .exe-Datei, also das ausführbare Programm (und nicht die zugrunde liegenden Quelltexte).

C) .cpp-Datei:

Im Projekt HelloWorld kann man die Datei HelloWorld.cpp zugleich mit dem Programm identifizieren. Genauer muss man sagen, dass in HelloWorld.cpp der Quelltext für das Programm HelloWorld.exe steht. Dass eine Quelltext-Datei für ein Programm steht, ist nur deswegen der Fall, weil zu Beginn ein möglichst einfaches Programm geschrieben werden sollte. Der Normalfall wird später sein, dass ein Programm aus sehr vielen Quelltext-Dateien besteht - in der Datei, die die Funktion main() enthält, wird meist nur sehr wenig stehen.

5. Programm-Schnipsel

Mit dem Wissen über die main()-Methode ist klar, wie man einen Algorithmus in ein lauffähiges Programm verwandelt: Die Befehle des Algorithmus werden in die main()-Methode verpackt und diese main()-Methode befindet sich wiederum in einer .cpp-Datei. Wird diese Datei compiliert und das entstehende Programm ausgeführt, wird somit der Algorithmus ausgeführt.

Später - bei der strukturierten Programmierung - wird man dazu übergehen, möglichst viele Befehle zu Unterprogrammen zusammenzufassen und aus der main()-Methode heraus diese Unterprogramme aufrufen. Da die strukturierte Programmierung noch weiterer Vorbereitungen bedarf, werden vorerst die Befehle eines Algorithmus in die main()-Methode geschrieben.

Im Folgenden werden daher immer nur Programm-Schnipsel angegeben; die umgebende main()-Methode und die .cpp-Datei wird meist nicht angegeben. Wenn Sie diese Programm-Schnipsel testen wollen, müssen Sie selbst die geeignete Umgebung bereitstellen; aber wie Sie gesehen haben, unterstützt Sie die Entwicklungsumgebung Code::Blocks dabei (wie man ein neues Projekt oder eine Konsolen-Anwendung anlegt, wurde gezeigt, siehe Vorgehensweise und Vorbereitungen zum Erlernen von C++).

Strichpunkte zum Abschluss eines Befehls

Alle Befehle innerhalb von main() werden mit ; abgeschlossen. Achten Sie beim Lesen von Quelltexten in Zukunft darauf, wann ein ; gesetzt wird - Sie werden leicht selber die Regel formulieren können, wann sie notwendig sind und wann nicht.

Formatierung zur leichteren Lesbarkeit

Bei Quelltexten, mit denen später weitergearbeitet werden soll, ist es extrem wichtig für eine leichte Lesbarkeit zu sorgen. Die einfachsten Hilfsmittel sind:

Konsolenausgaben

Die zentrale Zeile des Programmes HelloWorld.cpp ist die Konsolenausgabe:

std::cout << "Hello World!";

Mit std::cout wird ein sogenannter Namensraum (namespace), hier der Namensraum std angesprochen; und in diesem namespace wird cout aufgerufen (cout = character out), also der Befehl mit dem Zeichen auf der Konsole aus-gegeben werden können.

Mit Namensräumen können Konflikte vermieden werden, wenn in mehreren Programmen Befehle mit identischem Namen vorkommen; mehr dazu später, wenn Sie umfangreichere Programme schreiben und solche Konflikte öfter auftreten. Vorerst reicht es zu wissen, dass mit

#include<iostream>
. . .
std::cout << "Hello World!";

die Ausgabe von Hello World! auf der Konsole erzeugt werden kann.

Dazu fehlt noch der letzte Bestandteil des Befehls: Hello World! steht im Quelltext in Anführungsstrichen und ist damit eine Zeichenkette (string); der Operator << übergibt den String an cout.

Die Anführungsstriche kennzeichnen also den Beginn und das Ende des strings. Was wäre zu tun, um Anführungsstriche in einem string darzustellen? Dazu muss das Anführungszeichen maskiert werden, indem man einen backslash davorsetzt. Zum Beispiel erzeugt

std::cout << "Hello \"World\"!";

die Konsolenausgabe

Hello "World"!

Ebenso muss der Schrägstrich (backslash ) maskiert werden durch

"\\"        // String, der einen backslash \ enthält

Und es gibt noch weitere Steuerzeichen, die ein string enthalten kann, die wichtigsten sind newline und Tabulator:

"\n"    //  Zeilenumbruch 
"\t"    // Tabulator (meist 4 Leerzeichen)

Konsoleneingaben

Im Beispiel HelloWorld.cpp wurde nur gezeigt, wie man eine Ausgabe auf der Konsole erzeugen kann; man vermutet, dass es einen ähnlichen Befehl gibt, mit dem der Nutzer einen string in die Konsole eingeben kann. Das folge Beispiel demonstriert dies.

Beispiel CinCout.cpp

/* CinCout.cpp
Programm zur Ein- und Ausgabe auf der Konsole
*/

#include <iostream>
using namespace std;

int main(){

    string greeting;        // Definition von Variablen
    string name;            // vom Datentyp string (Zeichenkette)

    cout << "Geben Sie einen Gruss ein: ";
    cin >> greeting;
    cout << "Geben Sie Ihren Namen ein: ";
    cin >> name;
    
    cout << greeting  << endl << "von" << endl << name << endl;     // Verwendung von endl für den Zeilenumbruch
    cout << greeting + "\n" + "von \n" + name + "\n";               // Verwendung von "\n" für den Zeilenumbruch
}

Bei der Ausführung des Programmes erscheinen nacheinader die beiden Aufforderungen einen Text einzugeben:

Geben Sie einen Gruss ein: Viele Grüße
Geben Sie Ihren Namen ein: Mr. X

worauf das Programm die Ausgabe erzeugt:

Viele Grüße
von
Mr. X
Viele Grüße
von
Mr. X

1. Erklärung von using namespace std;:

Anders als im Programm HelloWorld.cpp wird nach dem Inkludieren von iostream (Zeile 5) die Direktive using namespace std; (Zeile 6) verwendet:

#include <iostream>
using namespace std;

damit sind alle Befehle aus dem Namensraum std zugänglich und das umständliche

std::cout << "Hello World!";

ist nicht mehr nötig, Der Befehl cout kann jetzt direkt verwendet werden:

cout << "Geben Sie einen Gruss ein: ";

Ebenso die Befehle cin (character input für die Eingabe) und endl (Zeilenumbruch).

Diese Erklärung ist natürlich nicht sehr hilfreich, denn man wüsste gerne, welche Befehle std bereitstellt. Dies wird später erklärt und vorerst werden Sie mit cout, cin und endl auskommen.

Tip:

In kleinen Programmen mit zahlreichen Ein- und Ausgaben ist es ratsam

#include <iostream>
using namespace std;

zu verwenden.

Werden aber mehrere Namensräume verwendet, ist es besser darauf zu verzichten, um die Namensräume besser zu erkennen.

2. Deklaration von Variablen:

Da das Programm Eingaben des Nutzers entgegennehmen soll, müssen Variablen definiert werden, in denen diese Eingaben abgespeichert werden (um später auf sie für die Weiterverarbeitung zugreifen zu können). Es wurde bereits diskutiert, dass unterschiedliche Datentypen existieren und dass sie nach unterschiedlichen Regeln im Speicher abgelegt werden. Das Problem, eine Nutzer-Eingabe entgegenzunehmen, führt sofort darauf, wie die Eingabe abgespeichert werden soll.

Dazu müssen vor der Eingabe zwei Dinge vereinbart werden:

Ersteres geschieht mit der Festlegung eines Namen der Variable, in unserem Beispiel gibt es zwei Nutzer-Eingaben und daher zwei Variablen greeting und name. Letzteres indem ein Datentyp für die Variablen vereinbart werden, hier sind es jeweils Zeichenketten, für die der Datentyp string geeignet ist.

Beides zusammen nennt man die Deklaration einer Variable, was man auf Deutsch mit Ankündigung der Variable bezeichnen könnte; im Programm oben werden zwei Variablen deklariert (Zeile 10 und 11):

string greeting;
string name;

Man kann sich eine Deklaration folgendermaßen vorstellen:

In den beiden Zeilen oben werden die zwei Variablen lediglich deklariert, aber der Speicher noch nicht mit Inhalt gefüllt. Diesen Schritt nennt man die Initialisierung der Variablen. Erst bei der Initialisierung wird der Variable ein Wert zugewiesen.

3. Eingabe mit cin:

Nach diesen Vorbereitungen zur Deklaration einer Variable, ergibt sich die Bedeutung der Zeile

cin >> greeting;

von alleine: Nach der Aufforderung einen Gruß einzugeben, wird die Konsoleneingabe in der Variable greeting abgespeichert, um sie später wieder ausgeben zu können. Man sagt die Variable greeting wird initialisiert. Man kann sich die Wirkung der beiden Operatoren << und >> leicht veranschaulichen: sie symbolisieren jeweils einen Pfeil; im Fall von

cout << greeting;

wird der Inhalt der Variable greeting an cout (Konsole) weitergereicht. Und im Fall von

cin >> name;

wird der string in der Konsole an die Variable name weitergereicht.

4. String-Konkatenation

Irritierend ist vielleicht die zweite Version, wie der Gruß ausgegeben wird:

cout << greeting + "\n" + "von \n" + name + "\n";

Hier werden mehrere Zeichenketten mit dem Operator + verknüpft. Die Addition ist für den Datentyp string als sogenannte String-Konkatenation definiert und hängt einfach die Zeichenketten aneinander.

Gleichzeitig sieht man hier so etwas wie eine Punkt vor Strich-Regel: Erst werden die Zeichenketten aneinandergehängt, dann wird die resultierende Zeichenkette ausgegeben. Um dies klarer auszudrücken, hätte man auch schreiben können:

cout << (greeting + "\n" + "von \n" + name + "\n");

Da aber auf den Operatoren eine Rangfolge (Operatoren-Präzedenz) definiert ist, ist dies nicht nötig.

Aufgabe:

Untersuchen Sie was passiert, wenn eine string-Variable zwar deklariert, aber nicht initialisiert wird. Welcher Wert wird ausgegeben? (Bei fundamentalen Datentypen wird sich ein anderes Verhalten zeigen.)