Kommentare in C++
Programme sind besser lesbar und auch nach langer Zeit noch nachvollziehbar, wenn du den Quellcode sinnvoll kommentierst. Außerdem kann es bei der Entwicklung umfangreicherer Programme manchmal sinnvoll bei der Fehlersuche sein, bestimmte Bereiche vorübergehend auszukommentieren. Das funktioniert, weil der Compiler Kommentare bei der Übersetzung eines Programms schlichtweg überliest und deren Inhalt ignoriert. C++ kennt zwei Arten von Kommentaren, die beide ihren Anwendungsbereich und ihre Vorteile haben.
’Oldstyle’ C Kommentare
Es gibt sie schon seit den Zeiten der Programmiersprache C. Man bezeichnet sie auch als mehrzeilige Kommentare, weil alles, was zwischen der einleitenden Zeichenkombination /* und der abschließenden Zeichenkombination */ steht, vom Compiler ignoriert wird. Trotzdem können sie auch als einzeilige Kommentare verwendet werden, wenn beide Kombinationen auf der gleichen Zeile zur Anwendung kommen. Beispiele:
/* Ich bin ein mehrzeiliger Kommentar im 'alten' Stil von ANSI C. Der Compiler ignoriert mein Geschwätz. */ /* Ich bin ein mehrzeiliger Kommentar, der wie ein Einzeiliger tut. */
’Newstyle’ C++ Kommentare
Sie sind einzeilig und typisch für C++. Alles, was innerhalb der selben Zeile hinter der Zeichenkombination // steht, wird vom Compiler ignoriert. Typische Anwendungsfälle sind Beschreibungen hinter einer Anweisung oder das Auskommentieren einzelner Zeilen. Beispiele:
// ich bin ein einzeiliger Kommentar! Der Compiler ignoriert mein Geschwätz. int i = 42 // Deklaration einer Integer-Variablen // 'z' wird auskommentiert: // int z = 4711;
Das Verschachteln von Kommentaren ist in C und C++ verboten:
/* Alle meine Entchen! /* Verboten: Ein verschachtelter Kommentar innerhalb eines Kommentars */ */
Anmerkung: Kommentare, die beschreiben, was eh schon jeder sieht, sind nicht besonders sinnvoll. Sie können sogar kontraproduktiv sein, wenn sich der Code ändert und der Programmierer vergisst, den Kommentar mit zu ändern. Aber was für den einen offensichtlich ist, ist für andere undurchsichtig. Deshalb ist sorgfältiges Abwägen gefragt.
Zu guter Letzt möchte ich noch anmerken, dass Kommentare nicht mitteilen sollten, was du machst, sondern warum du es machst.
Quelltext-Regeln und Formatierung
C++ ist eine formatfreie Sprache. Das bedeutet, dass du bei der optischen Gestaltung deiner Quelltexte mit wenigen Einschränkungen völlig frei bist. Nehmen wir einmal an, du hättest unser erstes Programm auf diese Art notiert:
#include <iostream> int main() { std::cout << "Hallo vom langweiligsten Programm der Welt!" << std::endl; return 0; }
Unübersichtlich, nicht wahr? Trotzdem ist der Quellcode aus Compilersicht legal und wird auch anstandslos übersetzt. Ob diese Art der Formatierung für das menschliche Auge angenehm und leicht lesbar ist, bedarf vermutlich keiner Diskussion.
Es existieren viele unterschiedliche Stile und Empfehlungen bezüglich der Quellcodeformatierung. Grundsätzlich sollte der Quellcode auf jeden Fall leicht lesbar und auf eine nachvollziehbare Strukturierung aufgebaut sein. Wenn du erst einmal einen persönlichen Stil entwickelt hast, der diesen Anforderungen gerecht wird – behalte ihn möglichst konsequent bei. Es wird dir auch nach Jahren noch das Verständnis für deinen Code enorm erleichtern. Hier einige Tipps:
- Rücke zusammengehörige Anweisungsblöcke und Bedingungsabfragen immer ihrer Logik nach ein. Schreibe dazu zusammengehörige Klammern stets untereinander und verwende Tabulatorsprünge, um deren Inhalte sichtbar als zugehörig zu kennzeichnen.
- Verwende Leerzeilen zwischen Funktionen und thematisch abgeschlossenen Bereichen.
- Achte auf die Zeilenlänge deiner Anweisungen – was auf einem hochauflösenden Monitor noch gut aussieht, kann auf dem Drucker ein Desaster sein. Beschränke dich auf bummelig 90 Zeichen pro Zeile und brich längere Zeilen unter Berücksichtigung syntaktischer Regeln um (siehe ‚Zeilenfortsetzung‘ im Abschnitt über den Syntax von C++).
- Verwende, wo immer nötig, Kommentare, um dein Programm nachvollziehbar zu halten. Ein Beispiel:
/* * Das Einsteigerseminar C++ * * hallo2.cpp * Das wohl langweiligste Programm der Welt, Version 2 */ #include <iostream> // Ein-/Ausgabebibliothek einbinden using namespace std; // Namensraum 'std' (Standard) einbinden int main() { // Lange Zeichenkette aufteilen mit '\': cout << "Das langweiligste Programm der Welt " \ "meldet sich zurueck mit einer sehr, " \ "sehr langen Zeichenkette!\n" << endl; // Zwei Zeichenketten nacheinander über zwei Zeilen ausgeben: cout << "Was ich noch sagen wollte:" << endl << "C++ macht Laune." << endl; return 10; // Gib den Wert 10 zurück an den Aufrufer }
Syntax-Regeln für die Sprache C/C++
Die Programmiersprache C/C++ beinhaltet gleich mehrere Sprachen/ Syntaxen:
- C-Syntax
- Präprozessor-Syntax
- Printf/Scanf Formatstring Syntax
- Terminal Emulation
- Compiler/Linker Anweisungen
In diesem Kapitel sollen zunächst nur allgemeine Eigenschaften der Sprache, der grundlegende Syntax und die Kontrollstrukturen erklärt werden. Bei vielen Erklärungen sind Code-Beispiele vorhanden. Vieles, was du hier zu lesen bekommst, wirst du erst im weiteren Verlauf des Tutorials vollständig verstehen – trotzdem ist es wichtig, von den einzelnen Begrifflichkeiten mal gehört zu haben.
Da man aus Fehlern am meisten lernt, sind zum Teil auch negative Beispiele enthalten. Für ein besseres Verständnis empfiehlt es sich, Code-Beispiele selbst nachzuvollziehen.
Zeichensatz:
Der Syntax von C nutzt die unteren 128 Zeichen des ASCII Zeichensatzes. Da UTF-8 in den ersten 128 Zeichen deckungsgleich zu ASCII ist, kann auch dieser zur Erstellung des Source Codes genutzt werden. Zeichen außerhalb dieses gültigen Zeichensatzes können folglich nur in Strings oder Kommentaren vorkommen. Tatsächlich empfiehlt sich die Verwendung eines auf UTF-8 kodierten Zeichensatzes, da dieser bei der Übernahme eines Quelltexts zwischen verschiedenen Betriebssystemen kaum Probleme aufwirft.
Namenskonventionen
Folgende Regeln gelten bzgl. der Benennung von Variablen und Funktionen:
- Variablen und Funktionsnamen können aus Buchstaben, Zahlen und dem Unterstrich bestehen. Sie müssen mit einem Buchstaben oder einem Unterstrich beginnen
- Seit C95 können weitere Zeichen aus dem Universal Coded Character Set (UCS) genutzt werden [C11 D.1]
- C/C++ ist Case sensitiv, d.h. es wird zwischen Groß- und Kleinbuchstaben unterschieden
- Schlüsselwörter können nicht für Variablen/Funktionsnamen/Datentypen genutzt werden
Namenskonventionen von Library-Funktionen:- In C (und C++) sind Schlüsselwörter und Standardlibrary-Funktionen zumeist in Kleinbuchstaben geschrieben. In der C-Standardlibrary werden oftmals verkürzte Ausdrücke wie z.B. isalnum() (zum Testen ob ein Zeichen ein Buchstaben oder ein Digit ist) und in C++ der Unterstrich als Worttrenner (z.b. out_of_range) genutzt.
- Makros werden per Konvention in GROSSBUCHSTABEN und ggf. mit Unterstrich als Worttrenner geschrieben.
- Namen beginnend mit doppelten Unterstrich oder beginnend mit einem Unterstrich gefolgt von einem Großbuchstaben (z.B. __LINE__ _Reserved) sind für den Compiler und der Standard-C-Library vorbehalten und sollten im eigenen Programm nicht benutzt werden.
Zeilenfortsetzung
Mit dem Backslash Operator (gefolgt von einem Zeilenende) kann eine Zeile in der nächsten Zeile fortgesetzt werden. Der Compiler löscht das \-Zeichen mit anschließendem Zeilenende und ersetzt dies durch nichts. Dies ist insbesondere bei Anweisungen notwendig, die am Ende der Zeile abgeschlossen sein müssen (z.B. Strings, Makros).
Beispiele:
char str1[]="Strings müssen am Ende per Anführungszeichen abgeschlossen sein so dass dies ein Fehler ist"; char str2[]="Dies\ ist ein Test"; //Vorsicht, führende Leerzeichen vor 'ist' //bleiben erhalten! /*mehrzeiliges Makro*/ #define MAX(a,b) (a>b?\ b: \ a) //Dies ist ein Zeilenkommentar \ welcher in dieser Zeile fortgesetzt wird /\ * dies ist ein Blockkommentar*\ / //hinter der Zeilenfortsetzung darf nur ein CR/LF folgen #define MAX2(a,b) \ //so dass hier kein Kommentar folgen darf a>b?a:b
Hinweis:
- Innerhalb von Char-Literatoren und Strings wird ‚\‘ als Escape-Operator genutzt, welche das ‚\‘ und ein oder mehrere folgende Zeichen ersetzt. Daher darf hinter Backslash als Zeilenfortsetzungszeichen kein weiteres Zeichen folgen.
Gültigkeit und Sichtbarkeit von Variablen
Vorrangig in der objektorientierten Programmierung werden mit Namensräumen Objekte und deren Methoden/Attribute in einer Art Baumstruktur strukturiert. Dies ermöglicht eine eindeutige Ansprache von Variablen/Objekte, aber auch eine doppelte Verwendung von Methoden-/Attributnamen in unterschiedlichen Namensräumen. Ergänzend zu den Namensräumen kann mit public/private/proteced eine Zugriffsbeschränkung von Methoden/Attributen definiert werden.
Die Programmiersprache C unterstützt keinen Namensraum. Zugriffsmodifikatoren werden indirekt über Header-Dateien getätigt. Hinsichtlich der Gültigkeit/Sichtbarkeit unterscheidet C folgende Bereiche:
- Funktionen (Function Scope)
- Datei (File Scope)
- Block (Block Scope)
- Funktionsparameter in Prototypen (Function Prototype Scope)
Innerhalb eines Gültigkeitsbereiches dürfen Variablen-/Funktions-/Datentypnamen nicht doppelt genutzt werden.
In der Programmiersprache C++ sind weitere Scope wie z.B. Class Scope, Enumationation Scope und ergänzend das Konzept von Namensräumen vorhanden (Beschreibung folgt).
Funktionsweite Sichtbarkeit
Eine Label-Definition (als Sprungmarke für die goto-Anweisungen) erfolgt immer mit funktionsweiter Sichtbarkeit/Gültigkeit.
Dateiweite Sichtbarkeit
Erfolgt eine Funktion-/Variablen-/Datentyp-Definition außerhalb eines Block-Scopes oder von Funktionsparameter, so sind diese innerhalb der gesamten Datei und bei Funktionen/Variablen ergänzend Projektweit (für alle Objektdateien) sichtbar/gültig (= global). Alle globalen Funktionen/Variablen können von allen Dateien aus genutzt/zugegriffen werden (sofern sie zuvor deklariert wurden).
Beispiel 1:
// Deklaration von func(), // welche in Datei2.c definiert wird extern void func(void); // Definition der Variablen global int global=0; int main(void) { func(); global++; return 0; }
Beispiel 2:
// Deklaration von global, // welche in Datei1.c definiert wird extern int global; // Definition der Funktion func() void func(void) { global++; }
Wird das Schlüsselwort ’static‘ der Variablen/Funktionsdefinition vorangestellt, so wird die Sichtbarkeit/Gültigkeit auf Dateiweit eingeschränkt. Variablen/Funktionen können nur innerhalb der (Objekt-) Datei genutzt werden und sind für anderen (Objekt-)Dateien unsichtbar.
Beispiel 1:
// Dateiweite Sichtbarkeit von var1 static int var1; int main(void) { var1++; return 0; }
Beispiel 2:
// Dateiweite Sichtbarkeit von var1 static int var1; void func(void) { static int var2; // Vorsicht, static // hat hier eine andere Bedeutung var1++; }
Projektweite Gültigkeit
bedeutet insbesondere, dass keine doppelten Benennung von Variablen/Funktionen/Datentypen innerhalb des gesamten Projektes erlaubt sind, d.h. dass alle Variablen/Funktionennamen über alle Dateien/Librarys eindeutig sein müssen.
Beispiel 1:
Datei 1:
// datei_1.cpp #include <stdio.h> int a=1; int main(int argc,char *argv[]) { printf("Hello World %d",a); void dummy(void); //Deklaration von Dummy dummy(); return 0; }
Datei 2:
// datei_2.cpp #include <stdio.h> int a=7; void dummy(void) { printf("Hello Again %d\n",a); }
Versuche, die beiden Dateien zusammen zu compilieren:
g++ -Wall datei_1.cpp datei_2.cpp -o beipiel1
Der Linker meldet beim Zusammenfügen der Objekt-Dateien, dass die Variable a bereits woanders definiert sei (multiple definition of ‚a‘):
gcc -Wall datei1.cpp -o beispiel1 datei_1.cpp:4:5: Fehler: Redefinition von »int a« 4 | int a=1; | ^ In Datei, eingebunden von datei_1.cpp:3: datei_2.cpp:3:5: Anmerkung: »int a« wurde bereits hier definiert 3 | int a=7; | ^
Beispiel 2:
// beispiel2.cpp g++ -Wall beispi #include <stdio.h> // fuer printf() size_t stderr int printf=7; int main() { printf("%d\n", printf); return 0; }
Hier meldet der Compiler eine Fehlermeldung, da das Symbol printf zum einen als Variable und zum anderen als Funktion (innerhalb der inkludierten Datei stdio.h beschrieben) genutzt wird (‚printf‘ redeclared as different kind of symbol):
g++ -Wall beispiel2.cpp beispiel2.cpp:2:5: Fehler: »int printf« als andere Symbolart redeklariert 2 | int printf=7; | ^~~~~~ In Datei, eingebunden von beispiel2.cpp:1: /usr/include/stdio.h:356:12: Anmerkung: vorherige Deklaration von »int printf(const char*, ...)« 356 | extern int printf (const char *__restrict __format, ...); | ^~~~~~ beispiel2.cpp: In Funktion »int main()«: beispiel2.cpp:5:12: Warnung: Format »%d« erwartet Argumenttyp »int«, aber Argument 2 hat Typ »int (*)(const char*, ...)« [-Wformat=] 5 | printf("%d\n", printf); | ~^ ~~~~~~ | | | | int int (*)(const char*, ...)
Blockweite Sichtbarkeit
Erfolgt eine Funktion-/Variablen-/Datentyp Definition innerhalb einer Funktion, als Funktionsparameter oder eines Blockes, so sind diese nur innerhalb des Blockes sichtbar/gültig (= Lokale Variable). Blöcke können verschachtelt sein, so dass das bei identischer Namensgebung innere Definitionen Vorrang haben. Ebenso haben Blockdefinitionen Vorrang vor Datei-/Projektdefinitionen (überdecken diese):
blockweit1.cpp
int main(void) { int var2; //var2 ist nur innerhalb //von main() sichtbar struct xyz //Datentyp ist nur innerhalb {int x,y,z;};//von main() //sichtbar/gültig extern void func(void); //Deklaration //ist nur innerhalb von //main() sichtbar/gültig func(); } void foo(void) { struct xyz var_xyz; //Fehler, da //Datentypedefinition hier nicht mehr //gültig ist! func(); //Fehler, da Deklaration //hier nicht mehr gültig ist }
blockweit2.cpp
#include <stdio.h> void func(void) { int var2=1; //var2 ist //nur innerhalb von //func() sichtbar { int var2=2; //var2 ist nur innerhalb //dieses Blockes sichtbar printf("%d\n",var2); } printf("%d\n",var2); }
Auch dieses aus zwei Dateien bestehende Programm wird vom Compiler angemeckert:
g++ -Wall blockweit1.cpp blockweit2.cpp -o blockweit blockweit1.cpp: In Funktion »int main()«: blockweit1.cpp:2:7: Warnung: Variable »var2« wird nicht verwendet [-Wunused-variable] 2 | int var2; //var2 ist nur innerhalb | ^~~~ blockweit1.cpp: In Funktion »void foo()«: blockweit1.cpp:14:13: Fehler: Aggregat »foo()::xyz var_xyz« hat unvollständigen Typ und kann nicht definiert werden 14 | struct xyz var_xyz; //Fehler, da | ^~~~~~~ blockweit1.cpp:18:2: Fehler: »func« wurde in diesem Gültigkeitsbereich nicht definiert 18 | func(); //Fehler, da Deklaration | ^~~~
Soviel zunächst zu Syntaxregeln in C/C++. Gräme dich nicht, wenn du gerade an ‚Bahnhof‘ und ‚Züge‘ denkst – wir werden im weiteren Verlauf des Tutorials an den jeweiligen Stellen wiederholend am praktischen Beispiel auf die Syntaxregeln eingehen.
[Inhaltsverzeichnis] | [zurück] | [vorwärts]
[…] C++ Tutorial – Kommentare und Quelltext-Formatierung […]
[…] C++ Tutorial – Kommentare, Quelltext-Formatierung und Syntax-Regeln […]