C-Tutorial (C oder C++, Vorwort, Installation, Kapitel 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

Kommentare, Operatoren

In diesem Kapitel:

4.1. Kommentare

Was denken Sie, ist schwerer: Quellcodes lesen oder selber welche schreiben? Was ist leichter, Bücher zu lesen oder diese selber zu schreiben? Bei letzterem werden Sie wohl sagen, es sei leichter, diese zu lesen. Aber bei Quellcodes? Erfahrungsgemäß kann ich sagen, dass ich nach spätestens einem halben Jahr fast nicht mehr weiß, wozu mein eigener Quellcode gut ist. Ganz so schlimm ist es nicht, aber ich brauche dann wieder Einarbeitungszeit, trotz selbstgeschriebenem Code.

Das Endergebnis sieht man natürlich, wenn man das Programm ausführt. Aber wozu sind einzelne Zeilen des Quellcodes gut? Zwar werden Sie mit großer Sicherheit wissen, was jede Zeile für sich macht, jedoch meistens nicht, wozu sie diese an der Stelle verwendet haben (der Sinn dahinter, also warum?). Wenn Sie nicht mehr wissen, was eine Anweisung (Funktion) macht, können Sie in einem Buch nachschlagen, in einem Tutorial wie diesem oder einer Online-Referenz nachlesen. Aber gegen das Problem des WESHALB?

C schafft hier Abhilfe, denn C erlaubt es, im Quellcode Kommentare einzufügen (andere Programmiersprachen können das natürlich genauso). Kommentare sind ganz normaler Text, der in den Quellcode geschrieben und vom Compiler ignoriert wird. Kommentare müssen extra gekennzeichnet werden. Am besten geben Sie in Ihren Kommentaren Antworten auf die WESHALB-Fragen. Beantworten Sie sich selbst die Fragen, die Sie sich später stellen werden. Klingt komplizierter als es ist. Im Grunde ein simples Wozu ist das gut?. Also, wenn Sie sich fragen: Wozu? Im Kommentar steht dann die Antwort! Ach so.

Es macht Sinn, alle paar Zeilen einen kurzen Kommentar einzufügen. Nicht übertreiben (kein Roman) aber auch nicht zu faul sein. Nicht kommentieren ist nicht cool und macht aus Ihnen auch keinen besseren Programmierer. ;-) Im Gegenteil, Kommentieren erhöht die Qualität Ihres Quellcodes und macht später Freude (oder verringert Frust) beim Wieder-Lesen.

Ein Kommentar wird wie folgt gekennzeichnet:

/* Kommentar */

Dieser kann sich auch über mehrere Zeilen erstrecken:

/* Wie
   eben
   dieser
   Kommentar
   hier! */

Der Text, der zwischen /* und */ steht, wird vom Compiler als Kommentar angesehen und ignoriert. Im ersten Beispiel ist das der Text Kommentar. Kommentare werden nicht mitcompiliert. Ob der Text da steht oder nicht, ist dem Compiler vollkommen egal (aber nicht für Sie als Programmierer!). Die ausführbare Datei wird nicht größer durch Kommentare in der Quelldatei.

Wichtig ist, dass Sie das abschließende */ nicht vergessen! Vergessen Sie dieses nämlich, reicht der Kommentar über den gesamten restlichen Quelltext, und alle kommenden Programmierzeilen werden als Kommentar angesehen. Der Compiler meldet dann natürlich (mindestens) einen Fehler.

4.2. Operatoren

Testen Sie einmal folgendes Programm. Das simple Programm beherrscht die vier Grundrechenarten und demonstriert die Verwendung arithmetischer Operatoren:

#include <stdio.h>
 
int main()
{
  int summand1, summand2, minuend, subtrahend, faktor1, faktor2;
  double dividend, divisor;
 
  printf ("Summanden eingeben: ");
  scanf ("%d %d", &summand1, &summand2);
 
  printf ("Minuend und Subtrahend eingeben: ");
  scanf ("%d %d", &minuend, &subtrahend);
 
  printf ("Faktoren eingeben: ");
  scanf ("%d %d", &faktor1, &faktor2);
 
  printf ("Dividend und Divisor eingeben: ");
  scanf ("%lf %lf", &dividend, &divisor);
 
  printf ("%d + %d = %d\n", summand1, summand2, summand1 + summand2);
  printf ("%d - %d = %d\n", minuend, subtrahend, minuend - subtrahend);
  printf ("%d * %d = %d\n", faktor1, faktor2, faktor1 * faktor2);
  printf ("%.2f / %.2f = %f\n", dividend, divisor, dividend / divisor);
 
  return 0;
}

