C Language
Operatoren
Suche…
Einführung
Ein Operator in einer Programmiersprache ist ein Symbol, das den Compiler oder Interpreter anweist, eine bestimmte mathematische, relationale oder logische Operation auszuführen und ein Endergebnis zu erzeugen.
C hat viele mächtige Operatoren. Viele C-Operatoren sind binäre Operatoren, dh sie haben zwei Operanden. In a / b
ist /
beispielsweise ein binärer Operator, der zwei Operanden ( a
, b
) akzeptiert. Es gibt einige unäre Operatoren, die einen Operanden verwenden (zum Beispiel: ~
, ++
) und nur einen ternären Operator ? :
.
Syntax
- expr1 Operator
- Operator Ausdruck2
- Ausdruck1 Operator Ausdruck2
- Ausdruck1? Ausdruck2: Ausdruck3
Bemerkungen
Die Betreiber haben ein arity, einen Vorrang und eine Assoziativität.
Arity gibt die Anzahl der Operanden an. In C gibt es drei verschiedene Operatoren:
- Unary (1 Operand)
- Binär (2 Operanden)
- Ternär (3 Operanden)
Vorrang gibt an, welche Operatoren sich zuerst an ihre Operanden "binden". Das heißt, welcher Operator hat Priorität, um seine Operanden zu bearbeiten. Beispielsweise folgt die C-Sprache der Konvention, dass Multiplikation und Division Vorrang vor Addition und Subtraktion haben:
a * b + c
Gibt das gleiche Ergebnis wie
(a * b) + c
Wenn dies nicht erwünscht ist, kann der Vorrang mithilfe von Klammern erzwungen werden, da sie die höchste Priorität aller Operatoren haben.
a * (b + c)
Dieser neue Ausdruck führt zu einem Ergebnis, das sich von den vorherigen beiden Ausdrücken unterscheidet.
Die C-Sprache hat viele Prioritätsstufen. Nachfolgend wird eine Tabelle aller Operatoren in absteigender Rangfolge aufgeführt.
Vorrang-Tabelle
Operatoren Assoziativität ()
[]
->
.
links nach rechts !
~
++
--
+
-
*
(dereference)(type)
sizeof
rechts nach links *
(Multiplikation)/
%
links nach rechts +
-
links nach rechts <<
>>
links nach rechts <
<=
>
>=
links nach rechts ==
!=
links nach rechts &
links nach rechts ^
links nach rechts |
links nach rechts &&
links nach rechts ||
links nach rechts ?:
rechts nach links =
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
rechts nach links ,
links nach rechts Assoziativität gibt an, wie Gleichheitsoperatoren standardmäßig gebunden werden, und es gibt zwei Arten: Links nach rechts und Rechts nach links . Ein Beispiel für die Links-Rechts- Bindung ist der Subtraktionsoperator (
-
). Der Ausdrucka - b - c - d
hat drei Subtraktionen mit identischer Priorität, ergibt jedoch das gleiche Ergebnis wie
((a - b) - c) - d
weil der am weitesten links
-
zuerst an seine zwei Operanden bindet.Ein Beispiel für die Assoziativität von rechts nach links sind die Dereferenzierungsoperatoren
*
und Post-Increment++
. Beide haben gleiche Priorität, wenn sie also in einem Ausdruck wie verwendet werden* ptr ++
ist das äquivalent zu
* (ptr ++)
weil der rechte, unäre Operator (
++
) zuerst an seinen einzelnen Operanden bindet.
Beziehungsoperatoren
Beziehungsoperatoren prüfen, ob eine bestimmte Beziehung zwischen zwei Operanden wahr ist. Das Ergebnis wird mit 1 (was wahr ist ) oder 0 (was falsch bedeutet) ausgewertet. Dieses Ergebnis wird häufig verwendet, um den Steuerungsfluss zu beeinflussen (über if
, while
, for
), kann aber auch in Variablen gespeichert werden.
Gleich "=="
Prüft, ob die angegebenen Operanden gleich sind.
1 == 0; /* evaluates to 0. */
1 == 1; /* evaluates to 1. */
int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr == yptr; /* evaluates to 0, the operands hold different location addresses. */
*xptr == *yptr; /* evaluates to 1, the operands point at locations that hold the same value. */
Achtung: Dieser Operator sollte nicht mit dem Zuweisungsoperator ( =
) verwechselt werden!
Nicht gleich "! ="
Prüft, ob die angegebenen Operanden nicht gleich sind.
1 != 0; /* evaluates to 1. */
1 != 1; /* evaluates to 0. */
int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr != yptr; /* evaluates to 1, the operands hold different location addresses. */
*xptr != *yptr; /* evaluates to 0, the operands point at locations that hold the same value. */
Dieser Operator gibt effektiv das Gegenteil von dem Gleichheitsoperator ( ==
) zurück.
Nicht "!"
Prüfen Sie, ob ein Objekt gleich 0
.
Die !
kann auch direkt mit einer Variablen verwendet werden:
!someVal
Das hat den gleichen Effekt wie:
someVal == 0
Größer als ">"
Überprüft, ob der linke Operand einen größeren Wert als der rechte Operand hat
5 > 4 /* evaluates to 1. */
4 > 5 /* evaluates to 0. */
4 > 4 /* evaluates to 0. */
Weniger als "<"
Überprüft, ob der linke Operand einen kleineren Wert als der rechte Operand hat
5 < 4 /* evaluates to 0. */
4 < 5 /* evaluates to 1. */
4 < 4 /* evaluates to 0. */
Größer oder gleich "> ="
Überprüft, ob der linke Operand dem rechten Operanden einen größeren oder gleichen Wert hat.
5 >= 4 /* evaluates to 1. */
4 >= 5 /* evaluates to 0. */
4 >= 4 /* evaluates to 1. */
Weniger oder gleich "<="
Überprüft, ob der linke Operand einen kleineren oder gleichen Wert für den rechten Operanden hat.
5 <= 4 /* evaluates to 0. */
4 <= 5 /* evaluates to 1. */
4 <= 4 /* evaluates to 1. */
Zuweisungsoperatoren
Weist den Wert des rechten Operanden dem vom linken Operanden genannten Speicherort zu und gibt den Wert zurück.
int x = 5; /* Variable x holds the value 5. Returns 5. */
char y = 'c'; /* Variable y holds the value 99. Returns 99
* (as the character 'c' is represented in the ASCII table with 99).
*/
float z = 1.5; /* variable z holds the value 1.5. Returns 1.5. */
char const* s = "foo"; /* Variable s holds the address of the first character of the string 'foo'. */
Mehrere arithmetische Operationen haben einen zusammengesetzten Zuweisungsoperator .
a += b /* equal to: a = a + b */
a -= b /* equal to: a = a - b */
a *= b /* equal to: a = a * b */
a /= b /* equal to: a = a / b */
a %= b /* equal to: a = a % b */
a &= b /* equal to: a = a & b */
a |= b /* equal to: a = a | b */
a ^= b /* equal to: a = a ^ b */
a <<= b /* equal to: a = a << b */
a >>= b /* equal to: a = a >> b */
Ein wichtiges Merkmal dieser zusammengesetzten Zuordnungen ist, dass der Ausdruck auf der linken Seite ( a
) nur einmal ausgewertet wird. ZB wenn p
ein Zeiger ist
*p += 27;
Dereferences p
nur einmal, während das Folgende zweimal tut.
*p = *p + 27;
Es sei auch darauf hingewiesen, dass das Ergebnis einer Zuweisung wie a = b
sogenannte r-Wert ist . Die Zuordnung hat also tatsächlich einen Wert, der dann einer anderen Variablen zugewiesen werden kann. Dies ermöglicht die Verkettung von Zuweisungen, um mehrere Variablen in einer einzigen Anweisung festzulegen.
Dieser Wert kann in den steuernden Ausdrücken von if
Anweisungen (oder Schleifen- oder switch
Anweisungen) verwendet werden, die Code für das Ergebnis eines anderen Ausdrucks oder Funktionsaufrufs schützen. Zum Beispiel:
char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
/* do something with buffer */
free(buffer);
}
else
{
/* report allocation failure */
}
Aus diesem Grund muss ein häufiger Tippfehler vermieden werden, der zu mysteriösen Fehlern führen kann.
int a = 2;
/* ... */
if (a = 1)
/* Delete all files on my hard drive */
Das wird katastrophale Folgen haben, da a = 1
wird immer bewerten 1
und damit der kontrollierende Ausdruck der , if
Aussage immer wahr sein wird (mehr über diese gemeinsame pitfall lesen hier ). Der Autor wollte den Gleichheitsoperator ( ==
) wie folgt verwenden:
int a = 2;
/* ... */
if (a == 1)
/* Delete all files on my hard drive */
Operator Assoziativität
int a, b = 1, c = 2;
a = b = c;
Dies ordnet c
zu b
, der kehrt b
, die als zugewiesen ist a
. Dies geschieht, weil alle Zuweisungsoperatoren rechte Assoziativität besitzen. Das bedeutet, dass die Operation ganz rechts im Ausdruck zuerst ausgewertet wird und von rechts nach links verläuft.
Rechenzeichen
Grundrechenarten
Gibt einen Wert zurück, der das Ergebnis der Anwendung des linken Operanden auf den rechten Operanden ist, und zwar mit der zugehörigen mathematischen Operation. Es gelten die üblichen mathematischen Regeln der Kommutierung (dh Addition und Multiplikation sind kommutativ, Subtraktion, Division und Modulus nicht).
Zusatzoperator
Mit dem Additionsoperator ( +
) werden zwei Operanden zusammengefügt. Beispiel:
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 7;
int c = a + b; /* c now holds the value 12 */
printf("%d + %d = %d",a,b,c); /* will output "5 + 7 = 12" */
return 0;
}
Subtraktionsoperator
Der Subtraktionsoperator ( -
) dient zum Subtrahieren des zweiten Operanden vom ersten. Beispiel:
#include <stdio.h>
int main(void)
{
int a = 10;
int b = 7;
int c = a - b; /* c now holds the value 3 */
printf("%d - %d = %d",a,b,c); /* will output "10 - 7 = 3" */
return 0;
}
Multiplikationsoperator
Mit dem Multiplikationsoperator ( *
) werden beide Operanden multipliziert. Beispiel:
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 7;
int c = a * b; /* c now holds the value 35 */
printf("%d * %d = %d",a,b,c); /* will output "5 * 7 = 35" */
return 0;
}
Nicht mit dem Dereferenzierungsoperator *
verwechseln.
Bereichsoperator
Der Divisionsoperator ( /
) teilt den ersten Operanden durch den zweiten. Wenn beide Operanden der Division Ganzzahlen sind, wird ein ganzzahliger Wert zurückgegeben und der Rest verworfen (verwenden Sie den Modulo-Operator %
zum Berechnen und Abrufen des Restes).
Wenn einer der Operanden ein Gleitkommawert ist, ist das Ergebnis eine Annäherung an den Bruch.
Beispiel:
#include <stdio.h>
int main (void)
{
int a = 19 / 2 ; /* a holds value 9 */
int b = 18 / 2 ; /* b holds value 9 */
int c = 255 / 2; /* c holds value 127 */
int d = 44 / 4 ; /* d holds value 11 */
double e = 19 / 2.0 ; /* e holds value 9.5 */
double f = 18.0 / 2 ; /* f holds value 9.0 */
double g = 255 / 2.0; /* g holds value 127.5 */
double h = 45.0 / 4 ; /* h holds value 11.25 */
printf("19 / 2 = %d\n", a); /* Will output "19 / 2 = 9" */
printf("18 / 2 = %d\n", b); /* Will output "18 / 2 = 9" */
printf("255 / 2 = %d\n", c); /* Will output "255 / 2 = 127" */
printf("44 / 4 = %d\n", d); /* Will output "44 / 4 = 11" */
printf("19 / 2.0 = %g\n", e); /* Will output "19 / 2.0 = 9.5" */
printf("18.0 / 2 = %g\n", f); /* Will output "18.0 / 2 = 9" */
printf("255 / 2.0 = %g\n", g); /* Will output "255 / 2.0 = 127.5" */
printf("45.0 / 4 = %g\n", h); /* Will output "45.0 / 4 = 11.25" */
return 0;
}
Modulo Operator
Der Modulo-Operator ( %
) empfängt nur ganzzahlige Operanden und wird verwendet, um den Rest zu berechnen, nachdem der erste Operand durch den zweiten geteilt wurde. Beispiel:
#include <stdio.h>
int main (void) {
int a = 25 % 2; /* a holds value 1 */
int b = 24 % 2; /* b holds value 0 */
int c = 155 % 5; /* c holds value 0 */
int d = 49 % 25; /* d holds value 24 */
printf("25 % 2 = %d\n", a); /* Will output "25 % 2 = 1" */
printf("24 % 2 = %d\n", b); /* Will output "24 % 2 = 0" */
printf("155 % 5 = %d\n", c); /* Will output "155 % 5 = 0" */
printf("49 % 25 = %d\n", d); /* Will output "49 % 25 = 24" */
return 0;
}
Inkrement- / Dekrementoperatoren
Die Operatoren inkrementieren ( a++
) und dekrementieren ( a--
) unterscheiden sich dadurch, dass sie den Wert der Variablen ändern, auf die Sie sie anwenden, ohne einen Zuweisungsoperator. Sie können Inkrementierungs- und Dekrementierungsoperatoren entweder vor oder nach der Variablen verwenden. Durch die Platzierung des Operators wird der Zeitpunkt der Inkrementierung / Dekrementierung des Werts vor oder nach der Zuweisung der Variablen geändert. Beispiel:
#include <stdio.h>
int main(void)
{
int a = 1;
int b = 4;
int c = 1;
int d = 4;
a++;
printf("a = %d\n",a); /* Will output "a = 2" */
b--;
printf("b = %d\n",b); /* Will output "b = 3" */
if (++c > 1) { /* c is incremented by 1 before being compared in the condition */
printf("This will print\n"); /* This is printed */
} else {
printf("This will never print\n"); /* This is not printed */
}
if (d-- < 4) { /* d is decremented after being compared */
printf("This will never print\n"); /* This is not printed */
} else {
printf("This will print\n"); /* This is printed */
}
}
Wie das Beispiel für c
und d
zeigt, haben beide Operatoren zwei Formen: Präfixnotation und Postfixnotation. Beide haben den gleichen Effekt beim Inkrementieren ( ++
) oder Dekrementieren ( --
) der Variablen, unterscheiden sich jedoch durch den von ihnen zurückgegebenen Wert: Präfixoperationen führen die Operation zuerst aus und geben dann den Wert zurück, während Postfixoperationen zuerst den Wert bestimmen, der zu verwenden ist zurückgegeben werden und dann die Operation ausführen.
Aufgrund dieses möglicherweise kontraintuitiven Verhaltens ist die Verwendung von Inkrementierungs- / Dekrementierungsoperatoren in Ausdrücken umstritten.
Logische Operatoren
Logisches UND
Führt eine logische boolesche UND-Verknüpfung der beiden Operanden durch, wobei 1 zurückgegeben wird, wenn beide Operanden nicht Null sind. Der logische AND-Operator ist vom Typ int
.
0 && 0 /* Returns 0. */
0 && 1 /* Returns 0. */
2 && 0 /* Returns 0. */
2 && 3 /* Returns 1. */
Logisches ODER
Führt eine logische boolesche ODER-Verknüpfung der beiden Operanden durch, wobei 1 zurückgegeben wird, wenn einer der Operanden nicht Null ist. Der logische Operator OR ist vom Typ int
.
0 || 0 /* Returns 0. */
0 || 1 /* Returns 1. */
2 || 0 /* Returns 1. */
2 || 3 /* Returns 1. */
Logisch NICHT
Führt eine logische Negation durch. Der logische NOT-Operator ist vom Typ int
. Der Operator NOT prüft, ob mindestens ein Bit gleich 1 ist. Wenn dies der Fall ist, wird 0 zurückgegeben. Andernfalls wird 1 zurückgegeben.
!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */
Kurzschlussbewertung
Sowohl &&
als auch ||
haben einige wichtige Eigenschaften gemeinsam :
- der linke Operand (LHS) wird vollständig ausgewertet, bevor der rechte Operand (RHS) überhaupt ausgewertet wird,
- es gibt einen Sequenzpunkt zwischen der Auswertung des linken Operanden und des rechten Operanden,
- und am wichtigsten ist, dass der rechte Operand überhaupt nicht ausgewertet wird, wenn das Ergebnis des linken Operanden das Gesamtergebnis bestimmt.
Das bedeutet, dass:
- Wenn die LHS als "wahr" (nicht Null) bewertet wird, ist die RHS von
||
wird nicht ausgewertet (weil das Ergebnis von "wahr ODER alles" ist "wahr"), - Wenn der LHS-Wert als "falsch" (Null) ausgewertet wird, wird der RHS-Wert von
&&
nicht ausgewertet (da das Ergebnis von "falsch AND irgendetwas" ist "falsch").
Dies ist wichtig, da Sie damit Code schreiben können:
const char *name_for_value(int value)
{
static const char *names[] = { "zero", "one", "two", "three", };
enum { NUM_NAMES = sizeof(names) / sizeof(names[0]) };
return (value >= 0 && value < NUM_NAMES) ? names[value] : "infinity";
}
Wenn ein negativer Wert an die Funktion übergeben wird, wird der value >= 0
als falsch ausgewertet, und der value < NUM_NAMES
wird nicht ausgewertet.
Inkrement / Dekrement
Die Inkrementierungs- und Dekrementierungsoperatoren sind in Präfix- und Postfixform vorhanden .
int a = 1;
int b = 1;
int tmp = 0;
tmp = ++a; /* increments a by one, and returns new value; a == 2, tmp == 2 */
tmp = a++; /* increments a by one, but returns old value; a == 3, tmp == 2 */
tmp = --b; /* decrements b by one, and returns new value; b == 0, tmp == 0 */
tmp = b--; /* decrements b by one, but returns old value; b == -1, tmp == 0 */
Beachten Sie, dass arithmetische Operationen keine Sequenzpunkte einführen, sodass bestimmte Ausdrücke mit ++
oder --
Operatoren undefiniertes Verhalten verursachen können .
Bedingter Operator / Ternärer Operator
Wertet den ersten Operanden aus und wertet den zweiten Operanden aus, wenn der resultierende Wert ungleich Null ist. Andernfalls wertet es den dritten Operanden aus, wie im folgenden Beispiel gezeigt:
a = b ? c : d;
ist äquivalent zu:
if (b)
a = c;
else
a = d;
Dieser Pseudo-Code repräsentiert es: condition ? value_if_true : value_if_false
. Jeder Wert kann das Ergebnis eines bewerteten Ausdrucks sein.
int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */
Der bedingte Operator kann geschachtelt werden. Der folgende Code bestimmt beispielsweise die größere von drei Zahlen:
big= a > b ? (a > c ? a : c)
: (b > c ? b : c);
Im folgenden Beispiel werden sogar Ganzzahlen in eine Datei und ungerade Ganzzahlen in eine andere Datei geschrieben:
#include<stdio.h>
int main()
{
FILE *even, *odds;
int n = 10;
size_t k = 0;
even = fopen("even.txt", "w");
odds = fopen("odds.txt", "w");
for(k = 1; k < n + 1; k++)
{
k%2==0 ? fprintf(even, "\t%5d\n", k)
: fprintf(odds, "\t%5d\n", k);
}
fclose(even);
fclose(odds);
return 0;
}
Der bedingte Operator verbindet sich von rechts nach links. Folgendes berücksichtigen:
exp1 ? exp2 : exp3 ? exp4 : exp5
Da die Zuordnung von rechts nach links erfolgt, wird der obige Ausdruck als ausgewertet
exp1 ? exp2 : ( exp3 ? exp4 : exp5 )
Komma-Operator
Wertet seinen linken Operanden aus, verwirft den resultierenden Wert und wertet dann seinen Rechte-Operanden aus, und result ergibt den Wert seines ganz rechten Operanden.
int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
Der Kommaoperator führt einen Sequenzpunkt zwischen seinen Operanden ein.
Beachten Sie, dass das Komma, das in Funktionsaufrufen verwendet wird, die separaten Argumente aufruft, NICHT der Kommaoperator ist , sondern als Trennzeichen bezeichnet wird, das sich vom Kommaoperator unterscheidet . Daher verfügt es nicht über die Eigenschaften des Kommaoperators .
Der obige Aufruf von printf()
enthält sowohl den Kommaoperator als auch das Trennzeichen .
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/* ^ ^ this is a comma operator */
/* this is a separator */
Der Kommaoperator wird häufig sowohl im Initialisierungsabschnitt als auch im Aktualisierungsabschnitt einer for
Schleife verwendet. Zum Beispiel:
for(k = 1; k < 10; printf("\%d\\n", k), k += 2); /*outputs the odd numbers below 9/*
/* outputs sum to first 9 natural numbers */
for(sumk = 1, k = 1; k < 10; k++, sumk += k)
printf("\%5d\%5d\\n", k, sumk);
Cast Operator
Führt eine explizite Konvertierung in den angegebenen Typ aus dem Wert durch, der sich aus der Auswertung des angegebenen Ausdrucks ergibt.
int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */
Hier ist der Wert von x
in einem umgewandelt wird double
fördert die Aufteilung um den Wert von y
zu double
, auch, und das Ergebnis der Division, ein double
geleitet wird , um printf
zum Drucken.
sizeof Operator
Mit einem Typ als Operand
Wertet die Größe der Objekte des angegebenen Typs in Bytes des Typs size_t
aus. Benötigt Klammern um den Typ.
printf("%zu\n", sizeof(int)); /* Valid, outputs the size of an int object, which is platform-dependent. */
printf("%zu\n", sizeof int); /* Invalid, types as arguments need to be surrounded by parentheses! */
Mit einem Ausdruck als Operand
Wertet die Größe von Objekten des Typs des angegebenen Ausdrucks in Bytes des Typs size_t
aus. Der Ausdruck selbst wird nicht ausgewertet. Klammern sind nicht erforderlich. Da der angegebene Ausdruck jedoch unärlich sein muss, sollten Sie ihn immer verwenden.
char ch = 'a';
printf("%zu\n", sizeof(ch)); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
printf("%zu\n", sizeof ch); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
Zeigerarithmetik
Zeigerzusatz
Wenn ein Zeiger und ein skalarer Typ N
, wird ein Zeiger auf das N
te Element des Zeigetyps ausgewertet, der direkt auf das Zeigefachobjekt im Speicher folgt.
int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */
Es spielt keine Rolle, ob der Zeiger als Operandenwert oder Skalarwert verwendet wird. Dies bedeutet, dass Dinge wie 3 + arr
gültig sind. Wenn arr[k]
das k+1
Glied eines Arrays ist, dann ist arr+k
ein Zeiger auf arr[k]
. Mit anderen Worten, arr
oder arr+0
ist ein Zeiger auf arr[0]
, arr+1
ist ein Zeiger auf arr[2]
und so weiter. Im Allgemeinen ist *(arr+k)
dasselbe wie arr[k]
.
Im Gegensatz zur üblichen Arithmetik werden durch Hinzufügen von 1
zu einem Zeiger auf ein int
4
Werte zum aktuellen Adresswert hinzugefügt. Da Arraynamen konstante Zeiger sind, ist +
der einzige Operator, mit dem auf die Mitglieder eines Arrays über die Zeigernotation über den Arraynamen zugegriffen werden kann. Wenn Sie jedoch einen Zeiger auf ein Array definieren, können Sie die Daten in einem Array flexibler verarbeiten. Beispielsweise können wir die Mitglieder eines Arrays wie folgt drucken:
#include<stdio.h>
static const size_t N = 5
int main()
{
size_t k = 0;
int arr[] = {1, 2, 3, 4, 5};
for(k = 0; k < N; k++)
{
printf("\n\t%d", *(arr + k));
}
return 0;
}
Durch die Definition eines Zeigers auf das Array entspricht das obige Programm dem folgenden:
#include<stdio.h>
static const size_t N = 5
int main()
{
size_t k = 0;
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr; /* or int *ptr = &arr[0]; */
for(k = 0; k < N; k++)
{
printf("\n\t%d", ptr[k]);
/* or printf("\n\t%d", *(ptr + k)); */
/* or printf("\n\t%d", *ptr++); */
}
return 0;
}
Stellen Sie arr
dass auf die Mitglieder des Arrays arr
mit den Operatoren +
und ++
zugegriffen wird. Die anderen Operatoren, die mit dem Zeiger ptr
werden können, sind -
und --
.
Zeigerabzug
Wenn zwei Zeiger auf denselben Typ gegeben werden, wird ein Objekt vom Typ ptrdiff_t
, das den Skalarwert enthält, der zum zweiten Zeiger hinzugefügt werden muss, um den Wert des ersten Zeigers zu erhalten.
int arr[] = {1, 2, 3, 4, 5};
int *p = &arr[2];
int *q = &arr[3];
ptrdiff_t diff = q - p;
printf("q - p = %ti\n", diff); /* Outputs "1". */
printf("*(p + (q - p)) = %d\n", *(p + diff)); /* Outputs "4". */
Zugriffsoperatoren
Die Mitglied Betreiber der Zugangskontrolle (dot .
Pfeil und ->
) verwendet , um ein Mitglied eines für den Zugriff auf struct
.
Mitglied des Objekts
Wertet den Wert lvalue aus, der das Objekt angibt, das Mitglied des aufgerufenen Objekts ist.
struct MyStruct
{
int x;
int y;
};
struct MyStruct myObject;
myObject.x = 42;
myObject.y = 123;
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Outputs ".x = 42, .y = 123". */
Mitglied des Objektes, auf das verwiesen wird
Syntaktischer Zucker zur Dereferenzierung, gefolgt vom Zugang der Mitglieder. Tatsächlich ist ein Ausdruck der Form x->y
eine Abkürzung für (*x).y
- der Pfeiloperator ist jedoch viel klarer, insbesondere wenn die Strukturzeiger verschachtelt sind.
struct MyStruct
{
int x;
int y;
};
struct MyStruct myObject;
struct MyStruct *p = &myObject;
p->x = 42;
p->y = 123;
printf(".x = %i, .y = %i\n", p->x, p->y); /* Outputs ".x = 42, .y = 123". */
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Also outputs ".x = 42, .y = 123". */
Adresse von
Die unären &
Operator ist die Adresse des Betreibers. Es wertet den angegebenen Ausdruck aus, wobei das resultierende Objekt ein Wert sein muss. Dann wird es zu einem Objekt ausgewertet, dessen Typ ein Zeiger auf den Typ des resultierenden Objekts ist, und enthält die Adresse des resultierenden Objekts.
int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */
Dereferenz
Der unäre *
Operator dereferenziert einen Zeiger. Sie wertet den Wert lvalue aus, der sich aus der Dereferenzierung des Zeigers ergibt, der sich aus der Auswertung des angegebenen Ausdrucks ergibt.
int x = 42;
int *p = &x;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 42, *p = 42". */
*p = 123;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 123, *p = 123". */
Indizierung
Indizierung ist syntaktischer Zucker für die Zeigeraddition gefolgt von Dereferenzierung. Ein Ausdruck der Form a[i]
ist effektiv äquivalent zu *(a + i)
- aber die explizite tiefgestellte Schreibweise wird bevorzugt.
int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */
Austauschbarkeit der Indexierung
Das Hinzufügen eines Zeigers zu einer Ganzzahl ist eine kommutative Operation (dh, die Reihenfolge der Operanden ändert das Ergebnis nicht), also pointer + integer == integer + pointer
.
Eine Folge davon ist, dass arr[3]
und 3[arr]
gleichwertig sind.
printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */
Die Verwendung eines Ausdrucks 3[arr]
anstelle von arr[3]
wird im Allgemeinen nicht empfohlen, da dies die Lesbarkeit des Codes beeinflusst. Es ist in populären Programmwettbewerben beliebt.
Operator für Funktionsaufruf
Der erste Operand muss ein Funktionszeiger sein (ein Funktionsbezeichner ist auch akzeptabel, weil er in einen Zeiger auf die Funktion umgewandelt wird), der die aufzurufende Funktion identifiziert, und alle anderen Operanden, sofern vorhanden, werden als Argumente des Funktionsaufrufs bezeichnet . Wertet den Rückgabewert aus, der sich aus dem Aufruf der entsprechenden Funktion mit den jeweiligen Argumenten ergibt.
int myFunction(int x, int y)
{
return x * 2 + y;
}
int (*fn)(int, int) = &myFunction;
int x = 42;
int y = 123;
printf("(*fn)(%i, %i) = %i\n", x, y, (*fn)(x, y)); /* Outputs "fn(42, 123) = 207". */
printf("fn(%i, %i) = %i\n", x, y, fn(x, y)); /* Another form: you don't need to dereference explicitly */
Bitweise Operatoren
Bitweise Operatoren können verwendet werden, um Variablen auf Bitebene zu bearbeiten.
Nachfolgend finden Sie eine Liste aller sechs in C unterstützten bitweisen Operatoren:
Symbol | Operator |
---|---|
& | bitweise UND |
| | bitweises ODER |
^ | bitweises exklusives ODER (XOR) |
~ | bitweise nicht (eine Ergänzung) |
<< | logische Linksverschiebung |
>> | logische Rechtsverschiebung |
Das folgende Programm veranschaulicht die Verwendung aller bitweisen Operatoren:
#include <stdio.h>
int main(void)
{
unsigned int a = 29; /* 29 = 0001 1101 */
unsigned int b = 48; /* 48 = 0011 0000 */
int c = 0;
c = a & b; /* 32 = 0001 0000 */
printf("%d & %d = %d\n", a, b, c );
c = a | b; /* 61 = 0011 1101 */
printf("%d | %d = %d\n", a, b, c );
c = a ^ b; /* 45 = 0010 1101 */
printf("%d ^ %d = %d\n", a, b, c );
c = ~a; /* -30 = 1110 0010 */
printf("~%d = %d\n", a, c );
c = a << 2; /* 116 = 0111 0100 */
printf("%d << 2 = %d\n", a, c );
c = a >> 2; /* 7 = 0000 0111 */
printf("%d >> 2 = %d\n", a, c );
return 0;
}
Bitweise Operationen mit vorzeichenbehafteten Typen sollten vermieden werden, da das Vorzeichenbit einer solchen Bitdarstellung eine besondere Bedeutung hat. Für die Schichtbetreiber gelten besondere Einschränkungen:
Das Verschieben von 1 Bit in das vorzeichenbehaftete Bit nach links ist fehlerhaft und führt zu undefiniertem Verhalten.
Wenn Sie einen negativen Wert (mit Vorzeichenbit 1) nach rechts verschieben, ist die Implementierung definiert und daher nicht portierbar.
Wenn der Wert des rechten Operanden eines Verschiebungsoperators negativ ist oder größer oder gleich der Breite des beförderten linken Operanden ist, ist das Verhalten undefiniert.
Maskierung:
Maskieren bezieht sich auf den Vorgang des Extrahierens der gewünschten Bits aus einer Variablen (oder Umwandeln der gewünschten Bits in eine Variable) unter Verwendung von logischen bitweisen Operationen. Der Operand (eine Konstante oder Variable), der zur Maskierung verwendet wird, wird als Maske bezeichnet .
Maskierung wird auf verschiedene Arten verwendet:
- Das Bitmuster einer Ganzzahlvariablen bestimmen.
- Um einen Teil eines bestimmten Bitmusters in eine neue Variable zu kopieren, während der Rest der neuen Variablen mit 0 gefüllt wird (unter Verwendung von bitweisem AND)
- Einen Teil eines bestimmten Bitmusters in eine neue Variable kopieren, während der Rest der neuen Variablen mit 1s gefüllt wird (unter Verwendung von bitweisem ODER).
- Um einen Teil eines bestimmten Bitmusters in eine neue Variable zu kopieren, während der Rest des ursprünglichen Bitmusters innerhalb der neuen Variablen invertiert wird (unter Verwendung des bitweisen Exklusiv-ODER).
Die folgende Funktion verwendet eine Maske, um das Bitmuster einer Variablen anzuzeigen:
#include <limits.h>
void bit_pattern(int u)
{
int i, x, word;
unsigned mask = 1;
word = CHAR_BIT * sizeof(int);
mask = mask << (word - 1); /* shift 1 to the leftmost position */
for(i = 1; i <= word; i++)
{
x = (u & mask) ? 1 : 0; /* identify the bit */
printf("%d", x); /* print bit value */
mask >>= 1; /* shift mask to the right by 1 bit */
}
}
_Alignof
Fragt die Ausrichtungsanforderung für den angegebenen Typ ab. Die Ausrichtungsanforderung ist eine positive integrale Potenz von 2, die die Anzahl der Bytes darstellt, zwischen denen zwei Objekte des Typs zugeordnet werden können. In C wird die Ausrichtungsanforderung in size_t
gemessen.
Der Typname darf weder ein unvollständiger Typ noch ein Funktionstyp sein. Wenn ein Array als Typ verwendet wird, wird der Typ des Array-Elements verwendet.
Auf diesen Operator wird häufig über das Komfort-Makro alignof
von <stdalign.h>
.
int main(void)
{
printf("Alignment of char = %zu\n", alignof(char));
printf("Alignment of max_align_t = %zu\n", alignof(max_align_t));
printf("alignof(float[10]) = %zu\n", alignof(float[10]));
printf("alignof(struct{char c; int n;}) = %zu\n",
alignof(struct {char c; int n;}));
}
Mögliche Ausgabe:
Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4
Kurzschlussverhalten logischer Operatoren
Kurzschließen ist eine Funktion, die das Auswerten von Teilen einer (wenn / während / ...) Bedingung auslässt, wenn dies möglich ist. Bei einer logischen Operation an zwei Operanden wird der erste Operand ausgewertet (wahr oder falsch), und wenn ein Ergebnis vorliegt (dh der erste Operand ist bei Verwendung von && falsch, der erste Operand ist bei Verwendung von || der zweite Operand.) nicht bewertet.
Beispiel:
#include <stdio.h>
int main(void) {
int a = 20;
int b = -5;
/* here 'b == -5' is not evaluated,
since a 'a != 20' is false. */
if (a != 20 && b == -5) {
printf("I won't be printed!\n");
}
return 0;
}
Überzeugen Sie sich selbst:
#include <stdio.h>
int print(int i) {
printf("print function %d\n", i);
return i;
}
int main(void) {
int a = 20;
/* here 'print(a)' is not called,
since a 'a != 20' is false. */
if (a != 20 && print(a)) {
printf("I won't be printed!\n");
}
/* here 'print(a)' is called,
since a 'a == 20' is true. */
if (a == 20 && print(a)) {
printf("I will be printed!\n");
}
return 0;
}
Ausgabe:
$ ./a.out
print function 20
I will be printed!
Kurzschlüsse sind wichtig, wenn Sie vermeiden möchten, Begriffe zu berechnen, die (rechenmäßig) teuer sind. Darüber hinaus kann es den Ablauf Ihres Programms wie in diesem Fall stark beeinträchtigen: Warum wird dieses Programm "verzerrt!" 4 Mal?