Dne toho bude trochu víc, takže snad vás následující přehled neodradí - je to jenom opakování a utřídění dosavadních poznatků:
Veškerá data jsou v počítači uložena jako směs nul a jedniček. Ukládáme-li tedy do proměnné nějaká data, musíme vědět o jaký typ dat jde. To definujeme v sekci var na začátku programu. Nestačí-li nám klasické typy proměnných, můžeme si v sekci type vytvořit vlastní (tyto typy chceme používat už v sekci var, type tedy musí být uveden dříve). Proměnné alokované v sekci var jsou statické, nemůžeme je rušit ani vytvářet za běhu programu. K tomu slouží jiný typ proměnných - proměnné dynamické, s nimž se pracujeme pomocí ukazatelů a speciálních funkcí.
Tyto typy dovolují nést dané proměnné jednu jedinou informaci. Jedná se o důležité složky dalších (strukturovaných či objektových) typů a jako jediné jdou přímo vypsat příkazem write či writeln (až na výjimky). Pro větší přehlednost se dále dělí na :
U ordinálních typů má každý možná hodnota své pořadové číslo. Jsou to tedy takové typy proměnných, kde se dá u každé hodnoty proměnné definovat hodnota předchozí i následující. (Nejde-li o první resp. poslední možnou hodnotu daného typu).
Pro ordinální typy jsou definovány následující funkce:
Pořadí | Příkaz | Význam |
---|---|---|
1 | Ord(x):Longint; | převede hodnotu dané proměnné x na její pořadové číslo |
2 | Pred(x):ordinal; | vrací předchůdce hodnoty proměnné ( Pred(2) = 1) |
3 | Succ(x):ordinal; | vrací následovníka hodnoty proměnné (Succ(8) = 9) |
4 | Low(x):ordinal; | nejnižší možná hodnota, kterou proměnná může nabýt |
5 | High(x):ordinal; | nejvyšší možná hodnota, kterou proměnná může nabýt |
Pro větší přehlednost se dále ordinální typy dále dělí na :
Jak je patrné z názvu, mohou nabývat celočíselných hodnot. Můžeme si je rozdělit na typy pouze kladné a typy i záporné.
Jedná se o tyto typy:
Typ | Rozsah | Nároky na paměť |
---|---|---|
byte | 0..255 | 1 byte |
word | 0.65535 | 2 byty |
shortint | -128..127 | 1 byte |
integer | -32 768..32 767 | 2 byty |
longint | -2 147 483 648 .. 2 147 483 647 |
4 byty |
Šetřete pamětí, ale s mírou, hodnota proměnné vám nikdy nesmí přetéct a
uvědomte si také, že x-bitový procesor nejrychleji zpracovává proměnné o x
bitech. 32-bitová mašinka tedy nejrychleji počítá s longintem.
Pozn. Ačkoliv typ comp může nabývat jen celočíselných hodnot, patří mezi typy
reálné, neboť nemá definovaného předchůdce ani následníka.
Funkce a procedury celočíselných typů (ordinální f-ce nejsou uváděny znova):
Pořadí | Příkaz | Význam |
---|---|---|
1 | Inc(X); | Zvýší hodnotu proměnné o jedničku. |
2 | Dec(X); | Sníží hodnotu proměnné o jedničku. |
3 | Inc(X,N); | Zvýší hodnotu proměnné o N. |
4 | Dec(X,N); | Sníží hodnotu proměnné o N. |
5 | Odd(X):Boolean | Vrací, zda je v proměnné liché číslo. |
6 | Abs(X):PůvodníTyp | Absolutní hodnota daného čísla (typu shortint, integer, nebo longint) |
Speciální funkce pro Integer a Word (uvědomte si, jak jsou čísla reprezentována):
Pořadí | Příkaz | Význam |
---|---|---|
1 | Lo(x:Word či Integer):Byte | funkce vrací nižší byte argumentu |
2 | Hi(x:Word či Integer):Byte | funkce vrací vyšší byte argumentu |
3 | Swap(x : Word či Integer):Word či Integer; | Funkce prohodí horní a dolní byte argumentu. |
Pozn. Procedury Inc a Dec jsou o něco rychlejší než tradiční přiřazení. Na druhou stranu jejich použití snižuje čitelnost programu.
Tento typ uchovává jeden znak (jedno písmeno). Zabírá 1 byte.
Funkce pro typ char:
Pořadí | Příkaz | Význam |
---|---|---|
1 | Chr(N:Byte):Char | Funkce najde N-tý znak v ASCII tabulce. |
2 | Upcase(C:Char):Char | Je-li v C malé písmeno, převede jej na velké. |
Tyto typy slouží k uchování logických hodnot. Mohou obsahovat pouze dvě hodnoty : True a False. V rámci kompatibility existuje několik Booleovských typů. Je definováno, že false < true. I booleovské typy lze tedy mezi sebou porovnávat.
Název typu | Nároky na paměť |
---|---|
Boolean | 1 byte |
ByteBool | 1 byte |
WordBool | 2 byty |
LongBool | 4 byty |
Šetřete pamětí, ale znova si uvědomte, že x-bitový procesor zpracovává nejrychleji x-bitové proměnné.
Nejjednodušší typ, který si může uživatel vytvořit. Proměnná
tohoto typu může nabývat jen hodnot uvedených uživatelem. Tento typ slouží
především k zpřehlednění programu, protože BarvaAuta:=baZluta je rozhodně
čitelnější než BarvaAuta:=14;
Typ se definuje velice jednoduše : type NazevDefinovanehoTypu =
(Polozka0, Polozka1, Polozka2); (Všimněte si, že první prvek má index nula -
Ord(Polozka0)=0 ).
Vzhledem k tomu, že názvy položek se nesmí opakovat (ani u jiných typů), je
kvůli přehlednosti dobrým zvykem začínat názvy položek zkratkou názvu daného
typu psanou malými písmeny:
type BarvaOci=(boModra,boZelena,boHneda,boCerna); BarvaAuta=(baCervena,baModra,baHneda,baCerna,baStribrna,baJina);
Bohužel se tento typ nedá přímo vypsat pomocí Write ani Writeln (Samozřejmě s pomocí if a case lze toto omezení obejít).
Potřebujeme-li omezit (nejčastěji kvůli kontrole vstupních dat)
rozsah možných hodnot nějaké proměnné, využijeme interval. Proměnná pak může
nabývat jen hodnot mezi udanými mezemi. Typ interval lze vytvořit z libovolného
jednoduchého typu , který již byl definován (tedy i z typu výčtového či z jiného
intervalu).
Definuje se také velice jednoduše : type NazevDefinovanehoIntervalu =
DolniMez .. HorniMez;
type HezkaBarvaOci = boModra..boZelena; Cislice = 0..9;
Obsah proměnné typu Interval lze přímo vypsat pomocí Write a Writeln, nejedná-li se o Interval z Výčtového typu.
Jedná se o další z jednoduchých typů. Tyto typy slouží k uchování reálných čísel. Proměnná může nabývat tolika možných hodnot, že nemá smysl definovat předchůdce a následníka. (Co následuje po 2.511? 2.512 nebo 2.5110001 ???). Malou výjimkou je typ comp, který sice může nabývat pouze celočíselných hodnot, ovšem v tak značném rozsahu, že u něj předchůdce a následník také nebyl zaveden. Je definována celá řada reálných (spojitých) typů :
Typ | Rozsah | Přesnost | Nároky na paměť |
---|---|---|---|
single | 1.5*10-45 až 3.4*1038 | 7 platných číslic | 4byty |
real | 2.9*10-39 až 1.7*1038 | 11 | 6bytů |
double | 5.0*10-324 až 1.7*10308 | 15 | 8bytů |
extended | 3.4*10-4932 až 1.1 * 104932 | 19 | 10bytů |
comp | -263+1 až 263-1 | 19 | 8bytů |
U reálných typů se jako dolní mez neuvádí nejmenší možné číslo, ale minimální
rozdíl dvou čísel, kdy je lze ještě odlišit. Výjimku tvoří typ comp.
Pokud chcete používat i jiné reálné typy než Real, musíme zaškrtnout v Options
/ Compiler tyto dvě políčka : 8087/80287 a Emulation. Jinak vám překladač oznámí
chybu.
Funkce pro reálné typy:
X zde znamená libovolný číselný typ.
Pokud pracujeme z typem Extended, jsou výsledky pochopitelně typu Extended.
(Pozor : Výsledkem Int(x) je reálný (tedy neceločíselný) typ).
Pořadí | Příkaz | Význam |
---|---|---|
1 | Sin(x):Real | Sin(x) |
2 | Cos(x):Real | Cos(x) |
3 | ArcTan(x):Real | Tan-1(x) |
4 | Pi:Real | π |
5 | Sqr(X):Real | x2 |
6 | Sqrt(X):Real | √x |
7 | Exp(X):Real | ex |
8 | Ln(X):Real | ln(x) |
9 | Frac(X):Real | desetinná část X |
10 | Int(X):Real | celá část X |
11 | Abs(X):Real | absolutní hodnota daného čísla (v případě, že je X celočíselný typ, je výsledek celočíselný!!) |
Jak již název napovídá, tyto proměnné uchovávají text. Mohou obsahovat více písmen, a tak u nich nemůže být zaveden předchůdce a následovník (co následuje po abeceda? abecedb? nebo spíš abecedaa?), nejedná se tedy o typy ordinální, i když ordinální char se sem občas formálně řadí.
Podle způsobu použití nabízí Pascal několik typů řetězců (znakových typů):
Znakový typ | výhody | nevýhody |
---|---|---|
char | může obsahovat jakékoliv znaky z ASCII, velice snadná manipulace, zabírá pouhý 1 byte | je povoleno jen použít 256 znaků (ASCII 0-ASCII 255), najednou uchová jen jeden znak.. |
string | může obsahovat jakékoliv znaky, snadná manipulace, rychlé zjištění délky řetězce | maximální délka pouze 255 znaků, velké nároky na paměť (256 bytů) |
string[N] | stejné jako u stringu | maximální délka N znaků, ale zabírá pouze N+1 bytů v paměti |
OpenString | stejně jako string. Tento typ je povolen pouze jako parametr funkce či procedury a umožňuje nedodržovat jednotnou délku řetězce. | maximálně 255 znaků, ztížená manipulace |
PChar | neomezená délka, zabírá v paměti jen místo, které skutečně obsazuje | Nemůže obsahovat znak #0, poměrně špatně se zjišťuje jeho délka, velice špatně se s ním manipuluje. Jeho použití vyžaduje použití programové jednotky Strings |
Procedury a funkce pro práci s řetězci (z jednotky System):
Pořadí | Příkaz | Význam |
---|---|---|
1 | Concat(a,b,c,...):string | spojí řetězce do jediného (lepší než x:=a+b+c+...); Pochopitelně lze zadat i samotný řetězec (Write(Concat('a'));) |
2 | Copy(s,od,pocet):string | vrací podřetězec řetězce s, začíná přitom od od a vyplivne pocet znaků |
3 | Delete(s,od,pocet); | Procedura vymaže z řetězce s pocet znaků (začne od od) |
4 | Insert(v,r,kam); | Procedura vloží na místo kam do řetězce r řetězec v. |
5 | Pos(subs,s):byte; | Najde, kde se v řetězci s nachází podřetězec subs; nenajde-li zadaný řetězec, vrací nulu |
6 | Length(s):integer | vrací skutečnou délku řetězce |
7 | Str(x,s); | Převede číslo x na řetězec s |
8 | Val(s,var v,c); | Převede řetězec s na číslo v, pokud se vyskytne chyba, je číslo prvního chybného znaku uloženo v proměnné c; |
Procedury a funkce pro práci s řetězci typu PChar (z jednotky Strings):
Pořadí | Příkaz | Význam |
---|---|---|
1 | StrCopy(Dest,Source:PChar) :PChar; | Zkopíruje Source do Dest (a vrací Dest) |
2 | StrPCopy(Dest:PChar, Source : string) :PChar; | Zkopíruje Source do Dest (a vrací Dest) |
3 | StrLCopy(Dest,Source:PChar,L) :PChar; | Zkopíruje Source do Dest (kopíruje maximálně L znaků) |
4 | StrCat(Dest,Source:PChar) :PChar; | Připojí Source k Dest |
5 | StrLCat(Dest,Source:PChar,L) :PChar; | Připojí Source k Dest, ale připojí max. L znaků |
6 | StrLen(S:PChar):Word; | Udává počet znaků v daném řetězci |
7 | StrLower(S:PChar):PChar; | Mění všechna velká písmena na malá. |
8 | StrUpper(S:PChar):PChar; | Mění všechna malá písmena na velká. |
9 | StrPas(S:PChar) : string; | Mění ASCIIZ řetězec na obyčejný string (maximálně prvních 255 znaků) |
10 | StrComp(s1,s2:PChar) : Integer; | Porovná dva řetězce. Výsledek je 0, jsou-li stejné. Záporné číslo, je-li s1 < s2 a číslo kladné znamená, že s1 > s2 |
11 | StrIComp(s1,s2:PChar) : Integer; | Pracuje stejně, ale nerozlišuje velká a malá písmena |
12 | StrLComp(s1,s2:PChar,L) : Integer; | Pracuje stejně jako StrComp, ale porovná pouze prvních L znaků. |
13 | StrLIComp(s1,s2:PChar,L) : Integer; | Stejně jako StrLComp porovná první L znaků dvou řetězců. Nerozlišuje velká a malá písmena. |
14 | StrECopy(Dest,Source:PChar):PChar; | Zkopíruje Source do Dest, ukazatel (typu PChar) nastaví na konec řetězce |
15 | StrEnd(s1:PChar):PChar; | ukazatel (typu PChar) na konec řetězce |
16 | StrMove(Dest,Source:PChar,Count:Word):PChar; | Zkopíruje Count znaků ze Source do Dest a vrací Dest |
17 | StrPos(s1,s2:PChar):PChar; | vrací ukazatel (typu PChar) na první výskyt s2 v řetězci s1. V případě, že se s2 v s1 nevyskytuje vrací nil |
18 | StrScan(s:PChar,ch:Char):PChar; | vrací ukazatel (typu PChar) na první výskyt znaku ch v řetězci s popř. nil. (Ukončovací znak je součástí s) |
19 | StrRScan(s:PChar,ch:Char):PChar; | vrací ukazatel (typu PChar) na poslední výskyt znaku ch v řetězci s popř. nil. |
20 | StrNew(s:PChar):PChar; | Zkopíruje s na nové místo v paměti a vrátí na něj ukazatel (v případě, že je s = nil, nedělá nic) |
21 | StrDispose(s:PChar); | Odstraní z paměti řetězec vytvořený pomocí StrNew; |
Tyto proměnné v sobě uchovávají odkaz na místo v paměti, kde
může být uložena nějaká proměnná. Jedná se o základní prvek všech dynamických
datových struktur.
Existují dva typy ukazatelů : Ukazatele na udaný typ a pointry, které mohou
ukazovat na cokoliv.
Ukazatel na uvedený typ se zavádí jednoduše :
type PInteger = ^Integer; (Ukazatel na Integer)
PBarvaAuta=^BarvaAuta;
Ukazatel bez udaného typu se definuje
ještě jednodušeji, poměrně špatně se s ním ale pracuje. Téměř vždy vyžaduje
type-casting.
type PNaCokoliv = pointer;
P = pointer;
Chceme-li, aby daný ukazatel ukazoval na nějakou proměnnou,
použijeme unární operátor @ : P:=@Promenna;
(Stejně tak je možné použít funkci Addr).
Chceme-li mít přístup k proměnné, na kterou ukazatel ukazuje, použijeme
^ : Writeln(P^), P^:=10;
Ukazatele umožňují alokovat dynamické proměnné
do paměti. Místu, které je vyhrazeno pro uložení dynamických proměnných
se říká Hromada (heap).
K samotné práci s dynamickými proměnnými pak slouží tyto procedury a funkce:
Pořadí | Příkaz | Význam |
---|---|---|
1 | New(P:Ukazatel); | procedura vytvoří novou dynamickou proměnnou a nastaví na ni ukazatel |
2 | Dispose(P:Ukazatel); | procedura zruší dynamickou proměnnou a ukazatel nastaví na nil. |
3 | GetMem(P:Ukazatel,Velikost:Word); | vytvoří dynamickou proměnnou o velikosti Velikost bytů a nastaví na ni ukazatel (pokud je v paměti dostatek místa) |
4 | FreeMem(P:Ukazatel,Velikost:Word); | zruší proměnnou vytvořenou pomocí GetMem |
5 | SizeOf(NázevProměnnéČiTypu):Word; | vrací kolik bytů zabírá daný typ (proměnná) v paměti |
6 | MaxAvail : Longint; | největší možný počet bytů, které lze najednou dynamicky alokovat (délka nejdelšího spojitého úseku volné paměti v hromadě) |
7 | MemAvail: Longint; | celkové volné místo v hromadě |
8 | Mark(P:Ukazatel); | uloží stav hromady (volné paměti) do ukazatele (nesmí být použito s GetMem a FreeMem) |
9 | Release(P:Ukazatel); | Obnoví stav hromady podle P (kam byl uložen pomocí Mark...) |
Raději si příkazy Mark a Release ukažme na příkladu:
var p : pointer; p1,p2,p3 : ^Integer; begin New(p1); { Alokuje dynamický Integer } Mark(p); { Uloží stav hromady } New(p2); { Alokujeme dva další Integery } New(p3); Release(p); { Paměť rezervovaná pro p2^ a p3^ byla uvolněna ale p1^ může být použita } end.
Pochopitelně občas potřebujeme nějakým způsobem změnit typ proměnné :
Pořadí | Příkaz | Význam |
---|---|---|
1 | Trunc(X:Real) : Longint; | Převede reálné číslo na celé (tím, že "usekne" vše za desetinnou čárkou) |
2 | Round(X:Real) : Longint; | Převede reálné číslo na celé (tím, že ho zaokrouhlí) |
3 | Chr(x) : char | vrátí x-tý znak v ASCII tabulce |
4 | Ord(x) : longint | vrátí o kolikátý znak daného typu se jedná (počítáno od nuly) |
5 | UpCase(x):char | převede písmeno na velké |
6 | Round(x):Longint | zaokrouhlí reálné číslo |
7 | Trunc(x):Longint | celá část reálného čísla |
8 | Str(x:Cislo,s:string); | procedura převede reálné číslo x na řetězec znaků s. (lze zde použít notace x : PočetMíst, x:PočetMíst:PočetMístZaDesetinnou) |
9 | Val(s:string,var v:Cislo,c:Integer); | Procedura převede řetězec s na reálné číslo v (=proměnná), v případě chyby vrátí v c u kolikátého znaku došlo k chybě |
Občas je vhodné pracovat s proměnnou, jako by se jednalo o jiný datový typ. Právě k tomu slouží Type-casting. Jedná se o funkci (nesmí tedy stát na levé straně přiřazovacího příkazu) a vypadá takto : ChtěnýTypProměnné(ProměnnáJinéhoTypu); Použití je jednoduché (umožňuje např. pomocí bytových operací pracovat i s ukazateli atd...)
Strukturované typy zavádí určité uspořádání mezi typy jednoduché. Skládají se z několika jednoduchých typů, mezi kterými pevně stanoví jednotlivé vazby. Jako takové velice rozšiřují programátorské možnosti. Společně s ukazateli se pak využívají při tvorbě dynamických datových struktur.
Jedná se o jeden z nejjednodušších strukturovaných typů. Daný
prvek do množiny buď patří, nebo nepatří. Všechny prvky v množině musejí být
stejného tzv. bázového typu (Bázovým typem se může stát libovolný
ordinální typ, který má maximálně 256 hodnot - tedy ShortInt, Byte, Interval,
nebo VýčtovýTyp)
set of BázovýTyp; (Tedy var A:set of Byte;
type MnozinaBarev = set of (Zluta,Cervena,Zelena,Bila,Cerna); )
Prázdná množina se značí [
] a je kompatibilní se všemi množinami (lze ji
přiřadit libovolné množině).
Množinu lze zapsat pomocí výčtu prvků BarvyAutNaParkovisti:=[Zluta,Bila,Pokalena];
Klasické operátory mají u množin poněkud posunutý význam :
Operace | Operátor | Výsledek |
---|---|---|
sjednocení | + | množina |
průnik | * | množina |
rozdíl | - | množina |
srovnání (jsou shodné?) | = | boolean |
srovnání (jsou rozdílné?) | <> | boolean |
je A podmnožinou B? | <= | boolean |
je B podmnožinou A? | >= | boolean |
Pomocí in zjistíme, zda je Prvek prvkem zadané Množiny.
if 'a' in MnozinaPismen then writeln('V dane mnozine se
vyskytuje pismeno a');
Pro množiny jsou dále definovány tyto procedury :
Pořadí | Příkaz | Význam |
---|---|---|
1 | Include(S:Množina,I:BázovýTyp); | Zařadí prvek I do množiny S; (není-li již přítomen) |
2 | Exclude(S:Množina,I:BázovýTyp); | Vyřadí prvek I z množiny S; (je-li tam) |
Tyto procedury jsou o něco výhodnější než pouhé S:=S±[I].
V poli jsou prvky daného elementárního typu uspořádány. K
uspořádání slouží ordinální indexy. Jednoduché pole se definuje takto:
array[OrdinarniTyp]
of ElementarniTyp; Ordinální typ udává, jakých hodnot mohou nabývat
index pole (vzhledem k paměti nesmí být příliš široký, nejčastěji se tedy
používá typ Interval). Elementárním Typem může být jakýkoliv typ. (Tedy např. var
TeplotaVzduchuVCervnu=array[1..30] of Real; type
RadkaSachovnice=array['a'..'h'] of Figury);
Pochopitelně není problém definovat pole polí či pole polí polí...Při
definici takovýchto (vícerozměrných polí) je však lepší využít zkrácený zápis :
array[OrdinálníTyp1,OrdinálníTyp2,...] of Typ;
(OrdinálníTyp1 a OrdinálníTyp2 mohou být stejné typy)
Např: type Sachovnice=array['a'..'h','1'..'8'] of TFigurka;
V parametrech procedur a funkcí se může vyskytnout pole bez udaného rozměru... (function Neco(Pole:array of Integer):Boolean;). Rozměr takovéhoto pole se pak zjistí pomocí funkce SizeOf. Takovýto styl zápisu umožňuje použít jednu funkci pro různě dlouhá pole)
Tento typ shromažďuje informace, které spolu nějak souvisí. Umožňuje tedy do jedné proměnné shrnout více informací. Konkrétně se definuje takto:
record Polozka1: TypPolozky1; (Popř. i více položek, jako u var) Polozka2: TypPolozky2; (Položka může být i další záznam, jenom to nesmí být soubor) ... end;
K jednotlivým položkám záznamu můžeme přistupovat pomocí
NázevZáznamu.NázevPoložky; (tedy pomocí tečky). Neustálé vypisování názvu
záznamu nás občas zdržuje (např. je-li položek třicet), ovšem můžeme se mu
vyhnout s pomocí with NázevZáznamu1,NázevZáznamu2,.. do Příkaz
(překladač sám vybere, ke kterému záznamu naše položka patří - překrývá-li se
více jmen, použije poslední uvedené). Samozřejmě, aby bylo použití with
efektivní, musíme použít příkaz složený:
with Memory, Songs do
begin
{Tady nemusíme opisovat Memory. a Songs. pořád dokola}
end;
Pochopitelně nám nic nebrání použít klasické tečky i uvnitř with... a vyhnout se tak některým obtížím.
Nejčastěji se typu rekord využívá při práci s dynamickými
proměnnými. Každý rekord v sobě uchová ukazatel na další (popř. předchozí)
proměnnou. Dynamické proměnné jsou tak jednoduše zpřístupněny (známe-li ukazatel
na první (popř. poslední) z nich).
Některé typy záznamů (což se týká především různých jednotek) se podle typu
použití můžou chovat různě. To lze v definici zařídit pomocí case.
Tedy např. takto:
record
Polozky:AJejichTypy; case NejakeJmeno:OrdinalniTyp; Moznost1 : (APolozka1:TypPolozky1; APolozka2:TypPolozky2) Moznost2 : (BPolozka1....); end;
Zapíšeme-li nyní něco do APoložky, bude se záznam chovat, jako by Moznost2 (a další uvedené možnosti) neexistovaly. Naopak, přistoupíme-li do BPolozky, přestanou existovat ostatní možnosti. Raději na příkladu :
type IT = (NemaPocitacAniMobil,MaPocitac,MaMobil,MaOboji); Kontakt = record Jmeno:string[25]; case Pocitac: IT of NemaPocitacAniMobil : (Ulice : string[50]; CisloPopisne : Word; Mesto : string[30]; PSC : Longint); MaPocitac : (Email: string[30]); MaMobil : (CisloMobilu : string[13]); MaOboji : (OEmail: string[30]; {OMail, protože názvy se nemohou překrývat} OCisloMobilu : string[13]);
end; var Osoba:Kontakt;
Zkuste si teď do takto vytvořeného
záznamu ukládat různá data a vypisovat je pomocí writeln (Osoba.OEmail:='Ja',
Osoba.Email:='nic', writeln(Osoba.OEmail)...)
Přestáváte se orientovat? Zdá se vám to zmatené? Tak proč to čtete, když to
stejně máte dávno znát?
Soubor slouží především k práci s externími soubory. Podle typu použití rozlišuje Pascal několik typů souborů. Soubor obsahuje uspořádané položky (nejprve máme přístup k první, pak k druhé,... až nakonec k poslední).
Existuje několik typů souborů. Nejprve se podíváme na soubor s
udaným typem. Ten se definuje takto:
file of DanyTyp;
Pro práci s textem má Pascal zvláštní typ souboru, který se
definuje jako Text;
Pokud neznáme dopředu typ dat (či pokud se typ dat položku od položky mění),
musíme použít Text či soubor bez udaného typu (se kterým se ovšem poněkud špatně
pracuje), tento soubor se pak definuje takto: file;
Soubory můžeme definovat například takto:
var fText : Text; fOut,fIn : file of Integer;
fZmatek : file;
Je dobrým zvykem začínat pojmenování proměnné typu soubor písmenem f (zvyšuje to přehlednost).
K dané proměnné typu soubor musíme nejprve přiřadit skutečný externí soubor (pomocí Assign). Poté musíme oznámit, co s daným souborem chceme dělat (Reset, Rewrite, Append) a ukončíme-li práci s daným souborem nesmíme ho zapomenou zavřít (Close);
Procedury a funkce pro práci se soubory (je-li něco v hranaté závorce, nemusí to být uvedeno):
Pořadí | Příkaz | Význam |
---|---|---|
1 | Assign(f:Soubor,DosovskyNazevExternihoSouboru); | Přiřadí proměnné soubor. Prázdný řetězec--> přiřadí soubor Output či Input (Obrazovku či klávesnici) |
2 | Reset(f:Soubor); | Otevře soubor pro čtení (nastaví pomyslný ukazatel na první položku souboru) |
3 | Rewrite(f:Soubor); | Otevře soubor pro zápis (smaže celý soubor a nastaví ukazatel na začátek souboru) |
4 | Append(f:Text); | Otevře textový soubor pro přidání (nastaví ukazatel na konec existujícího textového souboru) |
5 | Close(f:Soubor); | Zavře soubor (a případně také uloží veškeré změny v souboru na disk) |
6 | Eof(f:soubor) : Boolean; | Funkce nabývá hodnoty true, když je dosaženo konce souboru |
7 | Eof : Boolean; | takto použita udává, zda je dosaženo konce souboru Input (data z klávesnice) |
8 | SeekEof(f:soubor) : Boolean; | Jako Eof, přeskakuje však mezery, konce řádků a podobné znaky (lze použít v textovém souboru) |
9 | Eoln(f:Text):Boolean; | Udává, zda je v textovém souboru dosaženo konce řádky (znaky CR a LF) |
10 | SeekEoln(f:Text):Boolean; | Funguje stejně jako Eoln, přeskočí však všechny mezery a tabelátory |
11 | Flush(f:text); | Zapíše dosud nezapsané znaky z Bufferu na disk (normálně se zapisují až při zavolání Close, či je-li v bufferu víc než 128 dat) |
12 | SetTextBuffer(var f:text, var buff [;size: word]); | Změní velikost textového bufferu (zvětšení např. sníží počet přístupů na disk) |
13 | Write([f:soubor],v1,v2,...); | Vypíše do souboru f (či na obrazovku, není-li uveden), hodnoty v1, v2, které jsou typu char, celočíselného, reálného, Boolean nebo string. Délku výpisu hodnoty lze omezit pomocí v1:PocetMist, u reálných typů ještě takto r:PocetMist:PocetDesetinnychMist; |
14 | Writeln[([f:soubor],v1,v2,...)]; | Pracuje stejně jako Write, ale zapíše ještě konec řádky, je-li volána bez parametrů, zapíše pouze konec řádky |
15 | BlockRead(var f:file;var buf;count:word,[;result:word]); | čte hodnoty ze souboru bez udaného typu (přečte Count záznamů a uloží je do buf - což je proměnná libovolného typu, result vrací počet skutečně přečtených záznamů) |
16 | BlockWrite(var f:file;var buf;count:word,[;result:word]); | Procedura zapíše jeden nebo více záznamů do souboru s neudaným typem. Pracuje podobně jako BlockRead. |
17 | FilePos(f:Soubor) : longint; | Vrací současnou pozici v souboru (na začátku nula, na konci FileSize(f);). Nelze použít na soubor typu Text; |
18 | FileSize(f:Soubor) : longint; | Vrací celkový počet komponent v souboru (nesmí být použita na soubor typu Text) |
19 | Seek(f:Soubor, n : longint); | Nastaví pozici v souboru na n (nesmí být použita na soubor typu Text), číslo první komponenty je 0, poslední FileSize-1 |
20 | Truncate(f:Soubor); | Vymaže všechny záznamy od aktuální pozice až do konce. Po jejím provedení nabývá Eof hodnoty true. |
Objektové typy mohou kromě dat obsahovat i vlastní procedury a funkce, které s těmito daty mohou nakládat. Ohromně zjednodušují programátorovi život, obzvláště jsou-li předdefinovány (např. typ Windows, který má vlastní proceduru CreateWindows, která vytvoří Windowsácké okno se vším všudy...).
Tento typ umožňuje proceduře, aby se chovala jako proměnná a například vystupovala jako parametr jiné procedury či funkce.
Deklarace je jednoduchá :
type Nazev = procedure;
Nazev2 = procedure(var x:string);
Nazev3=function(I:Integer):real;
Tyto definice jsou však poněkud specifické a podléhají určitým omezením:
Procedurální proměnné mohou být součástí složitějších struktur (záznam, pole).
Výše uvedená pravidla se pochopitelně obcházejí. Pokud z nějakého důvodu
potřebujeme, aby daná funkce vrátila procedurální typ, uděláme to tak, že daná
funkce vrátí ukazatel na danou proceduru a my tento ukazatel posléze
přetypujeme.
Pokud naopak potřebujeme procedurální proměnné přiřadit standardní proceduru či
funkci, "zabalíme" tuto standardní funkci do jiné. Vše raději na konkrétním
příkladě :
type XProc = FUNCTION(X:Real):Real; var Operace:XProc; function Sinus(X:Real):Real; begin Sinus:=Sin(X); end; function Cosinus(X:Real):Real; begin Cosinus:=Cos(X); end; function VratProceduru(c:char):pointer; begin if c='a' then VratProceduru:=Addr(Sinus) else VratProceduru:=Addr(Cosinus); end;
begin Operace=XProc(VratProceduru('a')^); Writeln(Operace(Pi)); end.
Objekt je takový hodně vylepšený záznam. Obsahuje vlastní metody (funkce a procedury, které jako jediné smí pracovat s proměnnými daného objektu) a může dědit metody a proměnné od jiného objektu. Můžeme ho definovat např. takto:
type TNeco=object Jmeno : string; Data : TData; constructor Init(ZJmeno:string); procedure ZmenJmeno(ZJmeno:string); function PridejData(Data:TData):string; destructor Done; end;
Všechny uvedené metody musíme pochopitelně dále specifikovat, musíme zavést dané procedury, funkce, constructory a destructory :
constructor TNeco.Init; begin ... end; procedure TNeco.ZmenJmeno; begin ... end; function TNeco.PridejData; begin ... PridejData:=NejakaHodnota; {vystupni hodnota f-ce} end; destructor TNeco.Done; begin ... end;
Podívejme se nyní na constructor a destructor. Jedná se vlastně o procedury. Každý objekt smí mít jenom jeden constructor a jeden destructor. Aby se jejich názvy nepletly s ostatními metodami daného objektu, pojmenovává se constructor většinou Init a destructor pak Done. Úkolem constructoru je daný objekt inicializovat (vyhradit mu paměť, zobrazit ho,...). Destructor se pak stará o zrušení objektu (smazat z obrazovky, uvolnit zabranou paměť,...).
Takto jsme definovali svojí vlastní třídu. Nyní můžeme vytvořit proměnnou
této třídy (tzv. instanci třídy), např. pomocí var Neco:TNeco;
Při programování bychom měli proměnné dané instance měnit pouze pomocí jeho
metod. (Místo Neco.Jmeno:='nic' tedy raději Neco.ZmenJmeno('nic');)
Potřebujeme-li rozlišit, ke kterým proměnným má být povolen přístup "zvenčí" a ke kterým má být přístup dovolen jen v daném modulu (tedy programu, či unitě), použijeme slova public a private. Zadefinujeme-li něco v sekci public, je to přístupné i z jiných unit. Naproti tomu private se chová, jako by neexistovalo vně našeho modulu.
Raději na konkrétním příkladě:
type TNeco=object private Jmeno : string; public Data : TData; constructor Init(ZJmeno:string); procedure ZmenJmeno(ZJmeno:string); function PridejData(Data:TData):string; destructor Done; end;
Další užitečnou vlastností objektů a tříd je dědičnost, daný objekt (potomek) zdědí veškeré vlastnosti (metody a proměnné) od jiného objektu (předka). Např. potomka objektu TNeco definujeme takto:
type TPracovnik=object(TNeco) Plat : Longint; end; var Neco:TNeco; Lada:TPracovnik;{Vytvoříme instanci třídy TPracovnik}
Takto nadefinovaný objekt TPracovnik, zdědil vše od třídy TNeco, má však navíc ještě proměnnou Plat. Velkou výhodou je, že potomka lze bez problémů přiřadit svému rodiči. Nyní lze tedy napsat např. Neco:=Lada;
Jelikož chceme, aby constructor objektu TPracovník dělal trochu více, než constructor objektu TNeco, napíšeme si constructor pro třídu TPracovnik. (Musíme to ale oznámit již při definici TPracovnik) :
type TPracovnik=object(TNeco); Plat:Longint; constructor Init; end;
constructor TPracovnik.Init; begin inherited Init; {Nejprve zavoláme pomocí klíčového slova inherited zděděný constructor předka - je zbytečné vše dělat znova, když to jiný udělá za nás, že...} Plat:=10000; end;
Všimněte si, že náš nový constructor překryl ten starý. Metodám, které
překryjí stejnou metodu v předkovi říkáme metody statické.
Je vám to vše jasné? Raději si to procvičte na několika vlastních příkladech (a
ať i původní Init něco dělá...).Vyzkoušejte také rozvětvenější hierarchii
objektů (objekt bude mít více potomků, potomci budou sami předky dalších
objektů,...) a zkoušejte vypisovat různé proměnné daných objektů!!!
Podívejme se nyní na trošku objektovější příklad:
type TClovek = object Jmeno : string; Vek : Integer; constructor Init(CJmeno:string;CVek:Integer); procedure VypisUdaje;virtual; end; TPracovnik = object(TClovek) Plat : Integer; constructor Init(CJmeno:string;CVek,CPlat:Integer); procedure VypisUdaje;virtual; end; constructor TClovek.Init; begin Jmeno:=CJmeno; self.Vek:=CVek; {Každý objekt lze v jeho vlastních metodách zpřístupnit pomocí self, nehrozí-li záměna, lze self vynechat} end; procedure TClovek.VypisUdaje; begin Writeln('Jmeno : ', Jmeno:20,'Vek : ',Vek:10); end; constructor TPracovnik.Init; begin inherited Init; {Nejprve zavoláme constructor předka} Plat:=CPlat; end;
procedure TPracovnik.VypisUdaje; begin Writeln('Jmeno : ', Jmeno:20,'Vek : ',Vek:10,'Plat : ',Plat:20); end;
Všimněte si virtuálně nadefinovaných metod: teprve při jejich volání se zjistí, ke kterému objektu se daná metoda vztahuje. (Nyní lze např. nadefinovat proměnnou typu TClovek a přiřadit jí jejího potomka typu TPracovnik (vzhledem ke kompatibilitě), zavoláme-li nyní na danou proměnnou VypisUdaje, použije se procedura z objektu TPracovnik). U virtuálních metod se totiž uchovávají ukazatele na obě procedury a podle použití se rozhodne, která se zavolá.
Podobně se chovají metody dynamické. Na rozdíl od metod virtuálních ale při rozsáhlejší objektové hierarchii šetří čas. definují se takto:
object ... procedure Nazev(...);virtual Vyraz; end;
Vyraz je libovolný výraz, který je typu Integer a lze vyhodnotit již při překladu, výsledné číslo je tzv. index dynamické metody. Dynamické a virtuální mohou být všechny metody kromě constructoru (tedy funkce, procedury i destructory).
V obyčejném Pascalu se virtuální a dynamické metody moc neužívají.
Konstanty jsou data, která se při běhu programu nemění. Definují se obvykle na začátku programu, aby šly v případě potřeby rychle změnit. Použití konstant je velice výhodné, rozhodneme-li se později změnit nějakou hodnotu, stačí změnit jediné číslo a nemusíme předělávat celý program a dumat, co které číslo znamená a v jakém významu je použito. Takže např. můžeme použít const BarvaHrace=14; (a pak v celém programu používat BarvaHrace). Nebude-li se nám (popř. zákazníkovi) daná barva líbit, stačí změnit jediné číslo. Při definování konstant překladač sám rozhodne, jakého typu je daná konstanta a vyhradí jí v paměti patřičné místo.
Konstanty se definují velice jednoduše :
const Nazev = Hodnota;
Užití je podobné jako u type. Konstanty je možno definovat i z předem
definovaných uživatelských typů :
type Barvy=(Zelena,Modra,Ruzova);
const BarvaMehoAuta=Modra;
Pochopitelně je možné definovat konstanty pomocí výrazů, které
lze přeložit již při překladu (tyto výrazy mohou např. obsahovat již definované
konstanty) :
const SirkaPolicka =10;
PocetPolicek=50;
SirkaPole = SirkaPolicka*PocetPolicek;
V daném výrazu se smí objevit pouze tyto funkce : Abs, Chr, Hi, High, Length, Lo, Low, Odd, Ord, Pred, Ptr, Round, SizeOf, Succ, Swap, Trunc. (Operátor @ který vytváří ukazatel na něco, lze použít pouze na globální úrovni na dříve definované datové objekty s udaným typem - tedy ne na konstanty)
Konstanta typu množina se vytváří
takto
const Mnozina =[Prvek1,Prvek2,...]; Prvky jsou
pochopitelně částí nějakého předem definovaného bázového typu (se všemi
omezeními, která pro tento typ platí). Pochopitelně lze při definici konstant
využít i prázdné množiny...
Konstanta typu pole se definuje takto :
const Pole:TypPole=(Polozka1, Polozka2, Polozka3, ...); (Ve
skutečnosti se jedná o inicializovanou proměnnou), je-li pole více rozměrné,
musí se závorkovat, nejzazávorkovanější hodnota odpovídá nejpravějšímu indexu
(raději si to vyzkoušejte sami) :
const Pole : array[1..3,1..2]
of Byte = ((1,2),(2,3),(3,7));
Obsahuje-li pole typ soubor, není ho možné zkonstantovat.
Konstantu typu záznam pak vytvoříme takto :
const Zaznam : TypZaznamu = (NazevPolozky:Hodnota;...) -
pochopitelně je možné kombinovat (pole v záznamu, pole záznamů,...)
type IndexyPole=(PrvniPolozka,DruhaPolozka,TretiPolozka); TBod = record X:Real; Y:Real; Z:Real; end; TTrojuhelnik=array[1..3] of Bod; TZaznamSPolem=record Pole:array[IndexyPole] of Boolean; Nepole:string; end; const Bod:TBod = (X:1.0; Y: 2.5;Z:6.8); Trojuhelnik:TTrojuhelnik = ((X:1;Y:2;Z:3),(X:7;Y:5;Z:14),(X:11;Y:3;Z:0)); ZaznamSPolem:TZaznamSPolem = (Pole:(true,false,true);Nepole:'traktor');
Zkuste si nyní vypsat ZaznamSPolem.Pole[DruhaPolozka];...
I v tomto případě se jedná o inicializovanou proměnnou. Obsahuje-li záznam typ
soubor, nelze ho zkonstantovat.
Inicializované proměnné mají již od počátku programu nějakou hodnotu. Tu však lze kdykoliv změnit. Inicializované proměnné se definují v sekci const tímto jednoduchým způsobem:
const NazevPromenne : TypPromenne = HodnotaPromene;
Inicializované proměnné se využívají hlavně, chceme-li mít již od začátku v dané proměnné určitou hodnotu, mimo to se také jedná o jediný způsob, jak zkonstantovat pole a záznamy.
Inicializované proměnné však nejdou použít uvnitř funkcí a procedur. Tam mají totiž trošku odlišné chování.(Jedná se o lokální proměnné, které si pamatují svou hodnotu.) Příklad:
program Staticke; procedure pis; const a:integer=1; begin writeln(a); a:=a+1; end; var i:integer; begin for i:=1 to 10 do pis; readln; end.
Vypíše 1,2,3...10.
Direktiva absolute říká překladači, na jaké místo v paměti má umístit danou proměnnou. Jsou možné dvě použití této direktivy:
type tpoint=record c:char; color:byte; end; var a:integer; b:word absolute a; {Tento způsob říká, že proměnné a a b jsou uložené na stejném místě paměti. lze využít k úspoře paměti. A pokud víme, jakým způsobem jsou data v paměti počítače reprezentována, můžeme dělat divy.} text:array[1..25,1..80] of tpoint absolute $B000:$0000; {Takto zapíšeme proměnnou na jakékoli místo paměti chceme. Adresa $B000:$0000 je adresou videopaměti v textovém módu. Takže teď můžeme z text číst, jaká data jsou právě na obrazovce a případně je i měnit, je to rychlejší než write. Ale bude to fungovat jenom na našem počítači.}
Toto klíčové slovo jde napsat před název typu (var a: packed array[1..100] of real). V dřívějších dobách říkalo překladači, ať se pokusí šetřit pamětí. (Např. typ Real má 6bytů, ovšem v nespakovaném poli jedna položka bude zabírat 8 bytů.) Dneska je toto slovo v Pascalu nanic. Je zachováno kvůli kompatibilitě se staršími verzemi.
To by bylo o typech proměnných vše.
DCV: Pořádně si je zopakujte, vymyslete, udělejte si vlastní prográmek na každou zde popsanou funkci či operátor, zjistěte si pořadí operátorů. Přeložte si všechny zde probírané názvy do češtiny,...