Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Dieser Artikel ist Teil des C-Tutorials.

Eine Variable oder eine Konstante in C stellen einfache Ausdrücke dar. Diese elementaren Ausdrücke können durch Operatoren miteinander verknüpft werden und so zu neuen, komplexeren Ausdrücken zusammen gesetzt werden.

Einfache Beispiele für Ausdrücke sind also z.B.:

1
a
'a'
1 + a
a == 1

Auch Funktionen können einen Wert zurückliefern und in Ausdrücken weiter benutzt werden. In den folgenden Abschnitten wird gezeigt, welche Operatoren in C vorhanden sind, und wie man damit neue Ausdrücke aufbauen kann.

Lvalues

Ein Lvalue in C ist ein Ausdruck, dem ein anderer Ausdruck zugewiesen werden kann, dessen Wert also durch eine Zuweisung verändert werden kann. das 'L' leitet sich ab von 'left' bwz. 'links' und das 'value' bedeutet Wert: Ein Lvalue ist ein Ausdruck, der auf der linken Seite einer Zuweisung stehen darf. Ein Lvalue ist also immer auch ein gültiger Ausdruck, aber die Umkehrung gilt in aller Regel nicht.

Ein einfaches Beispiel für einen Lvalue ist eine "normale" Variable, die nicht mit const als Konstante markiert ist. Ihr kann ein Wert zugewiesen werden:

a = 1;

Hingegen ist der Ausdruck a+1 kein Lvalue, denn eine Zuweisung wie

a+1 = 2;

die mathematisch durchaus sinnvoll ist, erzeugt einen Compilerfehler, der etwa lauten könnte "illegal lvalue in assignment": "ungültiger Wert in Zuweisung"

Andere Beipiele für Lvalues sind die Komponenten von (nicht-konstanten) Strukturen und Unions, Array-Elemente und die Dereferenzierungen von Pointern: Die Konstante 4 wird durch den Cast in eine Adresse umgewandelt. Über die Dereferenzierung * wird an die Adresse 4 im Speicher eine 3 geschrieben. Ob das erlaubt bzw. sinnvoll ist, ist abhängig von der jeweiligen Architektur.

* ((unsigned int *) 4) = 3;

Hier ist der gesamte *-Ausdruck ein Lvalue

Logische (boolsche) Operatoren

Ausdruck Beschreibung
a && b wahr, wenn a wahr und b wahr
a || b wahr, wenn a wahr oder b wahr
a == b gleich
a != b ungleich
a <= b kleiner oder gleich
a < b kleiner als
a >= b glösser oder gleich
a > b grösser als
!a wahr, wenn a nicht wahr und vice versa

Eine interessante Eigenschaft der Operatoren && und || ist, dass sie die Auswertung abbrechen, sobald das Ergebnis feststeht. Die Ausdrücke werden dabei immer von links nach rechts ausgewertet. Ein oft anzutreffendes Codestück sieht so aus, dabei sei p ein Zeiger auf einen int:

Beispiel:

 if (p && *p == 5)
 {
    /* mach was */
 }

Zuerst wird in der Bedinung geprüft, ob Zeiger p einen Wert ungleich Null hat, also ob er überhaupt einen gültigen Wert enthält. Es ist weit verbreitete Konvention in C, daß Zeiger, die keinen gültigen Wert haben, die Adresse 0 enthalten. Nur dann, wenn ein Zeiger nicht ein Null-Pointer ist, darf überhaupt ein Zugriff über ihn erfolgen!

Vergleich von Variablen

Skalare Variablen (also ganze Zahlen, Gleitkommazahlen, Zeiger) können miteinander verglichen werden. Dazu gibt es die folgenden Operatoren in C:

Operator Bedeutung
== ist gleich
!= ist nicht gleich
< ist kleiner
<= ist kleiner oder gleich
> ist größer
>= ist größer oder gleich

