AMIGA REVIEW online
  Uvodná stránka     Software     Hry     Obaly     Download     Kniha návštev     Amiga na PC     Amiga Forever  

Kurz jazyka C - 2. díl

Pavel Čížek

Vítám Vás u dnešní, v pořadí již druhé části našeho kurzu. Čeká nás mnoho práce, takže se do ní rovnou pustíme. V dnešní části se totiž začneme seznamovat se všemi možnými datovými objekty jazyka C.

V úvodu si nejprve něco povíme o identifikaci, tj. o tom, jak lze označovat konstanty, proměnné, funkce a jejich parametry atd. Platí pro ně stejná pravidla. Identifikátor je posloupnost písmen nebo číslic, začínající písmenem. Přípustné jsou pouze znaky ASCII kódu (tj. 26 malých a velkých písmen anglické abecedy) a pro zpřehlednění i podtržítko "_" (i jím může identifikátor začínat). Správné identifikátory tedy jsou např. "test", "moje_2 funkce", ale chybné jsou již "malá funkce" nebo "2 pokus". Případné omezení délky identifikátorů a počet rozlišovaných znaků je dán konkrétní implementací, ale vždy se lze spolehnout na to, že identifikátory jsou rozlišovány nejméně podle prvních 8 znaků (norma ANSI doporučuje 31). Dalším podmínkou je, že identifikátor nesmí být totožný s některým z tzv. klíčových slov. Jedná se vyhrazená slova, která mají v Céčku speciální význam (většinou se jedná o příkazy - např. jsme již potkali klíčová slova "while", "if", "return" nebo "int"). Jako doporučení lze zavést konvence pro odlišení identifikátorů konstant, proměnných atd. V rámci tohoto kurzu budeme např. pro identifikátory konstant používat pouze velká písmena pro odlišení od proměnných a funkcí.
Počítače ve své podstatě dnes slouží téměř výhradně ke zpracování dat. Každý program (pokud vůbec něco dělá) vytváří či zpracovává data. Proto je u vyšších programovacích jazyků důležité, jak lze k datům přistupovat. Paměťové buňky již od minula nazýváme proměnnými. Dohoda o identifikaci buňky (jak si ji pojmenujeme) a o způsobu interpretace obsahu se nazývá deklarace. Obecně má tři části: "popis typu" (= interpretace obsahu), "deklarované objekty" (= pojmenování jednotlivých objektů) a případně i "počáteční hodnoty" (= počáteční obsah paměťové buňky). "Popis typu" se skládá z určení paměťové třídy (určuje požadovaný nebo doporučený způsob uložení proměnné) a z určení typu proměnné (lze použít již předdefinované typy, odvozené typy nebo lze definovat svoje vlastní; navíc je možné typ dále modifikovat). O tom všem si nyní podrobněji povíme. Na místě typu však může stát též speciální klíčové slovo "void", které je třeba chápat jako libovolný nebo neznámý typ (nebo také žádný!). Např. deklarace "void Nic(void)" označuje funkci, která nemá žádné parametry a nevrací žádnou hodnotu.

