V tomto článku najdete krátký přehled různých operátorů, se kterými se setkáme v jazyce C. Dále potom nějaké příklady, jak operátory používat.
Matematické operátory
V jazyce C nalezneme celkem pět základních matematických operátorů, které přesně kopírují pět základních matematických operací. Jde o sčítání, odčítání, násobení, dělení a modulo. Operátory mají následující symboly:
| operátor | popis |
|---|---|
+ |
Operátor sčítání |
++ |
Speciální operátor , který zvýší hodnotu proměnné o jedna |
- |
Operátor odčítání. Též unírní minus (záporné znaménko před číslem) |
-- |
Speciální operátor, který sníží hodnotu proměnné o jedna. |
* |
Operátor násobení |
/ |
Operátor dělení. Tento operátor zastává funkci jak operátoru celočíselného dělení, tak i funkci dělení necelých čísel. |
% |
Operátor modulo celým číslem (zbytek po dělení celým číslem). |
Operátor dělení si zaslouží malou poznámku. Jeho podoba je totiž daná vstupními typy parametrů. Je-li alespoň jeden ze vstupních parametrů neceločíselný, bude se počítat v desetinných číslech. V případě, že oba parametry jsou celočíselného typu, bude se počítat jako při dělení se zbytkem. Toto je velký rozdíl od Pascalu a při programování může způsobit velké potíže. V případě, že máme oba typy celočíselné a přesto chceme dělit v oboru reálných čísel, musíme některý z parametrů operátoru přetypovat.
int a = 3;
int b = 2;
float c = a/b; // výsledek bude 1.0!!
float d = (float)a/b; // výsledek bude 1.5, přetypování a
Operátory splňují standardní matematické priority operátorů. Tedy chceme-li např. dělit složitějším výrazem, je nutné výraz uzavřít do kulatých závorek, které mění prioritu.
float a = 3.0;
float b = 2.0;
float c = a/2*b; // výsledek bude 3.0, (3.0/2.0)*2.0!!
float d = a/(2*b); // výsledek bude 0.75, 3.0/(2.0*2.0)
Bitové operátory
V jazyce C je nepřímo možné pracovat s jednotlivými bity. Slouží k tomu bitové operátory.
& |
AND — bitové násobení |
| |
OR — bitové sčítání |
^ |
XOR — bitová non-ekvivalence |
~ |
bitová negace |
<< |
bitový posun vlevo. Posune bity o zadaný počet vlevo a zprava doplní nuly. |
>> |
bitový posun vpravo. Posune bity o zadaný počet vpravo. Zleva se doplní nuly, pokud posouváme neznaménkový typ, jinak se kopíruje nejvyšší bit (znaménko). |
Bitové posuny jsou nesmírně cenným nástrojem, pokud potřebujeme rychle generovat mocniny dvou. V každé poziční soustavě je posun vlevo vždy roven násobení základem soustavy, v našem případě dvěma.
int a = 1 << 2; // v a bude binárně 100, tedy 4
int b = 3 << 3; // b binárně 11 se posune doleva na 11000, tedy na 24
Jedním z možných použití bitových operátorů je testování sudosti nebo lichosti čísla. Porovnejme dva přístupy:
int a = 5;
if ((a % 2) == 0)
printf("cislo je sude");
else
printf("cislo je liche");
if ((a & 1) == 0)
printf("cislo je sude");
else
printf("cislo je liche");
Na první pohled se nic nezměnilo. V podmínkách se ale testují jiné výrazy. V prvním případě se provede modulo dvěma a testujeme, zdali je výsledek nula. Ve druhém případě provedeme logický AND s jedničkou, fakticky se tak ptáme, jestli je nejnižší bit čísla jedna, nebo nula, a testujeme, zdali je výsledek nula. Druhý způsob se může zdát složitý, ale je výpočetně mnohem rychlejší než první způsob.
Uděláme si test tohoto tvrzení
/* modulo.c
Program demonstraci rozdílu mezi operátorem modulo % a bitovým posunem.
(c) Jirka Chráska 2023, <jirka@lixis.cz>
*/
#include <stdio.h>
int main()
{
int a = 5;
if((a % 2) == 0) // pouziti operatoru modulo 2
printf("cislo je sude\n");
else
printf("cislo je liche\n");
return 0;
}
/* bityp.c
Program demonstraci rozdílu mezi operátorem modulo % a bitovým posunem.
(c) Jirka Chráska 2023, <jirka@lixis.cz>
*/
#include <stdio.h>
int main()
{
int a = 5;
if((a & 1) == 0) // pouziti testu zda 0. bit je roven 0
printf("cislo je sude\n");
else
printf("cislo je liche\n");
return 0;
}
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -g -o modulo modulo.c (1)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -g -o bityp bityp.c (2)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ./modulo (3)
cislo je liche
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ./bityp (4)
cislo je liche
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ objdump -d -S -s modulo (5)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ objdump -d -S -s bityp (6)
| 1 | překlad modulo.c s debug informacemi a bez optimalizace |
| 2 | překlad bityp.c s debug informacemi a bez optimalizace |
| 3 | spuštění modulo |
| 4 | spuštení bityp |
| 5 | zpětný překlad modulo |
| 6 | zpětný překlad bityp |
Porovnáme, jak to bude vypadat na úrovni strojových instrukcí procesoru (v assembleru). Uděláme zpětný překlad ze spustitelného souboru do assembleru pomocí příkazu objdump -d -S -s <program>
0000000000001149 <main>:
*/
#include <stdio.h>
int main()
{
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 10 sub $0x10,%rsp
int a = 5;
1155: c7 45 fc 05 00 00 00 movl $0x5,-0x4(%rbp)
if((a % 2) == 0) // pouziti operatoru modulo 2
115c: 8b 45 fc mov -0x4(%rbp),%eax
115f: 83 e0 01 and $0x1,%eax
1162: 85 c0 test %eax,%eax
1164: 75 0e jne 1174 <main+0x2b>
printf("cislo je sude\n");
1166: 48 8d 3d 97 0e 00 00 lea 0xe97(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
116d: e8 de fe ff ff callq 1050 <puts@plt>
1172: eb 0c jmp 1180 <main+0x37>
else
printf("cislo je liche\n");
1174: 48 8d 3d 97 0e 00 00 lea 0xe97(%rip),%rdi # 2012 <_IO_stdin_used+0x12>
117b: e8 d0 fe ff ff callq 1050 <puts@plt>
return 0;
1180: b8 00 00 00 00 mov $0x0,%eax
}
1185: c9 leaveq
1186: c3 retq
1187: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
118e: 00 00
#include <stdio.h>
int main()
{
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 10 sub $0x10,%rsp
int a = 5;
1155: c7 45 fc 05 00 00 00 movl $0x5,-0x4(%rbp)
if((a & 1) == 0) // pouziti testu zda 0. bit je roven 0
115c: 8b 45 fc mov -0x4(%rbp),%eax
115f: 83 e0 01 and $0x1,%eax
1162: 85 c0 test %eax,%eax
1164: 75 0e jne 1174 <main+0x2b>
printf("cislo je sude\n");
1166: 48 8d 3d 97 0e 00 00 lea 0xe97(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
116d: e8 de fe ff ff callq 1050 <puts@plt>
1172: eb 0c jmp 1180 <main+0x37>
else
printf("cislo je liche\n");
1174: 48 8d 3d 97 0e 00 00 lea 0xe97(%rip),%rdi # 2012 <_IO_stdin_used+0x12>
117b: e8 d0 fe ff ff callq 1050 <puts@plt>
return 0;
1180: b8 00 00 00 00 mov $0x0,%eax
}
1185: c9 leaveq
1186: c3 retq
1187: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
118e: 00 00
Tady něco nehraje. Oba programy jsou stejné. Máme chytrý překladač, který umí dobře generovat kód procesoru a vyřešil oba případy stejně.
Zkusíme překládat s optimalizací -O2 a porovnáme
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -O2 -g -o modulo modulo.c (1)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ gcc -O2 -g -o bityp bityp.c (2)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ./modulo (3)
cislo je liche
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ ./bityp (4)
cislo je liche
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ objdump -d -S -s modulo (5)
jirka@jirka-Precision-T3610:~/vyuka_sspvc/c_programming$ objdump -d -S -s bityp (6)
| 1 | překlad modulo.c s debug informacemi a optimalizací -O2 |
| 2 | překlad bityp.c s debug informacemi a optimalizací O2 |
| 3 | spuštění modulo |
| 4 | spuštení bityp |
| 5 | zpětný překlad modulo |
| 6 | zpětný překlad bityp |
0000000000001060 <main>:
*/
#include <stdio.h>
int main()
{
1060: f3 0f 1e fa endbr64
1064: 48 83 ec 08 sub $0x8,%rsp
}
__fortify_function int
printf (const char *__restrict __fmt, ...)
{
return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
1068: 48 8d 3d 95 0f 00 00 lea 0xf95(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
106f: e8 dc ff ff ff callq 1050 <puts@plt>
if((a % 2) == 0) // pouziti operatoru modulo 2
printf("cislo je sude\n");
else
printf("cislo je liche\n");
return 0;
}
1074: 31 c0 xor %eax,%eax
1076: 48 83 c4 08 add $0x8,%rsp
107a: c3 retq
107b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000001060 <main>:
*/
#include <stdio.h>
int main()
{
1060: f3 0f 1e fa endbr64
1064: 48 83 ec 08 sub $0x8,%rsp
}
__fortify_function int
printf (const char *__restrict __fmt, ...)
{
return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
1068: 48 8d 3d 95 0f 00 00 lea 0xf95(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
106f: e8 dc ff ff ff callq 1050 <puts@plt>
if((a & 1) == 0) // pouziti testu zda 0. bit je roven 0
printf("cislo je sude\n");
else
printf("cislo je liche\n");
return 0;
}
1074: 31 c0 xor %eax,%eax
1076: 48 83 c4 08 add $0x8,%rsp
107a: c3 retq
107b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Tady musíme konstatovat, že překladač gcc generuje i bez optimalizace velmi hezký strojový kód. S optimalizací -O2 překladač vyhodnotil program ještě lépe, zjistil, že pro test pro sudé číslo nikdy nenastane, protože zná hodnotu proměnné a a udělal kód ještě kratší a lepší.
Tvrzení se nepotvrdilo (nebo dělám někde zásadní botu), ale ukázali jsme si použití optimalizace u překladače gcc a nástroje objdump, který nám ukazuje, jak je program zkompilován a sestaven.
Pro úplnost verze překladače:
jirka@jirka-Precision-T3610:~/vyuka_sspvc$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Cvičení na bitové operátory
Máme soustavu 8 přepínačů, pokud je přepínač zapnut označíme 1, pokud je vypnut označíme 0.
V programu můžeme číst stav všech přepínaču jako 8 bitové číslo, stav bude v proměnné unsigned char sw.
1. Přečtěte stav přepínačů a vytiskněte na výstup.
Použijeme funkci printBits8(), která nám vytiskne 8 bitové číslo po bitech.
// Tisk stavu přepínačů
#include <stdio.h>
// vytiskne unsigned char po bitech
void printBits8(unsigned char num) {
int bits = 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main( void )
{
unsigned char sw = 0b01011001; // výchozí stav přepínačů
printf("Stav přepínačů: ");
printBits8(sw);
return 0;
}
Překladač nemusíte instalovat, krásně funguje Online C Compiler. Napíšete kód a stikněte tlačítko Execute, to přeloží a spustí program pokud je bez chyb, jinak musíte opravit chyby. w3schools C kompilátor je lepší, hlásí korektně chyby.
| V online kompilárotu můžete do řetězců psát české znaky bez omezení, ve skutečném Céčku to fungovat nebude. |

2. Změňte stav 6. přepínače na zapnuto (ostatní nechte být)
// Změna stavu 5. přepínače
#include <stdio.h>
void printBits8(unsigned char num) {
int bits = 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main( void )
{
unsigned char sw = 0b01011001;
unsigned char maska = 0b00100000;
printf("Výchozí stav přepínačů: ");
printBits8(sw);
sw = sw | maska; // zapnutí 5. přepínače (6.bit), použijeme bitový OR operátor
printf("Změněný stav přepínačů: ");
printBits8(sw);
return 0;
}
Výchozí stav přepínačů: 01011001
Změněný stav přepínačů: 01111001
3. Vraťte stav 6. přepínače zpět na vypnuto
// Změna stavu 5. přepínače
#include <stdio.h>
void printBits8(unsigned char num) {
int bits = 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main( void )
{
unsigned char sw = 0b01011001;
unsigned char maska = 0b00100000;
printf("Výchozí stav přepínačů: ");
printBits8(sw);
sw = sw | maska; // zapnutí 5. přepínače (6.bit), použijeme bitový OR operátor
printf("Změněný stav přepínačů: ");
printBits8(sw);
maska = 0b11011111;
sw = sw & maska; // vrátím 5. přepínač zpět na vypnuto, použijeme bitový AND operátor
printf("Změněný stav přepínačů: ");
printBits8(sw);
return 0;
}
Výchozí stav přepínačů: 01011001
Změněný stav přepínačů: 01111001
Změněný stav přepínačů: 01011001
To samé mohu dosáhnout toho, že si masku zneguji (nemusím ji negovat ručně).
// Změna stavu 5. přepínače
#include <stdio.h>
void printBits8(unsigned char num) {
int bits = 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main( void )
{
unsigned char sw = 0b01011001;
unsigned char maska = 0b00100000;
printf("Výchozí stav přepínačů: ");
printBits8(sw);
sw = sw | maska; // zapnutí 5. přepínače (6.bit), použijeme bitový OR operátor
printf("Změněný stav přepínačů: ");
printBits8(sw);
sw = sw & (~maska); // vrátím 5. přepínač zpět na vypnuto, použijeme bitový AND operátor
printf("Změněný stav přepínačů: ");
printBits8(sw);
return 0;
}
Výchozí stav přepínačů: 01011001
Změněný stav přepínačů: 01111001
Změněný stav přepínačů: 01011001
4. Přečtěte stav 3. přepínače (2.bit) a vytiskněte 1, je-li zapnut, jinak vytiskněte 0.
// Přečtení stavu 3. přepínače
#include <stdio.h>
void printBits8(unsigned char num) {
int bits = 8;
for (int i = bits - 1; i >= 0; i--) {
printf("%d", (num >> i) & 1);
}
printf("\n");
}
int main( void )
{
unsigned char sw = 0b01011001;
unsigned char maska = 0b00000100;
unsigned char stav;
printf("Výchozí stav přepínačů: ");
printBits8(sw);
stav = (sw & maska) >> 2; // přečtení stavu 3. přepínače (2.bit)
printf("Stav 3. přepínače: %d\n",stav);
return 0;
}
Výchozí stav přepínačů: 01011001
Stav 3. přepínače: 0
Úkol
-
Přečtěte stavy 2. a 4. přepínače (1. a 3. bit).
-
Změňte stavy přepínačů tak, že co je zapnuto bude vypnuto a co je vypnuto bude zapnuto.
-
Zapněte 8. a 5. vypínač, ostatní nechte být.
-
Vraťte 8. a 5. vypínač do předchozího stavu, ostatní nechte být.
Výsledkem vašeho snažení budou čtyři prográmky a k tomu výpisy z výstupů, vhodně to pojmenujte.
Operátory porovnání
Jedno z nejčastějších operací v podmíněných výrazech je různorodé porovnávání. K porovnávání slouží řada operátorů, které svým zápisem odpovídají operátorům v jiných programovacích jazycích.
|
Operátor ostře větší než. |
|
Operátor větší nebo rovno. |
|
Operátor ostře menší než |
|
Operátor menší nebo rovno. |
|
Operátor rovnosti. Neplést s operátorem přiřazení, který se píše jen s jedním rovnítkem! |
|
Logické AND (a současně). Vrátí pravda pouze, pokud jsou oba operandy pravdivé. Pokud je vyhodnocen 1. operand jako nepravdivý, 2. operand se již netestuje. |
|
Logické OR (nebo). Vrátí pravda, pokud je alespoň jeden operand pravdivý. Pokud je vyhodnocen 1. operand jako pravdivý, 2. operand se již netestuje. |
|
Logické NOT (negace). Otočí pravdivostní hodnotu operandu (Vykřičník se píše před operand, jehož pravdivostní hodnotu má otočit). |
Speciálně u porovnávání je nutné se mít na pozoru a nezaměnit dvě rovnítka za jedno, neboť kód bude syntakticky správně, ale výsledek bude zcela odlišný od zamýšleného. Také je nutné neplést si bitové a logické operátory, každá skupina slouží jinému účelu.
Zkrácený zápis
Návrh jazyka C je minimalistický a snaží se ušetřit všude, kde jen to jde. Podívejme se na následující kód:
int a;
// ...
a = a + 2;
Zde přiřazujeme do proměnné a původní hodnotu zvýšenou o dva. Protože jde neustále o tu samou proměnnou, je takový zápis zbytečně redundantní. Ekvivalentně lze poslední příkaz zapsat takto:
int a;
// ...
a += 2;
Nejde o nic jiného než o zkrácenou formu výše popsaného příkazu. Všechny binární operátory v jazyce C lze takto zkrátit.
Typy operátorů
Dosud jsme probírali převážně binární operátory, ty mají dva operandy.
operand operátor operand
a + 5
Unární operátory mají jenom jeden operand.
Unární operátory
Unární operátory potřebují jenom jeden operand. Jsou to operátory: \(\mathbf{-}\) (znaménko minus před číslem), \(\mathbf{++}\) (zvýšení o 1), \(\mathbf{--}\) (snížení o 1), ~ (bitová negace), ! (logické NOT).
Píší se před operand, kromě operátorů \(\mathbf{++}\) a \(\mathbf{--}\), které se mohou psát před operand i za operand.
Pokud se \(\mathbf{++}\) (nebo \(\mathbf{--}\)) napíše před operand, pak se hodnota proměnné nejdříve zvětší (zmenší) o jedničku a potom se vrátí hodnota proměnné (tento operand nelze použít pro konstantu).
Pokud se \(\mathbf{++}\) (nebo \(\mathbf{--}\)) napíše za operand, pak se nejprve vrátí hodnota proměnné a potom se hodnota proměnné zvětší o jedničku (tento operand nelze použít pro konstantu).
int a = 5
int b;
b = ++a; /* hodnota b bude rovna 6 (nejprve se zvýší o 1 a pak se vrátí hodnota výrazu) a hodnota a bude rovna 6 */
int a = 5
int b;
b = a++; /* hodnota b bude rovna 5 (nejprve se vráti hodnota proměnné a, tedy 5 a potom se proměnná a zvýší o 1) a hodnota a bude rovna 6 */
Mezi unární operátory patří také operátor sizeof(), přestože vypadá jako funkce. Mezi kulaté závorky se dosazuje jako operand buď typ, jehož velikost chceme vědět, nebo rovnou nějaká proměnná či konstanta.
int pocet_kusu = 58;
const char zacatek_abecedy = 'a';
// vytiskne nám, kolik bytů zabírá v paměti promenna s nazvem pocet_kusu (4 byty)
printf("Velikost promenne je %lu bytu", sizeof(pocet_kusu));
// vytiskne nám, kolik bytů zabírá v paměti konstanta zacatek_abecedy (1 byte)
printf("Velikost konstanty je %lu bytu.", sizeof(zacatek_abecedy));
// vytiskne nám, kolik bytů zabere v paměti typ double (8 bytu)
printf("Typ double zabere %lu bytu.", sizeof(double));
Priority operátorů
Důležitá věc, kterou je nutné pochopit. Z nepochopení priority operátorů vznikají těžko odhalitelné chyby v programu, které kompilátor při kompilaci neodhalí.
V následující tabulce je přehled priorit operátorů shora od nejvyšší priority dolů k nejnižžši prioritě
| Priorita | Operátor | Popis | Vyhodnocení |
|---|---|---|---|
1 |
x++ x-- |
přičtení/odečtení jedničky po |
zleva doprava |
+ - |
unární plus a minus |
||
[] |
přístup k členu pole |
||
. |
přístup k členu struktury nebo unionu |
||
-> |
přístup k lčenu struktury nebo unionu pomocí ukazatele |
||
(typ){seznam} |
složený literár (C99) |
||
2 |
++x --x |
přičtení/odečtení jedničky před |
zprava doleva |
+ - |
unární plus a minus |
||
! ~ |
logické NOT a bitové NOT |
||
(type) |
přetypování |
||
* |
ukazatel (dereference) |
||
& |
adresa něčeho |
||
sizeof |
operátor sizeof (velikost) |
||
_Alignof |
požadavek na zarovnání (C11) |
||
3 |
* / % |
násobení, dělení a modulo (celočíselný zbytek po dělení) |
zleva doprava |
4 |
+ - |
sčítání a odčítání |
|
5 |
<< >> |
bitový posun doleva a bitový posun doprava |
|
6 |
< <= |
relační operátor menší a menší nebo rovno |
|
> >= |
relační operátor větší a větší nebo rovno |
||
7 |
== != |
rovná se a nerovná se |
|
8 |
& |
bitové AND (bitové násobení) |
|
9 |
^ |
bitové XOR (bitová neekvivalence) |
|
10 |
| |
bitové OR (bitové sčítání) |
|
11 |
&& |
logické AND (logické a současně) |
|
12 |
|| |
logické OR (logické nebo) |
|
13 |
?: |
ternární podmínka |
zprava doleva |
14 |
= |
přiřazení |
|
+= -= |
přiřazení s přičtením a přiřazení s odečtením |
||
*= /= %= |
přiřazení s násobením, dělením a zbytkem |
||
<<= >>= |
přiřazení s bitovým posunem doleva a doprava |
||
&= ^= |= |
přiřazení s bitovým AND, XOR a OR |
||
15 |
, |
čárka |
zleva doprava |
Některé operátory jsme ještě neprobírali, ale určitě se o nich budeme bavit v dalších lekcích.
| přiřazení | inkrementace, dekrementace | aritmetické | logické | porovnání | přístup ke členu | ostatní |
|---|---|---|---|---|---|---|
a = b a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b |
++a --a a++ a-- |
+a -a a + b a - b a * b a / b a % b ~a a & b a | b a ^ b a << b a >> b |
!a a && b a || b |
a == b a != b a < b a > b a <= b a >= b |
a[b] *a &a a->b a.b |
a(...) a, b (type) a a ? b : c sizeof(a) _Alignof |