Die Operatoren in C sind:
Dabei haben (), [], -> und . aus der obersten Gruppe die höchste Priorität vor den Operatoren aus der zweiten Gruppe usw.; innerhalb jeder Gruppe haben alle Operatoren gleichen Rang.
Die Auswertungsreihenfolge läßt sich wie gewohnt mit Klammerung ( ...) übersteuern.
Einige Operatoren tauchen mehrmals auf (z.B. +). Ihre Bedeutung richtet sich dann danach, ob sie sich im gegebenen Zusammenhang auf einen (monadischer Operator) oder auf zwei (diadischer Operator) Operanden beziehen.
Die Operatoren bedeuten im einzelnen:
Der Operand ist nur zulässig für Felder und Zeiger; der Index muß ein ganzzahliger Ausdruck sein.
union
s
ist in struct und union erklärt.
Der linke Operand muß eine struct oder union sein; der rechte ein zugehöriges Element.
Oft kommt es vor, daß ein Zeiger, z.B. p, auf eine Struktur zeigt. Angenommen, die Struktur enthält ein Element mit dem Namen el, dann müßte man zum Zugriff auf dieses Element schreiben:
(*p).el
Hier ist die Klammerung nötig, da der Punktoperator höhere Priorität hat als der Verweis mit *, obwohl ja erst der Verweis und dann die Auswahl durchgeführt werden muß.
Deshalb gibt es als abkürzende Schreibweise den Operator ->. Das Beispiel kann man also auch so schreiben:
p->elAnsonsten sind beide Formen gleichwertig.
Letzterer ist nur zulässig für ganzzahlige Operatoren.
i = 5; j = ++i;weist an i erst den Wert 5 zu und erhöht i dann auf 6; an j wird der schon erhöhte Wert, also 6 zugewiesen.
i = 5; j = i++;dagegen weist an j noch den alten Wert, also 5, zu und erhöht i ebenfalls auf 6.
Zulässig für alle veränderbaren Grunddatentypen (also nicht für Feldnamen, nicht für Strukturen, Felder etc.).
Hat man die Vereinbarung
double *p;dann meint p den Wert des Zeigers, also eine Adresse. *p dagegen liefert den double-Wert, auf den p zeigt.
Natürlich nur zulässig für Zeiger.
double d;meint d den Inhalt von d, während &d die Speicherstelle liefert, an der d zur Laufzeit liegt.
Zulässig für alle Objekte im Speicher, wie Variablen, Feldelemente, Funktionen, über Zeiger adressierte Objekte.
Nicht zulässig für Registervariablen und Bitfelder.
int i; double d; i = 1; d = (double)i;verwandelt den Ausdruck i, der den Typ int liefert, in einen double-Wert und weist diesen an d zu.
In diesem Fall hätte man sich die explizite Umwandlung mit dem cast (double) auch sparen können, weil bei der Zuweisung
d = i;genau die gleiche Umwandlung automatisch vorgenommen wird.
Ein gecasteter Ausdruck ist nie ein Objekt, dem etwas zugewiesen werden kann (,,lvalue``).
Für Gleitkommaoperanden gibt es die Funktion fmod(); siehe fmod().
Zulässig für zwei arithmetische Operanden oder für einen ganzzahligen Operanden und einen Zeiger (oder Feld).
Im letzten Fall wird der ganzzahlige Operand implizit mit der Speichergrösse des Objekts multipliziert, auf das der Zeiger verweist.
Dabei ist der Ausdruck *(Zeiger+ganze_Zahl) identisch mit Zeiger[ganze_Zahl], aber (wegen der Kommutativität der Addition) auch mit *(ganze_Zahl+Zeiger). Deshalb kann man in C ungestraft den Ausdruck Zeiger[ganze_Zahl] ersetzen durch den gleichwertigen Ausdruck ganze_Zahl[Zeiger].
Die Anzahl der zu verschiebenden Stellen darf nicht größer sein als die binäre Stellenzahl des jeweiligen Operanden.
Beide Operanden müssen ganzzahlig sein; der rechte muß positiv sein!
Ein solcher Vergleich liefert einen Wert vom Typ int, und zwar 1, wenn die zugrundeliegende Bedingung erfüllt ist; sonst 0.
Achtung! Der Vergleich auf Gleichheit darf nicht mit dem Operator =, sondern nur mit == durchgeführt werden! Einer der häufigsten und manchmal gemeinsten Fehler in C ist das einfache Gleichheitszeichen in diesem Fall. Die Gemeinheit kommt daher, daß der Compiler in den meisten Fällen22stillschweigend die Zuweisung akzeptiert und keinen Fehler erkennt. Die Zuweisung selbst liefert wieder einen Wert, der dann anstelle des logischen Ausdrucks auf ungleich 0 verglichen wird.
Bei Gleitkommazahlen ist der Vergleich auf Gleichheit oder Ungleichheit gefährlich, wenn die Werte vorher berechnet wurden. Durch unbeabsichtigte Rundungsfehler wird das exakte Ergebnis nur in den seltensten Fällen getroffen und ein anschliessender Vergleich ist deshalb mit Vorsicht zu genießen. Anstatt
double a, b; a = ...; b = ...; if( a==b ) ...
sollte man stattdessen lieber eine Konstruktion der Art
#include <math.h> ... double a, b; a = ...; b = ...; if( fabs( a, b )<1.0E-10 ) ...
verwenden.
Nur zulässig für ganzzahlige Operatoren.
Nur zulässig für ganzzahlige Operatoren.
Nur zulässig für ganzzahlige Operatoren.
C hat eine nette Eigenart: Vergleiche mit && und || werden prinzipiell von links nach rechts bewertet, und zwar nur soweit, bis das Ergebnis des (Teil-) Ausdrucks feststeht (short circuit evaluation). Dies sorgt für eine schnellere Auswertung zur Laufzeit. Andererseits kann es vorkommen, daß dadurch Ausdrücke nicht bewertet werden. Insbesondere Funktionen mit Seiteneffekten fallen manchmal unangenehm auf, wenn man nicht damit rechnet, daß der zweite Teil eines Und- bzw. Oder-Vergleichs gar nicht ausgeführt wird.
Sowohl der Typ des zweiten als auch des dritten Operanden muß in der Umgebung des ?:-Operators verwendet werden können.
Man kann damit das Programmstück
if( i<j ) a = 123; else a = 456;ersetzen durch:
a = i<j ? 123 : 456;
Ein schönes Beispiel dafür sind die MAX- und MIN-Makros, die man sich so definieren kann:
#define MIN(a,b) ( (a)<(b) ? (a) : (b) ) #define MAX(a,b) ( (a)>(b) ? (a) : (b) )
Siehe dazu auch Der Praeprozessor.
Mit diesem zusätzlichen Ergebnis einer Zuweisung ist es ähnlich wie mit dem Rückgabewert einer Funktion: man kann es verwenden, man muß aber nicht.
Der zugewiesene Wert muß natürlich entweder den gleichen Typ haben wie das Objekt, an das zugewiesen wird, oder zumindest entsprechend konvertiert werden können.
Beispiel:
i = ( j = 5 ) + 1;weist an j den Wert 5 zu; dann wird zu dem zugewiesenen Wert nochmals 1 addiert und die resultierende 6 in die Variable i geschrieben. Die Zuweisung an i liefert den Wert 6 als Ergebnis, der dann nicht weiter verwendet wird.
Die Operatoren %=, &=, ^=, |=, «= und »= sind wiederum nur für ganzzahlige Operanden zulässig.
Beispielsweise kann man
i = i + 5;ersetzen durch:
i += 5;Besonders günstig ist dies, wenn der Wert auf der linken Seite ein komplizierter Ausdruck ist, der entsprechend Rechenzeit zur Auswertung benötigt.
Beispielsweise soll in einer Quelldatei eine Variable _fehler als static vereinbart sein. Auf sie kann man von anderen Quelldateien aus nur mit Hilfe einer Funktion fehlerp() zugreifen, welche die Adresse der Variablen liefert:
static int _fehler = 0; int *fehlerp( void ) { return &_fehler; }Will man die Variable jetzt von einem anderen Quelltext aus um den Wert 10 erhöhen, dann könnte man das so tun:
*fehlerp() = *fehlerp() + 10;Dadurch werden aber zwei Funktionsaufrufe ausgeführt!
Schneller läuft mit Sicherheit diese Programmversion:
*fehlerp() += 10;
Beispiel: Der Ausdruck
ergebnis = sin(x) + sqrt( sin(x) );muß zweimal die Funktion sin() aufrufen, die erfahrungsgemäß viel Zeit vertrödelt. Mit dem Ausdruck
tmp = sin(x), ergebnis = tmp + sqrt( tmp );erreicht man das Gleiche23.
Der Kommaoperator garantiert zwar, daß zuerst der linke Operand und dann der rechte ausgewertet wird. Dies hat jedoch nichts mit der Auswertungsreihenfolge der Parameter von Funktionen zu tun! Deren Berechnungsreihenfolge ist nicht definiert, auch wenn sie durch Kommata getrennt werden.
AnyWare@Wachtler.de