Neu für Sie sind das Formatelement %lf sowie die Anweisungen mit den arithmetischen Operatoren. Das %lf ist schnell erklärt: Es ist nichts anderes als das Formatelement %f, nur in Verwendung mit einer Variable vom Typ double. scanf() trifft zwischen float und double - im Gegensatz zu printf() - eine Unterscheidung. Der Vorteil von double liegt in einer höheren Genauigkeit, d.h. mehr Nachkommastellen werden (korrekt) gespeichert. Dafür benötigt double mehr Speicher.

Betrachten wir nun den Teil aus printf(), der für die Addition sorgt:

summand1 + summand2

Zeichen wie + bezeichnet man als Operatoren. Die Daten, mit denen der Operator "arbeitet", nennt man Operanden. Der +-Operator summiert die beiden Operanden (das sind summand1 und summand2 im Beispiel). Wir unterscheiden zwischen unären, binären und ternären Operatoren. Ein unärer Operator hat nur einen, ein binärer zwei und ein ternärer drei Operanden. Es existiert nur ein einziger ternärer Operator, diesen werden Sie im Zusammenhang mit Kontrollstrukturen kennen lernen.

Das Ergebnis des Ausdrucks summand1 + summand2 verwendet die Funktion printf() dann im obigen Beispiel zur Ausgabe. Das Gleiche mithilfe einer Zuweisung wäre etwa:

ergebnis = summand1 + summand2;

Wir hätten das auch auf diese Weise erledigen und mit printf() den Wert von ergebnis ausgeben können. Ich habe mich im Beispiel aber für die kürzeste Variante entschieden. Anmerkung am Rande: Die Variable ergebnis müsste natürlich dann auch am Anfang des Anweisungsblocks deklariert (und am besten mit 0 initalisiert) werden.

Eine weiterer Ausdruck war:

dividend / divisor

Hierbei muss beachtet werden, dass beide Operanden von einem Gleitkommatyp sind! (Später werden Sie sehen, wie man mittels cast-Operator auch einen anderen Datentyp verwenden kann.)

Der Zuweisungsoperator heißt übrigens auch nicht bloß zum Spaß so. ;-) Er gehört zur Gruppe der binären Operatoren (ein Wert links, einer rechts davon; also zwei Operanden).

Die nächste Tabelle zeigt alle Operatoren in C:

