Klíčová slova, konstanty, řetězce
V minulé kapitole jsem probral základní datové typy. Nyní vám ukáži další objekty, které vám jazyk C nabízí. Například pole, pomocí nichž se dají vytvářet (mimo jiné) textové řetězce.
Klíčová slova
ANSI norma jazyka C určuje následující slova jako klíčová. Tyto slova mají v jazyce C speciální význam a nelze je používat jako uživatelem definované identifikátory (např. jména funkcí, proměnných, konstant atd.). Jejich význam postupně proberu kapitolu po kapitole. Zatím se jejich významem nemusíte zatěžovat.
auto | break | case | const | continue | char | default |
do | double | else | enum | extern | float | for |
goto | if | int | long | register | return | short |
signed | sizeof | static | struct | switch | typedef | union |
unsigned | void | volatile | while |
Standard C99 přináší tyto nová klíčová slova:
inline | _Bool | _Complex | _Imaginary |
Konstanty
Konstanty jsou objekty, jejichž hodnota se za chodu programu nemění. Konstanty se definují za klíčovým slovem const. Po něm následuje datový typ konstanty. Pokud ne, překladač jej chápe jako int. Dále je v definici identifikátor konstanty a za rovnítkem hodnota.
Konstanty mají stejné vlastnosti jako proměnné, až na tu maličkost, že jejich hodnota se nedá změnit. Používají se v programu namísto přímého vložení hodnoty (čísla, textu atp.).
Pokud takovou konstantu použijete v programu na několika místech, stačí změnit její hodnotu v definici a tak se změní hodnota na všech místech programu kde byla použita. Pokud byste používali místo konstant přímo hodnoty, museli byste procházet celý program a měnit hodnotu na všech místech, kde je to potřeba. To je nejenom pracné, ale také si to říká o to, že někde zapomenete …
Použití konstant také ulehčuje práci překladači s optimalizací výsledného programu. Pokud překladač ví, že se hodnota nebude měnit, může ji uložit do optimální části paměti. Některé jednoduché výpočty s konstantami může provést už za překladu.
Jako konstantu můžete označit i argument fukce. Tím dáte najevo, že hodnota, kterou funkce dostane, se nebude uvnitř bloku funkce měnit. Pokud byste se konstatní proměnnou pokusili někde v těle funkce změnit, překladač by to nedovolil. Výhodou použití konstant není jen to, že to překladači pomáhá s optimalizací výsledného programu, ale i vám jakožto programátorům to pomůže s čitelností zdrojového kódu. (Stačí se podívat na deklaraci funkce a hned víte, že se argument funkce nebude nijak měnit.)
- /*------------------------------------------------*/
- /* c06/konst.c */
- #include <stdio.h>
- {
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- i = i + 1;
- }
- /*------------------------------------------------*/
Výstup z programu:
a/2 = 2.000000 b/2 = 2.000000 c/2 = 2.500000 Mala nasobilka pro cislo 5 5 * 0 = 0 5 * 1 = 5 5 * 2 = 10 5 * 3 = 15 5 * 4 = 20 5 * 5 = 25 5 * 6 = 30 5 * 7 = 35 5 * 8 = 40 5 * 9 = 45 5 * 10 = 50
Výsledkem programu je malá násobilka čísla 5. Pokud chcete malou násobilku např. čísla 4, stačí změnit konstantu a. Asi si říkáte, že kdyby a nebyla konstanta ale proměnná, vyšlo by to nastejno. Jenomže pak byste mohli v nějakém velkém programu zapomenout na to, že se hodnota a nemá měnit a překladač by vám změnu (na rozdíl od konstanty) umožnil. Až se jednou budete vracet k vlastnímu zdrojovému kódu třeba po půl roce, budete rádi, že máte důsledně oddělené proměnné od konstant (a také, když budete mít zdrojový kód dobře okomentován :-).
Další výhodou konstant je, že za výrazy jako je (a/2)*1.0
může už překladač dosadit výsledek, protože vše potřebné je
v době překladu známo. (Pravda ale je, že v takto jednoduchém programu
by překladač dokázal poznat jakou bude mít proměnná a
hodnotu, i kdyby nebyla označena jako konstanta.)
Používejte konstanty všude kde je to vhodné. Slouží to jako ochrana před nechtěnou změnou konstanty, jako komentář „tato hodnota je konstantní“ a pomáhá to překladači optimalizovat rychlost a velikost výsledného programu!
Řetězce
Kromě konstantních čísel můžete mít také v programu konstantní znaky a řetězce. Definice znakové konstanty není nijak překvapivá:
V jazyku C je znakový literál ('a') překvapivě typu int. V příkladu
výše se automaticky převede na typ char a uloží do proměnné pismeno,
takže se o to vlastně nemusíte starat. Jazyk C++ už definuje znakový literál
jako typ char
.
S konstantními řetězci je to už horší. Nejdříve vám musím říct, co to vlastně textový řetězec je. Jedná se o pole znaků. Co jsou to pole vysvětlím podrobněji později. Zatím stačí, když budete vědět, že pole je spojité místo v paměti vyhrazené pro posloupnost objektů.
Textový řetězec je tedy pole
znaků (ať už se jedná o konstantní pole nebo ne). Navíc má tu
zvláštnost (na rozdíl od ostatních polí), že má na konci tzv.
zarážku. Je to znak, který obsahuje nulu (číslo nula). Tak se pozná, kde
řetězec v paměti končí. Kde začíná je uloženo v ukazateli.
Takto se jednoznačně určí, kde se textový řetězec nachází a co do něj
patří. Zatímco znaky jsou v jednoduchých uvozovkách ('),
textové řetězce ve dvojitých (").
Nulový znak na konci textového řetězce za vás vloží překladač sám
(za znakem ne – konec konců, znak je jen jeden znak …).
Ukazatel veta je ukazatel na
typ char
a ukazuje na začátek textového řetězce,
v příkladě na místo v paměti, kde je uloženo písmeno T.
Pokud bude s řetězcem, na který ukazatel veta
ukazuje, pracovat například funkce printf()
,
podívá se do paměti kam veta ukazuje, přečte první písmeno, pak
se podívá na další bajt (velikost typu char je 8 bitů) a přečte
další písmeno atd., až narazí na bajt obsahující nulu a tím
skončí.
Klíčové slovo const
v předchozí delaraci označuje jako konstantu
pole, na které veta ukazuje.
Ale pozor! Textový literál (tj. textový řetězec který v programu zapíšete do uvozovek)
nelze měnit v žádném případě! Překladač takové literály ukládá do speciální části paměti,
kam nesmíte nic zapisovat (pokud to zkusíte, program selže – bude zabit operačním systémem).
Pro označení jako konstanty ukazatele dejte
const
před hvězdičku.
Toto je konstantní ukazatel na konstantí pole. Nejen že nemůžete změnit kam ukazuje ukazatel, ale nemůžete změnit ani to, na co ukazuje. Za domácí úkol zkuste přijít na to, jak definovat konstantní ukazatel na nekonstantí pole :-).
V jazyce C neexistuje žádný datový typ „řetězec“. Na řetězec (obecně jakékoliv pole objektů) se můžete odkazovat pouze pomocí ukazatelů.
- /*------------------------------------------------*/
- /* c06/konst2.c */
- #include <stdio.h>
- {
- ukazatel = &pismeno; /* ukazatel ukazuje na promennou pismeno */
- /* printf("9: Tisknu: %s\n",*pismeno); je uplna blbost */
- /* *veta = 'C'; chybne prirazeni */
- }
- /*------------------------------------------------*
Výstup z programu:
1: Tisknu pismena: a b 2: Tisknu vetu: Konstantni veta ! 3: Tisknu vetu: a 4: Tisknu pismeno: b 5: Tisknu pismeno: K 6: Tisknu pismeno: a
V prvním volání funkce printf()
se tisknou znaky tak, jak byste
očekávali. Když funkce printf()
narazí na sekvenci %c
najde si
svůj další argument a ten vytiskne jako znak.
Ve druhém volání se tiskne věta, na kterou ukazuje konstantní ukazatel
veta a ve třetím volání se tiskne věta, na kterou ukazuje ukazatel
veta2.
Funkce printf()
ve chvíli, kdy narazí na sekvenci
%s
si najde odpovídající argument, který bere jako adresu
do paměti. Z této paměti začne po znacích číst a znaky tisknout,
dokud nenarazí na nulový znak.
Věta "a"
obsahuje kromě písmena 'a' také onu zarážku (nulový bajt), je
tedy 2 bajty dlouhá (narozdíl od znaku 'a' uloženého v proměnné Kpismeno).
Ve čtvrtém volání tiskne funkce hodnotu, na kterou ukazuje proměnná ukazatel. Proměnné veta a veta2 jsou také ukazatele na typ char, úplně stejně jako ukazatel. Jen mají při své deklaraci přiřazenou hodnotu (odkaz na řetětězec v uvozovkách).
Ukazatele veta a
veta2
ukazují na začátky vět, tedy přesněji, obsahují adresu paměti,
na které je uloženo první písmeno věty.
Proto se v pátém a šestém volání printf()
vytiskne první znak věty.
Za příkazem return 0;
se nacházejí ještě další tři volání
funkce printf()
. Tyto volání jsou chybné.
Sedmé volání printf()
se
pokouší vytisknout větu, na kterou ukazuje ukazatel
ukazatel (jak šťastně pojmenováno :-). Jenomže
ukazatel neukazuje na větu,
ale na znak v proměnné pismeno.
Překladač nemůže kontrolovat kam proměnná ukazatel
v době spuštění ukazuje (zda na řetězec nebo znak nebo jestli vůbec někam …)
a tak nepozná chybu. Pokud by došlo k takovémuto volání funkce printf(),
vytiskl by se nejdříve znak v proměnné pismeno (tj. znak 'b')
a pak by se tiskli další a další bajty z paměti počítače, dokud by se nenarazilo někde na nulu.
Takže by se vytiskla hromada nesmyslů, nebo by program
zkolaboval (neoprávněný přístup do paměti). (Kdybych
inicializoval proměnou ukazatel např. takto:
ukazatel = veta;
, bylo by sedmé volání
printf()
naprosto v pořádku.)
Ovšem osmé volání je jednoznačně chyba. Namísto ukazatele jsem předal
proměnnou typu char. Na tuto chybu vás již překladač upozorní, ale přesto
dokáže program přeložit. Již jsem řekl, že adresa v ukazateli
není nic jiného než číslo v bitech, stejně tak písmena (a vůbec
všechny data v počítači).
V osmém volání funkce printf()
vezme znak v proměnné
pismeno a použije jej jako adresu do paměti, ze které
se pokusí přečíst větu. V tomto případě je téměř jisté, že vám
program zkolabuje kvůli neoprávněnému přístupu do paměti, nebo se
vypíše hromada nesmyslů, stejně jako v sedmém volání
printf()
.
Číselná hodnota písmena 'b' je 98. Poslední volání funkce
printf()
by se tedy
snažilo vytisknout větu začínající na 98. bajtu v paměti (kdyby toto
volání překladač umožnil).
Deváté volání je opravdu nesmysl, protože proměnná
pismeno není ukazatel,
nelze použít operátor *
(hvězdička, odborně dereference; ukazateli se totiž říká reference …). Toto by měl překladač odmítnout přeložit.
Na posledním řádku je ještě zakomentované chybné přiřazení *veta = 'C';
.
To je syntakticky správně – na adresu, kam ukazuje veta,
tj. do prvního písmene věty, se snažím uložit znak C
(totéž lze zapsat jako veta[0] = 'C';
).
Chyba je v tom, že veta ukazuje na pole označené jako konstantí.
(Nechme teď stranou, že se navíc jedná o řetězcový literál, do kterého se stejně
zapisovat nesmí.)
Escape sekvence
Escape sekvence jsou sekvence znaků, které vám umožňují vložit do řetězce některé zvláštní znaky. Přehled escape sekvencí vidíte v tabulce.
Escape znak | Význam | Popis |
---|---|---|
\0 | NULL | Nula, ukončení řetězce (má být na konci každého řetězce) |
\a | Alert (Bell) | Pípnutí. Na moderních počítačích už bohužel nefunguje. |
\b | Backspace | návrat o jeden znak zpět |
\f | Formfeed | nová stránka nebo obrazovka |
\n | Newline | přesun na začátek nového řádku |
\r | Carriage return | přesun na začátek aktuálního řádku |
\t | Horizontal tab | přesun na následující tabelační pozici |
\v | Vertical tab | stanovený přesun dolů |
\\ | Backslash | obrácené lomítko |
\' | Single quote | apostrof |
\" | Double quote | uvozovky |
\? | Question mark | otazník |
\000 | ASCII znak zadaný jako osmičková hodnota | |
\xHHH | ASCII znak zadaný jako šestnáctková hodnota |
O tabulce ASCII se zmíním později.
Ještě dodám, že pro vytištění znaku % pomocí funkce printf()
je třeba
zapsat znak % dvakrát za sebou. Nejedná se o escape znak, ale o
vlastnost funkce printf()
interpretovat % ve svém prvním argumentu zvláštním způsobem.
Příklad:
Výstup z programu:
% % BBAAAAA "" j
Při třetím volání Neozve se, protože nové počítače nemají pípátko.
printf()
by se mělo ozvat krátké pípnutí.