Ausdrücke und Operatoren

| ### Programmieren in C von Prof. Dr. Rainer Thomas

# 4. Ausdrücke und Operatoren

### 4.1 Arithmetische Operatoren
4.2 Vergleichsoperatoren
4.3 Logische Operatoren
4.4 Bitmanipulationsoperatoren
4.5 Zusammengesetzte Zuweisungsoperatoren
4.6 Typkonvertierung
4.7 Bedingte Ausdrücke
4.8 Abarbeitungsreihenfolge

### Ausdrücke und Operatoren in C - Allgemeines

- Unter einem Ausdruck versteht man die Verknüpfung von Operanden (Werte, z.B. Variable, Konstante) mittels Operatoren zu einem neuen Wert.
--> Ein Ausdruck liefert immer einen Wert.
- C besitzt gegenüber anderen Programmiersprachen (z.B. Pascal) ein wesentlich erweitertes Ausdrucks- und Operator-Konzept :

- C besitzt sehr viele Operatoren, darunter viele,

- die in anderen Sprachen nicht vorkommen
- bzw. dort nicht als Operatoren betrachtet werden

Es gibt unäre, binäre und einen ternären Operator, die in einer Präzedenzreihenfolge angeordnet sind.
- Auch das Zuweisungssymbol "=" ist ein Operator
--> einfacher Zuweisungsoperator
(es gibt daneben noch zusammengesetzte Zuweisungsoperatoren)

--> Die Zuweisung ist ein Ausdruck.
Ihr Wert ist der Wert, der der linken Seite zugewiesen wird.

z.B.: | `a=7` | hat den Wert 7

- Jeder mit einem ; abgeschlossene Ausdruck ist in C eine Anweisung.
--> Ausdrucksanweisung

--> die Zuweisung wird ebenfalls erst durch das abschließende ";" zu einer Anweisung.

Auch ein -wie ein Prozeduraufruf aussehender- alleiniger Funktionsaufruf stellt -mit abschließendem ";" - eine Ausdrucksanweisung dar.

z.B.: | `printf("Hallo !")` | ist ein Sonderfall eines Ausdrucks (ein einzelner Operand)
| `printf("Hallo !");` | ist eine Ausdrucksanweisung

## 4.1 Arithmetische Operatoren

### Arithmetische Operatoren (1) - konventionelle Operatoren

- Binäre Operatoren

`+` | Addition
`-` | Subtraktion
`*` | Multiplikation
`/` | Division (ganzzahlige u n d Gleitpunkt-Division)
`%` | Modulus

- Unäre Operatoren

`+` | Identität | (neu in ANSI-C)
`-` | Negation |  |

- Präzedenz (Priorität)

+ | - | (unär) | höhere Priorität
`*` | `/` | `%` |
`+` | `-` |  |
| ... |  |
`=` |  |  | niedrigere Priorität

Änderung der Priorität durch Klammerung :
Beispiel : `a = (b = c / ( d * e ) ) + f ;`