Paměťové třídy
Při tvorbě programu je třeba mít možnost popsat způsob používání proměnných. Jedná se především o rozlišení lokálních a globálních objektů. V Céčku jsou proměnné deklarované uvnitř bloku (je ohraničen složenými závorkami "{" a "}") v tomto bloku lokální. Tělo každé funkce je také blok a tedy proměnné deklarované v těle funkce jsou v této funkci lokální a vně této funkce jsou nepřístupné. Naopak proměnné deklarované mimo funkce jsou globální a přístupné ze všech funkcí (pokud nejsou zastíněny lokální deklarací - pokud máte globální proměnnou "test" a v těle nějaké funkce deklarujete lokální proměnnou "test", pak v této funkci nebude globální proměnná přístupná, "test" bude znamenat onu lokální).
Pokud je v nějakém modulu deklarována globální proměnná (např. "int citac;") a chceme ji používat i v jiném modulu, je nutno v rámci tohoto modulu kompilátoru oznámit, že chceme používat již někde existující externí objekt - k tomu slouží klíčové slovo "extern". Chceme-li užít např. proměnnou "citac" v jiném modulu, musíme před jejím použitím oznámit její existenci kompilátoru pomocí "extern int citac;". Pozor! Deklarace "int citac" zajistí vytvoření globální proměnné (tj. přiřadí ji paměť apod.) zatímco "extern int citac" udává pouze odkaz na již existující objekt, žádné místo v paměti se již neobsazuje. Poznamenejme, že globální proměnné jsou externí pro všechny funkce, ale v modulu, kde jsou deklarovány, lze využít implicitního dovozu globálních proměnných do funkcí.
Často je globální proměnná výhodným řešením, neboť na rozdíl od lokálních proměnných existuje po celou dobu běhu programu. Je však snadno přístupná v řadě funkcí, což může mít za následek nechtěné změny a těžko odhalitelné chyby. Globální proměnnou lze proto deklarovat jako "static" (např. "static chat znak") - tím oznamujeme překladači, že se jedná o globální proměnnou viditelnou a použitelnou pouze v daném modulu (resp. funkci). Podívejme se na příklad. Mějme funkci "pocet()", která bude počítat, kolikrát byla zavolána. Vždy po jejím volání vytiskneme onen počet.

Varianta s globální proměnnou viz první příklad.

Každý by asi čekal, že když dvakrát zavoláme funkci "pocet()", program vytiskne "1 2". Nicméně díky chybičce (překlepu) v prvním volání funkce "printf" vytiskne "1 3". To je právě nevýhoda globální proměnné, která slouží k jedinému účelu jedné funkci, ale je všude přístupná. Podívejme se, jak to dopadne při použití statické proměnné.

Varianta se statickou proměnnou viz program číslo 2.

Jak je vidět, nyní k žádným výše zmiňovaným problémům docházet nemůže. A jak je to s inicializací globálních proměnných (pozor - liší se od lokálních, viz dále)? Buď je počáteční hodnota předepsána již v deklaraci; pak je na počátku programu tato hodnota uložena do proměnné a více se k ní již program nevrací - např. "static int citac = 0" nastaví na počátku proměnnou "citac" na nulu. Pokud předepsána počáteční hodnota není, pak jsou globální proměnné nastaveny na nulu.
Není-li proměnná deklarována mimo jakoukoliv funkci, není globální, pak musí nutně být lokální. Lokální proměnné lze předznačit pro paměťovou třídu "auto", "register" nebo "static". Chování proměnné třídy "static" jsme si již osvětlili existuje stále, nevzniká a neinicializuje se při každém volání funkce. Všechny lokální proměnné, které nejsou nijak označeny, jsou považovány za tzv. automatické proměnné, proměnné třídy "auto". Tyto proměnné vznikají automaticky v okamžiku potřeby (= při vstupu do bloku) na zásobníku a při opuštění funkce zanikají. Mimo tento blok neexistují (šetří to paměť). Lokální proměnnou lze při vstupu (při každém!) nastavit na nějakou hodnotu - to lze zařídit v deklaraci. Pokud počáteční hodnota není udána, pak je po vytvoření obsah proměnné libovolný, přesněji řečeno, není definován. V klasickém Céčku je možno inicializovat pouze lokální proměnné jednoduchých typů (čísla, znak), v ANSI C lze inicializovat i podstatně složitější objekty. Pokud je nějaká lokální proměnná hodně využívána, mohlo by být výhodné umístit ji místo do paměti přímo do registru procesoru. Doporučit to můžete kompilátoru direktivou "register". Řada kompilátorů ji však ignoruje; dobré kompilátory pro procesory Motorola však nejpoužívanější proměnné umisťují do registrů automaticky.