Operator Klassifizierung Kardinalität Bedeutung Priorität Assoziativität Beispiel
[] Zugriffsoperator / Indizierung 15 von links array[0]
() Auswertungsoperator / Funktionsaufruf 15 von links funktion(4)
. Zugriffsoperator / Strukturzugriff 15 von links strukt->a
-> Zugriffsoperator / Indirektzugriff 15 von links dyn_strukt->a
++ Zuweisungsoperator unär Inkrement (post) 15 von links a++
-- Zuweisungsoperator unär Dekrement (post) 15 von links a--
++ Zuweisungsoperator unär Inkrement (pre) 14 von rechts ++a
-- Zuweisungsoperator unär Dekrement (pre) 14 von rechts --a
* Zugriffsoperator unär Dereferenzierung 14 von rechts *zeiger
& Zugriffsoperator unär Adressoperator 14 von rechts &variable
sizeof Datentypoperator unär Größe eines Typs 14 von rechts sizeof(a)
() Datentypoperator unär Cast-Operator 14 von rechts (int) x
+ Arithmetischer Operator unär pos. Vorzeichen 14 von rechts +a
- Arithmetischer Operator unär neg. Vorzeichen 14 von rechts -a
! Logischer Operator unär Verneinung 14 von rechts !(a)
~ Bitoperator unär Einerkomplement 14 von rechts ~a
* Arithmetischer Operator binär Multiplikation 13 von links a * b
/ Arithmetischer Operator binär Division 13 von links x / y
% Arithmetischer Operator binär Modulo-Division 13 von links a % b
+ Arithmetischer Operator binär Addition 12 von links a + b
- Arithmetischer Operator binär Subtraktion 12 von links a - b
<< Bitoperator binär Bitverschiebung nach links 11 von links a << 2
>> Bitoperator binär Bitverschiebung nach rechts 11 von links a >> 2
< Vergleichsoperator binär kleiner als 10 von links a < b
> Vergleichsoperator binär größer als 10 von links a > b
<= Vergleichsoperator binär kleiner oder gleich 10 von links a <= b
>= Vergleichsoperator binär größer oder gleich 10 von links a >= b
== Vergleichsoperator binär gleich 9 von links a == b
!= Vergleichsoperator binär ungleich 9 von links a != b
& Bitoperator binär bitweises Und (AND) 8 von links a & b
^ Bitoperator binär bitweises Entweder-Oder (XOR) 7 von links a ^ b
| Bitoperator binär bitweises Oder (OR) 6 von links a | b
&& Logischer Operator binär logisches Und 5 von links a && b
|| Logischer Operator binär logisches Oder 4 von links a || b
?: Vergleichsoperator ternär Bedingte Anweisung 3 von links a > b ? 1 : 0
= Zuweisungsoperator binär Zuweisung 2 von rechts a = b
+= Zuweisungsoperator binär Addition + Zuweisung 2 von rechts a += b
-= Zuweisungsoperator binär Subtraktion + Zuweisung 2 von rechts a -= b
*= Zuweisungsoperator binär Multiplikation + Zuweisung 2 von rechts a *= b
/= Zuweisungsoperator binär Division + Zuweisung 2 von rechts a /= b
%= Zuweisungsoperator binär Modulodiv. + Zuweisung 2 von rechts a %= b
&= Zuweisungsoperator binär logisches Und + Zuweisung 2 von rechts a &= b
|= Zuweisungsoperator binär logisches Oder + Zuweisung 2 von rechts a |= b
^= Zuweisungsoperator binär logisches Exklusiv-Oder + Zuweisung 2 von rechts a ^= b
<<= Zuweisungsoperator binär Linksshift + Zuweisung 2 von rechts a <<= b
>>= Zuweisungsoperator binär Rechtsshift + Zuweisung 2 von rechts a >>= b
, Auswertungsoperator binär sequentielle Auswertung 1 von links a , b

Falls Sie sich gefragt haben, ob es viele Operatoren in C gibt, wäre das damit auch beantwortet. ;-)
Bis Sie am Ende dieses Tutorials angelangt sind, werden Sie mit vielen Operatoren davon zu tun gehabt haben. Betrachten Sie einmal folgende Anweisung:

ergebnis = 3 + 3 * 3;

Was denken Sie, ist das Ergebnis? 18 oder 12? Schreiben Sie ein kleines C-Programm, das das Ergebnis ausgibt. Der Ausdruck enthält 3 Operatoren: den Zuweisungsoperator =, den +-Operator sowie den *-Operator. Da der *-Operator eine höhere Priorität + hat, kommt dieser zuerst zum Zug. Das heißt, es wird zuerst multipliziert und erst dann addiert - also auch mathematisch korrekt (Punkt- vor Strichrechnung). 3 * 3 = 9 + 3 = 12. ergebnis erhält also den Wert 12.

Ein paar Worte zu den einzelnen Operatoren:

[]
Hiermit greifen Sie auf ein Element eines Arrays zu. Näheres dazu finden Sie im Kapitel 9 dieses Tutorials.

()
Dieser Operator wird beim Funktionsaufruf benötigt. Zwischen den beiden Klammern können Argumente stehen, also Werte, die an die Funktion übergeben werden. Daneben hat () auch noch eine andere wichtige Bedeutung: Sie können Ausdrücke klammern, das heißt zusammenfassen. Angenommen, Sie wollen auf das obere Beispiel bezogen, zuerst 3 + 3 berechnen lassen, und erst dann mit 3 multiplizieren (sodass am Ende 18 herauskommt). Das ginge wie folgt:

ergebnis = (3 + 3) * 3;

