Kapitola 4. Operátory

Obsah

4.1. Výrazy
4.2. Přiřazení
4.3. Aritmetické výrazy
4.4. Logické operátory
4.5. Relační operátory
4.6. Bitové operátory
4.7. Adresový operátor
4.8. Podmíněný operátor
4.9. Operátor čárka
4.10. Přetypování výrazu
4.11. Priorita operátorů
4.12. Opakování

Časová náročnost
Časová náročnost kapitoly: 1 hodina 10 minut

V této kapitole se seznámíme s operátory a jejich rozdělením. Zaměříme se zejména na operátory aritmetické. Ukážeme si jejich použití s celočíselnými i racionálními operandy a nezapomeneme ani na výrazy smíšené.

Potřebné znalosti
Potřebné znalosti
K zvládnutí obsahu této kapitoly je nutné mít znalosti z kapitoly 2 a 3 - tedy znát strukturu programu, orientovat se v identifikátorech, klíčových slovech, komentářích, odsazovačích, číslech, řetězcích a konstantách (celočíselných, racionálních a znakových). A chápat také základy problematiky ukazatelů.

4.1. Výrazy

Časová náročnost
Časová náročnost: 2 minuty

Důležité

S pomocí operátorů vytváříme výrazy. Zapíšeme-li například v matematice a + b hovoříme výrazu. Ten má dva operandy a a b a jeden operátor +.

S operandy v jazyce C to není jednoduché. Mohou být tvořeny konstantami, identifikátory objektů, řetězci a voláním funkcí. Operandy však můžeme vytvářet i pomocí kombinací zmíněných možností a dalších operátorů i závorek.

4.2. Přiřazení

Časová náročnost
Časová náročnost: 7 minut

Důležité

Přiřazovací příkaz je nejčastěji používaným příkazem ve většině programovacích jazyků. Operátorem přiřazení v jazyce C je symbol =. Obecně musíme říci, že vlevo od = musí být výraz, odkazující se do paměti. Možná to zní nejasně, ale je jisté, že hodnotu zprava musíme někam zapsat. A kam jinam ji může program zapsat, než do paměti na adresu, kterou určí právě z levé strany přiřazovacího výrazu.

Adresu proměnné určí překladač jazyka C velmi snadno. Typ výrazu je určen typem operandů. Jsou-li operandy stejného typu, je výsledek téhož typu. Jinak je typu největšího z uvedených operandů.

Jednoduchý příklad
Příklad:

j = 5;
d = 'z';
f = f + 3.14 *i;

Protože přiřazení je výraz je možné několikanásobné přiřazení.

Jednoduchý příklad
Příklad:

int i,j,k;
i = j = k = 2;

Teorie zavedla pro pravou stranu přiřazení pojmenování hodnotový výraz, zatímco stranu levou pojmenovává adresový výraz. V anglické terminologii i v některých českých textech se používá zkrácené označení rvalue - r-hodnota, respektive lvalue - l-hodnota.

Důležité Platí tedy:
  • výraz má vždy hodnotu (číslo)
  • přiřazení je výraz a jeho hodnotou je hodnota na pravé straně
  • přiřazení se stává příkazem je-li ukončeno středníkem
Důležité Časté chyby:
  • C má přiřazení pouze pomocí = a ne pomocí := či ==
  • Porovnání je v C == ne pouze =

ISO norma jazyka C dále důrazně nedoporučuje používat výrazy, kde se vlevo i vpravo od operátoru přiřazení mění stejná hodnota.

Jednoduchý příklad
Příklad:

i = i++ *2;    /* takto NE */

4.3. Aritmetické výrazy

Časová náročnost
Časová náročnost: 7 minut

Aritmetické výrazy konstruujeme z operandů a aritmetických operátorů.

Důležité

Tabulka 4.1.

