Predchozi kapitola

Obsah

Konec

Nasledujici kapitola

3. Operatory a vyrazy

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.


3.1. Operand, operator, vyraz.zpet

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.

3.2. Rozdeleni operatoru.zpet

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.

3.3. Operator prirazeni, l-hodnota a p-hodnota.zpet

Vyrazy, jak jiz vime, jsou tvoreny posloupnosti operatoru a operandu. Vyraz predepisuje vypocet adresy nebo hodnoty. Upravime-li napriklad znamy vztah

Pytagorova veta

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

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.

3.4. Aritmeticke operatory - aditivni a multiplikativni.zpet

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;
}

/* vystup BC31
123 * 456 = -9448
295 / 2 = 147
295 % 2 = 1

nyni pozor:
20000 + 20001 = -25535
*/ 
/* vystup MWC + COHERENT
123 * 456 = 56088
295 / 2 = 147
295 % 2 = 1

nyni pozor:
20000 + 20001 = 40001
*/ 

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.

3.5. Logicke operatory.zpet

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.

3.6. Relacni operatory.zpet

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.

3.7. Bitove operatory.zpet

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.

3.8. Adresovy operator.zpet

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);

3.9. Podmineny operator.zpet

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.

3.10. Operator carka.zpet

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.

3.11. Pretypovani vyrazu.zpet

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.


Predchozi kapitola

Obsah

Zacatek

Nasledujici kapitola


Nazev: Programovani v jazyce C
Autor: Petr Saloun
Do HTML prevedl: Kristian Wiglasz
Posledni uprava: 29.11.1996