15. Operatoren in C

Wie in Python können Variablen und Literale mit Operatoren zu Ausdrücken kombiniert werden. Die Grundrechenarten laufen exakt analog zu Python, bis hin zur Integer-Division: 1/2 oder 4/5 ist Null, aber 1/2. ist 0.5. Grundsätzlich wird bei Operationen zwischen verschiedenen Typen immer auf den “größten” Typen interpoliert.

Das heißt, dass der Typ des Ergebnisses eines Ausdrucks dem größten Typen seiner Operanden entspricht und die anderen Operanden bei Bedarf ebenfalls auf diesen Typen gebracht werden. Dabei ist short größer als char, int größer als short, long größer als int, float größer als long, double größer als float. Ähnliches gilt übrigens für Python, wo die Sequenz int, long, float, complex ist. Diese Interpolierei ist häufig ein wenig magisch, weshalb strenger typisierte Sprachen als C solche Dingen nicht machen – sie ist allerdings so bequem, dass sie so bald nicht verschwinden wird.

Beispiel: Sei int i=1, j=2; double r=2.;. Dann gilt i+j==3, i-j==-1, j*r==4., i/j==0, i/r==0.5.

Technik: Bitweise Operatoren

C bietet bitweise Operatoren, die auf die binären Darstellungen ihrer Argumente operieren (das soll heißen, dass man die binären Darstellungen der beiden Operanden hinschreibt und dann bit für bit eine logische Operation ausführt):

  • ^ – bitweises exklusiv-oder
  • & – bitweises und
  • | – bitweises oder
  • ~ – bitweises nicht
  • <<, >> – bitweises shift left und right

Dabei heißt exklusiv-oder, dass im Ergebnis eine 1 steht, wenn entweder im einen oder im anderen Operanden eine 1 steht, sonst 0.

Bei “und” steht im Ergebnis 1, wenn in beiden Operanden eine 1 ist, bei “oder”, wenn in mindestens einem eine 1 ist.

“nicht” macht aus jeder 1 eine 0 und umgekehrt – “nicht” ist natürlich kein zweistelliger Operator mehr, es hat nur einen Operanden.

Die Schiebeoperationen schieben einfach alle bits nach rechts oder links. Das Schieben von vorzeichenbehafteten Zahlen sollte man in der Regel unterlassen, es sei denn, man versteht den Abschnitt 6.5.7 des ISO-C-Standards und weiß, warum die dort erwähnten undefinierten Werte undefiniert geblieben sind – Details gibt es auf Anfrage.

Beispiele:

Sei a=0xF2=111100102 und b=0x2E=001011102. Dann ist

<tt>a&b< /tt>=  001000102 = <tt>0x22< /tt> <tt>a| b</tt>=  111111102 = <tt>0xFE< /tt>
   <tt>a>>1< /tt>=  011110012 = <tt>0x79< /tt>  <tt>~a< /tt>=  000011012 = <tt>0x0D< /tt>
<tt>a <<1</tt> = 1 111001002 = <tt>0x1E4< /tt> <tt>a^b< /tt>=  110111002 = <tt>0xDC< /tt>

Diese bitweisen Operatoren existieren genau so in Python, werden dort aber nicht ganz so häufig verwendet.

Wozu ist das alles überhaupt gut? Grundsätzlich immer, wenn Bits manipuliert werden müssen, etwa bei der Grafikverarbeitung.

Ein anderer Anwendungsfall sind Mengen. In Python lassen sich Mengen recht einfach durch Dictionaries darstellen, C hat aber keine Dictionaries. Wenn die Mengen klein sind (sagen wir, nur 32 verschiedene Elemente haben können, was etwa bei nicht-ausschließenden Optionen von Programmen der Fall sein mag), lassen sie sich durch die bits in einem long darstellen.

In Python geschrieben könnte das etwa so aussehen, wenn wir zunächst damit zufrieden sind, wirklich nur die Zahlen zwischen 0 und 31 in der Menge zu haben:

class Set:

  def __init__(self, initMask=0):
    self.bitMask = initMask

  def __str__(self):
    return "{%s}"%(", ".join([str(i)
      for i in range(32) if (1<<i)&self.bitMask]))

  def addElement(self, elInd):
    self.bitMask |= 1<<elInd

  def delElement(self, elInd):
    self.bitMask &= ~(1<<elInd)

  def union(self, other):
    return Set(self.bitMask|other.bitMask)

  def section(self, other):
    return Set(self.bitMask&other.bitMask)

Wer will, kann die Klasse ja verbessern, z.B. um Fehlerprüfungen (die Elementnummern sollen ja beschränkt sein) oder auf eine einstellbare Anzahl von Elementen durch Verwendung einer geeigneten Liste von ganzen Zahlen. Mit Python-longs gehts natürlich auch ohne solche Tricks. Wer aber richtige Mengen in Python braucht, sollte wie gesagt entweder mit Dictionaries oder, ab Python 2.3, mit dem sets-Modul arbeiten.

Logische Ausdrücke

Logische Ausdrücke funktionieren ebenfalls wie in Python: Wahr ist alles, was verschieden von Null ist, falsch, was gleich Null ist.

Vergleiche sehen wie in Python aus: <, >, <=, >=, ==, !=

Die logischen Operatoren and, or und not von Python sind in C symbolisch geschrieben: &&, || und !.

Vorsicht: Die logischen und die bitweisen Operatoren machen ganz verschiedene Dinge, auch wenn sie zufällig mal übereinstimmen.

Vergleicht jetzt bitweise und logische Operatoren und spart euch später viel Pein und Verdruss.

Übrigens werden in C wie in Python logische Ausdrücke mit short circuit evalutation bewertet, d.h. nur so lange berechnet, bis das Ergebnis des Ausdrucks feststeht.

Vorsicht: if (a=0) ist in C legal, aber etwas ganz anderes als if (a==0).

Diese Konsequenz aus dem Umstand, dass die Zuweisung in C ein Operator ist (siehe unten – gute Compiler sagen allerdings etwas wie “Suggest parenthesis” oder so, wenn die Warnings eingeschaltet sind), ist eine endlose Quelle von ärgerlichen Programmierfehlern. Eine Möglichkeit, solche Fehler etwas zu reduzieren ist, sich anzugewöhnen, beim Vergleich gegen eine Konstante die Konstante immer zuerst zu schreiben: if (0=a) gibt einen Syntaxfehler, während if (0==a) natürlich völlig legal ist.

Die Warnung aus dem Python-Kurs bezüglich des Vergleichs von Fließkommazahlen gilt natürlich auch in C (tatsächlich übernimmt Python die Fließkommaimplementation vom C-System, mit dem es kompiliert wurde). In C sollte der Vergleich so aussehen:

fabs(a-b)<1e-10
– die Schranke muss dabei natürlich auf das Problem angepasst werden. Die Funktion fabs ist im Headerfile math.h definiert.

Übungen zu diesem Abschnitt

Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen

(1)

Sei a=0x3a und b=0x86, beide unsigned. Rechnet ~a, !a, a& b, a&&b, a|b, a||b und a<<1 sowie a>>1 aus.

(2)

Konstruiert einen Fall, in dem a&b logisch falsch, a&&b aber logisch wahr ergibt. Wie typisch ist das? Wie ist die Situation, wenn ihr “oder” statt “und” betrachtet?


Markus Demleitner

Copyright Notice