OperátorČinnost
+sčítání
-odčítání
*násobení
%zbytek po celočíselném dělení
src/op_ari_1.c
Příklad 4.1.
/******************************
*	op_ari_1.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 int a = 7, b = 2;
 
 printf("cislo a = %d, cislo b = %d\n", a, b);
 printf("soucet %d + %d = %d\n", a, b, a+b);
 printf("rozdil %d - %d = %d\n", a, b, a-b);
 printf("soucin %d * %d = %d\n", a, b, a*b);
 printf("podil %d / %d = %d\n", a, b, a/b);
 printf("zbytek po celociselnem deleni %d %% %d = %d", a, b, a%b);
 
 return 0;
}

Zkusme si ještě jeden příklad, kde argumenty aritmetických operací jsou celočíselné a levá strana je racionální.

src/op_ari_2.c
Příklad 4.2.
/******************************
*	op_ari_2.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 int i, j;
 float r, x;

 i = j = 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;
}

V tomto příkladu jasně vidíme, že výpočet na pravé straně probíhá podle typů operandů pravé strany. Teprve je-li to potřeba je výsledek převeden na typ odpovídajíc straně levé.

Proto je výsledek tohoto příkladu: i=5 j=25 r=8.000000 x=75.000000

Důležité

Pro určení pořadí vyhodnocování jednotlivých částí výrazů používáme kulaté závorky ().

4.4. Logické operátory

Časová náročnost
Časová náročnost: 5 minut

Logické hodnoty jsou dva, pravda a nepravda. Norma ISO C říká, že hodnota nepravda je představována nulou, zatímco pravda jedničkou. V případě hodnoty pravda se ovšem jedná pouze o doporučení. Neboť užívaným anachronismem je považovat jakoukoliv nenulovou hodnotu za pravdu.

Důležité

Tabulka 4.2.

OperátorČinnost
&and (logický součet)
||or (logický součin)
!not (negace)

Pravidla pro určení výsledku známe z Booleovy algebry.

Pro demonstrování funkcí AND, OR a NOT si napíšeme jednoduchý příklad.

src/op_log.c
Příklad 4.3.
/******************************
*	op_lg.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 int p,q;
 
 printf("Zadejte P (0 nebo 1): ");
 scanf("%d",&p);
 printf("Zadejte Q (0 nebo 1): ");
 scanf("%d",&q);
 printf("P AND Q: %d\n", p && q);
 printf("P OR Q: %d\n", p ¦¦ q);
 printf("NOT Q: %d\n", !q);
 
return 0;
}

4.5. Relační operátory

Časová náročnost
Časová náročnost: 3 minuty

Máme následující relační operátory:

Důležité

Tabulka 4.3.

OperátorČinnost
<menší
>větší
<=menší nebo rovno
>=větší nebo rovno
==rovno
!=nerovno

Tyto operátory jsou definovány pro operandy všech základních datových typů. Ovšem například pro ukazatele mají smysl pouze operátory rovnosti a nerovnosti. Výsledkem relačních operací jsou logické hodnoty pravda a nepravda.

4.6. Bitové operátory

Časová náročnost
Časová náročnost: 15 minut

Již podle názvu můžeme usuzovat, že nám bitové operátory umožňují provádět operace nad jednotlivými bity. Bitové operace je však možné provádět pouze s celočíselnými hodnotami.

Máme následující typy bitových operátorů:

Důležité

Tabulka 4.4.

OperátorČinnost
<<bitový posun vlevo
>>bitový posun vpravo
&bitový and (logický součin, konjunkce)
|bitový or (logický součet, disjunkce)
^bitový not (negace, inverze)
~bitový xor (nonekvivalence)

Při bitovém posunu vlevo (vpravo) << (>>), se jednotlivé bity posouvají vlevo (vpravo), tedy do pozice s (binárně) vyšším (nižším) řádem. Na nejpravější (nejlevější) posunem vytvořenou pozici je umístěna nula. Posuny ovšem probíhají aritmeticky. To znamená, že uvedené pravidlo neplatí pro posun vpravo hodnoty celočíselného typu se znaménkem. V takovém případě se nejvyšší bit (znaménkový), zachovává. Takto se při posunu doplňuje do bitového řetězce nový bit. Naopak před posunem nejlevější (nejpravější) bit je odeslán do říše zapomnění.

Bitový posun o jeden (binární) řád vpravo, respektive vlevo, má stejný význam, jako celočíselné dělení, respektive násobení, dvěma. Je-li bitový posun o více než jeden řád, jedná se o násobení (dělení) příslušnou mocninou dvou. Přirozeně to platí pouze pro bitový posun celočíselných typů unsigned.

Obecný formát pro operátory bitového posunu vlevo (vpravo):

hodnota << počet-bitů	   	 
hodnota >> počet-bitů

Bitové and &, or |, a xor ^ provádí příslušnou binární operaci s každým párem odpovídajících si bitů. Výsledek je umístěn do pozice stejného binárního řádu výsledku. Výsledky operací nad jednotlivými bity jsou stejné, jako v Booleově algebře. Bitové not ~ je operátorem unárním, provádí negaci každého bitu v bitovém řetězci jediného operandu. Tomuto operátoru se často říká bitový doplněk.

Pozn:
Pozn:
XOR dává výsledek jedna jen v případě různosti hodnot operandů.

Nyní si ukážeme jednoduchý příklad použití bitových posunů.

src/op_bit.c
Příklad 4.4.
/******************************
*	op_bit.c
*	19.03.2002
******************************/

