Axel Rogat
Objektorientiertes Programmieren mit C++ und JAVA
 
2.2: Aufbau Kapitel 2 3.1: Vordefinierte Typen 
 
  2.3 Lexikalische Elemente  
 

2.3.1 Kommentare

Kommentare im C-Stil werden mit /* eingeleitet und mit */ beendet. Dazwischen ist eine Kette aus beliebigen ASCII-Zeichen erlaubt, die nicht */ enthält.

char x; /* Das ist ein Kommentar im alten Stil */ char y;

Kommentare dürfen nicht geschachtelt werden (auch wenn einige Compiler das optional zulassen). Das bringt Probleme mit sich, wenn sich "echte" Kommentare in einem Programmteil befinden, der nur kurzfristig auskommentiert werden soll.

if (a>b) { /* <- ab hier soll auskommentiert werden int temp=a; /* vertausche a und b */ <- semantischer Fehler: der erste a=b; Kommentar endet hier unbeabsichtigt b=temp; */ <- Syntaxfehler: zwei Operatoren * / }

Kommentare im C++-Stil beginnen mit // und reichen bis zum Ende der Zeile. Solche Kommentare sollte man immer bevorzugen und Kommentare im alten Stil benutzen, um Programmteile vorübergehend auszublenden.

char z; // Das ist ein C++ Kommentar

Längere Programmteile, die C-Kommentare enthalten, kann man mit Hilfe des Präprozessors ausblenden:

#if 0 /* // alles kein Problem! */ #endif

2.3.2 Tokens

Unter lexikalischen Einheiten verstehen wir die kleinsten Einheiten, aus denen sich ein C++-Programm zusammensetzt. Das sind nicht die ASCII-Zeichen auf der Quelltext-Ebene, sondern die "Tokens", die der erste Teil des Compilers, der Scanner, erkennt und an die weiteren Teile des Compilers weitergibt.

Das Vorgehen, das Programm zunächst in solche Teile zu zerlegen, führt nicht nur zu einer Vereinfachung der Compiler. Für unsere Zwecke ist es wichtig, daß sie es überhaupt ermöglicht, die Sprache mit einer überschaubaren Anzahl von Regeln zu definieren.

Leerzeichen, Zeilenvorschübe, etc. ("whitespace") sowie Kommentare werden vom Scanner bereits ausgesiebt und zählen bei uns nicht mehr zu den Tokens.

Zwischen den Tokens ist beliebig whitespace erlaubt. Prinzipiell kann das gesamte C-Programm (von Präprozessor-Zeilen abgesehen) in einer einzigen Zeile stehen. Whitespace muß überhaupt nur gesetzt werden, um mehrere aufeinanderfolgende Bezeichner bzw. Konstanten, die mit einer Ziffer beginnen, eindeutig voneinander zu trennen.

Wir unterscheiden vier Klassen von lexikalischen Einheiten:

Schlüsselwörter (z.B. if, else, const)
Spezialzeichen (Interpunktionszeichen und Operatoren, z.B. <= oder ... oder ->*)
Bezeichner (z.B. wert, Ziffer123, ein_ziemlich_langer_name)
Literalkonstanten (z.B. -1 oder 2.99792458e+8 oder '{' oder "Hello World!")

Lexikalische Einheiten der letzten beiden Klassen besitzen zusätzlich ein Attribut "Name", bzw. "Wert".

Schlüsselwörter:

ANSI-C besitzt folgende 32 Schlüsselwörter, die ohne (oder ohne große -- s.o.) Bedeutungsänderung auch in C++ verwendbar sind:

auto default float register struct volatile
break do for return switch while
case double goto short typedef
char else if signed union
const enum int sizeof unsigned
continue extern long static void

Durch die ersten C++-Sprachentwürfe wurde die Liste durch folgende neue Schlüsselwörter erweitert:

asm delete inline operator protected this
class friend new private public virtual

Außerdem gab es zwischenzeitlich das Schlüsselwort overload, das mittlerweile aber wieder überflüssig geworden ist. Es wird aber (inklusive der zugehörigen Syntax) oft noch akzeptiert.

In den neueren Sprachentwürfen mit Templates, Exception-Handling, Namespaces und neuen Cast-Operatoren kamen schließlich noch folgende hinzu (Warnung: insbesondere solche, die ein Unterstrich-Zeichen enthalten):

bool explicit namespace throw typename
catch export reinterpret_cast true using
const_cast false static_cast try wchar_t
dynamic_cast mutable template typeid

Spezialzeichen:

Folgende Zeichen, bzw. Zeichenkombinationen in C++ (und ANSI-C) sind eigene Tokens, sogenannte "Interpunktionszeichen":

