Zum Berechnen und Weiterverarbeiten von Variableninhalten gibt es viele Operatoren. Im Folgenden wird die Verwendung solcher Operatoren besprochen. C C++ C/C++ arithmetische Operatoren Inkrement Dekrement xor or and not Bitweise Operator Operatoren in C - arithmetische Operatoren Inkrement Dekrement Bit Operator Kapitel 9: Operatoren

Zum Berechnen und Weiterverarbeiten von Variableninhalten gibt es viele Operatoren in C. Im Folgenden wird die Verwendung solcher Operatoren besprochen.

9.1. Exkurs zu Operatoren            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Damit auch Nicht-Mathematiker oder Hobby-Programmierer verstehen, wie sich Operatoren unterscheiden, hierzu ein kleiner Exkurs. Operatoren werden hinsichtlich der Zahl ihrer Operanden unterschieden:

In C werden Sie vorwiegend mit unären und binären Operatoren arbeiten. Es gibt aber auch einen tenären Operator. Des Weiteren wird unterschieden, welche Position der Operator einnimmt:

Vorwiegend werden Sie es mit der Infix-Schreibweise zu tun haben. Einige unäre Operatoren können sowohl in der Präfix- als auch in der Postfix-Schreibweise verwendet werden. Und schließlich werden die Operatoren noch hinsichtlich der Assoziativität differenziert. Als Assoziativität wird die Auswertungsreihenfolge bezeichnet, in der Operanden in einem Ausdruck ausgewertet werden. Dabei gibt es folgende Assoziativitäten der Operatoren:

Der Großteil der Operatoren in C ist linksassoziativ. Das bedeutet z.B. bei folgendem Ausdruck

var1 + var2 - var3; 

wird zuerst var1 mit var2 addiert und die Summe anschließend wird var3 von der Summe subtrahiert. Wären die Operatoren rechtsassoziativ, würde zuerst var2 mit var3 subtrahiert und danach erst mit var1 addiert. Ist dies erwünscht, müssen Klammern gesetzt werden:

var1 + (var2 - var3); 

Nachdem diese sprachlichen Stolpersteine beseitigt sind, können Sie sich mit den einzelnen Operatoren in der Programmiersprache C befassen.

9.2. Arithmetische Operatoren            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Für arithmetische Operationen (mathematische Gleichungen) gibt es folgende arithmetische Operatoren:

Operator Bedeutung
+ Addiert zwei Werte
- Subtrahiert zwei Werte
* Multipliziert zwei Werte
/ Dividiert zwei Werte
% Modulo (Rest einer Division)

Tabelle 9.1: Darstellung arithmetischer Operatoren in C

Für arithmetische Operatoren gelten folgende Regeln:

Ein Programmbeispiel verdeutlicht den Sachverhalt:

#include <stdio.h>

int main()
{
   int zahl1,zahl2,zahl3;
   int ergeb;

   zahl1=10;
   zahl2=20;
   zahl3=30;

   printf("Zahl 1= %d\n",zahl1);
   printf("Zahl 2= %d\n",zahl2);
   printf("Zahl 3= %d\n",zahl3);

   /*Möglichkeit 1: zuerst Berechnung, dann Ausgabe*/
   ergeb=zahl1+zahl2+zahl3;
   printf("Summe aller Zahlen:%d\n",ergeb);

   /*Möglichkeit 2: wie oben, nur mit Ausgabe in einem Schritt*/
   ergeb=zahl3-zahl2;
   printf("%d - %d = %d\n",zahl3,zahl2,ergeb);

   /*Möglichkeit 3: mit Anzeige */
   /*und Berechnung am Ende der*/
   /*'printf'-Anweisung */
   printf("%d * %d = %d\n",zahl1,zahl1,zahl1*zahl1);

   /*Möglichkeit 4: weitere 'printf'-Berechnung*/
   printf("Zahl 3 / Zahl 1 =%d\n",zahl3/zahl1);

   /*Möglichkeit 5: wieder eine mit 'printf'*/
   printf("Zahl 1 + x-Beliebige Zahl =%d\n",zahl1+11);

   /*Ein Klammerbeispiel*/
   ergeb=(zahl1+zahl2)/zahl3;
   printf("(%d + %d)/%d = %d\n",zahl1,zahl2,zahl3,ergeb);
   return 0;
}