#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("512 >> 8 = \t%d\t%#x\n", 512 >> 8, 512 >> 8);

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

Výsledkem tohoto programu je následující výpis:

1 << 1 =        2       0x2
1 << 7 =        128     0x80
-1 >> 1 =       -1      0xffffffff
512 >> 8 =      2       0x2
2 & 1 =         0       0
2 | 1 =         3       0x3
2 ^ 1 =         3       0x3

Operace XOR má jednu zajímavou vlastnost. Máme-li dvě hodnoty A a B, pak je-li na výsledek operace A XOR B uplatněna znovu operace XOR s operandem B dostáváme operand A. Tuto vlastnost si demonstrujeme na příkladu.

src/op_bi_xor.c
Příklad 4.5.
/******************************
*	op_lgxor.c
*	19.03.2002
******************************/

#include <stdio.h>

int main (void)
{
 int a = 10, b = 20;
 
 printf("Pocatecni hodnota a: %d\n",a);
 
 a = a ^ b;
 printf("Po prvnim XOR: %d\n",a);
 
 a = a ^ b;
 printf("Po druhem XOR: %d\n",a);
 
 return 0;
}

4.7. Adresový operátor

Časová náročnost
Časová náročnost: 2 minuty

Adresový operátor & je unární. Jak již název adresový operátor napovídá, umožňuje získat adresu objektu, na nějž je aplikován. Adresu objektu můžeme použít v nejrůznějších situacích, obvykle je to v souvislosti s předáváním výsledků funkcí. Takto například můžeme přečíst hodnoty dvou proměnných jedinou funkcí pro formátovaný vstup.

Jednoduchý příklad
Příklad:
int i;
float f;
scanf("%d %f",&i, &f);

4.8. Podmíněný operátor

Časová náročnost
Časová náročnost: 8 minut

Jazyk C obsahuje ternární (trojitý) operátor ?. Ternární operátor potřebuje tři operandy. Operátor ? se používá pro náhradu příkazu typu:

if(podmínka) příkaz1;
else příkaz2;

Obecný formát operátoru ? je:

Důležité
proměnná = podmínka ? výraz1 : výraz2
podm_vyr.swf
Flashová animace
Kliknutím na ikonu spustíte test.

Podmínka je výraz, který je vyhodnocen jako pravdivý nebo nepravdivý. Je-li pravdivý dosadí se do proměnné hodnota výrazu1. Je-li nepravdivý, dosadí se do proměnné hodnota výrazu2. Důvodem použití operátoru ? je to, že překladač jazyka C může v tomto případě vytvářet mnohem efektivnější kód než při použití příkazu if-else.

Ukažme si použití podmíněného operátoru na příkladu. Mějme program, který načte číslo a v případě že je větší nebo rovno nule jej převede na jedničku a v případě, že zadané číslo bylo záporné, na mínus jedničku.