Das Ergebnis der Auswertung ist eine ganze Zahl. Ist die Bedingung erfüllt, dann ist der Wert ungleich 0. Ist die Bedingung nicht erfüllt, dann ist ihr Wert gleich 0. Meistens wird man diese Operatoren in if-Konstrukten finden wie zum Beispiel

if (x >= 10)
   x = 10;

oder in Abbruchbedingungen von Schleifen, wie sie weiter unten erklärt werden.

Es ist auch möglich, das Ergebnis der Auswertung in einer int-Variablen zu speichern:

int i;
int z1, z2;

z1 = 5;
z2 = 100;
i = z1 <= z1;  /* Ein Vergleich. i wird "wahr", da z1 kleinergleich z2 ist */

Die Variable i ist ungleich 0 ("wahr"), wenn z1 kleiner oder gleich z2 ist. Ist z1 jedoch größer als z2, dann ist i gleich 0 ("unwahr").

Arithmetische Operatoren

Ausdruck Beschreibung
a + b Summe (Addition)
a - b Differenz (Subtraktion)
a * b Produkt (Multiplikation)
a / b Quotient (Division, evtl. mit Rest)
a % b Rest bei Division (Modulo)
-a Vorzeichenumkehr (Zweierkomplement)

Bit-Operatoren

Ausdruck Beschreibung
a & b bitweise und (and)
a | b bitweise oder (or)
a ^ b bitweise exclusiv-oder (xor, exor)
~a jedes Bit in a invertieren (not, Einerkomplement)

Index-Operator bei Arrays

Ausdruck Beschreibung
a[b] das (b+1)ste Element des Feldes a

Folgendes gilt es bei der Verwendung des Indexoperators zu beachten:

  1. a muss ein Feld oder Zeiger sein
  2. b muss ein Integer sein oder ein Datentyp, der sich in einen int umwandeln läßt (z.B. char)
  3. Es wird nicht geprüft, ob der Index b im Feld a gültig ist!
  4. Der erste Index eines Feldes ist immer 0. Daher (b+1)stes Element in der Beschreibung

Komponenten-Auswahl bei Structs und Unions

Ausdruck Beschreibung
a.b Element b der Struktur oder des Unions a

Adress-Operator und Dereferenzierung

Ausdruck Beschreibung
&a Speicheradresse der Variablen a
*a Wert, der an der Adresse a steht
a->b Wert des Elements b der Struktur, deren Adresse in a steht

Der Adressoperator & kann auf Variablen angewendet werden und gibt die Startadresse der Variablen im Speicher zurück.

Handelt es sich bei einer Variable um einen Zeiger, so enthält sie eine Speicheradresse. Um an den Wert zu gelangen, der an dieser Adresse steht, wird der Operator * vorangestellt.

Beispiel:

/* x ist eine Integervariable und hat den Wert 5 */
int x = 5;
    
/* z ist ein Zeiger auf eine Integer-Variable und enthaelt somit */
/* die Speicheradresse einer Integer-Variablen */
int *z;       
 
/* Verwendung des Adress-Operators: weist an z die Adresse von x zu */
z = &x;

/* Verwendung der Dereferenzierung */
/* erhoehe den Wert, der bei Adresse z steht, um eins */
*z = *z + 1;

/* da z auf x zeigt, hat x jetzt den Wert 6 */

Da in C häufig Zeiger auf Strukturen verwendet werden, ist für den Zugriff auf Struktir- und Union-Elemente eine abkürzende Schreibweise möglich:

Statt

 (*strukturZeiger).element

kann geschrieben werden

 strukturZeiger->element

Beide Schreibweisen sind absolut gleichbedeutend, die Klammern bei der ersteren sind notwendig.

Achtung!

Bei der Dereferenzierung durch * findet keine Prüfung statt, ob der Zeiger auch auf eine gültige Speicheradresse verweist. Folgendes Codestück führt zum Absturz oder zu einer Änderung irgendeiner Speicherstelle!
int *z; /* z ist ein Zeiger auf einen int */

