| #include <pronix.de> |
|
|
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
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Operator | Bedeutung |
| + | Addiert zwei Werte |
| - | Subtrahiert zwei Werte |
| * | Multipliziert zwei Werte |
| / | Dividiert zwei Werte |
| % | Modulo (Rest einer Division) |
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
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 |
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;
}
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) |
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 |
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
Analog verhält es sich mit dem Dekrement-Operator (--).
Inkrement- bzw. Dekrement-Operator werden vorwiegend bei Schleifen genutzt. Beide Operatoren sind unärer Natur.
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 |
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
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 |
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
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 |
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
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 |
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 |
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
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.
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
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'));