Wie Sie in diesem Beispiel sehen, kann die Berechnung auch in der printf-Anweisung ausgeführt werden.

9.2.1 Dividieren von Ganzzahlen
Wenn zwei Ganzzahlen wie z.B. 4/3 dividiert werden, bekommen Sie als Ergebnis 1 zurück. Der Grund ist ganz einfach, denn der Datentyp int entspricht einer Ganzzahlvariablen und schneidet daher den nicht ganzzahligen Rest einfach ab. Wird der Rest benötigt, können Sie den Modulo-Operator verwenden. Der Modulo-Operator hat % als Zeichen. Das ist doch das Formatierungszeichen der Funktionen printf() und scanf()? Nein, denn das eine hat mit dem anderen nichts zu tun. Das Formatierungszeichen von printf() und scanf() wird immer zwischen zwei Hochkommata geschrieben ("%d"). Der Modulo-Operator % ist ein Rechenoperator, genau genommen ein integraler Rechenoperator, der zwischen zwei Operanden steht. Hier ein kleines Programmbeispiel:

#include <stdio.h>

int main()
{
   int x=5;
   int y=2;

   x=x%y;
   printf("Der Rest von 5/2=%d\n",x); /* Rest=1 */
   return 0;
}

Eventuell ist der Nutzen dieses Operators nicht sofort ersichtlich. Seine Verwendung wird in späteren Beispielen noch deutlicher gemacht. Zunächst noch ein weiteres Beispiel des Modulo-Operators:

#include <stdio.h>

int main()
{
   int sekunden,minuten;

   printf("Bitte geben Sie die Zeit in Sekunden ein :");
   scanf("%d",&sekunden);
   minuten=sekunden/60;
   sekunden=sekunden%60;
   printf("genauer = %d min. %d sek.\n", minuten, sekunden);
   return 0;
}

Zuerst werden die eingegebenen Sekunden durch 60 dividiert, womit Sie die Minuten erhalten. Anschließend wird der Modulo-Operator benutzt, um den Divisionsrest zu berechnen - in diesem Beispiel also die restlichen Sekunden.

Abbildung 9.1: Rechenbeispiel mit dem Modulo-Operator
Abbildung 9.1: Rechenbeispiel mit dem Modulo-Operator

9.3. Erweiterte Darstellung arithmetischer Operatoren            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Die arithmetischen Operatoren, welche im vorangegangenen Abschnitt verwendet wurden, lassen sich auch noch in anderer Form darstellen, und zwar in einer kürzeren Schreibweise:

Erweiterte Darstellung Bedeutung
+= a+=b ist gleichwertig zu a=a+b
-= a-=b ist gleichwertig zu a=a-b
*= a*=b ist gleichwertig zu a=a*b
/= a/=b ist gleichwertig zu a=a/b
%= a%=b ist gleichwertig zu a=a%b

Tabelle 9.2: Erweiterte Darstellung arithmetischer Operatoren

Das Rechenzeichen und das darauf folgende = verkörpert eigentlich nur eine verkürzte Schreibweise von:

Zahl = Zahl <operand> Zahl

Es wird dabei auch von einem Zuweisungsoperator gesprochen. Somit sind die folgenden Schreibweisen gleichwertig:

printf("Die Fläche beträgt : %d\n",x*=x);
printf("Die Fläche beträgt : %d\n",x=x*x);

Zur Verdeutlichung ein Programm:

#include <stdio.h>

