C Language
operators
Zoeken…
Invoering
Een operator in een programmeertaal is een symbool dat de compiler of tolk vertelt om een specifieke wiskundige, relationele of logische bewerking uit te voeren en een eindresultaat te produceren.
C heeft veel krachtige operators. Veel C-operatoren zijn binaire operatoren, wat betekent dat ze twee operanden hebben. In a / b
is /
bijvoorbeeld een binaire operator die twee operanden accepteert ( a
, b
). Zijn er enkele unaire operatoren die één operand nemen (bijvoorbeeld: ~
, ++
), en slechts één ternaire operator ? :
.
Syntaxis
- expr1-operator
- operator expr2
- expr1 operator expr2
- expr1? expr2: expr3
Opmerkingen
Operators hebben een arity , een voorrang en een associativiteit .
Arity geeft het aantal operanden aan. In C bestaan drie verschillende exploitantariteiten:
- Unary (1 operand)
- Binair (2 operanden)
- Ternary (3 operanden)
Voorrang geeft aan welke operatoren eerst "binden" aan hun operanden. Dat wil zeggen, welke operator prioriteit heeft om op zijn operands te werken. Bijvoorbeeld, de taal C volgt de conventie dat vermenigvuldiging en deling voorrang hebben op optellen en aftrekken:
a * b + c
Geeft hetzelfde resultaat als
(a * b) + c
Als dit niet is wat gewenst is, kan voorrang worden afgedwongen met haakjes, omdat deze de hoogste voorrang hebben van alle operatoren.
a * (b + c)
Deze nieuwe uitdrukking zal een resultaat produceren dat verschilt van de vorige twee uitdrukkingen.
De taal C heeft veel prioriteitsniveaus; Een tabel wordt hieronder gegeven van alle operatoren, in afnemende volgorde van prioriteit.
Voorrangstabel
operators associativiteit ()
[]
->
.
van links naar rechts !
~
++
--
+
-
*
(dereference)(type)
sizeof
rechts naar links *
(vermenigvuldiging)/
%
van links naar rechts +
-
van links naar rechts <<
>>
van links naar rechts <
<=
>
>=
van links naar rechts ==
!=
van links naar rechts &
van links naar rechts ^
van links naar rechts |
van links naar rechts &&
van links naar rechts ||
van links naar rechts ?:
rechts naar links =
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
rechts naar links ,
van links naar rechts Associativiteit geeft aan hoe operators voor gelijke prioriteit standaard binden, en er zijn twee soorten: van links naar rechts en van rechts naar links . Een voorbeeld van links naar rechts binden is de operator voor aftrekken (
-
). De uitdrukkinga - b - c - d
heeft drie identieke voorrangsaftrekkingen, maar geeft hetzelfde resultaat als
((a - b) - c) - d
omdat de meest linkse
-
eerst aan zijn twee operanden bindt.Een voorbeeld van rechts naar links associativiteit zijn de dereference
*
en post-increment++
operatoren. Beide hebben dezelfde prioriteit, dus als ze worden gebruikt in een uitdrukking zoals* ptr ++
, dit is gelijk aan
* (ptr ++)
omdat de meest rechtse, unaire operator (
++
) eerst aan zijn enkele operand bindt.
Relationele operators
Relationele operatoren controleren of een specifieke relatie tussen twee operanden waar is. Het resultaat wordt geanalyseerd op 1 (waardoor true) of 0 (wat betekent false). Dit resultaat wordt vaak gebruikt om de besturingsstroom te beïnvloeden (via if
, while
, for
), maar kan ook worden opgeslagen in variabelen.
Is gelijk aan "=="
Controleert of de geleverde operanden gelijk zijn.
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. */
Let op: deze operator moet niet worden verward met de toewijzingsoperator ( =
)!
Niet gelijk aan "! ="
Controleert of de geleverde operanden niet gelijk zijn.
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. */
Deze operator retourneert in feite het tegenovergestelde resultaat met dat van de operator is gelijk aan ( ==
).
Niet "!"
Controleer of een object gelijk is aan 0
.
De !
kan ook direct worden gebruikt met een variabele als volgt:
!someVal
Dit heeft hetzelfde effect als:
someVal == 0
Groter dan ">"
Controleert of de linkerhand-operand een grotere waarde heeft dan de rechterhand-operand
5 > 4 /* evaluates to 1. */
4 > 5 /* evaluates to 0. */
4 > 4 /* evaluates to 0. */
Minder dan "<"
Controleert of de linkerhand-operand een kleinere waarde heeft dan de rechterhand-operand
5 < 4 /* evaluates to 0. */
4 < 5 /* evaluates to 1. */
4 < 4 /* evaluates to 0. */
Groter dan of gelijk aan "> ="
Controleert of de linkerhandand een grotere of gelijke waarde heeft als de rechteroperand.
5 >= 4 /* evaluates to 1. */
4 >= 5 /* evaluates to 0. */
4 >= 4 /* evaluates to 1. */
Kleiner dan of gelijk aan "<="
Controleert of de linker operand een kleinere of gelijke waarde heeft als de rechter operand.
5 <= 4 /* evaluates to 0. */
4 <= 5 /* evaluates to 1. */
4 <= 4 /* evaluates to 1. */
Opdrachtexploitanten
Wijst de waarde van de rechteroperand toe aan de opslaglocatie die wordt genoemd door de linkeroperand en retourneert de waarde.
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'. */
Verschillende rekenkundige bewerkingen hebben een samengestelde toewijzingsoperator .
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 */
Een belangrijk kenmerk van deze samengestelde toewijzingen is dat de uitdrukking aan de linkerkant ( a
) slechts eenmaal wordt geëvalueerd. Bijvoorbeeld als p
een pointer is
*p += 27;
dereferences p
slechts eenmaal, terwijl het volgende tweemaal doet.
*p = *p + 27;
Er moet ook worden opgemerkt dat het resultaat van een opdracht zoals a = b
een zogenaamde rvalue is . De toewijzing heeft dus eigenlijk een waarde die vervolgens aan een andere variabele kan worden toegewezen. Dit maakt het koppelen van toewijzingen mogelijk om meerdere variabelen in een enkele instructie in te stellen.
Deze p-waarde kan worden gebruikt bij de besturing van uitingen if
statements (of lussen of switch
statements) dat een code wacht op het resultaat van een expressie of functie-aanroep. Bijvoorbeeld:
char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
/* do something with buffer */
free(buffer);
}
else
{
/* report allocation failure */
}
Daarom moet ervoor worden gezorgd dat een veelvoorkomende typefout die tot mysterieuze bugs kan leiden, wordt vermeden.
int a = 2;
/* ... */
if (a = 1)
/* Delete all files on my hard drive */
Dit zal rampzalige gevolgen hebben, zoals a = 1
altijd evalueren 1
en derhalve de expressie reguleert van de if
instructie altijd waar is (lees meer over deze gemeenschappelijke valkuil hier ). De auteur was vrijwel zeker van plan de gelijkheidsoperator ( ==
) te gebruiken, zoals hieronder weergegeven:
int a = 2;
/* ... */
if (a == 1)
/* Delete all files on my hard drive */
Operator Associativiteit
int a, b = 1, c = 2;
a = b = c;
Dit wijst c
aan b
, die b
retourneert, wat dan is toegewezen aan a
. Dit gebeurt omdat alle toewijzingsoperatoren juiste associativiteit hebben, wat betekent dat de meest rechtse bewerking in de uitdrukking eerst wordt geëvalueerd en van rechts naar links verloopt.
Rekenkundige operatoren
Basis rekenen
Retourneer een waarde die het resultaat is van het toepassen van de linkerhandoperand op de rechteroperand, met behulp van de bijbehorende wiskundige bewerking. Normale wiskundige regels van commutatie zijn van toepassing (dwz optellen en vermenigvuldigen zijn commutatief, aftrekken, delen en modulus niet).
Toevoeging Operator
De toevoeging-operator ( +
) wordt gebruikt om twee operanden aan elkaar toe te voegen. Voorbeeld:
#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;
}
Operator voor aftrekken
De aftrekoperator ( -
) wordt gebruikt om de tweede operand van de eerste af te trekken. Voorbeeld:
#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;
}
Vermenigvuldigingsoperator
De vermenigvuldigingsoperator ( *
) wordt gebruikt om beide operanden te vermenigvuldigen. Voorbeeld:
#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;
}
Niet te verwarren met de *
dereference-operator.
Divisie-operator
De divisie-operator ( /
) deelt de eerste operand door de tweede. Als beide operanden van de divisie gehele getallen zijn, retourneert deze een geheel getal en wordt de rest genegeerd (gebruik de modulo-operator %
voor het berekenen en verkrijgen van de rest).
Als een van de operanden een drijvende-kommawaarde is, is het resultaat een benadering van de breuk.
Voorbeeld:
#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
De modulo-operator ( %
) ontvangt alleen gehele operanden en wordt gebruikt om de rest te berekenen nadat de eerste operand door de tweede is gedeeld. Voorbeeld:
#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;
}
Verhogen / verlagen exploitanten
De increment ( a++
) en decrement ( a--
) operatoren verschillen omdat ze de waarde van de variabele wijzigen waarop u ze toepast zonder een toewijzingsoperator. U kunt increment en decrement-operatoren gebruiken voor of na de variabele. De plaatsing van de operator verandert de timing van de toename / afname van de waarde in voor of na het toewijzen van de waarde aan de variabele. Voorbeeld:
#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 */
}
}
Zoals het voorbeeld voor c
en d
laat zien, hebben beide operatoren twee vormen, als prefixnotatie en postfixnotatie. Beide hebben hetzelfde effect bij het verhogen ( ++
) of verlagen ( --
) van de variabele, maar verschillen door de waarde die ze retourneren: prefix-bewerkingen voeren eerst de bewerking uit en retourneren vervolgens de waarde, terwijl postfix-bewerkingen eerst de waarde bepalen die worden geretourneerd en voer vervolgens de bewerking uit.
Vanwege dit potentieel contra-intuïtieve gedrag is het gebruik van increment / decrement-operatoren in expressies controversieel.
Logische operatoren
Logisch EN
Voert een logische booleaanse EN-ing uit van de twee operanden die 1 retourneren als beide operanden niet nul zijn. De logische operator AND is van het type int
.
0 && 0 /* Returns 0. */
0 && 1 /* Returns 0. */
2 && 0 /* Returns 0. */
2 && 3 /* Returns 1. */
Logisch OF
Voert een logische booleaanse OR-ing uit van de twee operanden die 1 retourneren als een van de operanden niet nul is. De logische operator OF is van het type int
.
0 || 0 /* Returns 0. */
0 || 1 /* Returns 1. */
2 || 0 /* Returns 1. */
2 || 3 /* Returns 1. */
Logisch NIET
Voert een logische ontkenning uit. De logische NOT-operator is van het type int
. De operator NOT controleert of ten minste één bit gelijk is aan 1, zo ja, dan wordt 0 geretourneerd. Anders geeft hij 1 terug;
!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */
Kortsluitingevaluatie
Er zijn enkele cruciale eigenschappen die zowel &&
als ||
gemeen hebben :
- de linker operand (LHS) wordt volledig geëvalueerd voordat de rechter operand (RHS) überhaupt wordt geëvalueerd,
- er is een volgorde tussen de evaluatie van de linker operand en de rechter operand,
- en, belangrijker nog, de rechter operand wordt helemaal niet geëvalueerd als het resultaat van de linker operand het algehele resultaat bepaalt.
Dit betekent dat:
- als het LHS evalueert als 'waar' (niet nul), is het RHS van
||
wordt niet geëvalueerd (omdat het resultaat van 'waar OF iets' 'waar' is), - als de LHS evalueert als 'onwaar' (nul), wordt de RHS van
&&
niet geëvalueerd (omdat het resultaat van 'onwaar EN iets' 'onwaar' is).
Dit is belangrijk omdat u hiermee code kunt schrijven zoals:
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";
}
Als een negatieve waarde wordt doorgegeven aan de functie, wordt de value >= 0
term als onwaar geëvalueerd en wordt de value < NUM_NAMES
term niet geëvalueerd.
Verhogen / verlagen
De increment en decrement operatoren bestaan in de vorm van een voorvoegsel en een postfix .
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 */
Merk op dat rekenkundige bewerkingen introduceren geen opeenvolging punten , zodat bepaalde uitdrukkingen met ++
of --
operators mogen invoeren onbepaald gedrag .
Voorwaardelijke operator / Ternary operator
Evalueert de eerste operand en, als de resulterende waarde niet gelijk is aan nul, evalueert de tweede operand. Anders evalueert het zijn derde operand, zoals getoond in het volgende voorbeeld:
a = b ? c : d;
is gelijk aan:
if (b)
a = c;
else
a = d;
Deze pseudo-code vertegenwoordigt het: condition ? value_if_true : value_if_false
. Elke waarde kan het resultaat zijn van een geëvalueerde uitdrukking.
int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */
De voorwaardelijke operator kan worden genest. De volgende code bepaalt bijvoorbeeld de grootste van drie getallen:
big= a > b ? (a > c ? a : c)
: (b > c ? b : c);
In het volgende voorbeeld worden zelfs gehele getallen naar een bestand geschreven en oneven gehele getallen naar een ander bestand:
#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;
}
De voorwaardelijke operator koppelt van rechts naar links. Stel je de volgende situatie voor:
exp1 ? exp2 : exp3 ? exp4 : exp5
Omdat de associatie van rechts naar links is, wordt de bovenstaande uitdrukking als geëvalueerd
exp1 ? exp2 : ( exp3 ? exp4 : exp5 )
Comma Operator
Evalueert de linkeroperand, negeert de resulterende waarde en evalueert vervolgens zijn rechtenoperand en het resultaat levert de waarde van de meest rechtse operand op.
int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
De komma-operator introduceert een reekspunt tussen zijn operanden.
Merk op dat de komma die in functies wordt gebruikt, roept dat afzonderlijke argumenten NIET de komma-operator zijn , maar eerder een scheidingsteken dat verschilt van de komma-operator . Het heeft dus niet de eigenschappen van de komma-operator .
De bovenstaande aanroep printf()
bevat zowel de komma-operator als het scheidingsteken .
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/* ^ ^ this is a comma operator */
/* this is a separator */
De komma-operator wordt vaak gebruikt in het initialisatiegedeelte en in het updategedeelte van een for
lus. Bijvoorbeeld:
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
Hiermee wordt een expliciete conversie in het gegeven type uitgevoerd op basis van de waarde die resulteert uit de evaluatie van de gegeven uitdrukking.
int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */
Daarbij is de waarde van x
wordt omgezet in een double
, de scheiding bevordert de waarde van y
te double
, ook, en het resultaat van de deling, een double
wordt doorgegeven aan printf
te drukken.
grootte van operator
Met een type als operand
Evalueert de grootte in bytes van type size_t
van objecten van het gegeven type. Vereist haakjes rond het type.
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! */
Met een uitdrukking als operand
Evalueert de grootte in bytes, van type size_t
, van objecten van het type van de gegeven uitdrukking. De uitdrukking zelf wordt niet geëvalueerd. Haakjes zijn niet vereist; omdat de gegeven uitdrukking echter unary moet zijn, wordt het als best practice beschouwd om ze altijd te gebruiken.
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. */
Aanwijzer rekenen
Aanwijzer toevoegen
Gegeven een aanwijzer en een scalair type N
, resulteert in een aanwijzer naar het N
-element van het puntige type dat direct volgt op het puntige object in het geheugen.
int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */
Het maakt niet uit of de aanwijzer wordt gebruikt als de operandwaarde of de scalaire waarde. Dit betekent dat dingen zoals 3 + arr
geldig zijn. Als arr[k]
het k+1
lid van een array is, dan is arr+k
een pointer naar arr[k]
. Met andere woorden, arr
of arr+0
is een aanwijzer naar arr[0]
, arr+1
is een aanwijzer naar arr[2]
, enzovoort. Over het algemeen is *(arr+k)
hetzelfde als arr[k]
.
In tegenstelling tot de gebruikelijke rekenkunde, zal toevoeging van 1
aan een pointer aan een int
4
bytes toevoegen aan de huidige adreswaarde. Aangezien arraynamen constante aanwijzers zijn, is +
de enige operator die we kunnen gebruiken om toegang te krijgen tot de leden van een array via aanwijzernotatie met behulp van de arraynaam. Door een aanwijzer naar een array te definiëren, kunnen we echter meer flexibiliteit krijgen om de gegevens in een array te verwerken. We kunnen bijvoorbeeld de leden van een array als volgt afdrukken:
#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;
}
Door een aanwijzer naar de array te definiëren, komt het bovenstaande programma overeen met het volgende:
#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;
}
Zie dat de leden van de array arr
worden benaderd met behulp van de operatoren +
en ++
. De andere operatoren die kunnen worden gebruikt met de aanwijzer ptr
zijn -
en --
.
Aanwijzer aftrekken
Gegeven twee verwijzingen naar hetzelfde type, wordt geëvalueerd in een object van het type ptrdiff_t
dat de scalaire waarde bevat die moet worden toegevoegd aan de tweede aanwijzer om de waarde van de eerste aanwijzer te verkrijgen.
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". */
Toegang Operators
Het lid toegang operators (dot .
En pijl ->
) worden gebruikt om de toegang van een lid van een struct
.
Lid van object
Evalueert in de waarde die het object aangeeft dat lid is van het geopende object.
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". */
Lid van gewezen object
Syntactische suiker voor dereferencing gevolgd door lidtoegang. In feite is een uitdrukking van de vorm x->y
afkorting voor (*x).y
- maar de (*x).y
is veel duidelijker, vooral als de structuuraanwijzers zijn genest.
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". */
Adres-of
De unaire &
operator is het adres van de operator. Het evalueert de gegeven uitdrukking, waarbij het resulterende object een waarde moet zijn. Vervolgens wordt het geëvalueerd in een object waarvan het type een pointer is naar het type van het resulterende object en het adres van het resulterende object bevat.
int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */
dereference
De operator unary *
verwijst naar een aanwijzer. Het evalueert de waarde die het gevolg is van het verwijderen van de verwijzingen naar de aanwijzer die het gevolg is van het evalueren van de gegeven uitdrukking.
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". */
Indexeren
Indexering is syntactische suiker voor toevoeging van aanwijzers gevolgd door dereferencing. In feite is een uitdrukking in de vorm a[i]
equivalent aan *(a + i)
- maar de expliciete subscriptnotatie heeft de voorkeur.
int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */
Uitwisselbaarheid van indexering
Het toevoegen van een pointer aan een geheel getal is een commutatieve bewerking (dwz de volgorde van de operanden verandert het resultaat niet) dus pointer + integer == integer + pointer
.
Een gevolg hiervan is dat arr[3]
en 3[arr]
equivalent zijn.
printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */
Het gebruik van een expressie 3[arr]
plaats van arr[3]
wordt over het algemeen niet aanbevolen, omdat dit de leesbaarheid van de code beïnvloedt. Het is vaak populair in onleesbare programmeerwedstrijden.
Functie Oproepoperator
De eerste operand moet een functiepointer zijn (een functie-aanduiding is ook acceptabel omdat deze wordt geconverteerd naar een pointer naar de functie), ter identificatie van de aan te roepen functie, en alle andere operanden, indien aanwezig, worden gezamenlijk de argumenten van de functieoproep genoemd . Evalueert de retourwaarde die resulteert uit het aanroepen van de juiste functie met de respectieve argumenten.
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 */
Bitwise Operators
Bitwise-operatoren kunnen worden gebruikt om bitniveau-bewerking op variabelen uit te voeren.
Hieronder is een lijst van alle zes bitsgewijze operatoren die worden ondersteund in C:
Symbool | operator |
---|---|
& | bitwise AND |
| | bitgewijs inclusief OF |
^ | bitgewijs exclusief OF (XOR) |
~ | bitwise not (iemands complement) |
<< | logische linkerverschuiving |
>> | logische juiste shift |
Het volgende programma illustreert het gebruik van alle bitsgewijze 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;
}
Bitgewijze bewerkingen met ondertekende typen moeten worden vermeden omdat het tekenbit van een dergelijke bitrepresentatie een bepaalde betekenis heeft. Bijzondere beperkingen zijn van toepassing op de ploegendiensten:
Een 1 bit naar links verplaatsen naar het ondertekende bit is fout en leidt tot ongedefinieerd gedrag.
Rechts verschuiven van een negatieve waarde (met tekenbit 1) is implementatie gedefinieerd en daarom niet draagbaar.
Als de waarde van de rechteroperand van een shift-operator negatief is of groter is dan of gelijk is aan de breedte van de gepromoveerde linkeroperand, is het gedrag niet gedefinieerd.
maskeren:
Masking verwijst naar het proces van het extraheren van de gewenste bits uit (of het transformeren van de gewenste bits in) een variabele met behulp van logische bitsgewijze bewerkingen. De operand (een constante of variabele) die wordt gebruikt om maskering uit te voeren, wordt een masker genoemd .
Masking wordt op veel verschillende manieren gebruikt:
- Om het bitpatroon van een integer variabele te bepalen.
- Een deel van een bepaald bitpatroon naar een nieuwe variabele kopiëren, terwijl de rest van de nieuwe variabele wordt gevuld met nullen (met bitsgewijze EN)
- Om een deel van een gegeven bitpatroon naar een nieuwe variabele te kopiëren, terwijl de rest van de nieuwe variabele wordt gevuld met 1s (met bitsgewijze OF).
- Om een deel van een gegeven bitpatroon naar een nieuwe variabele te kopiëren, terwijl de rest van het originele bitpatroon binnen de nieuwe variabele wordt omgekeerd (met bitwise exclusive OR).
De volgende functie gebruikt een masker om het bitpatroon van een variabele weer te geven:
#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
Hiermee wordt de uitlijningsvereiste voor het opgegeven type opgevraagd. De uitlijningsvereiste is een positief integraal vermogen van 2 dat het aantal bytes vertegenwoordigt waartussen twee objecten van het type kunnen worden toegewezen. In C wordt de uitlijningsvereiste gemeten in size_t
.
De typenaam mag geen onvolledig type of functietype zijn. Als een array als type wordt gebruikt, wordt het type van het array-element gebruikt.
Deze operator is vaak toegankelijk via de handige macro- alignof
van <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;}));
}
Mogelijke output:
Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4
Kortsluitgedrag van logische operatoren
Kortsluiting is een functionaliteit die het evalueren van delen van een (if / while / ...) conditie indien mogelijk overslaat. In het geval van een logische bewerking op twee operanden, wordt de eerste operand geëvalueerd (naar waar of onwaar) en als er een oordeel is (dat wil zeggen eerste operand is onwaar wanneer && wordt gebruikt, is eerste operand waar wanneer || wordt gebruikt) de tweede operand is Niet geëvalueerd.
Voorbeeld:
#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;
}
Bekijk het zelf:
#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;
}
Output:
$ ./a.out
print function 20
I will be printed!
Kortsluiting is belangrijk, als u wilt voorkomen dat termen worden geëvalueerd die (rekenkundig) duur zijn. Bovendien kan het de stroom van uw programma sterk beïnvloeden, zoals in dit geval: waarom drukt dit programma "gevorkt!" Af 4 maal?