3.1 Operand,
operator, vyraz.
3.2. Rozdeleni operatoru.
3.3. Operator prirazeni,
l-hodnota a p-hodnota.
3.4. Aritmeticke operatory -
aditivni a multiplikativni.
3.5. Logicke operatory.
3.6. Relacni operatory.
3.7. Bitove operatory.
3.8. Adresovy operator.
3.9. Podmineny operator.
3.10. Operator carka.
3.11. Pretypovani vyrazu.
Zapiseme-li v matematice napriklad a + b
,
hovorime o vyrazu. Ten ma dva operandy a
a b
a jeden operator +
. Jedna
se sice o vyraz velmi jednoduchy, nicmene nam umoznil
zopakovani potrebnych terminu.
Operatory rozdelujeme podle poctu operandu (arity) na operatory unarni, binarni a ternalni. Binarni operatory jsou aritmeticke, relacni, logicke, bitove a operatory prirazeni a posuvu. Aritmeticke operatory jsou aditivni a multiplikativni. Operatory maji svou prioritu a asociativitu. Priorita urcuje, ze napriklad nasobeni se vyhodnoti drive, nez treba scitani. Asociativita rika, vyhodnocuje-li se vyraz zleva doprava, nebo naopak.
Operatory rovnez delime podle pozice jejich zapisu vzhledem k operandu(-um). Takto rozlisujeme operatory prefixove, infixove a postfixove. Operatory v jednotlivych pripadech zapisujeme pred operandy, mezi operandy, respektive za operandy. Druha varianta je nam zrejme nejblizsi. Infixovy zpusob zapisu jsme pouzivali jiz na zakladni skole.
Poznamejme, ze v C uplatnime vsechny zminene varianty
operatoru. Zakladni prehled operatoru jazyka C,
rozlisenych podle arity, nasleduje:
Unarni operatory:
+, - |
aritmeticke plus a minus |
& |
reference (ziskani adresy objektu) |
* |
dereference (ziskani objektu dle adresy) |
! |
logicka negace |
~ |
bitova negace |
++, -- |
inkrementace resp. dekrementace hodnoty, prefixovy i postfixovy zapis |
(typ) |
pretypovani na typ uvedeny v zavorkach |
sizeof |
operator pro ziskani delky objektu nebo typu |
Binarni operatory:
= |
prirazeni, mozna je i kombinace s
jinymi operatory, napr. +=, -=, *=, /=,
<<=, ^= |
+ |
scitani |
- |
odcitani |
* |
nasobeni |
/ |
deleni |
% |
zbytek po celociselnem deleni (modulo) |
<<, >> |
bitovy posun vlevo resp. vpravo |
& |
bitovy soucin (and) |
| |
bitovy soucet (or) |
^ |
bitovy vylucovaci soucet (xor) |
&& |
logicky soucin (and) |
|| |
logicky soucet (or) |
. |
tecka, primy pristup ke clenu struktury |
-> |
neprimy pristup ke clenu struktury |
, |
carka, oddeleni vyrazu |
< |
mensi nez |
> |
vetsi nez |
<= |
mensi nebo rovno |
>= |
vetsi nebo rovno |
== |
rovnost |
!= |
nerovnost |
Ternalni operator:
? : |
podmineny operator |
Pri podrobnejsim pohledu na prehled operatoru podle
arity zahy objevime nektere z operatoru, ktere jsou
uvedeny jako unarni i binarni soucasne. Prikladem uvedme -
,
ktere muze vystupovat jako unarni minus i jako binarni
operator odcitani.
Operatory s uvedenim priority (v tabulce jsou
razeny sestupne od priority nejvyssi k priorite
nejnizsi) a asociativity:
operator | typ operatoru | asociativita |
---|---|---|
[ ] ( ) . -> postfixove ++
postfixove -- |
vyraz | zleva doprava |
prefixove ++ prefixove -- sizeof
& * + - ~ ! |
unarni | logicke OR |
pretypovani | unarni | zprava doleva |
* / % |
nasobeni | zleva doprava |
+ - |
scitani | zleva doprava |
<< >> |
bitoveho posunu | zleva doprava |
< > <= >= |
relacni | zleva doprava |
== != |
rovnosti | zleva doprava |
& |
bitove AND | zleva doprava |
^ |
bitove vylucovaci OR (XOR) | zleva doprava |
| |
bitove OR | zleva doprava |
&& |
logicke AND | zleva doprava |
|| |
logicke OR | zleva doprava |
?: |
podminene vyhodnoceni | zprava doleva |
= *= /= %= += -= <<= >>= &= |=
^= |
jednoduche prirazeni a prirazeni s vypoctem | zprava doleva |
, |
postupne vyhodnoceni | zleva doprava |
Unarni operatory jsou prefixove s moznym postfixovym pouzitim dekrementace a inkrementace. Binarni operatory jsou infixove.
Operatory jsou rovnez []
, ()
ohranicujici indexy resp. argumenty a #
, ##
,
ktere zpracovava jiz preprocesor. Preprocesoru v tomto textu
venujeme celou kapitolu. Uzitecnym operatorem je sizeof
,
ktery v prubehu prekladu vyhodnoti pametove naroky
sveho argumentu. Tento operator je nezbytny zejmena pri
dynamicke alokaci pameti, pripadne pri operacich
cteni/zapis z binarnich souboru.
Vyrazy, jak jiz vime, jsou tvoreny posloupnosti operatoru a operandu. Vyraz predepisuje vypocet adresy nebo hodnoty. Upravime-li napriklad znamy vztah
do syntakticky spravneho zapisu1 v jazyce C
c = sqrt(a*a + b*b);
muzeme zretelne ukazat nektere vyznamne vlastnosti operatoru
prirazeni =
2. Na prave strane operatoru prirazeni se
nachazi vyraz jehoz vyhodnocenim ziskame hodnotu tohoto
vyrazu. Ovsem na leve strane se nachazi vyraz (v nasem
pripade je to "jen" promenna), jehoz
vyhodnocenim ziskame adresu. Na tuto adresu, predstavujici
zacatek pametoveho mista pro umisteni hodnoty promenne c
,
je umistena hodnota z prave strany prirazovaciho
operatoru.
Jeste nez si zadefinujeme zminene pojmy, nesmime zapomenout na dulezitou skutecnost. Vysledkem vyrazu3 prirazeni je hodnota. Co to znamena? Napriklad moznost elegantne resit inicializaci vice promennych stejnou hodnotou, napriklad
int a, b, c;
a = b = c = -1;
Nezapominejme, ze prirazovaci operator je asociativni
zprava doleva. Nejprve se tedy vyhodnoti c=-1
,
vysledkem je hodnota -1
, ta tvori pravou stranu
prirazeni b=
, jehoz vysledkem je opet -1
.
A jak se uvedena hodnota dostane do promenne a
neni jiste treba popisovat. Vratme se vsak k nastinenym
pojmum.
Adresovy vyraz (lvalue -
l-hodnota) je vyraz, jehoz vypoctem se ziska adresa v
pameti. Napriklad, je-li P
nejaky vyraz
vyhodnoceny jako nenulovy ukazatel, pak *P
je
l-hodnota. V souvislosti s modifikatorem const
rozlisujeme modifikovatelnou a nemodifikovatelnou l-hodnotu.
Hodnotovy vyraz (rvalue -
p-hodnota) je vyraz, jehoz vypoctem se ziska hodnota
jisteho typu. Typ je jednoznacne urcen typem operandu.
Protoze se zejmena pri ciselnych vypoctech casto
setkavame s operandy ruznych typu, uvedme si pravidla, jez
urcuji typ vysledku. Poznamenejme, ze naplneni pravidel
testujeme v uvedenem poradi:
Alespon jeden z operandu je racionalniho typu, pak
long double
, je
rovnez druhy operand konvertovan na tento typ. double
, je
rovnez druhy operand konvertovan na tento typ. float
, je
rovnez druhy operand konvertovan na tento typ. unsigned long
,
je rovnez druhy operand konvertovan na tento typ. long
, a druhy
typu unsigned int
, je druhy operand
konvertovan na typ long
(16-bitovy
prekladac), nebo jsou oba operandy konvertovany na typ unsigned
long
(32-bitovy prekladac). long
, je
rovnez druhy operand konvertovan na tento typ. unsigned int
,
je rovnez druhy operand konvertovan na tento typ. int
. Vyrazy mohou vyvolavat i vedlejsi efekty. ANSI norma nedoporucuje pouzivat vyrazy, ktere behem vyhodnoceni zpusobuji vicenasobnou zmenu obsahu jednoho pametoveho mista. Napriklad:
cc = cc++ + 1.
Nektere vedlejsi efekty mohou byt implementacne zavisle, treba vyraz
a[i] = i++.
Operatoru prirazeni jsme se venovali jako v poradi
prvnimu. Duvod je prosty. Bez tohoto operatoru nemuzeme
uchovat vysledky v promennych. Soucasne nam tento operator
bude slouzit i v nasledujicim vykladu latky, vcetne
popisu dalsich operatoru.
Aritmeticke operatory + - * / %
predstavuji
zakladni matematicke operace scitani, odcitani,
nasobeni, deleni a zbytku po (celociselnem) deleni.
Nejlepsi ukazkou bude jiste priklad.
/********************************************************************/ /* program
op_int01.c
*/ /* celociselne nasobeni, deleni, zbytek po deleni */ /* navic je ukazano celociselne preteceni (jen pro 16-ti bitove int)*/ /********************************************************************/ #include <stdio.h> int main() { int o1 = 123, o2 = 456, o3 = 295, v1, v2, v3; int c1 = 20000, c2 = 20001, vc; v1 = o1 * o2; v2 = o3 / 2; v3 = o3 % 2; printf("%d * %d = %d\n", o1, o2, v1); printf("%d / %d = %d\n", o3, 2, v2); printf("%d %% %d = %d\n", o3, 2, v3); vc = c1 + c2; printf("\nnyni pozor:\n\t"); printf("%d + %d = %d\n", c1, c2, vc); return 0; }
|
|
Priklad ukazuje nejen inicializaci hodnot promennych
"operandu" o1
o2
a o3
,
ale po prvnich ocekavanych vysledcich i neocekavane
hodnoty5.
Ty jsou zpusobeny faktem aritmetickeho preteceni. Dale je
vhodne zduraznit, ze vzhledem k typu operandu int, je
i typ vysledku stejneho typu (viz pravidla uvedena drive). Z
toho duvodu je i vysledek deleni celociselny.
Podivejme se nyni na vysledky aritmetickych operaci, v nich argumenty jsou opet celociselne, ale leva strana je racionalniho typu.
/**********************************************************************/ /* program
op_int_f.c
*/ /* zakladni aritmeticke operace a prirazeni vysledky jsou sice i float*/ /* ale vypocty jsou provadeny jako int a teprve pote prevedeny */ /**********************************************************************/ #include <stdio.h> int main() { int i, j; float r, x; j = i = 5; j *= i; r = j / 3; x = j * 3; printf("i=%d\tj=%d\tr=%f\tx=%f\n", i, j, r, x); return 0; }
/* vystup BC31
i=5 j=25 r=8.000000 x=75.000000
*/
Rovnez v tomto prikladu vidime, ze vypocet probiha s
hodnotami typu podle zminenych pravidel a teprve pote je
ziskana p-hodnota konvertovana do typu odpovidajiciho
l-hodnote. Proto podil 25/3
dava 8.0
a nikoliv 8.333
.
Stejnym zpusobem probihaji aritmeticke operace s racionalnimi hodnotami.
Chceme-li zmenit poradi vyhodnoceni jednotlivych casti
vyrazu, pouzijeme k tomuto "pozmeneni priority"
kulatych zavorek6.
Logicke operatory predstavuji dve hodnoty, pravda a nepravda. ANSI norma C rika, ze hodnota nepravda je predstavovana 0 (nulou), zatimco pravda 1 (jednickou)7. Ve druhem pripade se ovsem jedna o doporuceni. Nebot uzivanym anachronismem je povazovat jakoukoliv nenulovou hodnotu za pravdu.
Logicke operatory jsou && || !
,
postupne and or a not. Provadi vypocet logickych vyrazu
tvorenych jejich operandy. Pravidla pro urceni vysledku
zname z Booleovy algebry. Logicke vyrazy casto obsahuji i
stanoveni (a overeni) podminek tvorenych relacnimi
operatory.
Relacni operatory jsou < > <= >= == !=
.
Porade mensi, vetsi, mensi nebo rovno, vetsi nebo
rovno, rovno a nerovno. Jsou definovany pro operandy vsech
zakladnich datovych typu8. Jejich vysledkem jsou logicke hodnoty pravda
a nepravda tak, jak jsou popsany v predchozim odstavci.
Jak sam nazev napovida, umoznuji provadet operace nad jednotlivymi bity. Tuto moznost zdaleka nemaji vsechny programovaci jazyky oznacovane jako vyssi. Jazyk C ji oplyva zejmena proto, ze byl vytvoren jako nastoj systemoveho programatora (OS Unix). Pouziti bitovych operatoru vyzaduje znalosti o ulozeni bitu v pameti, zpusobu kodovani cisel, ... .
Bitove operatory jsou: << >> & | ~ ^
, tedy posun vlevo, posun vpravo, and, or, not a xor. Bitove
operace jsou mozne pouze s celociselnymi hodnotami.
Podivejme se nyni na jednotlive zastupce bitovych
operatoru.
Pri bitovem posunu vlevo (vpravo) <<
,
( >>
) se jednotlive bity posouvaji vlevo
(vpravo), tedy do pozice s (binarne) vyssim (nizsim)
radem. Na nejpravejsi (nejlevejsi) posunem vytvorenou
pozici je umistena nula. Posuny ovsem probihaji aritmeticky.
To znamena, ze uvedene pravidlo neplati pro posun vpravo
hodnoty celociselneho typu se znamenkem. V takovem
pripade se nejvyssi bit (znamenkovy), zachovava. Takto
se pri posunu doplnuje do bitoveho retezce novy bit. Naopak
pred posunem nejlevejsi (nejpravejsi) bit je odeslan do
"rise zapomeni".
Bitovy posun o jeden (binarni) rad vpravo, respektive
vlevo, ma stejny vyznam, jako celociselne deleni,
respektive nasobeni, dvema. Je-li bitovy posun o vice nez
jeden rad, jedna se o nasobeni (deleni) prislusnou
mocninou dvou.
Bitove and &
, or |
,
a xor ^
provadi prislusnou binarni operaci s kazdym parem
odpovidajicich si bitu. Vysledek je umisten do pozice
stejneho binarniho radu vysledku. Vysledky operaci nad
jednotlivymi bity jsou stejne, jako v Booleove algebre9. Bitove
not ~
je operatorem unarnim, provadi
negaci kazdeho bitu v bitovem retezci jedineho operandu.
Tomuto operatoru se casto rika bitovy doplnek.
/*******************************************************************/ /* program
op_bit01.c
*/ /* ukazuje bitove posuny, a zakladni bitove operace and, or, xor */ /* a bitovy doplnek */ /*******************************************************************/ #include <stdio.h> int main() { printf("1 << 1 = \t%d\t%#x\n", 1 << 1, 1 << 1); printf("1 << 7 = \t%d\t%#x\n", 1 << 7, 1 << 7); printf("-1 >> 1 = \t%d\t%#x\n", -1 >> 1, -1 >> 1); printf("1024 >> 9 = \t%d\t%#x\n", 1024 >> 9, 1024 >> 9); printf("13 & 6 = \t%d\t%#x\n", 13 & 6, 13 & 6); printf("13 | 6 = \t%d\t%#x\n", 13 | 6, 13 | 6); printf("13 ^ 6 = \t%d\t%#x\n", 13 ^ 6, 13 ^ 6); printf("2 & 1 = \t%d\t%#x\n", 2 & 1, 2 & 1); printf("2 | 1 = \t%d\t%#x\n", 2 | 1, 2 | 1); printf("2 ^ 1 = \t%d\t%#x\n", 2 ^ 1, 2 ^ 1); return 0; }
/* BC31 - 16-ti bitovy kod
1 << 1 = 2 0x2
1 << 7 = 128 0x80
-1 >> 1 = -1 0xffff
1024 >> 9 = 2 0x2
13 & 6 = 4 0x4
13 | 6 = 15 0xf
13 ^ 6 = 11 0xb
2 & 1 = 0 0
2 | 1 = 3 0x3
2 ^ 1 = 3 0x3
*/
Jestlize jsme v uvodu k bitovym operatorum naznacili jejich systemovou orientaci, ukazme ji na prikladu. Casto popisujeme rozdily mezi 16-ti a 32-bitovym kodem (prekladacem, ktery kod generuje). Nasledujici program nam umozni zjistit, s jakym prekladacem mame cest.
/************************************************/ /* soubor
int_size.c
*/ /* zjisti kolika bitove int pouziva prekladac */ /************************************************/ #include <stdio.h> int main(void) { unsigned int ui = ~0; int i = 1; while (ui >>= 1) i++; printf("prekladac pouziva %2d-ti bitovou reprezentaci celeho cisla\n", i); return 0; }
Povsimneme si pouziti bitoveho doplnku ui = ~0
.
Tak snadno (a zejmena prenositelne) ziskame bitovy
retezec tvoreny samymi jednickami.
Tento operator &
je unarni. Jak jiz
nazev adresovy operator napovida, umoznuje ziskat adresu
objektu, na nejz je aplikovan. Adresu objektu muzeme
pouzit v nejruznejsich situacich, obvykle je to ale v
souvislosti s ukazateli. Bez tohoto operatoru bychom nebyli
schopni pracovat se soubory a ani standardni vstup bychom nebyli
schopni cist jinak, nez po znacich. Takto napriklad
muzeme precist hodnoty dvou promennych jedinou funkci pro
formatovany vstup:
int i;
float f;
scanf("%d %f", &i, &f);
Podmineny operator je pomerne nezvykly. Proto bude vhodne, objasnime-li si jeho vyznam. Mejme napriklad vypocet, ktery potrebujeme provest, v zavislosti na nejake podmince, jednou ze dvou variant (pochopitelne odlisnych). Vysledek vypoctu prirazujeme vzdy stejne promenne. Pokud navic je cast vyrazu, popisujici vypocet obou variant, shodna, jedna se o typicky priklad vyuziti podmineneho vyrazu.
Budme vsak radeji konkretnejsi. Chceme-li vypocist absolutni hodnotu nejakeho cisla, pouzijeme velmi pravdepodobne podmineny operator. Vypis zdrojoveho textu takoveho vypoctu nasleduje:
/**********************************/ /* soubor
op_cond.c
*/ /* ukazuje pouziti podmineneho */ /* operatoru - absolutni hodnota */ /**********************************/ #include <stdio.h> int main(void) { int i, abs_i; printf("\nZadej cele cislo: "); scanf("%d", &i); abs_i = (i < 0) ? -i : i; printf("abs(%d) = %d\n", i, abs_i); return 0; }
Dalsi pouziti (ternalniho) podmineneho operatoru ukazuje nasledujici radek.
return((znak == 0x0) ? getch() + 0x100 : znak);
Tento radek zajistuje pripadne nacteni a prekodovani libovolne stisknute klavesy na tzv. rozsirene klavesnici IBM PC AT. Zakladni klavesy teto klavesnice produkuji kod odpovidajici jejich pozici v ASCII tabulce. Rozsirene klavesy produkuji dvojici celociselnych kodu, z nichz prvni je nula. Nez si popiseme jeho cinnost, doplnme i predchazejici prikaz pro nacteni znaku:
znak = getch();
return((znak == 0x0) ? getch() + 0x100 : znak);
Pomoci podminky (znak == 0x0)
otestujeme,
jednalo-li se o rozsirenou klavesu. Jestlize ano, je
proveden prvni prikaz nasledujici za podminenym
operatorem, getch() + 0x100
. Je jim nacten tzv.
scan kod rozsirene klavesy, ktery je dale zvetsen o
hodnotu 100hex. Jestlize podminka splnena nebyla, je vykonan
prikaz za dvojteckou. Navratovou hodnotou je pak neupravena
hodnota, nactena do promenne znak
tesne pred
ternalnim operatorem.
Carka je operatorem postupneho vyhodnoceni. Ma
nejnizssi prioritu ze vsech operatoru a vyhodnocuje se
zleva doprava10. Carkou muzeme oddelit jednotlive vyrazy
v miste, kde je ocekavan jediny vyraz. Napriklad v cyklu for
.
Jazyk C nam umoznuje pretypovat vyrazy podle nasi potreby. Pretypovani ma prirozene sva omezeni, ta vsak casto plne odpovidaji zdravemu rozumu. Tezko se napriklad vyskytne potreba pretypovat znak na typ ukazatel na double.
Uvodni uvaha postrada konkretni ukazku, z niz by
vyplyval syntakticky zapis pretypovani. Pripomenme si
priklad op_int_f.c
, v nemz se vyskytoval
prikaz
r = j / 3;
ktery byl ovsem vyhodnocen celociselne a teprve pote
konvertovan na float
. Chceme-li, aby jiz podil
probehl v racionalnim oboru, musime pravou stranu upravit. S
pretypovanim muze vypadat prava strana takto:
r = (float) j / 3;
Pretypovani provadime tak, ze pred hodnotu, kterou chceme pretypovat, napiseme typ, ktery chceme ziskat, v kulatych zavorkach. Syntakticky tedy zapiseme pretypovani takto:
(type) expression
Vysvetlivky:
1 Predpokladejme, ze
pouzite promenne jsou vhodneho typu, napriklad double
..
2 Nesmime se nechat svest
jinymi programovacimi jazyky. Jedna se o jeden z nejcasteji
se vyskytujicich operatoru, proto mu nalezi zapis
jedinym symbolem.
3 Podrobneji se vyrazum
budeme venovat v kapitole 6. Rizeni chodu programu.
Zatim tolik, ze prikaz je vyraz zakonceny strednikem.
4 Celociselne typy jsou jak
vsechny varianty typu int
, tak za celociselny
typ povazujeme typ char
. Jak jsme v predchozi
kapitole videli, muzeme i u mej pouzit modifikatory unsigned
a signed
. Ve vyrazech je konvertovan na typ int
plus pripadne znamenko.
5 V pripade 16-ti bitoveho
prekladace. 32-bitovy dava spravne vysledky.
6 Je-li potreba vice urovni
zavorek, pouzivame vzdy jen zavorek kulatych. Hranate a
slozene zavorky predstavuji operatory jazyka C s jinym
vyznamem, nez je stanoveni poradi vyhodnoceni.
7 Jedna se o celociselne
hodnoty.
8 Ovsem napriklad pro
ukazatele maji smysl pouze operatory rovnosti a nerovnosti.
9 Pripomenme alespon, ze xor
dava vysledek 1 jen v pripade ruznosti hodnot operandu.
10 Tento operator zajistuje
poradi vyhodnoceni zleva doprava. Vysledkem je hodnota
nejpravejsiho vyrazu ze seznamu vyrazu oddelenych
carkami.
Nazev: | Programovani v jazyce C |
Autor: | Petr Saloun |
Do HTML prevedl: | Kristian Wiglasz |
Posledni uprava: | 29.11.1996 |