int main()
{
   int x=2, y=4, z=6;
   printf("x=%d\n",x);
   printf("y=%d\n",y);
   printf("z=%d\n",z);

   printf("%d\n",x+=y);
   printf("%d\n",z+=y);
   printf("%d\n",z+=x);

   printf("x=%d\n",x);     /*x wurde verändert*/
   printf("y=%d\n",y);     /*y bleibt gleich*/
   printf("z=%d\n",z);     /*z wurde verändert*/
   return 0;
}

9.4. Inkrement- und Dekrement-Operatoren            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Bei einem Inkrement oder Dekrement wird der Wert einer Variablen um 1 erhöht bzw. erniedrigt. Diese Operatoren werden in C folgendermaßen geschrieben:

Operator Bedeutung
++ Inkrement (Variable um 1 erhöhen)
-- Dekrement (Variable um 1 verringern)

Tabelle 9.3: Inkrement- und Dekrementoperator

Dabei gibt es für diese Operatoren jeweils zwei Möglichkeiten:

Verwendung Bezeichnung
var++ Postfix-Schreibweise
++var Präfix-Schreibweise
var-- Postfix-Schreibweise
--var Präfix-Schreibweise

Tabelle 9.4: Postfix- und Präfixschreibweisen

Folgende Unterschiede gibt es zwischen der Postfix- bzw. Präfix-Schreibweise:

Hierzu ein Beispiel:

#include <stdio.h>

int main()
{
   int i=1;

   printf("i=%d\n",i);         /*i=1*/
   i++;
   printf("i=%d\n",i);         /*i=2*/
   printf("i=%d\n",i++);       /*i=2*/
   printf("i=%d\n",i);         /*i=3*/
   printf("i=%d\n",++i);       /*i=4*/
   return 0;
}

Abbildung 9.2: Verwendung des Inkrement-Operators
Abbildung 9.2: Verwendung des Inkrement-Operators

Analog verhält es sich mit dem Dekrement-Operator (--).

Inkrement- bzw. Dekrement-Operator werden vorwiegend bei Schleifen genutzt. Beide Operatoren sind unärer Natur.

9.5. Bit-Operatoren            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Mit Hilfe von Bit-Operatoren kann direkt auf die binäre Darstellung der Zahlen zurückgegriffen werden. Zuerst eine kurze Übersicht, welche Bit-Operatoren es gibt:

Bit-Operator Bedeutung
&, &= Bitweise AND-Verknüpfung
|, |= Bitweise OR-Verknüpfung
^, ^= Bitweise XOR
~ Bitweises Komplement
>>, >>= Rechtsverschiebung
<<, <<= Linksverschiebung

Tabelle 9.5: Übersicht der bitweisen Operatoren

Wie schon bei den arithmetischen Operatoren steht Ihnen auch bei den bitweisen Operatoren die erweiterte Zuweisungsschreibweise zur Verfügung.

9.5.1 Bitweises UND
Steht der &-Operator zwischen zwei Operanden, so handelt es sich um den bitweisen UND-Operator. Dieser ist leicht mit dem unären Adressoperator (siehe scanf()) zu verwechseln.

Der Operator wird hauptsächlich dafür verwendet, einzelne Bits gezielt zu löschen. Folgendes Programmbeispiel soll dies demonstrieren:

#include <stdio.h>

int main()
{
   int x=55;
   printf("x=%d\n",x);
   x= x&7;
   printf("x=%d\n",x);  /* x=7 */
   return 0;
}

Nach der Ausführung des Programms werden Sie sich fragen, warum die Verknüpfung mit dem UND-Operator zum Ergebnis 7 führt. Sehen Sie sich dies wieder in der Bitdarstellung an (unter Verwendung der ersten 8 Bits):

Abbildung 9.4: Verwendung des bitweisen UND-Operators

Abbildung 9.4: Verwendung des bitweisen UND-Operators

Dabei gelten per Definition folgende Regeln für den bitweisen UND-Operator:

