V tomto díle se podíváme na proměnné, ukazatele, pole a záznamy. Bude toho opravdu hodně, ale je to poměrně důležitá část.
Jazyk Delphi
Delphi je založeno na tzv. OOP. K programování se používá jazyk zvaný Object Pascal, který vychází z, některým možná známého, jazyka Pascal. V jazyku se používá tzv. tečková notace – jednotlivé třídy se od sebe oddělují tečkami:
Form1.Canvas.Brush.Color:=clRed;
Tento příkaz znamená, aby se u štětce plátna formuláře Form1
nastavila červená barva. Jak vidíte, nejvyšší třída je vždy první a poslední vlastnost (Color) je až potomkem několika dalších tříd, z nichž každá je potomkem třídy, která ji v zápisu předchází.
Tohle byla asi nejdůležitější informace, abyste pochopili, jak vypadá jazyk Delphi. Další jednotlivé vlastnosti jazyka budu uvádět až v dalších dílech, až je budeme potřebovat znát.
Proměnné
Každý program potřebuje nějak pracovat s proměnnými. Pro ty, co s programováním úplně začínají a netuší, která bije: proměnná je hodnota (může to být číslo, řetězec, téměř cokoliv), která je uložena v paměti. Pro přístup k proměnné se používají tzv. identifikátory.
Delphi je přísně typový jazyk, to znamená, že mu opravdu velmi vadí, když se do celočíselné proměnné pokusíte zapsat číslo 1,89, popřípadě se do řetězcové proměnné pokusíte přiřadit číslo 10. Ale abychom na Delphi nehledali jen chyby, tak, na rozdíl od některých jiných jazyků, Delphi nerozlišuje velká a malá písmena v názvech proměnných (a nejen těch – prakticky v čemkoliv).
Do proměnné se hodnota přiřazuje nikoliv pomocí znaku =
(rovná se), jak by asi každý čekal, ale pomocí dvou znaků :=
(dvojtečka, rovná se).
a:=15;
a=15
lze použít při porovnávání, nikoliv ale když bych chtěl do proměnné a zapsat hodnotu 15!
V Delphi existuje nepřeberné množství typů proměnných – tzv. datových typů. Zde je myslím dostatečně názorná tabulka:
typ proměnné | hodnota, kterou může obsahovat | velikost, kterou zabírá v paměti | poznámka |
Integer | −2 147 483 648 … 2 147 483 647 | 4 bajty | celočíselná |
LongInt | −2 147 483 648 … 2 147 483 647 | 4 bajty | celočíselná |
Cardinal | 0 … 4 294 967 295 | 4 bajty | celočíselná |
ShortInt | −128 … 127 | 1 bajt | celočíselná |
SmallInt | −32 768 … 32 767 | 2 bajty | celočíselná |
Int64 | −2·1063 … 2·1063 | 8 bajtů | celočíselná |
Byte | 0 … 255 | 1 bajt | celočíselná |
Word | 0 … 65 535 | 2 bajty | celočíselná |
LongWord | 0 … 4 294 967 295 | 4 bajty | zaměnitelné za typy Integer a Cardinal, celočíselná |
Real | 5·10−324 … 1,7·10308 | 8 bajtů | reálné číslo |
Real48 | 2,9·10−39 … 1,7·1038 | 6 bajtů | reálné číslo |
Single | 1,5·10−45 … 3,4·1038 | 4 bajty | reálné číslo |
Double | 5·10−324 … 1,7·10308 | 8 bajtů | reálné číslo |
Extended | 3,6·10−4 951 … 1,1·104 932 | 10 bajtů | reálné číslo |
Comp | 2·10−63 … 2·1063 | 8 bajtů | reálné číslo |
Currency | −922 337 203 685 477,580 8 … 922 337 203 685 477,580 7 | 19-20 bajtů | reálné číslo, pro finanční výpočty |
ShortString | text o max. délce 255 znaků | 2-256 bajtů | kompatibilní s typem String |
Boolean | True, False | 1 bajt | ze všech booleanovských typů se používá nejvíc |
ByteBool | True, False | 1 bajt | |
WordBool | True, False | 2 bajty | |
LongBool | True, False | 4 bajty |
Takovým samostatným článkem je proměnná typu VARIANT. Ta může obsahovat všechny výše uvedené typy. Když si tedy nadefinuji proměnnou v typu variant, jsou platné všechny následující operace:
v:="slovo";
v:=22;
v:=5.56;
Její použití ale příliš nedoporučuji.
Před použitím je nutné proměnnou deklarovat, aby Delphi při překládání aplikace vědělo, kolik má v paměti vyhradit místa. Proměnné se dají deklarovat dvěma způsoby – lokálně a globálně.
Lokální proměnné
Tyto proměnné se deklarují v procedurách a funkcích. Deklarace se uvádí mezi klíčová slova procedure
(popř. function
), begin
a end
a uvádí se za klíčovým slovem var
. Takto deklarovaná proměnná platí pouze v rámci oné procedury či funkce.
procedure TForm1.Button1Click(Sender: TObject);
var a: integer;
b: integer;
begin
...
...
end;
procedure TForm1.Button2Click(Sender: TObject);
var a: integer;
c: integer;
begin
...
...
end;
V tomto příkladu máme dvě procedury. V první proceduře (Button1Click) mám proměnnou a typu integer, stejně jako v proceduře druhé. Protože jsou tyto proměnné deklarované lokálně, platí jen v rámci procedury, a tudíž a v první proceduře může mít hodnotu třeba 15 a v druhé 27.
Proměnné c a b jsou též deklarovány jen lokálně, takže v první proceduře nemůžete pracovat s proměnnou c a v druhé s proměnnou b.
Globální proměnné
Přesný opak lokálních proměnných. Tyto proměnné se deklarují již na začátku zdrojového kódu, ještě před klíčovým slovem implementation
a jejich hodnota je sdílená v rámci celé aplikace.
var
Form1: TForm1;
d: string;
implementation
{$R *.dfm}
Proměnná d nyní platí ve všech procedurách. Pokud bychom k tomu začlenili ještě předchozí příklad, můžeme s proměnnou d klidně pracovat v první i druhé proceduře.
Když pak v první proceduře do proměnné d přiřadíme hodnotu 15, tak v druhé proceduře bude mít d taky hodnotu 15.
Při používání globálních proměnných si musíte ale dávat pozor, abyste si omylem hodnotu v jedné proceduře nepřepsali a pak se nedivili, že v jiné proceduře je tam úplně něco jiného, než má být!
Další důležitou věci, kterou musím zmínit, je to, že globálně deklarovanou proměnnou již není vhodné deklarovat lokálně! Když si nadeklarujete proměnnou slovo globálně i lokálně v nějaké proceduře, pak v té proceduře, kde jste to deklarovali lokálně, bude Delphi pracovat s tou lokální proměnnou, ale v ostatních procedurách bude pracovat s tou globální. Tohoto se opravdu vyvarujte – je to velmi nepříjemné, vím, o čem mluvím :). Některé verze Delphi vám ani nedovolí tohle provést a začnou protestovat.
Ukazatele
Tato kapitola není životně důležitá a lze ji přeskočit, protože s ukazateli se příliš často asi setkávat nebudete. Pokud ale chcete, klidně si tuto část přečtěte ;).
„Nevýhodou“ proměnných je to, že zabírají místo v paměti po celou dobu běhu programu, i když třeba zrovna nejsou využívané. Někdy programátor potřebuje udělat aplikaci, která bude v paměti zabírat co nejméně místa, může využít tzv. ukazatele.
Od standardních typů se ukazatele liší tím, že neobsahují žádnou hodnotu, ale jenom ukazují na místo v paměti, kde je ona hodnota uložená. Každý ukazatel zabírá 4 bajty v paměti.
Ukazatele mohou ukazovat na jakýkoliv typ proměnných, podle toho se mu říká třeba ukazatel na integer nebo ukazatel na pole apod.
Ukazatel se definuje pomocí operátoru ^
. Takto třeba vypadá definice ukazatele na string:
type UkazatelNaString = ^string; //nadefinujeme ukazatel UkazatelNaString
var
PNaString: UkazatelNaString; //nyní nadeklarujeme proměnnou PnaString, která budetypu UkazatelNaString;
prom: integer; //klasická proměnná typu integer
PnaString:=@prom; //a teď nastavíme, aby proměnná PnaString ukazovala na proměnou prom
Nejprve jsme tedy nadefinovali ukazatel UkazatelNaString a potom jsme globálně nadefinovali proměnnou Pnastring typu ukazatelnastring
a klasickou proměnnou prom typu integer
. Nakonec jsme nastavili, aby proměnná PNaString ukazovala na proměnnou prom.
Je takovým nepsaným pravidlem, že všechny proměnné typu Pointer
(ukazatel) začíná na „P“ – např. Pstr, Pprom apod.
Pomocí ukazatele nyní můžeme nejen přistupovat k hodnotám uloženým v paměti, ale také je můžeme měnit:
PNaString^:="ahoj";
Nyní se do proměnné v paměti, kam ukazuje ukazatel PNaString, uloží hodnota ahoj. Tento příkaz je úplně stejný, jako kdybychom provedli:
prom:="ahoj"; //nyní jsme k proměnné prom přistoupili přímo
Pokud Vás nyní napadlo to samé jako mě, když jsem se oukazatelích učil, tak vězte, že ukazatele jsou k něčemu dobré, opravdové využití ukazatelů je totiž trochu někdě jinde.
Pokud někomu, kdo programování rozumí, řeknete slovo ukazatele, vybaví se mu dynamicky alokované proměnné. Tyto proměnné se od klasických (statických) liší tím, že se vytvářejí až za běhu programu. Když je jich potřeba, tak se vytvoří a naopak – když zase není potřebná, tak se z paměti jednoduše odstraní. K takovýmto proměnným lze přistupovat pouze přes ukazatele.
Pro práci s ukazateli existují 4 procedury:
- New, GetMem – vytvoří dynamickou proměnnou
- Dispose, FreeMem – dynamicky alokovanou proměnnou zase zruší
Pro dokonalé pochopení této problematiky zde uvádím ukázku zrojového kódu i s komentáři.
type ukazatelnastring = ^string; //deklarujeme ukazatel
var
Form1: TForm1;
PNaString: ukazatelnastring; {deklarujeme proměnnou typu ukazatelnastring.
Všiměte si, že nikde není nadeklarována proměnná typu string! }
implementation
...
...
//nyní potřebujeme proměnnou string, tak si ji prostě vytvoříme
New(PNaString); //nyní se v paměti vyhradil prostor pro proměnnou string
PnaString^:="Delphi"; //nyní s touto nově vytvořenou proměnnou můžeme manipulovat jako s jakoukoliv jinou normální proměnnou typu string
…
...
Dispose(PNaString); //teď už proměnnou typu string nepotřebujeme tak ji zrušíme a pamět alokovaná pro naši proměnnou string se opět uvolní
Myslím, že nyní by již vše mělo být jasné. Ještě pro úplnost zde uvedu i ukázku použití zbylých dvou funkcí. Funkce GetMem
a FreeMem
nejsou tak „chytré“ jako ty předchozí, protože se jim musí říct, kolik místa v paměti musí alokovat, popř. uvolnit.
Jenže: co když zrušíme proměnnou typu string
a poté se pokusíme do ní (ačkoliv už neexistuje) přistoupit přes ukazatel? To se programu příliš líbit nebude a začne vyhazovat chyby. Tomu se ale dá jednoduše zabránit. Stačí do ukazatele přiřadit hodnotu NIL. To způsobí, že ukazatel přestane ukazovat někam do paměti a v případě, že se přes něj pokusíte přistoupit k oné již neexistující proměnné, nic se nestane.
Opět takové další nepsané pravidlo říká, že NIL se do ukazatele přiřazuje ihned po uvolnění paměti:
...
Dispose(PNaString);
PnaString:=NIL;
...
Toť k ukazatelům vše.
Pole
Je hezké, když můžu mít v proměnné uloženou hodnotu třeba 10. Ale co když potřebuju mít v jedné proměnné uložených víc proměnných? Na to existují tzv. pole.
Pole si lze představit asi jako tabulku, kde každý soupec a řádek mají své číslo (tzv. index). Index řádku a index sloupce udávají souřadnice buňky, ve které může být nějaká hodnota. Přes tyto indexy se pak k hondnotám přistupuje.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
0 | 0,0 | 1,0 | 2,0 | 3,0 | 4,0 | 5,0 | 6,0 | 7,0 |
1 | 0,1 | 1,1 | 2,1 | 3,1 | 4,1 | 5,1 | 6,1 | 7,1 |
2 | 0,2 | 1,2 | 2,2 | 3,2 | 4,2 | 5,2 | 6,2 | 7,2 |
Toto je ukázka dvojrozměrného pole.
Tučná čísla označují indexy sloupců a řádků pole, ona dvě čísla v buňkách označují souřadnice dané buňky (v klasickém pořadí X,Y). Je důležité si uvědomit, že Delphi standardně používá jako nejnižší index nulu, nikoliv jedničku (ale to se dá snadno změnit). Jednorozměrné pole má pouze osu X. Delphi samozřejemě umí pracovat i s troj-, čtyř-, pěti-, šesti- i vícerozměrnými poli, to si však lze jen velmi těžko představit (pokud vůbec lze), takže to je velmi nepřehledné, a je lepší zůstat u těch jedno-, dvou-, nanejvýš třírozměrných polí.
Pole mohou být všech možných typů, například pole typu integer
, typu string
apod., podle toho se také deklarují.
Zde je ukázka deklarace jednorozměrného pole typu integer
:
var pole1: array[0..10] of integer;
Toto nadeklaruje pole o 11 sloupcích (ano, opravdu jedenácti, nikoliv 10) s počátečním indexem 0 a horním 10.
Deklarace dvojrozměrného pole:
var pole2: array[1..10, 1..20] of string; //Pole2 tedy bude mít velikost 11×21 buněk.
Trojrozměrné pole:
var pole2: array[1..10, 1..20, 1..30] of string; //Pole2 tedy bude mít velikost 11×21×31 buněk.
Velikost se nemusí deklarovat pouze v rozsahu 0 až 10, ale třeba i 125 až 136.
S poli lze manipulovat jako s klasickými proměnnými:
var pole1: array [0..10] of integer; //deklarujeme dvě pole typu integer
pole2: array [0..10] of integer;
begin
pole1[5]:=10; //do 6. sloupce pole1 přiřadíme číslo 10
pole2[2]:=pole1[5]; //a nyní do 3. sloupce pole2 přiřadíme hodnotu 6. sloupce pole1 (tedy v našem případě číslo 10)
pole1:=pole2; //nyní budou obě pole shodná - přepíší se totiž všechny hodnoty
for i:=Low(pole1) to Hight(pole1) do
pole1[i]:=0; //takto se postupně vynulují všechny sloupce pole1
..
end;
Funkce Low
vrací nejnižší index pole a funkce Hight
nejvyšší. U pole o rozměru 0 až 10 vrátí funkce Low
nulu a funkce High
desítku. Pomocí funkce SizeOf
lze získat aktuální velikost pole (v bajtech).
To je k polím asi vše, nyní záznamy.
Záznamy
Dnes poslední, avšak velmi důležitou částí, jsou tzv. záznamy. Záznamy jsou objekty, které mohou obsahovat různé datové typy. Zde je příklad pro lepší pochopení:
zamestnanec1.jmeno:="Tomáš";
zamestnanec1.prijmeni:="Novák";
zamestnanec1.vek:=42;
...
zamestnanec2.jmeno:="Josef";
zamestnanec2.prijmeni:="Svoboda";
zamestnanec2.vek:=32;
...
...
Z tohoto příkladu je vám asi jasné, v čem spočívají záznamy. Nejlepší asi bude ukázat si vše na nějakém příkladu. Zůstaneme třeba u našich zaměstnanců:
type
zamestnanci = record //definujeme záznam zamestnanci
jmeno: string; //a jednotlivé dataové typy záznamu
prijmeni: string;
vek: integer;
bydliste: string;
oddeleni: string;
end;
...
var zamestnanec1: zamestnanci; //zde deklarujeme proměnné typu ZAMESTNANCI
zamestnanec2: zamestnanci;
zamestnanec3: zamestnanci;
...
zamestnanec1.jmeno:="Franta"; //přes ony nadeklarované proměnné přistupujeme k jednotlivým datovým typům záznamu
zamestnanec1.prijmeni:="Novák";
zamestnanec1.vek:=32;
zamestnanec1.bydliste:="Praha";
zamestnanec1.oddeleni:="Vývoj SW";
zamestnanec2.jmeno:="Josef";
zamestnanec2.prijmeni:="Svoboda";
zamestnanec2.vek:=25;
zamestnanec2.bydliste:="Teplice";
zamestnanec2.oddeleni:="Kontrola kvality";
zamestnanec3.jmeno:="Michal";
...
Výhoda záznamů je zřejmá již na první pohled – nejen že máme vše přehlednější (je to přecijen lepší, než mít zamestnanec1prijmeni:=xxx
, zamestnanec2prijmeni:=yyy
), ale navíc celý záznam stačí nadefinovat jen jednou a pak už stačí ho jen stačí nadeklarovat tolikrát, kolikrát ho potřebujeme a světe div se, ke všem hodnotám v záznamu můžeme přistupovat přes tečkovou notaci.
Úkol
Dnes opět budou úkoly jednoduché, jako minule.
- Zkuste pochopit a zapamatovat si, co jsou to proměnné a k čemu slouží.
- Zkuste pochopit a zapamatovat si, co jsou to pole a k čemu slouží.
- Zkuste pochopit a zapamatovat si, co jsou to datové typy a k čemu slouží :).
- No a protože jsme v seriálu o programování v Delphi, tak dostanete první úkol na vytvoření aplikace. Na formulář hoďte jedno tlačítko. Dvojklikem na to tlačítko se vám vytvoří procedura Button1Click, která se provede vždy, když na tlačítko kliknete. Činnost programu bude následující: pokaždé, když kliknete na tlačítko, se hodnota nějaké číselné proměnné zvětší o jedničku a zobrazí se jako popiska onoho tlačítka (pro změnu popisky použijte
Button1.Caption:=IntToStr(nazev_promenne);
). Dost jsem vám poradil, tak se ukažte :).
Závěrem
Tento díl byl opravdu vyčerpávající a pro dnešek vynecháme i program. Doufám, že se vám i tento díl líbil a že se mnou půjdete do dalšího, kde se už dostaneme k zajímavějším věcem a nakonec si i naprogramujeme jednoduchou aplikaci.