3. Operatoren und Ausdrücke Motivation Ein Ausdruck ist eine Folge aus Operanden und Operatoren und hat einen Wert Beispiel: 1 1.0 1.0 + + + 2 2 2.0 Allgemein: Operand 1 Operator Operand 2 „drei“ „drei“ „drei“ ergibt ein Ergebnis Fragestellungen: Was passiert bei unterschiedlichen Typen von Operanden? Welchen Typ erhält das Ergebnis? Welche Rechenvorschrift verbirgt sich hinter dem Operator ? In welcher Reihenfolge wird bei mehr als 2 Operanden ausgewertet (Vorrang)? Wie kann man Typen ggf. umwandeln? Dr. Norbert Spangler / Programmieren I 16.10.2015 2 3.1 Zuweisungsoperator Der „Befehl“ Zuweisung: i = 1; Variable = Ausdruck L-Wert Operand Zuweisungsoperator Operator R-Wert Operand Ergebnis/Wert? C++ ist so aufgebaut, dass Ausdrücke (also auch eine Zuweisung) immer einen Wert haben, der auch abgefragt werden kann. Hier wird der Wert des Ausdrucks rechts vom = Operator genommen, also 1 d.h. der Ausdruck i=1 hat den Wert 1, d.h. den R-Wert. Dr. Norbert Spangler / Programmieren I 16.10.2015 3 Wirkungsweise Variable = Ausdruck L-Wert = R-Wert Es wird zuerst der Wert des Ausdrucks berechnet (R-Wert), der i.d.R. in einem internen Speicherplatz gespeichert wird. Dieser Wert überschreibt dann den alten Inhalt der Variablen (L-Wert) Beispiel: loesung1=(-b+sqrt(b*b-4*a*c)/2; loesung2=(-b-sqrt(b*b-4*a*c)/2; double h=loesung1; //Vertauschen von loesung1 und loesung2 loesung1=loesung2; loesung2=h; i=j=5; // Vorrang:Auswertung von rechts nach links also i=(j=1); Dr. Norbert Spangler / Programmieren I 16.10.2015 4 Zusammengesetzte Zuweisung Anstelle von Übliche Schreibweise i=i+1; Alternative in C++ i+=1; d=d*(r+2); d*=r+2; summe=summe+summand; summe+=summand; Summation: neue Summe = alte Summe + Summand Allgemein v=v op r; Beispiele += Dr. Norbert Spangler / Programmieren I -= v op= r; *= /= %= 16.10.2015 5 3.2 Binäre arithmetische Operatoren + - * / % (modulo für ganzzahlige Op.) für arithmetische Datentypen Beispiele : 7+5 22-10 2.5*2.5*3.14159 23/10 23.0/10 7%2 ergibt 1 27%4 ergibt 3 In einem Ausdruck können Funktionen (genauer Funktionswerte), Variable, Konstante und Literale auftreten und natürlich auch Ausdrücke, auch in Klammern. sqrt(b*b-4*f(a)*(b-a))/(x+y) Dies entspricht der üblichen mathematischen Schreibweise. Dr. Norbert Spangler / Programmieren I 16.10.2015 6 Typ des Ergebnisses In C++ werden immer Operanden eines Typs miteinander verknüpft. Das Ergebnis ist dann vom selben Typ. Unterschiedliche Typen werden vorher intern „passend“ umgewandelt int op int -> Ergebnistyp: int Bsp. 3/2 ergibt 1 (!!!!) double op double -> Ergebnistyp: double Bsp. 3.2/2.5 ergibt 1.28 int op double -> double op double -> Ergebnistyp: double Bsp. 3/2. ergibt 1.5 Weitere Beispiele zur internen Umwandlung short op int -> int op int float op double -> double op double usw. Dr. Norbert Spangler / Programmieren I 16.10.2015 7 Vorrangregeln Bei Ausdrücken wie a op b op c …gelten die üblichen Vorrangregeln Punkt vor Strich Klammern haben höhere Priorität Bei gleichen Operatoren oft von links nach rechts Beispiel : 7 + 5*3*4 – 7 5*3 15 * 4 7 + 60 67 – 7 ergibt 15 ergibt 60 ergibt 67 ergibt 60 Hinweis : Klammern verwenden !!!!! Merke : a + b + c ist nicht immer dasselbe wie a + c + b (Rundung !!!!) Dr. Norbert Spangler / Programmieren I 16.10.2015 8 3.3 Unäre arithmetische Operatoren Vorzeichenoperatoren Wirkung : + - -a Umkehrung des Vorzeichens +n Wert des Operanden (nicht benötigt) Dr. Norbert Spangler / Programmieren I 16.10.2015 9 Inkrement/Dekrement Inkrement-Operator Dekrement-Operator Wirkung ++ -++i erhöhe i um 1 rechne/arbeite mit dem erhöhten Wert i++ rechne/arbeite mit dem „alten“ Wert erhöhe danach i um 1 --i vermindere i um 1 rechne/arbeite mit dem verminderten Wert i-- rechne/arbeite mit dem „alten“ Wert vermindere danach i um 1 Merke : nicht auf Konstanten anwendbar Dr. Norbert Spangler / Programmieren I 16.10.2015 10 Inkrement/Dekrement: Beispiel #include <iostream> using namespace std; void main() { int i=0; cout<<++i<<endl; cout<<i++<<endl; cout<<i<<endl; cout<<--i<<endl; cout<<i--<<endl; cout<<i<<endl; } Dr. Norbert Spangler / Programmieren I 16.10.2015 11 Inkrement/Dekrement Die Verwendung von ++ bzw. -- hat zur Konsequenz, dass während der Auswertung eines (ev. sehr komplexen) Ausdrucks „nebenbei“ eine Variable geändert wird -> Seiteneffekt (sollte dort vermieden werden) -> Fehleranfällig -> schwer lesbar/nachvollziehbar etwa bei j=(i++ - k--)*(++i - --k) Verwendung in Schleifen ist „Standard“: for (int i=0;i<n;i++) ->später Erhöhung einer Variablen um 1: schritte++; //anstelle von schritte=schritte+1 oder schritte+=1; Dr. Norbert Spangler / Programmieren I 16.10.2015 12 3.4 Vergleichsoperatoren Beispiel: a>0 Der Ergebnistyp ist bool ! < <= > >= == != kleiner kleiner gleich größer größer gleich gleich Häufiger Fehler: man schreibt a=0 ungleich arithmetische Operatoren haben Vorrang vor Vergleichsoperatoren a–3 <b+1 Berechne a-3 Berechne b+1 Vergleiche Lesbarer ist (a-3) < (b+1) Dr. Norbert Spangler / Programmieren I 16.10.2015 13 Vergleichsoperatoren/Beispiel #include <iostream> using namespace std; void main() { int i=0; cout<<"i<1 "<<(i<1)<<endl; cout<<"i>1 "<<(i>1)<<endl; cout<<"i=1 "<<(i=1)<<endl; } Dr. Norbert Spangler / Programmieren I !!!!! 16.10.2015 14 Vergleichsoperatoren/weitere Beispiele char c, d; c < d // c steht im ASCII-Code vor d string s1,s2 s1 < s2 // s1 steht im Lexikon vor s2 (gemäß ASCII-Code) bool b; b==true // ungeschickt b // ist gleichwertig und besser Noch besser: lesbare Namen für boolsche Variable wie : istWahr – istKleiner – istBebaut Dr. Norbert Spangler / Programmieren I 16.10.2015 15 3.5 Logische Operatoren && || ! Tabelle a true true false false und oder nicht b true false true false Dr. Norbert Spangler / Programmieren I (binärer Operator) (binärer Operator) (unärer Operator) a&&b true false false false a||b true true true false !a false false true true 16.10.2015 16 Logische Operatoren/Beispiele double x; 0 < x && x <= 1 // x liegt im Intervall (0,1]) besser ( 0 < x ) && ( x <= 1 ) char c; ('a' <= c) && (c<='z') // c ist ein Kleinbuchstabe ('A' <= c) && (c<= 'Z') // c ist ein Großbuchstabe ('0' <= c) && (c<='9') // c ist eine Ziffer bool b,c; b && !c || c Dr. Norbert Spangler / Programmieren I 16.10.2015 17 3.6 Typumwandlungen Implizite Typumwandlungen (automatisch durch C++) Konvertierung arithmetischer Operanden z.B. int -> double Beispiel: u=2*r*pi; Explizite Typumwandlungen Aufruf eines Cast-Operators Anmerkung Man sollte in der Regel einen Cast-Operator verwenden und nicht die implizite Typumwandlung. Damit wird dokumentiert, dass die Umwandlung vom Programmierer beabsichtigt ist und kein Versehen. Dr. Norbert Spangler / Programmieren I 16.10.2015 18 Implizite Typumwandlungen Bei Rechnungen mit arithmetischen Datentypen findet eine automatische Konvertierung statt. Beispiel : #include <iostream> using namespace std; void main() { int i=1,j; double r,s=2.3; r=i; j=s; cout << i<<" "<<s<<endl; cout <<showpoint<< r<<" "<<j<<endl; } ->Eventuell Warnung wg. Informationsverlust c:\test\main.cpp(9) : warning C4244: '=': Konvertierung von 'double' in 'int', möglicher Datenverlust Dr. Norbert Spangler / Programmieren I 16.10.2015 19 Explizite Typumwandlungen/C-Cast Durch Anwendung des Cast-Operators (typ) ist es möglich, explizit den Typ eines Ausdrucks zu ändern : (typ) Ausdruck // sogenannter C-Cast Alternative Schreibweise typ(Ausdruck) //sogenannter „Funktions-Cast“ Beispiel: int i=5,j=2,k; double y=2.5; k=int(y) ; y=(double)i/j; y=double(i)/double(j) ; y=(double)(j/i); Dr. Norbert Spangler / Programmieren I Ergebnis k=2 y=2.5 / j wird implizit umgewandelt y=2.5 y=0.0 16.10.2015 20 Explizite Typumwandlungen /C++-Cast Schreibweisen: static_cast<typ>(Ausdruck) Beispiel: int i=5,j=2,k; double y=2.5; k=static_cast<int>(y); Ergebnis k=2 y=static_cast<double>(i)/j; y=2.5 / j wird implizit umgewandelt y=static_cast<double>(i)/static_cast<double>(j); y=2.5 / schwer lesbar Anmerkung: Diese Schreibweise orientiert sich an der üblichen für Cast-Operatoren in C++ (es gibt noch weitere). Dr. Norbert Spangler / Programmieren I 16.10.2015 21 3.7 sizeof-Operator Der sizeof-Operator gibt die Anzahl der erforderlichen Bytes für die Speicherung von Typen, Variablen, Konstanten bzw. Ausdrücken an. #include <iostream> using namespace std; void main() { cout <<"\n int \t\t "<< sizeof(int); cout <<"\n int \t\t "<< sizeof(1+2*3); cout <<"\n long \t\t "<< sizeof(long); cout <<"\n char A \t "<< sizeof('A'); cout <<"\n Zeichenkette A "<< sizeof("A") << " !!!!"; cout <<"\n cin \t\t"<< sizeof(cin); cout <<"\n cout \t\t" << sizeof(cout); cout <<endl; } Dr. Norbert Spangler / Programmieren I 16.10.2015 22 3.8 Vorrangregeln Wenn mehrere Operanden und Operatoren in einem Ausdruck auftreten sind Klammern (höchste Priorität) und Vorrangregeln (z.B. Punkt vor Strich) zu beachten, aber auch, ob gleichrangige Operatoren von links nach rechts oder umgekehrt ausgewertet werden. Möglichst Klammern verwenden anstelle von Regeln Komplexe Ausdrücke zerlegen mit Hilfsvariablen cout<<“Ergebnis“<<-u+v*r-s<<endl; besser cout<<“Ergebnis“<< (-u+v*r-s) <<endl; x1=(-(r+s)/3+sqrt((r+s)*(r+s)/9-4*(u-2*v)*c))/(2*(u-2*v)); besser double a=u-2*v; Siehe Tabellen double b=(r+s)/3; double dis=b*b-4*a*c; x1 =( -b+sqrt(dis))/2/a; Dr. Norbert Spangler / Programmieren I 16.10.2015 23 3.9 Kommentare Kommentare werden durch besondere Zeichen eingeleitet: 1) Alles was nach den Zeichen // in einer Zeile steht 2) Alles was zwischen den Zeichen /* .... */ steht Kommentare beeinflussen den Lauf eines Programms in keiner Weise. großzügige Verwendung von Kommentaren Was macht das Programm – der Programmabschnitt – der Befehl ? Was wird eingelesen – was wird ausgegeben (Schnittstellen) ? Welche Variablen werden verwendet (sofern keine sprechenden Namen) ? Dr. Norbert Spangler / Programmieren I 16.10.2015 24 3.10 Escape-Sequenzen Zeichen mit besonderer Bedeutung wie etwa " , sowie grafisch nicht darstellbare Zeichen können nur mittels einer besonderen Technik verwendet werden: Escape-Sequenzen Diese beginnt immer mit dem Zeichen \ (Backslash). und wird in der Regel innerhalb von Zeichenketten speziell bei der Ausgabe eingesetzt: cout<< "\n"; gibt das Zeichen Neue Zeile aus cout<< "\tabc"; positioniert abc auf der nächsten Tabulatorposition cout<< "\""; gibt das Zeichen Doppelapostroph aus cout<< "\\"; gibt das Zeichen Backslash aus Dr. Norbert Spangler / Programmieren I 16.10.2015 25 Escape-Sequenzen Einzelzeichen Bedeutung ASCII-Zeichen ASCII-Code \a alert BEL 07 \b backspace BS 08 \t horizontal tab HT 09 \n line feed LF 0A \v vertical tab VT 0B \f form feed FF 0C \r carriage return CR 0D \” “ “ 22 \‘ ‘ ‘ 27 \? ? ? 3F \\ \ \ 5C \0 Stringende-Zeichen NUL 00 \ooo (max. 3 Oktalziffern) Numerischer Wert eines Zeichens ooo \xhh (Hexadezimalziffern) Numerischer Wert eines Zeichens hh Dr. Norbert Spangler / Programmieren I 16.10.2015 26 3.11 Sonstige Operatoren Später: * Operator -> Pfeiloperator & Adressoperator #include <iostream> using namespace std; void main() { int i=1,j=2; cout<<"i&&j "<<(i&&j)<<endl; cout<<"i&j "<<(i&j)<<endl; Bitoperatoren (eventuell später) cout<<"i||j "<<(i||j)<<endl; & und cout<<"i|j "<<(i|j)<<endl; | oder } ^ exklusives oder ~ nicht >> Rechtsshift << Linksshift Dr. Norbert Spangler / Programmieren I 16.10.2015 27 3.12 Risiken der Arithmetik Ganze Zahlen: Überlauf 0111000011110000 positiv Ganze Zahlen werden „naiv“ addiert ohne + 0111000011110000 positiv Berücksichtigung des Vorzeichenbits = 1110000111100000 negativ Sind positive Summanden zu groß kann das Ergebnis negativ werden !!!! Gleitpunksubtraktion – Genauigkeitsverlust z=0.00000798702 float x=0.321699, y=0.321691,z=y-x; nur noch 2 Stellen genau !!! wahres Ergebnis z=0.00000800000 x und y hatten 6 Dezimalstellen Genauigkeit – z nur noch 2 !!!!! Die Subtraktion etwa gleich großer Zahlen führt zum Verlust der Genauigkeit. Gleitpunktaddition – kleine + große Zahlen es ist c = a double a=1.0,b=1.0e-16,c=a+b; a+b+b+b+b+b+b+b+b+b+b = a ( Auswertung von links nach rechts) b+b+b+b+b+b+b+b+b+b+a >a Die Addition unterschiedlich großer Zahlen führt zu Rechenfehlern. Dr. Norbert Spangler / Programmieren I 16.10.2015 28 Risiken der Arithmetik/Beispielprogramm void main() { //Ueberlauf bei ganzen Zahlen short i=28912, j=i, k=i+j; cout<<"Ueberlauf ganzer Zahlen"<<endl; cout<<i<<" + "<<j<<" = "<<k<<endl; //Genauigkeitsverlust bei Gleitkommazahlen float x=0.321699,y=0.321691,z=y-x; cout<<endl<<"Subtraktion gleich grosser Zahlen"<<endl; cout<<y<<" - "<<x<<" = "<<z<<endl; //Kleine+große Gleitkommazahlen double a=1.0,b=1.0e-16,c=a+b; cout<<endl<<"Addition kleiner und grosser Zahlen"<<endl; cout<<" a = c "<<boolalpha<<(a==c)<<endl; cout<<setw(18)<<setprecision(16)<<a+b+b+b+b+b+b+b+b+b+b<<endl; cout<<setw(18)<<setprecision(16)<<b+b+b+b+b+b+b+b+b+b+a<<endl; } Dr. Norbert Spangler / Programmieren I Genauere Betrachtung -> Numerische Mathematikt 16.10.2015 29 Risiken der Arithmetik/numerische Probleme Beispiel 1: Quadratische Gleichung ax2+bx+c=0 mit a=0.01 b=0.06 c=0.09 Lösung: 2-fache Nullstelle -3 Diskriminante mit float : b2-4ac = -2.2E-10<0 -> keine reelle Lösung!!! Beispiel 2 (RWTH Aachen): Man berechne f(x) = 1 – x*( (x+1)/x – 1) für double x=1E-7 Lösung: es ist stets f(x)=0 Ergebnis: -2.2E-16 Beispiel 3( Uni Karlsruhe): Berechne für x=192119201 und y=35675640 den Ausdruck z=(1682xy4 + 3x3 + 29xy2 – 2 x5 +832)/107751 Korrekt ist z=1783 z mit long double: 7.1806E+20 Dr. Norbert Spangler / Programmieren I 16.10.2015 30
© Copyright 2025 ExpyDoc