; { } , ( ) < > : = ...

Außerdem gibt es in C++ folgende Tokens für Operatoren, die aus Sonderzeichen bestehen:

! % ^ & * - + = | ~ <
> ? / , . -> ++ -- .* ->* <<
>> <= >= == != && || *= /= %= +=
-= <<= >>= &= ^= |= ::

Die vier Symbole , < > = tauchen in beiden Gruppen auf. Folgende drei Operatoren sind gegenüber C neu: .*   ->*   ::

Wir wollen es uns hier ersparen, die Ersatz-Schreibweisen für einige Spezialzeichen aufzuführen, die für Systeme mit eingeschränktem Zeichensatz gedacht sind. Es ist dann ein spezieller Präprozessor nötig, der beispielsweise <: und :> durch {, bzw. } ersetzt. Ebenso darf man xor_eq für ^= schreiben, etc.

Bezeichner (Identifier):

Der Namensraum, der in C++ für Namen von Objekten und Typen zur Verfügung steht, wird genau durch den regulären Ausdruck

B (B | Z)*

beschrieben, wobei B=(A|...|Z|a|...|z| _) und Z=(0|...|9).

Identifier bestehen also aus Buchstaben, Unterstrichen und Ziffern, dürfen aber (zur Unterscheidung von numerischen Konstanten) nicht mit einer Ziffer beginnen. Groß- und Kleinschreibung ist signifikant, d.h. x1 und X1 sind unterschiedliche Namen. Der Standard legt tatsächlich fest, daß sie beliebig lang sein dürfen. Die meisten Compiler unterstützen dies, andere Teile der Entwicklungsumgebung, z.B. der Linker, eventuell nicht, was dann zu Problemen führen könnte.

Man sollte keine Bezeichner vergeben, die mit einem Unterstrich beginnen oder mehrere Unterstriche hintereinander enthalten. Bei parametrisierten Typen, dynamischem Binden, etc. werden oft compiler-intern Namen aus benutzervergebenen Objekt- und Typnamen zusammengebaut, die dann solche Unterstrich-Muster enthalten.

2.3.3 Literalkonstanten

Hierunter fallen Konstanten zu den in C++ schon vordefinierten Typen.

Logischwertige Typen:

Hier haben wir einen Sonderfall vor uns. Der Typ bool kann genau zwei Werte annehmen, die als Konstanten mit den Schlüsselwörtern true und false bezeichnet werden. (Der Typ ist neu gegenüber ANSI-C).

Ganzzahlige Typen:

Die verschiedenen Integer-Typen werden später genau besprochen. Integer-Literale sind in C++ immer positiv. Damit ist -1 also ein Ausdruck mit dem unären Operator - und der Konstanten 1.

  • Ganzzahlige Konstanten können im Dezimal-, Oktal- oder Hexadezimalsystem dargestellt werden (Beschreibung durch reguläre Ausdrücke):

    Dezimalkonstanten: (1|...|9)(0|...|9)*
    Sie dürfen also nicht mit einer führenden 0 beginnen!

    Oktalkonstanten: 0(0|...|7)*
    Sie müssen mit der Ziffer 0 beginnen; 0 ist also eine Oktalkonstante.
    Beispiele: 07=7, 010=8, 0100=64, 011111=4681.

    Hexadezimalkonstanten: (0x|0X)(0|...|9|A|...|F|a|...f)+
    Beispiele: 0x1=1, 0Xa=10, 0x10=16, 0Xff=255, 0xffffffff=4294967295.

  • Der genaue Integer-Typ der entstehenden Konstante hängt von ihrer Größe ab. Dezimalkonstanten sind vom "kleinsten" noch passenden Typ aus {int, long, unsigned long}. Oktal- und Hexadezimalkonstanten sind vom kleinsten passenden Typ aus {int, unsigned int, long, unsigned long }. Die Typvergabe ist damit implementationsabhängig.

    Ist die Konstante zu groß für den "größten" Integer-Typ, wird eine Fehlermeldung ausgegeben.

  • Die automatische Typvergabe kann umgangen werden, indem l oder L für long, bzw. u oder U für unsigned, bzw. beides für unsigned long direkt angehängt werden.

    1234u ist damit unsigned int (wogegen 1234 meistens automatisch int ist).

    Auf den meisten Maschinen ist 0xDADAcafe vom Typ unsigned long und hat den gleichen Wert wie 033266545376lu und 3671771902UL (und die gleiche interne Darstellung wie -623195394l).

Gleitkomma-Typen:

Gleitkommakonstanten können nur in dezimaler Schreibweise angegeben werden. Sie sind nach dem üblichen Muster 1.23E-45 aufgebaut. Auch sie sind immer positiv.