/* An dieser Stelle ist z immer noch keine Speicheradresse zugewiesen. */
/* z enthaelt irgendeine ungueltige Adresse!! */

/* "Erhoehe einen Integer _irgendwo_ im Speicher um 1" -> CRASH !!! */
*z = *z + 1;

Viele C-Compiler erzeugen in der Standardeinstellung für das obige Codestück keine Warnung!

Cast-Operator

Der Cast Operator dient dazu, den Datentyp eines Wertes zu ändern. Dafür wird einfach der neue Datentyp in Klammern vor den Wert geschrieben.

Um zum Beispiel aus einem Float ein Integer zu machen:

var  = (int) 5.60;

Dabei wird der Wert aber auch gerundet, und es findet somit ein Informationsverlust statt.

Ein weiteres Beispiel ist das Umwandeln einer ganzen Zahl in eine Adresse:

int * addr;
addr = (int*) 0x1234;

Damit ist addr ein Zeiger auf einen int an Adresse 0x1234.

Achtung!

Der Cast-Operator selbst führt keine Konvertierung durch, sondern unterdrückt nur die Compilerwarnungen oder -fehler, die bei einer "verdächtigen" Umwandlung passieren. Er kann also nicht benutzt werden, um einen Text in eine Zahl oder umgekehrt zu wandeln:

  #include <stdio.h>
  int main(int argc, char ** argv)
  {
        char text[] = "5.6";
        int zahl = (int)text;

        printf("%d\n", zahl);

        return 0;
  }

Ausgegeben wird weder 5 noch 6 sondern die Speicheradresse, an der der Text "5.6" beginnt. Der Cast-Operator unterdrückt nur die Warnung, die auf den Fehler hinweisen will.

Komma-Operator

Mit einem , können mehrere Ausdrücke nacheinander ausgewertet werden. Die Auswertung erfolgt von links nach rechts.

Solche Konstrukte sieht man manchmal in Abfragen wie

FILE  *file;
if (file = fopen ("foo.exe", "r"), file != NULL)

was erst an file einen Wert zuweist und den if-Block nur betritt, wenn file nicht der Nullpointer ist.

Zuweisungen und Operatoren mit Nebeneffekt

Bedingter Ausdruck

(<Bedingung>) ? <Ausdruck1> : <Ausdruck2>

Wenn Bedingung erfüllt ist, dann wertet dieser Ausdruck aus zu Ausdruck1. Ist er nicht erfüllt, dann wertet er aus zu Ausdruck2.

Beispiel:

x = (x >= 3) ? 0 : x+1;

Startet man x mit dem Wert 0, dann nimmt es bei mehrfacher Anwendung dieser Zeile (z.B. in einer Schleife) nacheinander die folgende Werte an:

1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, ...

Reihenfolge der Auswertung

Boolsche Logik

Boolean-Variablen können miteinander verknüpft werden. Dies geschieht mit den boolschen Operatoren && ( logisches "und"), || (logisches "oder") und ! (logisches "nicht") (es gibt noch weitere, auf die hier nicht eingegangen wird).

b1= b2 && b3;


Der Wert von b1 ist dann 1 (TRUE), wenn b2 und ("AND") b3 1 (TRUE) sind. Ist eine der beiden (oder auch beide) 0 (FALSE), dann ist b1 ebenfalls 0 (FALSE).


b1= b2 || b3;


Der Wert von b1 ist dann TRUE, wenn b2 oder ("OR") b3 TRUE ist. Ist also einer (oder beide) TRUE, dann ist auch b1 TRUE, sind beide FALSE, ist auch b1 FALSE.


b1= !b2;


Der Wert von b1 ist dann TRUE, wenn der von b2 FALSE ist. Die NOT Verknüpfung "dreht den logischen Wert der Variablen um".