| ← datové typy | C/C++ | vstup a výstup → |
V minulé kapitole jsme se podívali na základní datové typy. Nyní se podíváme na další objekty, které nám umožňuje jazyk C využívat a také na tzv. pole, pomocí nějž vytváříme (mimo jiné) řetězce.
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 si postupně probereme v kapitole za kapitolou. Zatím se jejich významem nezabývejte.
| auto | double | int | struct | break | else | long |
| switch | case | enum | register | typedef | char | extern |
| return | union | const | float | short | unsigned | continue |
| for | signed | void | default | goto | sizeof | volatile |
| do | if | static | while |
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 by jste používali místo konstant přímo hodnoty, museli by jste 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 ....
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 by jste se takovou proměnnou někde v těle funkce změnit pokusili, překladač by vám to nedovolil.
1: /*------------------------------------------------*/ 2: /* konst.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: const a = 5; /* konstantu a vyhodnoti prekladac jako int 5 */ 9: const b = 5.9; /* konstantu b vyhodnoti prakladac jako int 5 */ 10: const float c = 5; /* konstanta c je urcena jako float,tj. 5.0 */ 11: int i = 0; 13: printf("a/2 = %f\n", (a / 2) * 1.0); /* celociselne deleni ... */ 14: printf("b/2 = %f\n", (b / 2) * 1.0); /* celociselne deleni ... */ 15: printf("c/2 = %f\n\n", (c / 2) * 1.0); /* deleni s des. carkou ... */ 17: printf("Mala nasobilka pro cislo %i\n\n", a); 18: printf("%i * %i = %i\n", a, i, i * a); 19: i = i + 1; 20: printf("%i * %i = %i\n", a, i, i * a); 21: i = i + 1; 22: printf("%i * %i = %i\n", a, i, i * a); 23: i = i + 1; 24: printf("%i * %i = %i\n", a, i, i * a); 25: i = i + 1; 26: printf("%i * %i = %i\n", a, i, i * a); 27: i = i + 1; 28: printf("%i * %i = %i\n", a, i, i * a); 29: i = i + 1; 30: printf("%i * %i = %i\n", a, i, i * a); 31: i = i + 1; 32: printf("%i * %i = %i\n", a, i, i * a); 33: i = i + 1; 34: printf("%i * %i = %i\n", a, i, i * a); 35: i = i + 1; 36: printf("%i * %i = %i\n", a, i, i * a); 38: return 0; 39: } 41: /*------------------------------------------------*/
Výstup z programu:
a/2 = 2.000000 b/2 = 2.000000 c/2 = 2.500000 Mala nasobilka pro cislo 5 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 by jste 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 :-).
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!
Kromě konstantních čísel můžete mít také v programu konstantní znaky a řetězce. Definice znakové konstanty není nijak překvapivá:
const char pismeno = 'a';
S konstantními řetězci je to už horší. Nejdříve si musíme říct, co to vlastně textový řetězec je. Jedná se o pole znaků. Co jsou to pole si vysvětlíme za chvíli. Zatím stačí, když budete vědět, že pole je 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. Tak program pozná, kde pole v paměti končí. Kde pole 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. Nulu na konci textového řetězce za vás vloží překladač sám.
const char *veta = "Toto je konstantni retezec.";
Ukazatel veta je ukazatel na typ char a ukazuje na začátek textového řetězce, v našem případě tedy na písmeno T. Pokud bude program pracovat s řetězcem na který ukazatel veta ukazuje, podívá se do paměti kam 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čí.
V jazyce C neexistuje žádný datový typ „řetězec“. Na řetězec (obecně jakékoliv pole objektů) se můžete odkazovat pouze pomocí ukazatelů.
1: /*------------------------------------------------*/ 2: /* konst2.c */ 4: #include <stdio.h> 6: int main(void) 7: { 8: const char Kpismeno = 'a'; /* konstanta */ 9: char pismeno = 'b'; 10: const char *veta = "Konstantni veta !\n"; 11: const char *veta2 = "a"; 12: char *ukazatel; /* ukazatel na typ char */ 14: ukazatel = &pismeno; /* ukazatel ukazuje na promennou pismeno */ 16: printf("1: Tisknu pismena: %c %c\n", Kpismeno, pismeno); 18: printf("2: Tisknu vetu: %s", veta); 19: printf("3: Tisknu vetu: %s\n", veta2); 21: printf("4: Tisknu pismeno: %c\n", *ukazatel); 23: printf("5: Tisknu pismeno: %c\n", *veta); 24: printf("6: Tisknu pismeno: %c\n", *veta2); 26: return 0; 27: printf("7: Tisknu vetu: %s\n", ukazatel); 28: printf("8: Tisknu vetu: %s\n", pismeno); 29: /* printf("9: Tisknu: %s\n",*pismeno); uplna blbost */ 30: } 32: /*------------------------------------------------*/
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 by jste
očekávali. Když funkce printf() narazí na sekvenci %c najde si
další argument a ten pak 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 příslušný další 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ý bajt. Věta "a"
obsahuje kromě písmena
'a' také onu zarážku (nulový bajt).
Ve čtvrtém volání tiskneme
hodnotu, na kterou ukazuje proměnná
ukazatel. Jelikož
proměnné veta a veta2 jsou také ukazatele, lze je
použít stejným způsobem.
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 vytisknou v (pátém a šestém volání printf()) první znaky z 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 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ší bajty v paměti počítače, dokud by se nenarazilo na nulu. Takže by se vytiskla hromada nesmyslů nebo by mohl program zkolabovat (neoprávněný přístup do paměti). Kdybychom inicializovali proměnou ukazatel např. takto: ukazatel = veta; bylo by sedmé volání printf() naprosto v pořádku.
Ovšem osmé volání je už blbost. Na místo ukazatele jsme předali typ char. Na tuto chybu vás již překladač upozorní, ale přesto dokáže program přeložit. Již jsme si řekli, ž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, 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().
ASCII 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). To by překladač odmítl přeložit.
Escape sekvence jsou sekvence, které ná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í |
| \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:
1: /*------------------------------------------------*/ 2: /* esc.c */ 3: #include <stdio.h> 5: int main(void) 6: { 7: printf("%s %%\n", "%"); 8: printf("%s\r%s\n", "AAAAAAA", "BB"); 9: printf("\"\a\"\n"); 10: printf("\x6a\n"); 11: return 0; 12: } 14: /*------------------------------------------------*/
Výstup z programu:
% % BBAAAAA "" j
Při třetím volání printf() by se mělo ozvat krátké pípnutí.
| ← datové typy | C/C++ | vstup a výstup → |