Základní datové typy
V jazyce C jsou tyto základní typy: char, int, float, double. Později se seznámíme i s typy odvozenými. Existují různé varianty těchto typů, které jsou dány použitím různých modifikátorů - short, LONG, unsigned, signed. Poslední modifikátor je pouze v ANSI C. Základní rozdíl mezi prvními a druhými dvěma je v reprezentaci čísel. Typy char a int představují čísla v pevné řádové čárce (tj. celé číslo se znaménkem), typy float a double reprezentují čísla v pohyblivé řádové čárce (tj. reálná čísla = exponent + mantisa). Protože nemusí být obory těchto typů v každé implementaci totožné, jsou v hlavičkovém souboru "limit.s" shrnuty základní charakteristiky celočíselných typů, v souboru "float.h" jsou pak charakteristiky typů float a double. V následující tabulce jsou přehledně uvedeny všechny základní a odvozené typy spolu s jejich rozsahem na procesorech Motorola (rozsah je uveden číselně i pomocí zmíněných konstant z hlavičkových souborů). Navíc jsou v tabulce poznámky - v Céčku je možné speciální typ (třeba dlouhý na psaní, ale i základní jako např. "int") nějak pojmenovat svým vlastním názvem. Na Amize jsou v souboru "exec/types.h" zavedena pro přehlednost různá další označení základních typů; ta jsou uvedena u dalších poznámek k těmto typům. Poznamenejme ještě, že u typů odvozených od "int", např. "unsigned int", lze klíčové slovo "int" vynechávat a psát jen "unsigned" apod. Je toho pro stručnost využito i v tabulce.
Povězme si o těchto typech něco více. Hned v úvodu musím upozornit, že na Amize je typ "int" reprezentován 4 byty (= 32 bitů) a má tedy stejný rozsah jako "long". Pro označování hodnot typu "int" (a samozřejmě typů odvozených) lze využívat jednak běžný dekadický zápis (např. 97), nelze v něm však psát vedoucí nevýznamné nuly. Zápisy uvozené znakem "0" jsou chápány jako zápisy v jiných číselných soustavách. Je to buď oktalová soustava, pokud za "0" následuje znak "0" - "7", nebo hexadecimální (šestnáctková) soustava, začíná-li zápis znaky "0x", resp. "0X". Číslo 97 lze tedy zapsat takto: 97 = 0141 = 0x61. Jak jste si již všimli minule, pokud chceme celá čísla číst/tisknout pomocí scanf/printf, uváděli jsme na příslušné místo kombinaci "%d". Pokud se místo ní objeví "%o", resp. "%x", pak se čísla budou načítat/tisknout v oktalové, resp. hexadecimální soustavě. Jistě jste už také pochopili, že typy normální jsou se znaménkem, typy předznačené "unsigned" jsou bez znaménka.
Znakové konstanty (typ char) se zapisují jako tištitelný znak mezi apostrofy (např. "A", "*"). Chceme-li zapsat znak apostrof, resp. znak obrácené lomítko, musí mu předcházet obrácené lomítko -"", resp. "\". Této konvence se využívá i pro některé řídící ASCII znaky, např. " " = nový řádek, "f" = nová stránka (form feed), "" = návrat o 1 znak (backspace). Navíc libovolný znak ASCII kódu může být vyjádřen svou oktalovou hodnotou uvedenou za lomítkem, např. "12" je totéž co" ".
A zde jsou pro zajímavost některé na dodefinované typy z modulu "exec/types.h". V závorce vždy uvedu odpovídající klasickou definici. LONG (= long), ULONG (= unsigned long int), LONGBITS (= unsigned long int, má naznačit, že manipulujeme s jednotlivými bity), WORD (= short), UWORD (= unsigned short), WORDBITS (= unsigned short, opět značí manipulaci s bity), SHORT (= short), USHORT (= unsigned short), COUNT (= short, tj. počet), UCOUNT (= unsigned short), BOOL (= short, slouží pro logické hodnoty, dodefinovány jsou i TRUE a FALSE), BYTE (= signed char), UBYTE (= unsigned char), BYTEBITS (= unsigned char, opět přístup po bitech), TEXT (= unsigned char).
Typy float a double slouží pro reprezentaci čísel v pohyblivé řádové čárce. Zmíněný "long double" nemusí být povinně implementován; odpovídá rozsahu a formátu čísel, v jakém pracují koprocesory (FPU). Konstanty v pohyblivé řádové čárce se zapisují obvyklým způsobem, např.
1., .78, -0.456, l .5e10, 5E-7 atd.
Nyní můžeme na základě těchto znalostí vytvořit jednoduchý prográmek, který načte číslo z klávesnice, převede ho do oktalové a hexadecimální soustavy a vytiskne jej. Uvidíte, jak je to jednoduché.
Ukážeme si ještě jeden prográmek, který by vás měl upozornit na jistá nebezpečí v jazyce C. Céčko si dělá nárok na generování rychlého a krátkého kódu. Proto neprovádí kontroly ničeho (pouze zásobníku). Na tom je založen následující program, který má zjistit rozsah typu "short". Princip spočívá v tom, že když do proměnné budeme stále přičítat 1, pak někdy dojde k tomu, že číslo v proměnné "x" bude mimo rozsah daného typu - budete pro něj potřebovat více než 16 bitů (= rozsah short). Protože "x" je typu short, zůstane v proměnné pouze oněch dolních 16 bitů, což má za následek, že v "x" se najednou objeví (pokud tam bylo největší možné číslo a přičteme 1) číslo nejmenší, dojde k tzv. přetečení. A toho právě využijeme.
Snad jste se moc nevyděsili. Takových zajímavých situací může nastávat celá řada. A vše jen díky tomu, že nejsou prováděny žádné kontroly problémových a kritických situací. Je tedy třeba dávat pozor a takovýchto situací se vyvarovat. V příštím díle se podíváme na složitější typy (jako jsou ukazatele, struktury, pole apod.) a začneme asi s příkazy jazyka. Pak už budeme vybaveni alespoň tím základním pro pronikání do operačního systému Amigy. Nashledanou.

 