Nachkommastellen oder Exponententeil können entfallen, aber nicht beide (zur Unterscheidung von Integer-Konstanten).

Normalerweise sind diese Konstanten vom Typ double. Wenn sie vom Typ float oder long double sein sollen (weniger genau oder besonders genau, mehr dazu später), muß dies durch eine Endung f, F, bzw. l, L gekennzeichnet werden.

Integer-Konstanten können durch diese Endungen nicht zu Gleitkommakonstanten gemacht werden (1F ist nicht erlaubt, 1.F schon).

Mit Z=0|...|9 (und µ=leeres Wort) erhält man folgenden regulären Ausdruck für die Darstellung:

((Z+|Z+.Z*|.Z+) (e|E)(+|-|µ)Z+ |Z+. Z* | . Z+) (f|F|l|L|µ)

Zeichen-Typen:

Eine Zeichenkonstante besteht aus einem oder zwei in Hochkommata ' eingeschlossenen Zeichen. Nicht erlaubt sind dabei der Backslash \, das Hochkomma selber und Zeichen wie Tabulator, Zeilenvorschub, etc.

Wenn ein einzelnes Zeichen eingeschlossen wird, hat die Konstante den Typ char. Achtung: bei ANSI-C hätte sie den Typ int!

char ist ein normaler Integer-Typ, und der Wert der Konstanten als Integer-Zahl entspricht dem Code des Zeichens im verwendeten Zeichencode, meist ASCII.

Beispiel: '*' hat den Integer-Wert 42.

Spezialzeichen und die nicht erlaubten Zeichen lassen sich in Zeichenkonstanten wie folgt durch Escape-Sequenzen ersetzen:

\a Alarm (Bell) \r Return \? ?
\b Backspace \t Tabulator \' '
\f Formfeed \v vert. Tab. \" "
\n NewLine \\ \

Mit speziellen Escape-Sequenzen lassen sich beliebige Zeichencodes in Zeichenkonstanten einbauen. Dazu wird der Backslash von einer entsprechenden Oktal- oder Hexadezimal-Konstante gefolgt (diese Konstante reicht bis zum ersten Zeichen, das keine Oktal- bzw. Hexadezimalziffer ist).

Beispielsweise ist '\x2a' die gleiche Zeichenkonstante wie '*'. '\x1b' entspricht auf den meisten Systemen ESCAPE.

Zeichenkonstanten mit zwei Zeichen ('xl') sind vom Typ int und dürfen nicht etwa mit Zeichenketten verwechselt werden. Ihre Werte sind implementationsabhängig. Sie hängen meistens von der Reihenfolge der Bytes einer Mehr-Byte-Zahl im Speicher ab, z.B. 'xl'=0x6c78 oder 'xl'=0x786c.

Es gibt einen weiteren Zeichen-Typ wchar_t ("wide character"), der in vielen Compilern aber noch nicht implementiert ist. Er ist gedacht für Zeichensätze mit mehr als 256 Zeichen (z.B. Unicode, siehe dazu den JAVA-Teil). Konstanten dieses Typs beginnen mit L', z.B. L'xl'. Entsprechende Compiler behandeln zweistellige Literale mit und ohne L gleich.

Zeichenketten-Typ:

Eine Zeichenketten-Konstante ist eine (eventuell leere) in Anführungszeichen " eingeschlossene Folge von Zeichen (außer " und \) und Escape-Sequenzen (s.o.).

Beispiele: "", "Hello World!\n", "int main(void){}".

Das String-Literal darf nicht durch Zeilenumbrüche im Quelltext auseinandergerissen werden. Bei zu langen Texten gibt es eine Möglichkeit zur Aufteilung:

Folgen mehrere Zeichenketten-Konstanten aufeinander (nur ggf. durch Spaces, Zeilenvorschübe, etc. getrennt), stellen sie eine einzige, zusammenhängende Zeichenkette dar:

"St" "ri" "ng" ist die gleiche Konstante wie "String".

Zeichenketten haben den Typ char[], wobei die eigentliche Zeichenfolge mit einem Zeichen '\0' als Ende-Kennzeichen abgeschlossen wird. Das entstehende Array ist also ein Zeichen länger als die eigentliche Zeichenkette.

Zeichenketten-Konstanten haben also nicht etwa den Typ const char[], können mit Pointer-Operationen also zur Laufzeit überschrieben werden, was man natürlich meistens unterlassen sollte (je nach System Zugriffsfehler)!

 
2.2: Aufbau Startseite 3.1: Vordefinierte Typen 
 

© 1998 Axel Rogat