Da () eine höhere Priorität als der arithmetische Operator * hat (siehe Tabelle!), geht () vor. Es wird also zuerst der Klammerausdruck berechnet und erst dann mit 3 multipliziert.

. ->
Diese beiden Operatoren werden Sie im Zusammenhang mit Strukturen kennenlernen, um auf diese zuzugreifen. Näheres dazu erfahren Sie in Kapitel 13.

++ --
Bei den Kontrollstrukturen (Kapitel 6, 7, 8) werde ich ich die Post- und Prä-Version beider Operatoren ausführlich behandeln. Soviel sei gesagt: ++ erhöht den Wert einer Variable um 1, und -- verringert ihn um 1.

* &
Diese beiden Operatoren werden Sie in Kapitel 11 kennen lernen, wenn es um Zeiger geht. & liefert die Adresse einer Variable (zeigt also, wo sich eine Variable im Speicher befindet) und * dereferenziert einen Zeiger, greift also auf den Wert zu, auf den dieser zeigt.

sizeof
In Kapitel 2 habe ich darauf aufmerksam gemacht, dass Datentypen je nach System unterschiedlich viel Speicher benötigen können. Mit sizeof können Sie es herausfinden:

printf ("a benötigt %d Bytes an Speicher.", sizeof(a));

sizeof lässt sich auch direkt auf einen Datentyp anwenden. Zum Beispiel:

printf ("Variablen vom Datentyp int benötigen %d Bytes.", sizeof(int));

() als Cast-Operator:
Die Klammern sind zwar die Gleichen wie bei einem Funktionsaufruf, werden hier aber als Typecast angewandt. Der Compiler erkennt von selbst, ob ein Cast vorliegt. Um Casts (= explizite Datentypumwandlungen) geht es in Kapitel 10.

+ - * / % als binäre Operatoren:
Diese arithmetischen Operatoren kennen Sie ja bereits vom Beispiel mit den Grundrechenarten.

+ - als unäre Operatoren:
Wenn Sie vor eine Variable ein Minuszeichen stellen, so verwenden Sie den --Operator in seiner unären Version. Ihr Compiler erkennt das daran, dass es nur einen Operanden (das ist die Variable, der Sie das Minus voranstellen) gibt.

...
int wert = 2, negativerWert = 0;
...
negativerWert = -wert;   /* -2 zuweisen */

Die Variable negativerWert speichert nach der Anweisung den Wert -2. Häufiger ist die Verwendung des unären - bei Wertangaben:

i = -2;   /* hier das - wegzulassen, wäre fatal */
j = +2;   /* das + ist überflüssig */

! && ||
Diese Operatoren werden bei den Kontrollstrukturen ausgiebig behandelt.

~ << >> & ^ |
Dabei handelt es sich um Bitoperatoren. Diesen begegnen Sie vor allem in hardwarenahen Anwendungen. Sie sind nicht Thema dieses Tutorials.

< > <= >= == !=
Mit diesen logischen Operatoren werden Sie in den Kapiteln über Kontrollstrukturen (6, 7, 8) zu tun haben.

?:
Der ternäre Operator ist soviel wie eine verkürzte if-else-Anweisung und ist Thema in Kapitel 7.

= += -= *= /= %= &= |= ^= <<= >>=
Wenn Sie mit derselben Variable, der Sie einen Wert zuweisen wollen, eine Berechnung durchführen möchten, können Sie sich die Arbeit etwas vereinfachen. Beispiel:

ergebnis = ergebnis + a;

ergebnis und a werden addiert und das Endergebnis wieder in ergebnis gespeichert. Das lässt sich in C kürzer schreiben:

ergebnis += a;

Zu dem Wert in ergebnis wird dann a hinzu-addiert. Ähnliches gilt für -= *= /= %= &= |= ^= <<= >>= .

,
Im Zusammenhang mit der Variablendeklaration haben Sie diesen Operator bereits kennen gelernt. Durch Beistriche getrennt, können (u.a.) mehrere Variablen und Konstanten auf einmal deklariert (und definiert) werden.

Lassen Sie sich von der kurzen Abhandlung oder der Vielzahl der Operatoren an dieser Stelle nicht entmutigen. Die Operatoren werden Sie nach und nach kennen lernen.

Vorheriges Kapitel Nächstes Kapitel