#include <stdio.h>
int citac = 0; /* globální počitadlo */

int pocet() /* funkce pocet - vrací počet volání */
{
/* zvětšíme pocet = proměnná "citac" a vrátíme její novou hodnotu */,
return( ++citac );
}
main()
{
/* oznámíme existenci globální proměnné, v rámci jednoho modulu to ale není nutné */
extern int citac;

/* zavoláme vždy funkci "pocet" a tiskneme "citac" */
pocet(); printf("%d ", citac++);
pocet(); printf("%d ", citac);
}

 

#include <stdio.h>
int pocet() /* funkce pocet - vrací počet volání */
{
static int citac = 0;
/* deklarujeme uvnitř funkce statickou proměnnou */
/* bude tedy existovat po dobu běhu celého programu, ale používat ji lze jen v této funkci */
/* zvětšíme počet = proměnná "citac" */
/* a vytiskneme hlášení ..: */
printf("Pocet volání funkce: %d ", ++citac);
/* a vrátíme její novou hodnotu */
return( citac );
}
main()
{
/* zavoláme vždy funkci "pocet", a to je vše */
pocet();
pocet();
}

 

main()
{
int x; /* deklarace celočíselné proměnné x */
/* vypíšeme výzvu a načteme číslo z klávesnice do x */
printf("Zadejte číslo v desítkové soustavě: ");
scanf("%d", &x);

/* vypíšeme zadanou hodnotu v dekadickém zápisu */
printf(" Zadaná hodnota: %d ", x);
/* vypíšeme zadanou hodnotu v oktálovém zápisu */
printf("Číslo,v oktalové soustavě: %o ", x);
/* vypíšeme zadanou hodnotu v hexadecimálním zápisu */
printf("Číslo v hex. soustavě: %X ", x);
}

 