BitA BitB BitA&BitB
0 0 0
0 1 0
1 0 0
1 1 1

Tabelle 9.6: Regeln einer bitweisen UND-Verknüpfung

Mit dem bitweisen UND-Operator lässt sich sehr gut testen, ob eine Zahl gerade oder ungerade ist. Es muss nur Bit 0 (bzw. das 1. Bit) daraufhin überprüft werden, ob es gesetzt (ungerade, also = 1) oder nicht gesetzt (gerade, also = 0) ist. Folgendes Beispiel demonstriert dies:

#include <stdio.h>

int main()
{
   int x;
   printf("Bitte geben Sie eine Zahl ein: ");
   scanf("%d",&x);
   if(x&1)  /* Ist das erste Bit gesetzt? */
      printf("Eine ungerade Zahl\n");
   else  /* Nein, es ist nicht gesetzt */
      printf("Eine gerade Zahl\n");
   return 0;
}

9.5.2 Bitweises ODER
Mit dem bitweisen ODER-Operator können Sie gezielt zusätzliche Bits setzen. Verwendet wird dieser wie schon zuvor der bitweise UND-Operator:

char x = 1;
x = x|126; /* x=127 */

Auch hierzu die Bitdarstellung:

Abbildung 9.5: Verwendung des bitweisen ODER-Operators

Abbildung 9.5: Verwendung des bitweisen ODER-Operators

Für den ODER-Operator gilt folgende Verknüpfungstabelle:

BitA BitB BitA | BitB
0 0 0
0 1 1
1 0 1
1 1 1

Tabelle 9.7: Regeln einer bitweisen ODER-Verknüpfung

9.5.3 Bitweise XOR
Dieser exklusive ODER-Operator liefert nur dann eine 1 zurück, wenn beide Bits unterschiedlich sind. Er ist sehr gut geeignet, um Bits umzuschalten. Alle gesetzten Bits werden gelöscht und alle gelöschten gesetzt. Beispielsweise:

char x=20;
x = x^55; /* x=35 */

In binären Darstellung ergibt sich aus dieser Operation folgendes Bild:

Abbildung 9.6: Verwendung des exklusiven ODER-Operators

Abbildung 9.6: Verwendung des exklusiven ODER-Operators

Für XOR-Verknüpfungen gilt folgende Verknüpfungstabelle:

BitA BitB BitA ^ BitB
0 0 0
0 1 1
1 0 1
1 1 0

Tabelle 9.8: Regeln einer bitweisen XOR-Verknüpfung

9.5.4 Bitweises Komplement
Der NOT-Operator (~) wirkt sich auf Zahlen so aus, dass er jedes einzelne Bit invertiert. Bei vorzeichenbehafteten Datentypen entspricht das einer Negation mit anschließender Subtraktion von 1:

char x=20;
x=~x;  /* x= -21 */

Für den NOT-Operator gilt folgende Verknüpfungstabelle:

BitA ~BitA
0 1
1 0

Tabelle 9.9: Regeln einer bitweisen NOT-VerknüpfungLinksverschiebung

Mit einer Linksverschiebung (<<) werden alle Bits einer Zahl um n Stellen nach links gerückt. Die rechts entstehenden Leerstellen werden mit 0 aufgefüllt.



Achtung
 

Achtung bei Vorzeichen! Ist der Datentyp signed, ändert sich das Vorzeichen, wenn eine 1 in die Bitstelle des Vorzeichens gerückt wird. Falls der linke Operand aber einen negativen Wert hat, so ist das Ergebnis Compiler-spezifisch.

Beispiel einer Linksverschiebung:

#include <stdio.h>

int main()
{
   char x=8;
   printf("x=%d\n",x);

   x<<=1;        /*Alle Bits um 1 Stelle nach links*/
   printf("x=%d\n",x);
   return 0;
}

Warum aus dem Wert 8 eine 16 wurde, wird aus der folgenden Bitdarstellung ersichtlich:

Abbildung 9.7: Bitverschiebung nach links

Abbildung 9.7: Bitverschiebung nach links

Sie werden es bemerkt haben, hier wurde eine Multiplikation durchgeführt. Auf diese Weise können Zahlen sehr gut potenziert werden. Die Bitstelle um eine Position nach links zu rücken, bedeutet mathematisch eine Multiplikation mit 2. Bei Einrückung um zwei Stellen nach links wird mit 4 multipliziert, bei drei mit 8, bei vier mit 16 usw. Solche Bitverschiebungen können - abhängig vom System - bis zu 40(!)-mal schneller ablaufen als normale arithmetische Berechnungen im Stil von 4*x.

9.5.6 Rechtsverschiebung
Die Rechtsverschiebung mit dem >>-Operator ist das Gegenstück zur Linksverschiebung (<<). Damit können Sie statt einer Multiplikation mit 2 eine Division durch 2 bewirken. Ansonsten gilt das Gleiche wie für die Linksverschiebung.

9.5.7 Rezept für Fortgeschrittene
Oft ist eine Funktion wünschenswert, mit der eine Zahl daraufhin getestet wird, ob ein bestimmtes Bit gesetzt ist, oder mit der sich gezielt einzelne Bits setzen oder löschen lassen. Hierzu ein Listing mit entsprechenden Funktionen:

#include <stdio.h>
#define BYTE unsigned char

/* Funktion Bit_Test()
 * val=der Wert, den es zu testen gilt
 * bit=Bitnummer, die abgefragt wird, ob gesetzt (0-7)
 *
 * Rückgabewert=  (1)=Bit gesetzt; (0)=Bit nicht gesetzt
 */

int Bit_Test(BYTE val, BYTE bit)
{
   BYTE test_val = 0x01;    /* dezimal 1 / binär 0000 0001 */
   /* Bit an entsprechende Pos. schieben */
   test_val = (test_val << bit);
   /* 0=Bit nicht gesetzt; 1=Bit gesetzt */
   if ((val & test_val) == 0)
      return 0;                  /* Nicht gesetzt */
   else
      return 1;                  /* gesetzt */
}

/* Funktion Bit_Set()
 * val=Wert, bei dem Bit gesetzt werden soll
 * bit=Bitnummer, die gesetzt werden soll (0-7)
 *
 * Rückgabewert=  keiner
 */

void Bit_Set(BYTE *val, BYTE bit)
{
   BYTE test_val = 0x01;      /* dezimal 1 / binär 0000 0001 */
   /* Bit an entsprechende Pos. schieben */
   test_val = (test_val << bit);
   *val = (*val | test_val);     /* Bit an Pos bit setzen */
}

/* Funktion Bit_Clear()
 * val=Wert, bei dem Bit gelöscht werden soll
 * bit=Bitnummer, die gelöscht werden soll (0-7)
 *
 * Rückgabewert=  keiner
 */

void Bit_Clear(BYTE *val, BYTE bit)
{
   BYTE test_val = 0x01;        /* dezimal 1 / binär 0000 0001 */
   /* Bit an entsprechende Pos. schieben */
   test_val = (test_val << bit);
   *val = (*val & (~test_val));   /* Bit an Pos bit löschen*/
}

int main()
{
   BYTE wert = 0;
   /* Test, ob Bit 0 gesetzt */
   printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt");
   Bit_Set(&wert, 0);    /* Bit 0 setzen */
   /* Wieder testen, ob Bit 0 gesetzt */
   printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt");
   Bit_Clear(&wert, 0);  /* Bit 0 wieder löschen */
   /* Wieder testen ob Bit 0 gesetzt */
   printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt");
   return 0;
}

Die Funktionen können natürlich den eigenen Bedürfnissen entsprechend angepasst werden und dienen nur als Anregung für weitere Spielereien mit Bits und Bytes.

