4 Lexikálne prvky jazyka
Program v jazyku C pozostáva z postupnosti lexikálnych prvkov, ktoré môžu na seba nadväzovať alebo byť oddelené oddeľovačmi. Ak je možné tieto prvky rozlíšiť, oddeľovač sa môže (ale nemusí) použiť, v ostatných prípadoch je samozrejme jeho použitie potrebné. Za oddeľovač je v jazyku C považovaná neprázdna postupnosť:
- medzier
- tabelátorov
- znakov prechodu na nový riadok
- znakov prechodu na novú stránku
- komentárov
Prvé štyri kategórie oddeľovačov sa nazývajú tzv. biele znaky (v angličtine white spaces). Komentár v jazyku C je postupnosť, ktorá sa začína dvojicou znakov /* a končí tou istou dvojicou v opačnom poradí */ (v tom istom alebo v hociktorom ďalšom riadku programu. Komentáre nie je možné do seba vnárať.
Lexikálne prvky jazyka C tvoria
- identifikátory
- kľúčové slová
- konštanty
- interpunkčné znaky
- reťazce
- operátory
Identifikátory i kľúčové slová boli popísané v predchádzajúcej kapitole v odstavci 3.1.
Konštanty sa používajú pre označenie konkrétnej hodnoty jedného dátového objektu daného typu. Preto sa u konštanty vždy rozlišuje jej typ, ktorý je daný typom jej konkrétnej hodnoty. Konštanty sa podľa typu delia na konštanty:
- celočíselné
- znakové
- v pohyblivej rádovej čiarke
- vymenovaného typu
- typu reťazec
-Celočíselné konštanty môžu byť dekadické, oktálové (začína '0') a hexadecimálne (začína '0x'). Ak presiahne hodnota konštanty rozsah typu int, považuje sa za konštantu typu long (explicitná prípona pre typ long je 'l', resp. 'L', napr. 010L, 0X1EL).
-Znakové konštanty môžu byť vyjadrené ako znak medzi apostrofmi ('z') alebo svojou oktálovou hodnotou za obráteným lomítkom ( napr. '\65' a 'a' sú ekvivalentné, podobne '\12' a '\n' atď.).
-Konštanty v pohyblivej rádovej čiarke môžu byť zapísané v bežnom tvare (celá a desatinná časť, oddelená desatinnou bodkou) alebo v semilogaritmickom tvare (mantisa a exponent).
-Konštanta vymenovaného typu môže nadobúdať jednu z hodnôt tohoto typu.
-Konštanta typu reťazec je postupnosť znakov medzi dvojicou znakov "", napr. "Typ retazec". Každá konštanta tohoto typu je uložená v statickom poli typu char s dĺžkou o jedna väčšou ako dĺžka konštanty, pretože ukončovacím znakom reťazca je vždy znak '\0' (binárna nula). Súčasťou reťazca môžu byť aj znaky zmenových postupností (začínajú znakom '\'). Reťazec musí byť napísaný v jednom riadku. V prípade dlhého reťazca sa tento na prechode riadkov musí ukončiť a na ďalšom riadku opäť začať znakom apostrof ("). V takom prípade sa dvojica apostrofov a znak prechodu na nový riadok vypúšťa z obsahu reťazca.
Interpunkčné znaky (symboly) jazyka C sú [ ] ( ) { } * , : = ; ... #
Pre reťazce platí všetko, čo bolo uvedené v odstavci Konštanta typu reťazec.
4.1 Priorita operátorov
Operátory sú vyhodnocované podľa ich priority a asociativity. Vyššia priorita znamená skoršie vyhodnotenie operátora. Pri rovnakej priorite sa operátory vyhodnocujú podľa ich asociativity (-> znamená zľava doprava, <- naopak).
priorita symbol typ operácie asociativita 12 () [] . -> výraz -> - ~ ! * & unárne operátory <- ++ -- inkrement, dekrement <- (cast) pretypovanie <- (typ) (roly) sizeof operátor dĺžky <- * / % multiplikatívne operátory -> 11 + - aditívne operátory -> 10 << >> operátory posuvu -> 9 < > <= >= operátory relačné -> 8 == != rovnosť, nerovnosť -> 7 & bitový súčin AND -> 6 ^ bitový exkl. súčet XOR -> 5 | bitový súčet OR -> 4 && logický súčin AND -> 3 || logický súčet OR -> 2 ? : podmienený operátor <- 1 = *= /= %= priradenie <- += -= <<= >>= &= |= ^= , čiarka ->
Operátory slúžia na vytváranie výrazov z konštánt a premenných. Podľa počtu argumentov sa delia na operátory :
- unárne
- binárne
- ternárne
4.2 Unárne operátory
Unárny operátor má jeden argument, pred ktorý sa zapisuje. V prípade viacerých operátorov sa tieto vyhodnocujú sprava doľava. Operátory ++ a -- je možné písať i za argument.
Unárne operátory sú: | |||
* | -> | dereferencia (získanie objektu podľa adresy) | |
& | -> | referencia (získanie adresy objektu) | |
- | -> | aritmetické mínus | |
! | -> | logická negácia (0 -> 1, nenulové -> 0) | |
~ | -> | bitový doplnok (v bitovej reprezentácii zmení 0 na 1 a naopak) | |
++ | -> | inkrementácia hodnoty pred vyhodnotením nasledujúceho, resp. po vyhodnotení predchádzajúceho operandu | |
-- | -> | dekrementácia hodnoty pred vyhodnotením následujúceho, resp. po vyhodnotení predchádzajúceho operandu | |
([typ]) | -> | explicitný prevod na typ v zátvorke(pretypovanie) | |
sizeof | -> | získane dĺžky objektu alebo typu(v byte-och) |
Operátor * predpokladá ako argument smerník na objekt a vracia ako hodnotu tento objekt (ak s je smerník na typ int, *s je tiež typu int). Operátor & vracia adresu premennej, ktorá sa môže priradiť smerníku. Teda platí že v = *&v (hodnota premennej v sa rovná obsahu adresy premennej v), ale neplatí v = &*v, pretože v nie je smerník. Operátor logickej negácie ! je možné aplikovať i na aritmetické typy a smerníky. Výsledok negácie je typu int. Aj bitový doplnok ~ sa používa iba na typ int. Operátor ++, resp. -- znamená zvýšenie, resp. zníženie hodnoty objektu o 1 v závislosti na type objektu. Pre typ int platí ++i = i + 1, pre typ smerník na pole znamená presun smerníka na následujúci prvok poľa podľa jeho typu. Funkcia strlen vracia dĺžku reťazca:
int strlen(char *s) /* *s - smerník na reťazec */ { int i=0; while(*s++) i++; return(i); }
Pre pretypovanie objektu sa používa operátor ([typ]) (podľa K&R operátor (roly)), ktorý vykoná konverziu z jedného dátového typu na druhý (ktorý je uvedený v zátvorkách). Určité rozumné konverzie vykonáva kompilátor automaticky, napr. typ int na typ float. Operátor sizeof vracia dĺžku objektu alebo typu v bytoch. Argumentom sizeof môže byť aj pole, štruktúra alebo zjednotenie.
4.3 Binárne operátory
Binárny operátor sa zapisuje medzi dva argumenty. Binárne operátory sa členia na operátory:
aritmetické sčítanie + odčítanie - násobenie * delenie / modulo % (zvyšok po celočíselnom delení) relačné menší ako < väčší ako > menší alebo rovný <= väčší alebo rovný >= rovný == nerovný <> logické logický súčin (AND) && +---+---+-----+-----+-----+ logický súčet (OR) || | x | y | x&x | x^y | x|y | bitové bitový súčin (AND) & +---+---+-----+-----+-----+ bit. exkluzívny súčet (XOR) ^ | 0 | 0 | 0 | 0 | 0 | bitový súčet (OR) | | 0 | 1 | 0 | 1 | 1 | priradenia priradenie = | 1 | 0 | 0 | 1 | 1 | posuvu posuv vľavo << | 1 | 1 | 1 | 0 | 1 | posuv pravo >> +---+---+-----+-----+-----+
Aritmetické operátory sa vyhodnocujú sprava doľava. Operátor + a - je možné aplikovať aj na smerníky (smerníková aritmetika: ak s je smerník na nejaký objekt, potom s+1 ukazuje na nasledujúci objekt, s-1 na predchádzajúci objekt danej dĺžky; je to ekvivalentné s++, resp. s--). Odčítaním dvoch smerníkov dostaneme vzdialenosť dvoch objektov v jednotkách ich dĺžky.
Relačné operátory sa používajú pre porovnávanie objektov. Výsledkom porovnania je splnenie relácie (hodnota 1) alebo jej nesplnenie (hodnota 0). Uvedené hodnoty sú typu int. Na rovnosť, resp. nerovnosť je možné porovnávať aj smerníky.
Logickéoperátory sa vyhodnocujú vždy zľava doprava. Pre hodnotu a typ výsledku platí predchádzajúci odstavec. Pozor na rozdiel medzi logickými a bitovými operátormi.
Bitové operátory slúžia na vykonávanie základných operácií (logického súčinu, súčtu či nonekvivalencie) po bitoch. Sledujme hodnoty logického i bitového súčtu i súčinu v nasledujúcom príklade. Vidíme, že bitové operácie dávajú v konečnom dôsledku číselný výsledok, zatiaľ čo logické operácie poskytujú "logický" výsledok, teda hodnoty 0 (false) a 1 (true).
Príklad:
/* priklad pr4_1.c logicke a bitové operátory */
void main(void)
{
int x=1,y=2;
printf(" x = %d y = %d x&y = %d\n",x,y,x&y);
printf(" x = %d y = %d x&&y = %d\n",x,y,x&&y);
printf(" x = %d y = %d x|y = %d\n",x,y,x|y);
printf(" x = %d y = %d x||y = %d\n",x,y,x||y);
}
Operátor priradenia sa môže vyskytovať okrem základného tvaru = aj v jednom z tvarov +=, -=, /=, *=, %=, >>=, <<=, &=, ^=, |=. Sú to tzv. kombinované tvary, ktoré skracujú zápis, napr.
a = a+2 je ekvivalentné a += 2
Operátor priradenia možno použiť viacnásobne, napr.
a=b=c=0 je ekvivalentné a=(b=(c=0))
Operátory posuvu umožnujú vykonávať posuv po bitoch doprava alebo doľava o počet bitov, ktorý je typu int. Pri posuve doľava sú bity sprava nulované pre objekty typu unsigned, resp. sa kopíruje znamienkový bit.
Operátor čiarka (operátor zabudnutia) oddeľuje dva výrazy a vyhodnocuje sa zľava doprava. Používa sa predovšetkým ako oddeľovač v príkaze cyklu typu for, napr.
for(i=0,j=100;i<25;i++,j--)
4.4 Ternárne operátory
Operátor ? je jediným operátorom jazyka, spájajúcim tri argumenty. Jeho význam ilustruje nájdenie menšej z dvoch hodnôt x,y zápisom :
min = x < y ? x : y;
ktorý interpretujeme takto:
Do premennej min ulož hodnotu x, ak je splnená podmienka x < y, inak tam ulož hodnotu premennej y.
4.5 Výrazy
Výraz v jazyku C je tvorený postupnosťou operátorov a operandov. Ich vyhodnotenie vedie na výpočet adresy alebo hodnoty. Preto sa v jazyku C rozlišuje tzv. adresový výraz - lvalue, ktorý sa používa na ľavej strane priradzovacieho príkazu (odtiaľ left value) a hodnotový výraz - rvalue, ktorý sa používa (vyskytuje, vyhodnocuje) na pravej strane príkazu.
Vyhodnotenie výrazu v C môže mať vedľajší efekt, ktorému sa snažíme zabrániť, napr. používaním výrazov, ktoré behom výpočtu nespôsobia viacnásobnú zmenu obsahu toho istého pamäťového miesta.
Výrazy sa vytvárajú z operandov pomocou operátorov, pričom operandom môže byť konštanta, premenná alebo opäť výraz. Základom výrazov sú tzv. primárne výrazy, kam patrí:
- identifikátor
- konštanta
- reťazec
- výraz v okrúhlych zátvorkách
- indexový výraz, t.j. primárny výraz nasledovaný výrazom v hranatých zátvorkách
- volanie funkcie, t.j. primárny výraz nasledovaný výrazom v okrúhlych zátvorkách s prípadným zoznamom argumentov
- výraz selekcie položiek (príkazy výberu) v tvare:
[primárny_výraz].[identifikátor_položky]
Je možný aj iný spôsob selekcie položky s využitím smerníka na položku v tvare:
[smerník na štruktúrovaný objekt]->[identifikátor_položky]
Ak s je smerník na nejakú štruktúru, prístup na jej zložku x zapíšeme s->x, čo je to isté ako (*s).x
Konštantné výrazy spravidla využívajú pamäťovú triedu static. Vyhodnotenie takého výrazu sa vykoná už počas kompilácie programu. Pri volaní funkcie musí byť dodržaný typ a počet argumentov funkcie a taktiež typ návratovej hodnoty funkcie. V prípade, že funkcia nie je v programe deklarovaná, kompilátor ju automaticky označí za externú funkciu s návratovým parametrom typu int, t.j
extern int f();
čo môže spôsobiť nedefinované chovanie programu. Podľa normy ANSI musí byť každá použitá funkcia deklarovaná.