main()
{
/* deklarace celočíselných proměnných max, min a x */
/* x má na počátku hodnotu = 0 */
short max, min, x = 0;

/* provede se přirazení max = x, poté se zvýší proměnná x o 1 (příkaz x++); */
/* pak se testuje, zda hodnota výrazu (max = x++) */
/* (tj. hodnota max, což je hodnota x před přičtením 1) */
/* je menší niž x (obsahuje novou hodnotu); */
/* to platí (a, do té doby provádíme cyklus while = dokud),*/
/* pokud nedojde k přetečení pak se cyklus zastaví */
while ( (max = x++) < x);

/* v max je potom nejvyšší možná hodnota, v x je nejmenší,*/
/* a proto ji uložíme do min */
min = x;

/* vypíšeme maximum a minimum rozsahu short */
printf(" Rozsah typu short: %d ... %d ", min, max);
}

 

Tabulka typů:

Typ Dolní mez Horní mez
(signed) char -128 (SCHAR_MIN) 127 (SCHAR_MAX)
unsigned char 0 255 (UCHAR_MAX)
short int -32768 (SHORT_MIN) 32767 (SHORT_MAX)
int -2147483648 (INT_MIN) 2147483647 (INT_MAX)
long int -2147483648 (LONG_MIN) 2147483647 (LONG_MAX)
unsigned short 0 65535 (USHRT_MAX)
unsigned int 0 4294967295 (UINT_MAX)
unsigned long 0 4294967295 (ULONG_MAX)

 

Nejmenší kladné Největší kladné

  Nejmenší kladné Největší kladné
float 1*10^-37 (FLT_MIN) 1*10^37 (FLT_MAX)
  FLT_DIG = 6 = počet platných cifer mantisy  
double 1*10^-307 (DBL_MIN) 1*10^37 (FLT_MAX)
  DBL_DIG = 15  
long double 1*10^-4932 1*10^4932
  FLT_DIG = 33  

Vytlačiť článok


© ATLANTIDA Publishing Všechna práva vyhrazena.
Žádna část nesmí být reprodukována nebo jinak šířena bez písemného svolení vydavatele.



Amiga na Vašem PC rychle, snadno a zdarma!


none

AMIGA REVIEW

57 ( 11-12 / 2000 )
56 ( 9-10 / 2000 )
55 ( 7-8 / 2000 )
54 ( 5-6 / 2000 )
53 ( 3-4 / 2000 )
52 ( 1-2 / 2000 )
 
51 ( 12 / 1999 )
50 ( 11 / 1999 )
49 ( 10 / 1999 )
48 ( 9 / 1999 )
46-47 ( 7-8 / 1999 )
45 ( 6 / 1999 )
44 ( 5 / 1999 )
43 ( 4 / 1999 )
42 ( 3 / 1999 )
41 ( 2 / 1999 )
40 ( 1 / 1999 )
 
39 ( 12 / 1998 )
38 ( 11 / 1998 )
37 ( 10 / 1998 )
36 ( 9 / 1998 )
35 ( x / 1998 )
34 ( x / 1998 )
33 ( 1-2 / 1998 )
 
32 ( 11-12 / 1997 )
31 ( 9-10 / 1997 )
30 ( 7-8 / 1997 )
29 ( 6 / 1997 )
28 ( 5 / 1997 )
27 ( 4 / 1997 )
26 ( 3 / 1997 )
25 ( 2 / 1997 )
24 ( 1 / 1997 )
 
23 ( 12 / 1996 )
22 ( 11 / 1996 )
21 ( 10 / 1996 )
20 ( 9 / 1996 )
18-19 ( 7-8 / 1996 )
17 ( 6 / 1996 )
16 ( 5 / 1996 )
15 ( 4 / 1996 )
14 ( 3 / 1996 )
13 ( 2 / 1996 )
12 ( 1 / 1996 )
 
11 ( 12 / 1995 )
10 ( 11 / 1995 )
9 ( 10 / 1995 )
8 ( 9 / 1995 )
7 ( 7 / 1995 )
6 ( 5 / 1995 )

ATLANTIDA NEWS

5 ( 3 / 1995 )
4 ( 1 / 1995 )
 
3 ( 11 / 1994 )
2 ( 9 / 1994 )
1 ( 7 / 1994 )
0 ( 5 / 1994 )