C Language
operatörer
Sök…
Introduktion
En operatör på ett programmeringsspråk är en symbol som berättar kompilatorn eller tolkaren att utföra en specifik matematisk, relationell eller logisk operation och producera ett slutresultat.
C har många kraftfulla operatörer. Många C-operatörer är binära operatörer, vilket innebär att de har två operander. Till exempel, i a / b
, /
är en binär operator som accepterar två operander ( a
, b
). Det finns några unary operatörer som tar en operand (till exempel: ~
, ++
), och bara en ternary operatör ? :
.
Syntax
- expr1 operatör
- operatör expr2
- expr1 operatör expr2
- expr1? expr2: expr3
Anmärkningar
Operatörer har en arity , en företräde och en associativitet .
Arity anger antalet operander. I C finns tre olika operatörsgrupper:
- Unary (1 operand)
- Binär (2 operander)
- Ternary (3 operander)
Företräde indikerar vilka operatörer som "binder" först till sina operander. Det vill säga vilken operatör som har prioritet att driva på sina operander. Till exempel följer C-språket konventionen att multiplikation och delning har företräde framför tillsats och subtraktion:
a * b + c
Ger samma resultat som
(a * b) + c
Om detta inte är vad som ville, kan företräde tvingas med parenteser, eftersom de har högsta prioritet hos alla operatörer.
a * (b + c)
Detta nya uttryck kommer att ge ett resultat som skiljer sig från de tidigare två uttryckena.
C-språket har många prioritetsnivåer; Nedan ges en tabell över alla operatörer i fallande prioritetsordning.
Företrädes tabell
operatörer associativitet ()
[]
->
.
vänster till höger !
~
++
--
+
-
*
(dereference)(type)
sizeof
höger till vänster *
(multiplikation)/
%
vänster till höger +
-
vänster till höger <<
>>
vänster till höger <
<=
>
>=
vänster till höger ==
!=
vänster till höger &
vänster till höger ^
vänster till höger |
vänster till höger &&
vänster till höger ||
vänster till höger ?:
höger till vänster =
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
höger till vänster ,
vänster till höger Associativity indikerar hur operatörer med lika hög prioritet binder som standard och det finns två typer: Vänster till höger och höger till vänster . Ett exempel på bindning från vänster till höger är subtraktionsoperatören (
-
). Uttrycketa - b - c - d
har tre subtraktioner av identisk föregång, men ger samma resultat som
((a - b) - c) - d
för att vänster mest
-
binder först till sina två operander.Ett exempel på associativitet från höger till vänster är operationerna för dereference
*
och post-increment++
. Båda har samma prioritet, så om de används i ett uttryck som* ptr ++
, detta motsvarar
* (ptr ++)
eftersom den högsta, unära operatören (
++
) binder först till sin enda operand.
Relationsoperatörer
Relationsoperatörer kontrollerar om ett specifikt samband mellan två operander är sant. Resultatet utvärderas till 1 (vilket betyder sant ) eller 0 (vilket betyder falskt ). Detta resultat används ofta för att påverka kontrollflödet (via if
, while
, for
), men kan också lagras i variabler.
Lika "=="
Kontrollerar om de medföljande operanderna är lika.
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. */
OBS: Den här operatören ska inte förväxlas med uppdragsoperatören ( =
)!
Inte lika med "! ="
Kontrollerar om de medföljande operanderna inte är lika.
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. */
Denna operatör returnerar effektivt det motsatta resultatet till det för lika ( ==
) operatören.
Inte "!"
Kontrollera om ett objekt är lika med 0
.
Den !
kan också användas direkt med en variabel enligt följande:
!someVal
Detta har samma effekt som:
someVal == 0
Större än ">"
Kontrollerar om vänsteroperand har ett större värde än högeroperand
5 > 4 /* evaluates to 1. */
4 > 5 /* evaluates to 0. */
4 > 4 /* evaluates to 0. */
Mindre än "<"
Kontrollerar om vänsteroperand har ett mindre värde än högeroperand
5 < 4 /* evaluates to 0. */
4 < 5 /* evaluates to 1. */
4 < 4 /* evaluates to 0. */
Större än eller lika "> ="
Kontrollerar om den vänstra handen har ett större eller lika värde som höger operand.
5 >= 4 /* evaluates to 1. */
4 >= 5 /* evaluates to 0. */
4 >= 4 /* evaluates to 1. */
Mindre än eller lika "<="
Kontrollerar om den vänstra operand har ett mindre eller lika värde som höger operand.
5 <= 4 /* evaluates to 0. */
4 <= 5 /* evaluates to 1. */
4 <= 4 /* evaluates to 1. */
Uppdragsoperatörer
Tilldelar värdet på den högra operanden till den lagringsplats som har namngivits av den vänstra operanden och returnerar värdet.
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'. */
Flera aritmetiska operationer har en sammansatt tilldelningsoperatör .
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 */
En viktig egenskap hos dessa sammansatta uppdrag är att uttrycket på vänster sida ( a
) endast utvärderas en gång. Exempelvis om p
är en pekare
*p += 27;
dereferences p
bara en gång, medan följande gör det två gånger.
*p = *p + 27;
Det bör också noteras att resultatet av en uppdrag som a = b
är det som kallas en rvalue . Således har tilldelningen faktiskt ett värde som sedan kan tilldelas en annan variabel. Detta gör det möjligt att kedja uppdrag för att ställa in flera variabler i ett enda uttalande.
Denna rvalue kan användas i styrande uttryck för if
uttalanden (eller slingor eller switch
uttalanden) som skyddar någon kod på resultatet av ett annat uttryck eller funktionssamtal. Till exempel:
char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
/* do something with buffer */
free(buffer);
}
else
{
/* report allocation failure */
}
På grund av detta måste man vara noga med att undvika en vanlig typfel som kan leda till mystiska buggar.
int a = 2;
/* ... */
if (a = 1)
/* Delete all files on my hard drive */
Detta kommer att ha katastrofala resultat, eftersom a = 1
alltid utvärderar till 1
och därmed det kontrollerande uttrycket för if
uttalandet alltid kommer att vara sant (läs mer om detta vanliga fallgrop här ). Författaren menade nästan säkert att använda jämställdhetsoperatören ( ==
) enligt nedan:
int a = 2;
/* ... */
if (a == 1)
/* Delete all files on my hard drive */
Operator Associativity
int a, b = 1, c = 2;
a = b = c;
Detta tilldelar c
till b
, som returnerar b
, som är tilldelat till a
. Detta händer eftersom alla tilldelningsoperatörer har höger associativitet, det betyder att den längst längst till höger i uttrycket utvärderas först och fortsätter från höger till vänster.
Aritmetiska operatörer
Grundläggande aritmetik
Returnera ett värde som är resultatet av att tillämpa den vänstra operanden på den högra operand med hjälp av den tillhörande matematiska operationen. Normala matematiska regler för kommutering gäller (dvs. tillägg och multiplikation är kommutativa, subtraktion, delning och modul är inte).
Tilläggsoperatör
Tilläggsoperatören ( +
) används för att lägga till två operander tillsammans. Exempel:
#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;
}
Operatör för subtraktion
Subtraktionsoperatören ( -
) används för att subtrahera den andra operanden från den första. Exempel:
#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;
}
Multiplikationsoperatör
Multiplikationsoperatören ( *
) används för att multiplicera båda operanderna. Exempel:
#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;
}
Inte att förväxla med *
dereferenceoperatören.
Avdelningsoperatör
Divisionsoperatören ( /
) delar den första operanden med den andra. Om båda operandarna i divisionen är heltal, kommer det att returnera ett heltal och kasta resten (använd moduloperatören %
för att beräkna och skaffa återstoden).
Om en av operandema är ett flytande punktvärde, är resultatet en approximation av fraktionen.
Exempel:
#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-operatör
Moduloperatören ( %
) får endast heltalsoperander och används för att beräkna återstoden efter att den första operanden är dividerad med den andra. Exempel:
#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;
}
Öknings- / minskningsoperatörer
Operatörerna för tillägg ( a++
) och dekrement ( a--
) skiljer sig åt genom att de ändrar värdet på den variabel du tillämpar dem på utan en uppdragsoperatör. Du kan använda inkrement- och minskningsoperatörer antingen före eller efter variabeln. Placeringen av operatören ändrar tidpunkten för inkrementering / dekrementering av värdet till före eller efter tilldelning till variabeln. Exempel:
#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 */
}
}
Som exemplet för c
och d
visar har båda operatörerna två former, som prefixnotation och postfixnotation. Båda har samma effekt när det gäller att öka ( ++
) eller dekrementera ( --
) variabeln, men skiljer sig åt efter värdet de returnerar: prefixoperationer gör operationen först och sedan returnerar värdet, medan postfixoperationer först bestämmer värdet som ska returneras och gör sedan operationen.
På grund av detta potentiellt motintuitiva beteende är användningen av inkrement / dekrementoperatörer i uttryck kontroversiell.
Logiska operatörer
Logiskt OCH
Utför en logisk booleska OCH-ing av de två operandarna som returnerar 1 om båda operandema är icke-noll. Den logiska OCH-operatören är av typ int
.
0 && 0 /* Returns 0. */
0 && 1 /* Returns 0. */
2 && 0 /* Returns 0. */
2 && 3 /* Returns 1. */
Logiskt ELLER
Utför en logisk boolesk OR-ing av de två operandarna som returnerar 1 om någon av operandema är icke-noll. Den logiska ELLER-operatören är av typen int
.
0 || 0 /* Returns 0. */
0 || 1 /* Returns 1. */
2 || 0 /* Returns 1. */
2 || 3 /* Returns 1. */
Logiskt INTE
Utför en logisk negation. Den logiska INTE-operatören är av typen int
. NOT-operatören kontrollerar om minst en bit är lika med 1, i så fall kommer den tillbaka 0. Annars returnerar den 1;
!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */
Kortslutningsutvärdering
Det finns några avgörande egenskaper som är gemensamma för både &&
och ||
:
- den vänstra operanden (LHS) utvärderas fullt ut innan den högra operanden (RHS) utvärderas alls,
- det finns en sekvenspunkt mellan utvärderingen av den vänstra operanden och den högra operanden,
- och, viktigast av allt, är den högra operanden inte utvärderas alls om resultatet av den vänstra operanden bestämmer det totala resultatet.
Detta innebär att:
- om LHS utvärderar till 'sant' (icke-noll), RHS för
||
kommer inte att utvärderas (eftersom resultatet av "sant ELLER något" är "sant"), - om LHS utvärderar till "falskt" (noll) kommer RHS för
&&
inte att utvärderas (eftersom resultatet av "falsk OCH något" är "falskt").
Detta är viktigt eftersom det tillåter dig att skriva kod som:
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";
}
Om ett negativt värde överförs till funktionen utvärderas value >= 0
term till falskt och value < NUM_NAMES
term utvärderas inte.
Ökning / minskning
Öknings- och minskningsoperatörerna finns i prefix- och postfixform .
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 */
Observera att aritmetiska operationer inte introducerar sekvenspunkter , så vissa uttryck med ++
eller --
operatörer kan införa odefinierat beteende .
Villkorad operatör / ternär operatör
Utvärderar sin första operand, och om det resulterande värdet inte är lika med noll utvärderas dess andra operand. Annars utvärderar den sin tredje operand, som visas i följande exempel:
a = b ? c : d;
är ekvivalent med:
if (b)
a = c;
else
a = d;
Den här pseudokoden representerar den: condition ? value_if_true : value_if_false
. Varje värde kan vara resultatet av ett utvärderat uttryck.
int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */
Den villkorade operatören kan kapslas. Till exempel bestämmer följande kod det största av tre siffror:
big= a > b ? (a > c ? a : c)
: (b > c ? b : c);
Följande exempel skriver även heltal till en fil och udda heltal till en annan fil:
#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;
}
Den villkorade operatören associerar från höger till vänster. Tänk på följande:
exp1 ? exp2 : exp3 ? exp4 : exp5
Eftersom föreningen är från höger till vänster, utvärderas ovanstående uttryck som
exp1 ? exp2 : ( exp3 ? exp4 : exp5 )
Komma-operatör
Utvärderar sin vänstra operand, kastar det resulterande värdet och utvärderar sedan dess rättigheter operand och resultatet ger värdet på dess operand längst till höger.
int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
Kommaoperatören introducerar en sekvenspunkt mellan sina operander.
Observera att kommatecken som används i funktioner kallar att separata argument INTE är kommatoperatören , snarare kallas det en separator som skiljer sig från kommaoperatören . Därför har det inte egenskaperna hos komma operatören.
Ovanstående printf()
-samtal innehåller både kommaoperatören och separatorn .
printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/* ^ ^ this is a comma operator */
/* this is a separator */
Kommaoperatören används ofta i initieringsavsnittet såväl som i uppdateringsavsnittet för en for
slinga. Till exempel:
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);
Gjutoperatör
Utför en uttrycklig konvertering till den givna typen från värdet som härrör från utvärdering av det givna uttrycket.
int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */
Här konverteras värdet på x
till en double
, divisionen främjar också värdet på y
till double
, och resultatet av divisionen, en double
överförs till printf
för utskrift.
storlek på operatör
Med en typ som operand
Utvärderar till storleken i byte, av typen size_t
, för objekt av den angivna typen. Kräver parenteser runt typen.
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! */
Med ett uttryck som operand
Utvärderar till storleken i byte, av typen size_t
, för objekt av typen av det givna uttrycket. Uttrycket i sig utvärderas inte. Parenteser krävs inte; eftersom det givna uttrycket måste vara ojämnt anses det dock som bästa praxis att alltid använda dem.
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. */
Pekare Aritmetik
Pekertillägg
Givet en pekare och en skalartyp N
, utvärderar till en pekare till det N
e elementet i den pekade till typen som direkt lyckas det pekade objektet i minnet.
int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */
Det spelar ingen roll om pekaren används som operandvärde eller skalarvärde. Detta betyder att saker som 3 + arr
är giltiga. Om arr[k]
är k+1
medlemmen i en matris, är arr+k
en pekare till arr[k]
. Med andra ord är arr
eller arr+0
en pekare till arr[0]
, arr+1
är en pekare till arr[2]
, och så vidare. I allmänhet är *(arr+k)
samma som arr[k]
.
Till skillnad från den vanliga aritmetiken, kommer 1
till en pekare till en int
att lägga till 4
byte till det aktuella adressvärdet. Eftersom matrisnamn är konstanta pekare, är +
den enda operatören som vi kan använda för att få åtkomst till medlemmarna i en matris via pekarenotation med hjälp av matrisnamnet. Men genom att definiera en pekare till en matris kan vi få mer flexibilitet att bearbeta data i en matris. Vi kan till exempel skriva ut medlemmarna i en matris på följande sätt:
#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;
}
Genom att definiera en pekare till arrayen motsvarar programmet ovan följande:
#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;
}
Se att medlemmarna i array- arr
har åtkomst med operatörerna +
och ++
. De andra operatörerna som kan användas med pekaren ptr
är -
och --
.
Pointer subtraktion
Givet två pekare till samma typ, utvärderar till ett objekt av typen ptrdiff_t
som har det skalära värdet som måste läggas till den andra pekaren för att få värdet på den första pekaren.
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". */
Åtkomstoperatörer
Operatörerna medlem bredband (prick .
Och pil ->
) används för att tillgång till en medlem i en struct
.
Medlem av objekt
Utvärderar i lvalue som anger objektet som är medlem av det åtkomna objektet.
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". */
Medlem av pekat objekt
Syntaktiskt socker för dereferencing följt av medlemsåtkomst. Effektivt är ett uttryck av formen x->y
korta för (*x).y
- men (*x).y
är mycket tydligare, särskilt om strukturpekarna är kapslade.
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". */
Adress-of
Unary &
operator är operatörens adress. Den utvärderar det givna uttrycket, där det resulterande objektet måste vara en lvalue. Därefter utvärderas det till ett objekt vars typ är en pekare till det resulterande objektets typ och innehåller adressen till det resulterande objektet.
int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */
dereference
Den unary *
-operatören leder till en pekare. Den utvärderar in i lvalue som är resultatet av att du återföra pekaren som är resultatet av utvärdering av det givna uttrycket.
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". */
indexering
Indexering är syntaktiskt socker för pointertillägg följt av dereferencing. Effektivt är ett uttryck av formen a[i]
ekvivalent med *(a + i)
- men den uttryckliga subskriptnotationen föredras.
int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */
Utbytbarhet av indexering
Att lägga till en pekare till ett heltal är en kommutativ operation (dvs. ordningen för operandarna ändrar inte resultatet) så pointer + integer == integer + pointer
.
En konsekvens av detta är att arr[3]
och 3[arr]
är likvärdiga.
printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */
Användning av ett uttryck 3[arr]
istället för arr[3]
rekommenderas vanligtvis inte, eftersom det påverkar kodläsbarheten. Det tenderar att vara ett populärt i dolda programmeringstävlingar.
Funktionssamtaloperatör
Den första operanden måste vara en funktionspekare (en funktionsbeteckning är också acceptabel eftersom den kommer att konverteras till en pekare till funktionen), identifiera funktionen att ringa, och alla andra operander, om några, är kollektivt kända som funktionssamtalets argument . Utvärderar till returvärdet som uppstår genom att anropa lämplig funktion med respektive argument.
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 */
Bitvisa operatörer
Bitvisa operatörer kan användas för att utföra bitnivåoperation på variabler.
Nedan finns en lista över alla sex bitvisa operatörer som stöds i C:
Symbol | Operatör |
---|---|
& | bitvis OCH |
| | bitvis inkluderande ELLER |
^ | bitvis exklusiv ELLER (XOR) |
~ | bitvis inte (ens komplement) |
<< | logisk vänsterskift |
>> | logisk högerväxling |
Följande program illustrerar användningen av alla bitvisa operatörer:
#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;
}
Bitvisa operationer med signerade typer bör undvikas eftersom teckenbiten för en sådan bitrepresentation har en speciell betydelse. Särskilda begränsningar gäller för skiftoperatörerna:
Vänster som flyttar en bit till den signerade biten är felaktigt och leder till odefinierat beteende.
Högerförskjutning av ett negativt värde (med teckenbit 1) är implementeringsdefinierat och därför inte bärbart.
Om värdet på en högeroperand hos en skiftoperatör är negativ eller är större än eller lika med bredden på den främjade vänstra operanden, är beteendet odefinierat.
maskering:
Maskering avser processen att extrahera de önskade bitarna från (eller transformera de önskade bitarna i) en variabel med hjälp av logiska bitvisa operationer. Operanden (en konstant eller variabel) som används för att utföra maskering kallas en mask .
Maskering används på många olika sätt:
- Att bestämma bitmönstret för en heltalvariabel.
- För att kopiera en del av ett givet bitmönster till en ny variabel, medan resten av den nya variabeln är fylld med 0s (med bitvis OCH)
- För att kopiera en del av ett givet bitmönster till en ny variabel, medan resten av den nya variabeln är fylld med 1s (med bitvis ELLER).
- För att kopiera en del av ett givet bitmönster till en ny variabel, medan resten av det ursprungliga bitmönstret inverteras inom den nya variabeln (med bitvis exklusiv ELLER).
Följande funktion använder en mask för att visa bitmönstret för en variabel:
#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
Frågor om justeringskravet för den angivna typen. Inriktningskravet är en positiv integrerad effekt av 2 som representerar antalet byte mellan vilka två objekt av typen kan tilldelas. I C mäts size_t
i size_t
.
Typnamnet kanske inte är en ofullständig typ eller en funktionstyp. Om en matris används som typen används typen av arrayelementet.
Denna operatör nås ofta via bekvämlighetsmakro- alignof
från <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öjlig utgång:
Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4
Kortslutningsbeteende hos logiska operatörer
Kortslutning är en funktionalitet som hoppar över utvärdering av delar av ett (om / medan / ...) tillstånd när det är möjligt. Vid en logisk operation på två operander utvärderas den första operanden (till sann eller falsk) och om det finns en dom (dvs. första operand är falsk när man använder &&, är första operand sant när man använder ||) den andra operanden är inte utvärderad.
Exempel:
#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;
}
Kolla in det själv:
#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;
}
Produktion:
$ ./a.out
print function 20
I will be printed!
Kortslutning är viktigt när du vill undvika att utvärdera termer som är (beräkningsmässigt) kostsamma. Dessutom kan det starkt påverka flödet för ditt program som i det här fallet: Varför trycker detta program "forked!" 4 gånger?