Arithmetische Operatoren
Arithmetische Operatoren wenden Standard-Rechenoperationen auf ihre Operanden an.
| Dieser Abschnitt ist unvollständig Grund: Berücksichtigen Sie eine allgemeinere ToC für diese und andere Tabellen, die mehrere Themen abdecken |
| Operator | Operatorname | Beispiel | Ergebnis |
|---|---|---|---|
| + | unäres Plus | +a | der Wert von a nach den Promotionen |
| - | unäres Minus | -a | das Negative von a |
| + | Addition | a + b | die Addition von a und b |
| - | Subtraktion | a - b | die Subtraktion von b von a |
| * | Produkt | a * b | das Produkt von a und b |
| / | Division | a / b | die Division von a durch b |
| % | remainder | a % b | der Rest von a geteilt durch b |
| ~ | Bitweises NICHT | ~a | das bitweise NICHT von a |
| & | Bitweises UND | a & b | das bitweise UND von a und b |
| | | Bitweises ODER | a | b | das bitweise ODER von a und b |
| ^ | Bitweises XOR | a ^ b | das bitweise XOR von a und b |
| << | Bitweise Linksverschiebung | a << b | a um b Stellen nach links verschoben |
| >> | Bitweise Rechtsverschiebung | a >> b | a um b Stellen nach rechts verschoben |
Inhalt |
[editieren] Überläufe
Arithmetik mit vorzeichenlosen ganzen Zahlen wird immer modulo 2n
durchgeführt, wobei n die Anzahl der Bits in dieser spezifischen Ganzzahl ist. Z.B. für unsigned int ergibt die Addition von eins zu UINT_MAX 0, und die Subtraktion von eins von 0 ergibt UINT_MAX.
Wenn bei der arithmetischen Operation mit vorzeichenbehafteten ganzen Zahlen ein Überlauf auftritt (das Ergebnis passt nicht in den Ergebnistyp), ist das Verhalten undefiniert: Es kann gemäß den Regeln der Darstellung (typischerweise 2er-Komplement) umbrechen, es kann auf einigen Plattformen oder aufgrund von Compiler-Optionen (z. B. -ftrapv in GCC und Clang) einen Fehler auslösen oder vom Compiler vollständig wegoptimiert werden.
[editieren] Gleitkomma-Umgebung
Wenn #pragma STDC FENV_ACCESS auf ON gesetzt ist, folgen alle Gleitkomma-Arithmetikoperatoren der aktuellen Rundungsrichtung für Gleitkommazahlen und melden Gleitkomma-Rechenfehler wie in math_errhandling angegeben, es sei denn, sie sind Teil eines statischen Initialisierers (in diesem Fall werden keine Gleitkomma-Ausnahmen ausgelöst und der Rundungsmodus ist "nächstliegend").
[editieren] Gleitkomma-Kontraktion
Sofern #pragma STDC FP_CONTRACT nicht auf OFF gesetzt ist, kann die gesamte Gleitkomma-Arithmetik so durchgeführt werden, als ob die Zwischenergebnisse unendliche Reichweite und Präzision hätten, d. h. Optimierungen, die Rundungsfehler und Gleitkomma-Ausnahmen weglassen, die beobachtet würden, wenn der Ausdruck exakt wie geschrieben ausgewertet würde. Dies erlaubt zum Beispiel die Implementierung von (x*y) + z mit einer einzigen fusionierten Multiply-Add-CPU-Anweisung oder die Optimierung von a = x*x*x*x; als tmp = x*x; a = tmp*tmp.
Unabhängig von der Kontraktion können Zwischenergebnisse der Gleitkomma-Arithmetik eine Reichweite und Präzision haben, die sich von der durch ihren Typ angezeigten unterscheidet; siehe FLT_EVAL_METHOD.
[editieren] Unäre Arithmetik
Die unären arithmetischen Operator-Ausdrücke haben die Form
+ expression |
(1) | ||||||||
- expression |
(2) | ||||||||
| expression | - | Ausdruck eines jeden arithmetischen Typs |
Sowohl das unäre Plus als auch das unäre Minus wenden zuerst Ganzzahl-Promotionen auf ihren Operanden an, und dann
- gibt das unäre Plus den Wert nach der Promotion zurück
- gibt das unäre Minus das Negative des Wertes nach der Promotion zurück (außer dass das Negative einer NaN eine andere NaN ist)
Der Typ des Ausdrucks ist der Typ nach der Promotion, und die Wertkategorie ist nicht-lvalue.
[editieren] Hinweise
Das unäre Minus ruft undefiniertes Verhalten aufgrund eines Überlaufs bei vorzeichenbehafteten Ganzzahlen hervor, wenn es auf INT_MIN, LONG_MIN oder LLONG_MIN angewendet wird, auf typischen (2er-Komplement) Plattformen.
In C++ kann der unäre Operator + auch mit anderen eingebauten Typen wie Arrays und Funktionen verwendet werden, in C jedoch nicht.
#include <stdio.h> #include <complex.h> #include <limits.h> int main(void) { char c = 'a'; printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c); printf("-1, where 1 is signed: %d\n", -1); // Defined behavior since arithmetic is performed for unsigned integer. // Hence, the calculation is (-1) modulo (2 raised to n) = UINT_MAX, where n is // the number of bits of unsigned int. If unsigned int is 32-bit long, then this // gives (-1) modulo (2 raised to 32) = 4294967295 printf("-1, where 1 is unsigned: %u\n", -1u); // Undefined behavior because the mathematical value of -INT_MIN = INT_MAX + 1 // (i.e. 1 more than the maximum possible value for signed int) // // printf("%d\n", -INT_MIN); // Undefined behavior because the mathematical value of -LONG_MIN = LONG_MAX + 1 // (i.e. 1 more than the maximum possible value for signed long) // // printf("%ld\n", -LONG_MIN); // Undefined behavior because the mathematical value of -LLONG_MIN = LLONG_MAX + 1 // (i.e. 1 more than the maximum possible value for signed long long) // // printf("%lld\n", -LLONG_MIN); double complex z = 1 + 2*I; printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z)); }
Mögliche Ausgabe
sizeof char: 1 sizeof int: 4 -1, where 1 is signed: -1 -1, where 1 is unsigned: 4294967295 -(1+2i) = -1.0-2.0
[editieren] Additive Operatoren
Die binären additiven arithmetischen Operator-Ausdrücke haben die Form
lhs + rhs |
(1) | ||||||||
lhs - rhs |
(2) | ||||||||
- beide haben arithmetische Typen, einschließlich komplexer und imaginärer Zahlen
- einer ist ein Zeiger auf einen vollständigen Objekttyp, der andere hat einen Ganzzahltyp
- beide haben arithmetische Typen, einschließlich komplexer und imaginärer Zahlen
- lhs hat einen Zeiger auf einen vollständigen Objekttyp, rhs hat einen Ganzzahltyp
- beide sind Zeiger auf vollständige Objekte von kompatiblen Typen, wobei Qualifizierer ignoriert werden
[editieren] Arithmetische Addition und Subtraktion
Wenn beide Operanden arithmetische Typen haben, dann
- werden zuerst übliche arithmetische Konversionen durchgeführt
- dann werden die Werte der Operanden nach den Konversionen gemäß den üblichen mathematischen Regeln addiert oder subtrahiert (bei der Subtraktion wird rhs von lhs subtrahiert), außer dass
- wenn ein Operand NaN ist, ist das Ergebnis NaN
- unendlich minus unendlich ist NaN und FE_INVALID wird ausgelöst
- unendlich plus negativer Unendlichkeit ist NaN und FE_INVALID wird ausgelöst
Die Addition und Subtraktion von komplexen und imaginären Zahlen ist wie folgt definiert (beachten Sie, dass der Ergebnistyp imaginär ist, wenn beide Operanden imaginär sind, und komplex, wenn ein Operand reell und der andere imaginär ist, wie durch die üblichen arithmetischen Konversionen bestimmt)
| + oder - | u | iv | u + iv |
|---|---|---|---|
| x | x ± u | x ± iv | (x ± u) ± iv |
| iy | ±u + iy | i(y ± v) | ±u + i(y ± v) |
| x + iy | (x ± u) + iy | x + i(y ± v) | (x ± u) + i(y ± v) |
// work in progress // note: take part of the c/language/conversion example
[editieren] Zeiger-Arithmetik
- Wenn der Zeiger
Pauf ein Element eines Arrays mit dem IndexIzeigt, dann
- P+N und N+P sind Zeiger, die auf ein Element desselben Arrays mit dem Index
I+Nzeigen - P-N ist ein Zeiger, der auf ein Element desselben Arrays mit dem Index
I-Nzeigt
- P+N und N+P sind Zeiger, die auf ein Element desselben Arrays mit dem Index
Das Verhalten ist nur dann definiert, wenn sowohl der ursprüngliche Zeiger als auch der Ergebniszeiger auf Elemente desselben Arrays oder eine Position hinter dem Ende dieses Arrays zeigen. Beachten Sie, dass die Ausführung von p-1, wenn p auf das erste Element eines Arrays zeigt, undefiniertes Verhalten ist und auf einigen Plattformen fehlschlagen kann.
- Wenn der Zeiger
P1auf ein Element eines Arrays mit dem IndexI(oder eine Position dahinter) zeigt undP2auf ein Element desselben Arrays mit dem IndexJ(oder eine Position dahinter) zeigt, dann
- P1-P2 hat den Wert gleich I-J und den Typ ptrdiff_t (ein vorzeichenbehafteter Ganzzahltyp, typischerweise halb so groß wie die Größe des größten Objekts, das deklariert werden kann)
Das Verhalten ist nur dann definiert, wenn das Ergebnis in ptrdiff_t passt.
Für die Zwecke der Zeiger-Arithmetik wird ein Zeiger auf ein Objekt, das kein Element eines Arrays ist, als Zeiger auf das erste Element eines Arrays der Größe 1 behandelt.
// work in progress int n = 4, m = 3; int a[n][m]; // VLA of 4 VLAs of 3 ints each int (*p)[m] = a; // p == &a[0] p = p + 1; // p == &a[1] (pointer arithmetic works with VLAs just the same) (*p)[2] = 99; // changes a[1][2]
[editieren] Multiplikative Operatoren
Die binären multiplikativen arithmetischen Operator-Ausdrücke haben die Form
lhs * rhs |
(1) | ||||||||
lhs / rhs |
(2) | ||||||||
lhs % rhs |
(3) | ||||||||
- zuerst werden übliche arithmetische Konversionen durchgeführt. Dann...
[editieren] Multiplikation
Der binäre Operator * führt die Multiplikation seiner Operanden (nach üblichen arithmetischen Konversionen) gemäß den üblichen arithmetischen Definitionen durch, außer dass
- wenn ein Operand NaN ist, ist das Ergebnis NaN
- Multiplikation von Unendlich mit Null ergibt NaN und FE_INVALID wird ausgelöst
- Multiplikation von Unendlich mit einer Nicht-Null ergibt Unendlich (auch für komplexe Argumente)
Da in C jeder komplexe Wert mit mindestens einem unendlichen Teil ein Unendlich ist, auch wenn sein anderer Teil eine NaN ist, gelten die üblichen arithmetischen Regeln nicht für die komplexe-komplexe Multiplikation. Andere Kombinationen von Gleitkomma-Operanden folgen der folgenden Tabelle
| * | u | iv | u + iv |
|---|---|---|---|
| x | xu | i(xv) | (xu) + i(xv) |
| iy | i(yu) | −yv | (−yv) + i(yu) |
| x + iy | (xu) + i(yu) | (−yv) + i(xv) | Spezielle Regeln |
Neben der Behandlung von Unendlichkeiten darf die komplexe Multiplikation Zwischenergebnisse nicht überlaufen, außer wenn #pragma STDC CX_LIMITED_RANGE auf ON gesetzt ist, in diesem Fall kann der Wert berechnet werden, als ob (x+iy)×(u+iv) = (xu-yv)+i(yu+xv), da der Programmierer die Verantwortung für die Begrenzung des Wertebereichs der Operanden und die Behandlung von Unendlichkeiten übernimmt.
Trotz der Zulassung von überflüssigen Überläufen kann die komplexe Multiplikation fälschlicherweise Gleitkomma-Ausnahmen auslösen (andernfalls ist es prohibitiv schwierig, nicht überlaufende Versionen zu implementieren).
#include <stdio.h> #include <stdio.h> #include <complex.h> #include <math.h> int main(void) { // TODO simpler cases, take some from C++ double complex z = (1 + 0*I) * (INFINITY + I*INFINITY); // textbook formula would give // (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN // but C gives a complex infinity printf("%f + i*%f\n", creal(z), cimag(z)); // textbook formula would give // cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN // but C gives ±∞+i*nan double complex y = cexp(INFINITY + I*NAN); printf("%f + i*%f\n", creal(y), cimag(y)); }
Mögliche Ausgabe
inf + i*inf inf + i*nan
[editieren] Division
Der binäre Operator / teilt den ersten Operanden durch den zweiten (nach üblichen arithmetischen Konversionen) gemäß den üblichen arithmetischen Definitionen, außer dass
- wenn der Typ nach den üblichen arithmetischen Konversionen ein Ganzzahltyp ist, ist das Ergebnis der algebraische Quotient (kein Bruch), gerundet in implementierungsdefinierter Richtung(bis C99)abgeschnitten zur Null(seit C99)
- wenn ein Operand NaN ist, ist das Ergebnis NaN
- wenn der erste Operand eine komplexe Unendlichkeit ist und der zweite Operand endlich ist, dann ist das Ergebnis des Operators
/eine komplexe Unendlichkeit - wenn der erste Operand endlich ist und der zweite Operand eine komplexe Unendlichkeit ist, dann ist das Ergebnis des Operators
/eine Null.
Da in C jeder komplexe Wert mit mindestens einem unendlichen Teil eine Unendlichkeit ist, auch wenn sein anderer Teil eine NaN ist, gelten die üblichen arithmetischen Regeln nicht für die komplexe-komplexe Division. Andere Kombinationen von Gleitkomma-Operanden folgen der folgenden Tabelle
| / | u | iv |
|---|---|---|
| x | x/u | i(−x/v) |
| iy | i(y/u) | y/v |
| x + iy | (x/u) + i(y/u) | (y/v) + i(−x/v) |
Neben der Behandlung von Unendlichkeiten darf die komplexe Division Zwischenergebnisse nicht überlaufen, außer wenn #pragma STDC CX_LIMITED_RANGE auf ON gesetzt ist, in diesem Fall kann der Wert berechnet werden, als ob (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u2
+v2
), da der Programmierer die Verantwortung für die Begrenzung des Wertebereichs der Operanden und die Behandlung von Unendlichkeiten übernimmt.
Trotz der Zulassung von überflüssigen Überläufen kann die komplexe Division fälschlicherweise Gleitkomma-Ausnahmen auslösen (andernfalls ist es prohibitiv schwierig, nicht überlaufende Versionen zu implementieren).
Wenn der zweite Operand Null ist, ist das Verhalten undefiniert, es sei denn, wenn die IEEE-Gleitkomma-Arithmetik unterstützt wird und die Gleitkomma-Division stattfindet, dann
- Die Division einer Nicht-Null-Zahl durch ±0.0 ergibt die korrekt vorzeichenbehaftete Unendlichkeit und FE_DIVBYZERO wird ausgelöst
- Die Division von 0.0 durch 0.0 ergibt NaN und FE_INVALID wird ausgelöst
[editieren] Rest
Der binäre Operator % liefert den Rest der Division des ersten Operanden durch den zweiten (nach üblichen arithmetischen Konversionen).
Das Vorzeichen des Rests ist so definiert, dass, wenn der Quotient a/b im Ergebnistyp darstellbar ist, dann (a/b)*b + a%b == a.
Wenn der zweite Operand Null ist, ist das Verhalten undefiniert.
Wenn der Quotient a/b nicht im Ergebnistyp darstellbar ist, ist das Verhalten sowohl von a/b als auch von a%b undefiniert (das bedeutet, dass INT_MIN%-1 auf 2er-Komplement-Systemen undefiniert ist).
Hinweis: Der Rest-Operator funktioniert nicht mit Gleitkommatypen. Die Bibliotheksfunktion fmod bietet diese Funktionalität.
[editieren] Bitweise Logik
Die bitweisen arithmetischen Operator-Ausdrücke haben die Form
~ rhs |
(1) | ||||||||
lhs & rhs |
(2) | ||||||||
lhs | rhs |
(3) | ||||||||
lhs ^ rhs |
(4) | ||||||||
wobei
| lhs, rhs | - | Ausdrücke vom Ganzzahltyp |
Zuerst führen die Operatoren &, ^ und | übliche arithmetische Konversionen auf beiden Operanden durch, und der Operator ~ führt Ganzzahl-Promotionen auf seinem einzigen Operanden durch.
Dann werden die entsprechenden binären Logikoperatoren bitweise angewendet; das heißt, jedes Bit des Ergebnisses wird entsprechend der logischen Operation (NICHT, UND, ODER oder XOR), angewendet auf die entsprechenden Bits der Operanden, gesetzt oder gelöscht.
Hinweis: Bitweise Operatoren werden häufig zur Manipulation von Bit-Sets und Bit-Masken verwendet.
Hinweis: Für vorzeichenlose Typen (nach der Promotion) ist der Ausdruck ~E äquivalent zum maximal darstellbaren Wert des Ergebnistyps minus dem ursprünglichen Wert von E.
Mögliche Ausgabe
Promoted mask: 0x000000f0 Value: 0x12345678 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x00000070
[editieren] Shift-Operatoren
Die bitweisen Shift-Operator-Ausdrücke haben die Form
lhs << rhs |
(1) | ||||||||
lhs >> rhs |
(2) | ||||||||
wobei
| lhs, rhs | - | Ausdrücke vom Ganzzahltyp |
Zuerst werden Ganzzahl-Promotionen einzeln auf jeden Operanden angewendet (Hinweis: dies ist im Gegensatz zu anderen binären arithmetischen Operatoren, die alle übliche arithmetische Konversionen durchführen). Der Typ des Ergebnisses ist der Typ von lhs nach der Promotion.
Das Verhalten ist undefiniert, wenn rhs negativ ist oder größer oder gleich der Anzahl der Bits in dem aufgerufenen lhs ist.
Für vorzeichenloses lhs ist der Wert von LHS << RHS der Wert von LHS * 2RHS
, reduziert modulo dem Maximalwert des Rückgabetyps plus 1 (d. h. es wird eine bitweise Linksverschiebung durchgeführt und die Bits, die aus dem Zieltyp herausgeschoben werden, werden verworfen). Für vorzeichenbehaftetes lhs mit nicht-negativen Werten ist der Wert von LHS << RHS LHS * 2RHS
, wenn er im aufgerufenen Typ von lhs darstellbar ist, andernfalls ist das Verhalten undefiniert.
Für vorzeichenloses lhs und für vorzeichenbehaftetes lhs mit nicht-negativen Werten ist der Wert von LHS >> RHS der Ganzzahlteil von LHS / 2RHS
. Für negatives LHS ist der Wert von LHS >> RHS implementierungsdefiniert, wobei die meisten Implementierungen eine arithmetische Rechtsverschiebung durchführen (sodass das Ergebnis negativ bleibt). So füllen die meisten Implementierungen bei einer Rechtsverschiebung eines vorzeichenbehafteten LHS die neuen höherwertigen Bits mit dem ursprünglichen Vorzeichenbit (d. h. mit 0, wenn es nicht-negativ war, und mit 1, wenn es negativ war).
#include <stdio.h> enum {ONE=1, TWO=2}; int main(void) { char c = 0x10; unsigned long long ulong_num = 0x123; printf("0x123 << 1 = %#llx\n" "0x123 << 63 = %#llx\n" // overflow truncates high bits for unsigned numbers "0x10 << 10 = %#x\n", // char is promoted to int ulong_num << 1, ulong_num << 63, c << 10); long long long_num = -1000; printf("-1000 >> 1 = %lld\n", long_num >> ONE); // implementation defined }
Mögliche Ausgabe
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
[editieren] Referenzen
- C17-Standard (ISO/IEC 9899:2018)
- 6.5.3.3 Unäre arithmetische Operatoren (S. 64)
- 6.5.5 Multiplikative Operatoren (S. 66)
- 6.5.6 Additive Operatoren (S. 66-68)
- 6.5.7 Bitweise Shift-Operatoren (S. 68)
- 6.5.10 Bitwise AND Operator (S. 70)
- 6.5.11 Bitwise Exclusive OR Operator (S. 70)
- 6.5.12 Bitwise Inclusive OR Operator (S. 70-71)
- C11-Standard (ISO/IEC 9899:2011)
- 6.5.3.3 Unäre arithmetische Operatoren (S. 89)
- 6.5.5 Multiplikative Operatoren (S. 92)
- 6.5.6 Additive Operatoren (S. 92-94)
- 6.5.7 Bitweise Shift-Operatoren (S. 94-95)
- 6.5.10 Bitwise AND Operator (S. 97)
- 6.5.11 Bitwise Exclusive OR Operator (S. 98)
- 6.5.12 Bitwise Inclusive OR Operator (S. 98)
- C99-Standard (ISO/IEC 9899:1999)
- 6.5.3.3 Unäre arithmetische Operatoren (S. 79)
- 6.5.5 Multiplikative Operatoren (S. 82)
- 6.5.6 Additive Operatoren (S. 82-84)
- 6.5.7 Bitweise Shift-Operatoren (S. 84-85)
- 6.5.10 Bitwise AND Operator (S. 87)
- 6.5.11 Bitwise Exclusive OR Operator (S. 88)
- 6.5.12 Bitwise Inclusive OR Operator (S. 88)
- C89/C90-Standard (ISO/IEC 9899:1990)
- 3.3.3.3 Unäre arithmetische Operatoren
- 3.3.5 Multiplikative Operatoren
- 3.3.6 Additive Operatoren
- 3.3.7 Bitweise Shift-Operatoren
- 3.3.10 Bitwise AND Operator
- 3.3.11 Bitwise Exclusive OR Operator
- 3.3.12 Bitwise Inclusive OR Operator
[editieren] Siehe auch
| Häufige Operatoren | ||||||
|---|---|---|---|---|---|---|
| Zuweisung | Inkrement Dekrement |
Arithmetik | Logisch | Vergleich | Member Zugriff |
Sonstiges |
|
a = b |
++a |
+a |
!a |
a == b |
a[b] |
a(...) |
| C++-Dokumentation für Arithmetische Operatoren
|