Základní typy proměnných

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í.

Jednoduché typy

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 :

Typy ordinální

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:

Procedury a funkce pro práci s ordinálními typy
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 :

Typy celočíselné

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):

Funkce a procedury celočíselných typů
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):

Speciální funkce pro typy Integer a Word
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.

Typ char

Tento typ uchovává jeden znak (jedno písmeno). Zabírá 1 byte.
Funkce pro typ char:

Procedury pro datový 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é.

Booleovské typy

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é.

Výčtové typy

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).

Intervaly

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.

Typy reálné

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).

Procedury a funkce pro práci s reálnými datovými typy
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ý!!)

Typy znakové

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):

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):

Procedury a funkce pro práci s řetězci typu PChar
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;

Ukazatel

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:

Procedury a funkce pro práci s dynamickými proměnnými
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.

Konverzní funkce

Pochopitelně občas potřebujeme nějakým způsobem změnit typ proměnné :

Převodní funkce a procedury
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ě

Type-casting

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

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.

Množina

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 :

Speciální množinové funkce
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].

Pole

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)

Záznam

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

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):

Procedury a funkce pro práci s dynamickými proměnnými
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

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...).

Procedurální typ

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:

  1. Musí být uvedeny na nejvyšší úrovni (nesmí tedy být lokální)
  2. Do proměnné nesmíme zařadit standardní funkci (např. Write).
  3. Nesmí se jednat o inline a interrupt procedury a funkce
  4. Procedurální typ nemůže být použit jako návratová hodnota funkce

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.

Typ objekt

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

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é

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

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.}

Klíčové slovo packed

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,...