src/op_condi.c
Příklad 4.6.
/******************************
*	op_condi.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 int i;
 
 printf("Zadejte cislo: ");
 scanf("%d",&i);
 i = i>=0 ? 1: -1;
 printf("Vysledek: %d",i);
 
 return 0;
}

4.9. Operátor čárka

Časová náročnost
Časová náročnost: 5 minut

Operátor čárka má zcela jedinečnou funkci. Říká překladači udělej tohle a tohle a tohle. To znamená, že se čárka používá pro zřetězení několika operací.

V následujícím cyklu se čárka používá v inicializační části pro inicializaci dvou řídících proměnných cyklu a v části pro inkrementaci i a j.

Pozn:
Pozn:
O cyklu for si více řekneme v následující kapitole.
Jednoduchý příklad
Příklad:

for(i=0, j=0; i+j<pocet; i++, j++) .......

Hodnotu seznamu výrazů oddělených čárkami určuje výraz, který stojí nejvíce vpravo. V následujícím příkladu se do proměnné hodnota přiřadí číslo 100.

Jednoduchý příklad
Příklad:

hodnota = (pocet, 90, 50, 100);

Závorky jsou zde potřebné, neboť operátor čárka má nižší prioritu než přiřazovací operátor.

4.10. Přetypování výrazu

Časová náročnost
Časová náročnost: 5 minut

Někdy potřebujeme dočasně změnit typ proměnné. Můžeme například chtít používat pro nějaký výpočet hodnotu v pohyblivé řádové čárce, ale někde pro ni chceme zase použít operátor celočíselného dělení. Jelikož však lze tento operátor použít pouze na celá čísla máme problém. Jedním z možných řešení je vytvořit celočíselnou proměnnou, která se bude používat pro celočíselné dělení a ve vhodné chvíli ji přiřadit hodnotu proměnné s pohyblivou řádovou čárkou. To je však poněkud neelegantní řešení. Jiným řešení je použít přetypování, které způsobí dočasnou změnu typu.

Přetypování má obecný tvar:

Důležité
(typ) hodnota

kde typ je jméno platného typu jazyka C.

Jednoduchý příklad
Příklad:

float f;
f = 100.2;
printf("%d", (int) f);   /* přetypování na celé číslo */

4.11. Priorita operátorů

Časová náročnost
Časová náročnost: 5 minut

Následující přehled všech operátorů ukazuje jejich prioritu od nejvyšší po nejnižší.

Důležité

Tabulka 4.5.

OperátoryAsociativita
( ) [ ] ->.zleva doprava
! ~ + - ++ -- (přetypování) *(pointer) &(adresní operátor) sizeofzprava doleva
* / %zleva doprava
+ -zleva doprava
<< >>zleva doprava
< <= > >= zleva doprava
== !=zleva doprava
&zleva doprava
^zleva doprava
| zleva doprava
&&zleva doprava
|| zleva doprava
?: zprava doleva
= += -= *= /= %= >>= <<= &= |= ^=zprava doleva
,zleva doprava

4.12. Opakování

Cvičení
Cvičení

Úkol k textu

Zadání 1)

Napište program, který načte dvě desetinná čísla (použijte typ float) a pak vytiskněte jejich součet.
Řešení

Úkol k textu

Zadání 2)

Napište program, který vypočte objem kvádru. Program se bude ptát uživatele na jednotlivé rozměry a pak vypíše objem.
Řešení

Úkol k textu

Zadání 3)

Napište program, který vypočítá počet sekund v nepřestupném roce.
Řešení

Úkol k textu

Zadání 4)

Je tento výraz pravdivý?
!(10==9)
Řešení

Úkol k textu

Zadání 5)

Dávají tyto dva výrazy stejný výsledek?
  1. 0&&1||1
  2. 0&&(1||1)
Řešení

Úkol k textu

Zadání 6)

Jelikož hodnota v pohyblivé řádové čárce nemůže být použita spolu s operátorem %, jak můžete upravit tento příkaz?
x = 123.123 % 3 ;
Řešení

Úkol k textu

Zadání 7)

Jedním zvláště dobrým příkladem použití operátoru ? je ochrana před dělením nulou. Napište program, který přečte dvě celá čísla zadaná uživatelem a vypíše výsledek dělení prvního čísla druhým. Použijte ? abyste zabránili dělením nulou.
Řešení

Úkol k textu

Zadání 8)

Převeďte následující příkaz na jeho ekvivalentní příkaz pomocí operátoru ?.
if(a>b) pocet = 5;
else pocet = 10;
Řešení

Úkol k textu

Zadání 9)

Jaký je výsledek těchto operací?
  1. 10100011 & 01011101
  2. 01011101 | 11111011
  3. 01010110 ^ 10101011
Řešení
test4.swf
Test
Kliknutím na ikonu spustíte test.