9.6. sizeof Operator            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Dieser Operator gibt Auskunft über die Größe eines Datentyps in Byte. Der sizeof-Operator wird später noch häufiger zur dynamischen Speicherreservierung verwendet. Zur Erklärung des sizeof-Operators wieder ein Programmbeispiel:

#include <stdio.h>

int main()
{
   printf("char     =%d Byte\n",sizeof(char));
   printf("int      =%d Bytes\n",sizeof(int));
   printf("long     =%d Bytes\n",sizeof(long int));
   printf("float    =%d Bytes\n",sizeof(float));
   printf("double   =%d Bytes\n",sizeof(double));
   printf("66       =%d Bytes\n",sizeof(66));
   printf("Hallo    =%d Bytes\n",sizeof("Hallo"));
   printf("A        =%d Bytes\n",sizeof((char)'A'));
   printf("34343434 =%d Bytes\n",sizeof(34343434));
   return 0;
}

Abbildung 9.8: Verwendung des sizeof-Operators
Abbildung 9.8: Verwendung des sizeof-Operators

Das Programm macht nichts anderes, als Ihnen die Größe, die der Datentyp belegt, in Byte auszugeben. Zum Beispiel: 'A' benötigt ein Byte Speicherplatz, da 'A' vom Typ char ist. Bei sizeof("Hello") sollten Sie sich jetzt noch keine Gedanken darüber machen, warum dieses Wort sechs Byte Speicher benötigt, obwohl es nur aus fünf Zeichen besteht. Das sechste Byte wird für das Stringende-Zeichen verwendet. Hier geht es im Augenblick lediglich um den sizeof-Operator.

Jetzt fällt es sicher nicht mehr schwer, zu verstehen, wie Sie mit dem sizeof-Operator Speicherplatz reservieren können. Sie müssen dazu lediglich abfragen, wie viel Speicherplatz ein Datentyp benötigt und den ermittelten Wert dann einer anderen Variable (genauer: einem Zeiger) übergeben. Des Weiteren wird der sizeof-Operator zur Portierbarkeit von Programmen auf verschiedenen Systemen verwendet. Wie Sie bereits erfahren haben, gibt es Systeme, auf denen ein int-Wert eine Speichergröße von zwei Byte hat. Auf anderen Systemen beträgt die Größe von int wiederum vier Byte. Als Beispiel folgender Pseudocode:

BRAUCHE_SPEICHER_PLATZ = (Datentyp)sizeof(4); 

Der Datentyp BRAUCHE_SPEICHER_PLATZ benötigt hier Speicherplatz der Größe eines int-Werts, hier vier Bytes. Wenn das Programm jetzt beispielsweise auf ein anderes System portiert werden soll, auf dem der Datentyp int eine Größe von zwei Byte hat, dann könnte dies zu Problemen führen. Aus diesem Grund ist es besser, dem Compiler zur Übersetzungszeit diese Aufgabe zu überlassen. Dies geschieht folgendermaßen (Pseudocode):

BRAUCHE_SPEICHER_PLATZ = (Datentyp)sizeof(int); 

Somit können Sie sicher sein, dass das Programm auf jedem System läuft und die richtige Menge an Speicherplatz reserviert.

Hinweis
 

Der sizeof-Operator ist keine Funktion, obwohl das oft behauptet wird. Der sizeof-Operator holt sich nur die Größe eines Datentyps während der Compilier-Zeit. Er ist somit tatsächlich ein echter Operator.

9.6.1 C versus C++
Im Programmbeispiel des sizeof-Operators dürfte Ihnen folgende Zeile aufgefallen sein:

printf("A =%d Bytes\n",sizeof((char)'A'));

Da in C eine Zeichen-Literale vom Typ int ist, musste ich hier den Datentyp char casten. In C++ würde folgende Zeile auch 1 Byte ausgeben:

printf("A =%d Bytes\n",sizeof('A')); 

Weiter mit Kapitel 10: Typenumwandlung            zum Inhaltsverzeichnis