of 27/27
Prof. Dr. Nikolaus Wulff Programmieren in C Programmieren in C Operatoren, Variablen und deren Sichtbarkeit

Programmieren in C Operatoren - fh-muenster.de€¦ · sich Java, PHP, C, C# und C++ bei dieser Operationen gleich verhalten sollten (Konjunktiv!) … Prof. Dr. Nikolaus Wulff Programmieren

  • View
    3

  • Download
    0

Embed Size (px)

Text of Programmieren in C Operatoren - fh-muenster.de€¦ · sich Java, PHP, C, C# und C++ bei dieser...

  • Prof. Dr. Nikolaus Wulff

    Programmieren in CProgrammieren in C

    Operatoren, Variablen und deren Sichtbarkeit

  • Prof. Dr. Nikolaus Wulff Programmieren in C 2

    Auswertung von Ausdrücken

    Was passiert wenn ein Ausdruck wie z. B.

    im Computer abgearbeitet wird?• Welchen Wert haben x und y nach der Ausführung?

    int y,x=2; y = ++x * x++;

    • ++x: Der Wert von x wird um 1 erhöht => x=3• *: Multipliziere x mit sich selbst => 9• y= Weise das Ergebnis y zu => y=9• x++: Der Wert von x wird um 1 erhöht =>x=4• Der Ausdruck liefert also x=4, y=9

  • Prof. Dr. Nikolaus Wulff Programmieren in C 3

    Seiteneffekte von Operatoren

    Dieses Code-Fragment wurde ~2008 getestet.

    int y,x=2; y = ++x * x++;

    Bemerkung (2018):

    Das Ergebnis x=4,y=9 lies sich 2018 mit dem gcc nicht verifizieren, es lieferte x=4, y=12!

    Ein Java Compiler lieferte 2018 nach wie vor x=4, y=9 obwohl sich Java, PHP, C, C# und C++ bei dieser Operationen gleich verhalten sollten (Konjunktiv!) …

  • Prof. Dr. Nikolaus Wulff Programmieren in C 4

    Pedantic & All-Warnings

    • Deshalb sollten die compiler settings auf Pedantic und All-Warnings gesetzt werden, um solche Fehler rechtzeitig zu erkennen. Dann verweigert der gcc die Übersetzung:

  • Prof. Dr. Nikolaus Wulff Programmieren in C 5

    Operator Rangfolge

    • Das Beispiel führt zur Fragestellung in welcher Reihenfolge die C Operatoren ausgeführt werden.

    • Dies darf nicht dem Zufall überlassen sein, sondern muss in der Sprachsyntax festgelegt sein, damit der Compiler eindeutigen Maschinencode generiert.

    • C definiert hierzu eine Rangfolge für alle Operatoren, die regelt welche Operatoren und Ausdrücke in welcher Reihenfolge ausgewertet werden.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 6

    Typen von Operatoren

    • C kennt unäre, binäre und einen trinären Operator, diese benötigen 1, 2 oder 3 Argumente.

    • Unär: – z.B.: -3 , !x , ~y

    • Binär: – z.B.: 3 + 5, x & y, i || (j %2)

    • Trinär: ? : – z.B.: x

  • Prof. Dr. Nikolaus Wulff Programmieren in C 7

    Rangfolge der Operatoren() [] -> . pointer! ~ ++ -- + - * & (type) sizeof* / % arithmetic + - arithmetic> bit-shift< = boolean== != boolean& bit-operation^ bit-operation| bit-operation&& boolean|| boolean?: ternär= += -= *= /= %= &= ^= |= =,

    Höchster Rang

    Niedrigster Rang

    • Wie zu erwarten gilt Punkt- vor Strichrechnung. • Die Vergleiche == und != haben jedoch einen höheren Rang

    als die Bit Operatoren &,| und ^. Deshalb immer klammern! ...

    unärbinär

  • Prof. Dr. Nikolaus Wulff Programmieren in C 8

    Rangeleien

    • Bei Ausdrücken mit gleichem Rang schreibt C die Reihenfolge der Bewertung nicht vor.

    • In diesem Fall ist nicht klar, ob zuerst f(x) und dann g(x) oder umgekehrt ausgewertet wird.

    • Dies sollte im Allgemeinen auch keine Rolle spielen. Anders sieht es aus bei

    • sollten hier f oder g den Wert der Variablen x verändern, so ist das Ergebnis y vermutlich falsch...

    y = f(x) + g(x)

    y = f(&x) + g(&x)

  • Prof. Dr. Nikolaus Wulff Programmieren in C 9

    Pathologische Fälle...

    • Die Implementierung der linken Seite verändert die Variable x, so dass die Berechnung

    • nicht den „richtigen Wert“• sondern statt dessen bzw. liefert.

    double two(double *x) { *x *=2; return *x;}

    double half(double *x) { *x /=2; return *x;}

    double two(double *x) { double tmp = *x; return 2*tmp;}

    double half(double *x) { double tmp = *x; return tmp/2;}

    y = two(&x) + half(&x)

    y = 2*x + x/2 = 2.5*x

    y = 3*x y = 1.5*x

  • Prof. Dr. Nikolaus Wulff Programmieren in C 10

    C's Systemnähe

    • C ermöglicht sehr effizienten, schnellen Code.• Zeiger und Referenzen sowie die Bit Operatoren

    erlauben eine maschinennahe Programmierung mit der sich auf jede Speicherzelle des Rechners zugreifen lässt.

    • Assembler wird seit C immer seltener. • Sinnvoll eingesetzt ist dies eine der Stärken von C.

    Die meisten Hardwaretreiber und Betriebsysteme werden daher in C realisiert.

    • Zugleich ist dies jedoch auch in den Händen des ungeübten Programmierers eine Gefahr...

  • Prof. Dr. Nikolaus Wulff Programmieren in C 11

    Noch einmal Stringcopy

    void stringcopy(char* src, char* dest) { while(*src) { *dest++ = *src++; }}

    void stringcopy(char* src, char* dest) { while( (*dest++ = *src++) ); //nop}

    • Unter Ausnutzung der Operatoren Rangfolge kann unsere stringCopy Implementierung

    • verkürzt werden zu:

    – Zuerst wird die rechte Seite *src ausgewertet,– das Ergebnis wird der linken Seite *dest zugewiesen,– dann wird überprüft ob das Ergebnis ungleich 0 ist, falls ja– werden die Zeiger dest und src inkrementiert ...

  • Prof. Dr. Nikolaus Wulff Programmieren in C 12

    Zeichenketten verschlüsseln

    • Einfach lassen sich Zeichenketten mit der XOR Operation verschlüsseln:

    • Der XOR Operator ^ sorgt für die Verschlüsselung.• *++s == '\0' ist die Abbruchbedingung.• Testlauf: „Hallo World“ coded: „W~ssp?Hpms{“• Frage wie lässt sich die resultierende Zeichenkette

    wieder dekodieren?

    #define MASK 0x1Fvoid encode(char *s) { do { *s ^= MASK; } while(*++s); }

  • Prof. Dr. Nikolaus Wulff Programmieren in C 13

    Zeichenfelder vergleichen

    • Effizient lässt sich das alphabetische Vergleichen zweier Zeichenketten implementieren:

    • Die Funktion liefert 0 wenn die Felder s und d identisch sind, ansonsten einen negativen oder positiven Wert, je nach dem ob s alphabetisch kleiner ist als d oder nicht.

    int stringcompare(char s[], char d[]) { int i; for(i=0; s[i] == d[i]; i++) { if(s[i] == '\0') return 0; } return s[i] - d[i] ;}

  • Prof. Dr. Nikolaus Wulff Programmieren in C 14

    Zeichenketten Differenz

    • Die Methode stringdiff liefert die Differenz der Zeichenketten s und d. D.h. sucht das Vorkommen der ersten Abweichung von s in d.

    • Als Resultat liefert sie einen Zeiger char* auf die restliche Zeichenkette aus s zurück.

    char* stringdiff(char *s, char *d) { while((*s++ == *d++)) { if(*s == '\0') return s; } return (--s);}

  • Prof. Dr. Nikolaus Wulff Programmieren in C 15

    Falsche StringDiff Variante

    • Sehr leicht lässt sich mit Zeigern Chaos anrichten, wie das folgende Beispiel zeigt:

    • Das Programm enthält einen schweren Fehler:– Der Aufrufende erwartet eine Zeichenkette erhält

    jedoch eine Referenz auf eine lokale Variable &c.

    char* stupidDiff(char *s, char *d) { char c; for(; *s; s++, d++) { c = *s; if (c != *d) { return &c; } } return '\0';}

    Achtung Bug!Der Compiler generierteine Warnung.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 16

    Lebensdauer von Variablen

    Diese letzte Beispiel wirft eine Reihe von Frage auf:

    • Welche Zeiger dürfen eigentlich sinnvoller Weise von Methoden zurückgegeben werden?

    • Wie lange sind die Variablen gültig?• Wann und von wem wird der von den Variablen

    belegte Speicherplatz wieder frei gegeben?• Wie lange sind Zeiger auf Variablen gültig?

  • Prof. Dr. Nikolaus Wulff Programmieren in C 17

    Variablen • Ein Programm besteht im Prinzip nur aus

    Funktionen und Variablen. • Die Funktionen kapseln das Verhalten.• Die Variablen den Status des Programms.• Variablen besitzen eine bestimmte Lebensdauer und

    eine bestimmte Sichtbarkeit.• Diese subtileren Eigenschaften kommen erst dann

    zum Tragen, wenn das Programm aus verschiedenen Funktionen und möglicherweise unterschiedlichen Quelldateien besteht, die zusammen gebunden werden.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 18

    Interne & lokale Variablen

    • n: Variable als Parameter• i, f: lokale Variablen, gültig

    und sichtbar innerhalb der Funktion factorial.

    • es wird nicht f, sondern der Wert von f per return zurückgegeben

    • Eine Variable kann als Parameter an eine Funktion übergeben werden oder lokal innerhalb einer Methode definiert werden:

    • Ausserhalb der Funktion factorial besitzen i und f keine Gültigkeit und sind dem Compiler unbekannt.

    int factorial(int n){ int i, f; f = 1; for(i=2; i

  • Prof. Dr. Nikolaus Wulff Programmieren in C 19

    Sichtbarkeit von Variablen• Variablen sind i. A. nur lokal innerhalb ihrer sie

    definierenden Funktion gültig und sichtbar. • Der selbe Bezeichner kann eine unterschiedliche

    Bedeutung in verschieden Funktionen haben:

    void foo(int x){ int sum; ...}void bar(double sum) { int x;}

    • Beide Funktionen foo und bar verwenden zwei Bezeichner: x und sum.

    • Diese haben jedoch nichts miteinander gemeinsam, außer (zufällig per Konstruktion) den selben Namen.

    • Jede der Variablen ist nur lokal innerhalb der jeweiligen Funktion foo oder bar sichtbar.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 20

    > i=4, j=2

    Block scope• Lokale Variablen sind immer innerhalb eines durch

    die Klammern „{“ und „}“ markierten Blocks gültig und sichtbar.

    • Dies kann auch bedeuten, dass eine Variable innerhalb einer Methode zweimal deklariert wird:

    void local() { int i = 1; printf("local i=%d \n", i); { /* block begin */ int j = 2; int i = 4; printf("block i=%d, j=%d \n", i,j); } /* block end */ printf("after block local i=%d \n", i); printf("after block local j=%d \n", j);}

    Compile Error

    > i=1

    > i=1

  • Prof. Dr. Nikolaus Wulff Programmieren in C 21

    Lokale Variablen• Lokale Variablen sind nur innerhalb der sie

    definierenden Funktion und Blöcke gültig.• Sobald der sie einschließende Block verlassen wird

    verschwindet die Variable, d.h. der Speicherplatz wird automatisch freigegeben.

    • Automatische Variablen behalten ihren Wert zwischen zwei Funktionsaufrufen nicht. Sie müssen daher immer wieder neu initialisiert werden.

    • Einzige Ausnahme ist, die Variable innerhalb der Methode als static zu definieren, dann bleibt der Wert zwischen zwei Aufrufen erhalten.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 22

    Statische lokale Variablen• Die Variable count behält ihren Wert zwischen

    zwei Aufrufen der Funktion remember:

    • Im Gegensatz zu den automatischen besitzen die lokalen statischen Variablen ein „Gedächtnis“.

    int remember(int n) { static int count; if(n==0) { count=0; } else { count+=n; } return count;}

    count ist statisch definiert

  • Prof. Dr. Nikolaus Wulff Programmieren in C 23

    Globale Variablen• Anstatt eine lokale Variable zu verwenden, hätte

    count auch als eine globale Variable außerhalb der Funktion definiert werden können:

    • Auch diese Variable besitzt ein „Gedächtnis“, das für die gesamte Dauer des Programms gültig ist.

    int count=0;

    int remember(int n) { if(n==0) { count=0; } else { count+=n; } return count;}

    count ist global definiert

  • Prof. Dr. Nikolaus Wulff Programmieren in C 24

    Achtung: Die Funktionen können sich die Werte unter Umständen gegenseitig überschreiben. Dies kann erwünscht oder unerwünscht sein...

    Funktionsübergreifende Variablen• Eine globale Variable ist nicht nur innerhalb einer

    Funktion sichtbar, sondern ist für die gesamte Programmlaufzeit definiert.

    • Somit kann der Wert dieser Variablen von verschiedenen Funktionen gemeinsam genutzt werden.

    • Alle Funktionen innerhalb einer *.c Datei „teilen“ sich die globalen Variablen.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 25

    Externe Variablen• Eine in einer Datei global definierte Variable kann

    aus einer anderen Quelldatei aus angesprochen und verwendet werden, wenn sie dort als extern definiert ist (geschieht meist in einer *.h Datei):int count=0;

    int remember(int n) { if(n==0) { count=0; } else { count+=n; } return count;}

    extern int count;

    void dosomething() { if(count==10) { count=-5; } else { ... } }

    remember.c something.c

  • Prof. Dr. Nikolaus Wulff Programmieren in C 26

    Sichtbarkeit globaler Variablen• Globale Variablen können im Programm nur einmal

    definiert werden. Dies kann zu Problemen führen, wenn solch allgemeine Bezeichner wie i, j, count oder ähnliches verwendet werden, die in vielen Kontexten vorkommen.

    • Soll vermieden werden, dass eine globale Variable außerhalb der Quelldatei sichtbar ist, so muss sie als static deklariert werden.

    • Sie ist dann global, behält ihr „Gedächtnis“ ist jedoch nur von Funktionen innerhalb der selben Quelldatei aus erreichbar.

  • Prof. Dr. Nikolaus Wulff Programmieren in C 27

    Statische globale Variablen• Sowohl remember.c als auch something.c besitzen

    ihre eigene globale count Variable, diese sind jedoch verschieden und nur innerhalb ihrer jeweiligen Quelldatei sichtbar:

    static int count=0;

    int remember(int n) { if(n==0) { count=0; } else { count+=n; } return count;}

    static int count=4;

    void dosomething() { if(count==10) ...}void somethingelse() { if(count==3) { ...

    remember.c something.c

    Programmieren in CFolie 2Folie 3Folie 4Folie 5Folie 6Folie 7Folie 8Folie 9Folie 10Folie 11Folie 12Folie 13Folie 14Folie 15Folie 16Folie 17Folie 18Folie 19Folie 20Folie 21Folie 22Folie 23Folie 24Folie 25Folie 26Folie 27