### Arithmetische Operatoren - konventionelle Operatoren

    /*-----------------------------------------------------------------*/
    /* Programm DIVDEM                                                 */
    /*-----------------------------------------------------------------*/
    /* Demonstrationsprogramm zum Divisions-Operator */
    /*-----------------------------------------------------------------*/

    #include <stdio.h>

    void main(void)
    {
      int   i=7, j=4, k;
      double a,b,c,d;

      k=i/j;            /* k = 1     ! */

      a=i;              /* automatische     */
      b=j;              /* Typkonvertierung */

      c=a/b;            /* c = 1.75  ! */
      d=i/j;            /* d = 1.0   ! */

      printf("\nk = %d\nc = %f\nd = %f\n",k,c,d);

      k=a/b;
      printf("\nk = %d\n",k);

Bildschirmausgabe :

    E:\>divdem

    k = 1
    c = 1.750000
    d = 1.000000

    k = 1

### Arithmetische Operatoren - Increment- und Decrement-Operator

- Unäre Operatoren

`++` | Increment (Erhöhung des Operanden um 1) `x++` entspricht `x=x+1`
`--` | Decrement (Erniedrigung des Operanden um 1) `x--` entspricht `x=x-1`

Operand muß eine Variable sein.
- Verwendung beider Operatoren

  als Prefix-Operator oder
  als Postfix-Operator

Prefix-Operator :

++n | bzw. | --n

  erst Increment bzw. Decrement des Operanden, dann Verwendung des so veränderten Werts

z.B. | `n=5;` `x=++n;` | entspricht | `n=n+1;` `x=n;` | `(x <-- 6)`

Postfix-Operator :

n++ | bzw. | n--

  erst Verwendung des ursprünglichen Operandenwerts, dann Increment bzw. Decrement

z.B. | `n=5;` `x=n++;` | entspricht | `x=n;` `n=n+1;` | `(x <-- 5)`

In Fällen, in denen der Wert des Operanden nicht in zusammengesetzten Ausdrücken vorkommt, sondern allein der Inc.-/Dec.-Effekt zur Veränderung des Operanden ausgenutzt wird, besteht kein Unterschied zwischen Prefix und Postfix.
- Präzedenz

`++` und `--` haben gleiche Priorität wie unäre `+` und `-`.

## 4.2 Vergleichs-Operatoren

- Binäre Operatoren

`>` | größer
`>=` | größer gleich
`<` | kleiner
`<=` | kleiner gleich
`==` | gleich
`!=` | ungleich

Die Vergleichsoperationen liefern als Ergebnis einen Integer-Wert :

1 | wenn der Vergleich erfüllt ist | (--> "TRUE")
0 | wenn der Vergleich nicht erfüllt ist | (--> "FALSE" )

- Präzedenz

arithmetische Operatoren | höhere Priorität

  ...

`>` | `>=` | `<` | `<=`

`==` | `!=`

  ...

`=` | niedrigere Priorität

- Demonstrationsbeispiel

    /*------------------------------------------------------------------*/
    /* Programm VERGLDEM                                                */
    /*------------------------------------------------------------------*/
    /* Demonstration der bei Anwendung der Vergleichsoperationen        */
    /* entstehenden Werte                                               */
    /*------------------------------------------------------------------*/

    #include <stdio.h>

    void main(void)
    {
      int i=3, j=3, k=2;

      printf("i==j --> %d\n",i==j);       /* --> 1  entspr. "TRUE"  */
      printf("i>k  --> %d\n",i>k);        /* --> 1  entspr. "TRUE"  */
      printf("i<k  --> %d\n",i<k);        /* --> 0  entspr. "FALSE" */
    }

    /*------------------------------------------------------------------*/

Bildschirmausgabe :

    E:\>vergldem
    i==j --> 1
    i>k  --> 1
    i<k  --> 0

## 4.3 Logische Operatoren

### Logische Operatoren in C

- Obwohl es in C keinen logischen Datentyp gibt, existieren logische Operatoren.
Logische Werte werden dabei durch int-Werte nachgebildet:

Wert | `0` | entspricht | FALSE
Werte | `!=0` | entsprechen | TRUE

Als Ergebnis einer logischen Operation wird für TRUE immer der Wert "1" erzeugt.
- Binäre Operatoren

`&&` | UND
|| | ODER

- Unärer Operator

`!` | Negation | `!` konvertiert einen Nicht-0 (= TRUE)- Operanden in den Wert 0 und einen 0 (=FALSE)-Operanden in den Wert 1

  Beispiel für Anwendung :

statt | `if (inword==0)`
--> | `if (!inword)`

- Präzedenz :

`!` hat die gleiche Priorität wie die anderen unären Operatoren (z.B. -)

`&&` und `||` haben eine geringere Priorität als die Vergleichsoperatoren, wobei `&&` höhere Priorität als `||` hat.

- Logische Ausdrücke mit && und || werden - von links nach rechts - nur soweit ausgewertet bis der logische Wert des Ergebnisses feststeht.

z.B. wird in `(x&&y++) y++` nur ausgeführt, wenn `x!=0 (=TRUE)` ist

- Die logischen Operatoren lassen sich auch auf float- und double-Werte anwenden.

### Beispiel zu logischen Operatoren in C

    /* ------------------------------------------------------------------- */
    /* Programm CHARZAHL */
    /* ------------------------------------------------------------------- */
    /* Zaehlen der Bu, Zi, SoZ und StZ in dem ueber stdin eingegebenen     */
    /* Text                                                                */
    /* ------------------------------------------------------------------- */

    #include <stdio.h>

    void main(void)

    { int c, zbu, zzi, zso, zst;

      zbu=zzi=zso=zst=0;

      while((c=getchar())!=EOF)
        if (c<='\x1F' || c=='\x7F')                       /* Steuerzeichen */
          zst++;
        else if (c>='0' && c<='9')                       /* Ziffern */
          zzi++;
        else if (c>='A' && c<='Z' || c>='a' && c<='z')   /* Buchstaben */
          zbu++;
        else zso++;                                      /* Sonderzeichen */

      putchar('\n');
      printf("Buchstaben    = %4d\n",zbu);
      printf("Ziffern       = %4d\n",zzi);
      printf("Sonderzeichen = %4d\n",zso);
      printf("Steuerzeichen = %4d\n",zst);
    }
    /* -------------------------------------------------------------------- */

Bildscirmausgabe :

    E:\RT\C\VORL>charzahl <charzahl.c

    Buchstaben    =  318
    Ziffern       =   12
    Sonderzeichen =  823
    Steuerzeichen =   31

## 4.4 Bitmanipulationsoperatoren

- Bitweise logische Operatoren

- Binäre Operatoren

    &   bitweises UND
    |   bitweises ODER
    ^   bitweises EXOR

- Unärer Operator

    ~   bitweise Negation (1-Komplement)

- Anwendung : Maskieren (Setzen, Rücksetzen, Invertieren) einzelner Bits
- Beispiele

1. Setzen aller höherwertigen - bis auf die 7 niederwertigsten - Bits zu 0

    int c,n;
    ...
    c = n & 0x7f;

2. Umwandlung Kleinbuchstaben in Großbuchstaben (ASCII-Code)

    char c;
    ...
    if (c<='z' && c>='a')
      c = c & 0xdf;

3. Umwandlung Großbuchstaben in Kleinbuchstaben (ASCII-Code)

    char C;
    ...
    if (c<='Z' && c>='A')
      c = c | 0x20;

4. Setzen der 6 niederwertigen Bits eines Integer-Wertes zu 0

`int x;` |
... |
`x = x & 0xffc0;` | nur richtig für 16-Bit-Integer ---> nicht portabel
... |
`x = x &~0x3f;` | unabhängig von Integer-Länge ---> portabel

- Schiebeoperatoren

- Binäre Operatoren

`<<` | Linksschieben
`>>` | Rechtsschieben

Der linke Operand wird um soviel Stellen (Bit) nach links bzw rechts geschoben, wie der rechte Operand angibt

Linksschieben : | Nachschieben von 0 |

Rechtsschieben : | wenn linker Operand unsigned : | logisches Schieben
| wenn linker Operand signed : | arithmetisches oder logisches Schieben (compilerabhängig)

- Beispiele

    int x;
    ...
    x = x << 2;         /* Linksschieben des Werts von x um 2 Bit,      */
                        /* Nachschieben von 0  --> Multiplikation mit 4 */

    unsigned y;
    ...
    y = y >> 2;         /* Rechtsschieben des Werts von y um 2 Bit, */
                        /* Nachschieben von 0                       */
                        /* --> Division durch 4, unsigned !         */

1. Funktion getbits(x,p,n), die das n Bit lange Teilfeld der unsigned- Variablen x, das bei der p-ten Bitposition beginnt (Bit 0 : LSB), rechtsbündig liefert (zu den höherwertigen Bits hin durch 0 ergänzt)

    unsigned getbits( unsigned x, unsigned p, unsigned n)
    {
      return ((x >> (p+1-n)) & ~(~0 << n));
    }

## 4.5 Zusammengesetzte Zuweisungsoperatoren

- In praktischen Programmen kommen häufig Anweisungen der Form

    var = var operator ausdruck

vor

Beispiele :

    x = x + wert;
    y = y * faktor;

- Zur vereinfachten Formulierung derartiger Anweisungen existieren in C zusammengesetzte Zuweisungsoperatoren :

`+=` | `:` | `x += ausdr` | `<--` | `x = x + ausdr`
`-=` | `:` | `x -= ausdr` | `<--` | `x = x - ausdr`
`*=` | `:` | `x *= ausdr` | `<--` | `x = x * ausdr`
`/=` | `:` | `x /= ausdr` | `<--` | `x = x / ausdr`
`%=` | `:` | `x %= ausdr` | `<--` | `x = x % ausdr`
`&=` | `:` | `x &= ausdr` | `<--` | `x = x & ausdr`
`|=` | `:` | `x |= ausdr` | `<--` | `x = x | ausdr`
`^=` | `:` | `x ^= ausdr` | `<--` | `x = x ^= ausdr`
`<<=` | `:` | `x <<= ausdr` | `<--` | `x = x << ausdr`
`>>=` | `:` | `x >>= ausdr` | `<--` | `x = x >> ausdr`

- Vorteile der zusammengesetzten Zuweisungsoperatoren :

- Verkürzung der Schreibarbeit
- größere Übersichtlichkeit und Verringerung der Fehlerhäufigkeit bei komplizierteren Variablenbezeichnungen (Komponenten zusammengesetzter Variabler und/oder Pointerobjekte)
- u.U. Erzeugung effektiveren Codes durch Compiler

- Ein mit einem zusammengesetzten Zuweisungsoperator gebildeter Ausdruck hat - wie beim einfachen Zuweisungsoperator - den Wert der der linken Variablen zugewiesen wird.

Beispiel :

`x = 3 + 2` | `/* Ausdruck hat den Wert 5 */`
`x *= 2` | `/* Ausdruck hat den Wert 10 */`

- Priorität :

Die zusammengesetzten Zuweisungsoperatoren haben die gleiche Priorität wie der einfache Zuweisungsoperator (zweitniedrigste Prioritätsstufe)

## 4.6 Typkonvertierung

### Implizite Typumwandlung in C (ANSI-C)

- C gestattet die Verknüpfung unterschiedlicher arithmetischer Datentypen in Ausdrücken --> implizite (automatische) Typumwandlung
- Eine implizite Typumwandlung findet in folgenden Situationen statt :

- Bei Zuweisungen wird der Wert der rechten Seite in den Typ der linken Seite ( = linker Operand) umgewandelt (assignment conversion) :

`int` | `<--` | `float` | `)`
`int` | `<--` | `double` | `)`
| Weglassen des gebrochenen Anteils

`int` | `<--` | `long` | `)`
`char` | `<--` | `int` | `)`
`char` | `<--` | `short` | `)`
| Weglassen der höherwertigen Bits

`float` | `<--` | `double` |
`float )` | `<-- )` | `long, int` |
`double )` | `)` | `short, char` |
| Runden oder Abschneiden (implementierungsabhängig) Wenn nicht exakt darstellbar: Runden oder Abschneiden (implementierungsabh.)

- Bei der Parameterübergabe an Funktionen :

- bei Vorliegen eines Function Prototypes :
Umwandlung des Typs des aktuellen Parameters in den Typ des korrespondierenden formalen Parameters wie bei Zuweisungen
- ohne Function Prototype :
Umwandlung des Typs des aktuellen Parameters nach folgenden Regeln:

`char` | `-->` | `int`
`short` | `-->` | `int`
`float` | `-->` | `double`

- Automatische Umwandlung in arithmetischen Ausdruecken :
(integral widening, integral promotion)

`char (signed), short` | `-->` | `int`
`unsigned char, unsigned short` | `-->` | `int` (wenn alle Werte darstellbar) `unsigned int` (sonst)

- Bei 2-stelligen arithmetischen Operationen wird bei unterschiedlichen Typen der beiden beteiligten Operanden der jeweils "niedrigere" Typ in den "höheren" Typ umgewandelt. Das Ergebnis ist vom jeweils "höheren" Typ. Dabei gilt folgende Typ-Hierarchie :

`long double` | ("höchster" Typ)
`double` |
`float` |
`unsigned long int` |
`long int` |
`unsigned int` |
`int` | ("niedrigster" Typ)

- Umwandlungsregeln für die Umwandlung des "niedrigeren" Typs in den "höheren" Typ bei 2-stelligen arithmetischen Operationen mit typgemischten Operanden (usual arithmetic conversion):

### Explizite Typumwandlung in C : Cast-Operator

- Eine explizite Typumwandlung (Type Cast) erfolgt mittels des Cast-Operators

Der Cast-Operator wird unmittelbar vor den Ausdruck gesetzt, dessen Typ umgewandelt werden soll

---> Cast-Operator ---> Ausdruck --->

- Cast-Operator

- unärer Operator : eine in Klammern gesetzte Typangabe

---> (Typangabe) --->

- Wirkung :
Der Wert des Ausdrucks wird in den durch den Cast-Operator festgelegten Typ umgewandelt
- Präzedenz :
gleiche Priorität wie alle anderen unären Operatoren

- Anwendung :
Überall dort sinnvoll anwendbar, wo ein ganz bestimmter Datentyp notwendig ist, aber eine automatische (implizite) Typumwandlung nicht stattfindet,

z.B.
  bei Pointern,
  bei Pointer-Objekten,
  u.U. bei akt. Parametern, wenn kein Function Prototype vorliegt,
  usw.

- Beispiele :

    ...
    int n=4;
    double dWert;
    dWert = sqrt((double)n);   /* Type Cast notwendig, wenn für sqrt()
                                  Funktionsdeklaration alter Art verwendet
                                  wird, also kein Function Prototype vor-
                                  liegt, da sqrt() Parameter vom Typ
                                  double erwartet */
    ...
    int j=2, k=3;
    float f;

    f = k/j;                   /* f bekommt den Wert 1.0 */

    f = (float)k/j ;           /* f bekommt den Wert 1.5 */

## 4.7 Bedingte Ausdrücke in C

- In C lassen sich mittels des bedingten Auswerteoperators ?: (ein ternärer Operator !) bedingte Ausdrücke formulieren:
e1 ? e2 : e3
Der Wert dieses Ausdrucks ist

= e2, wenn e1 != 0 (enstpr. TRUE)
= e3, wenn e1 == 0 (entspr. FALSE)

- Achtung ! :

  Es wird immer nur entweder e2 oder e3 ausgewertet, nie e2 und e3

- Anwendung :

  Ersatz einer mittels einer if-Anweisung formulierten alternativen Wertzuweisung.

Beispiel : Ermittlung des Maximums zweier Zahlen a und b

statt :
  `if (a>b)`
  `z=a;`
  `else`
  `z=b;`

einfacher und eleganter : `z = (a>b) ? a : b;`

Definition als parameterisierter Makro:

  `#define max(x,y) ((x)>(y)) ? (x) : (y)`

- Falls e2 und e3 von verschiedenem Typ sind, wird der Typ des Ergebnisses durch die impliziten Typumwandlungsregeln bestimmt.

Beispiel :
  `float f;`
  `int n;`
  `(n>0) ? f : n;` ist vom Typ float ! (ANSI-C)

## 4.8 Abarbeitungsreihenfolge

### Hierachie der Operatoren in C

Priorität | Operator | Operation | Assoziativität
--- | --- | --- | ---
15 *) | `()` `[]` `.` `->` | Funktionsaufruf Indizierung (Array) Element (Structure) Element (Structure) | `--->` **)
14 | `-` `!` `++ --` `-` `+` `*` `¨` `(type)` `sizeof` | bitw. Komplement Komplement Increment/Decrement Negation (arithm.) Identität (arith.) Pointer-Objekt Typkonvertierung Größe in Bytes | `<---` **)
13 | `* / %` | Mult./Div. Modulo | `--->`
12 | `+ -` | Addition/subtrakt. | `--->`
11 | `<< >>` | Verschiebung | `--->`
10 | `< > <= >=` | Vergleich | `--->`
9 | `== !=` | Vergleich | `--->`
8 | `&` | bitweises UND | `--->`
7 | `^` | bitweises EXOR | `--->`
6 | `|` | bitweises ODER | `--->`
5 | `&&` | UND | `--->`
4 | `||` | ODER | `--->`
3 | `?:` | bedingte Auswertung | `<---`
2 | `=` `*= /= %=` `+= -=` `<<= >>=` `&= |= ^=` | Zuweisung | `<---`
1 | `,` | sequentielle Auswer. | `--->`

Zum Inhaltsverzeichnis | Zum nächsten Abschnitt

###### Copyright © FH München, FB 04, Prof. Dr. Rainer Thomas
V - PC - 401 - 00 - TH - 01
V - PC - 411 - 00 - TH - 06
V - PC - 412 - 00 - TH - 06
V - PC - 421 - 00 - TH - 04
V - PC - 431 - 00 - TH - 03
V - PC - 440 - 00 - TH - 02
V - PC - 441 - 00 - TH - 05
V - PC - 451 - 00 - TH - 02
V - PC - 452 - 00 - TH - 03
V - PC - 461 - 00 - TH - 02
V - PC - 472 - 00 - TH - 03
V - PC - 473 - 00 - TH - 04
V - PC - 474 - 00 - TH - 02
V - PC - 481 - 00 - TH - 02
V - PC - 491 - 00 - TH - 06