Trocha nápovědy do Pascalu

Z obsahu:

[o úroveň výše]


Jak pracovat s ukazatelem Pointer v Pascalu?
Použijte například tuto procedurku, která to zařídí:
procedure PosunUkazatel(var p:Pointer;okolik:Integer);
type    tub=^Byte;
var     ub:tub;
begin
 ub:=p;Inc(ub,okolik);p:=ub;
end;

Poznámka: Stačí ukazatel převést např. na ukazatel na Byte, který lze již přesouvat a potom převést zase zpět. Zajímavá je použití procedury Inc, která provede změnu ukazatele (bude ukazovat na něco jiného).
Použití: Ukazatel typu Pointer nastavím např. na začátek pole a posunem mohu zpřístupnit další prvky pole (i na to, které má předem neznámou délku - dynamicky alokované pole - např. matice, jinak bychom mohli definovat ukazatel na Byte jako ^Byte). Mohlo by to vypadat jako adresová aritmetika v C++, ale ta je v C++ trochu mocnější, ale zase nebezpečnější.


Jak psán binárně do textového souboru v Pascalu?
Použijte například tuto procedurku, která to zařídí:
procedure Zapis(var f:Text;var adresa;pocet:Word);
const   n=250;
var     s:String;
        adr:Pointer;
begin
 adr:=Addr(adresa);
 while (pocet>n) do {Řetězec je v Pascalu dlouhý max. 255 znaků!!!}
 begin
  s[0]:=Chr(n);Move(adr^,s[1],n);Write(f,s);
  PosunUkazatel(adr,n);Dec(pocet,n);
 end;
 s[0]:=Chr(pocet);Move(adr^,s[1],pocet);
 Write(f,s);  end;
Poznámka: Komentář není nutný pro toho kdo to bude chtít použít a začátečník to asi těžko použije. Snad jen tolik, že v Pascalu lze zapsat do textového souboru řetězec String, který je dlouhý maximálně 255 znaků. Této možnosti využijeme. Nejdříve si definujeme pomocný ukazatel, který bude probíhat pole, které je větší než řetězec. Nastavíme ho na začátek pole nebo toho, co chceme zapsat. Potom pole rozdělíme na části po n bytech (n musí být menší než 255), abychom se vešli do max. délky řetězce - ta je 255. V nultém prvku (znaku) řetězce je délka řetězce String v Pascalu. Ke zkopírování části pole do řetězce použijeme Move. Nezapomeneme za adr napsat "^", což je důležité, abychom nepřepisovali paměť, která nechceme. Znak ^ označuje, že chceme pracovat s pamětí kam ten ukazatel adr ukazuje a ne z místem, kde je ukazatel uložen. Prostě ho musíme dereferencovat, v Pascalu se to dělá ^. Důvod s[1] je ten, že v nultém prvku řetězci (jak jsme si již řekli) je délka řetězce. Jelikož je to jen jeden Byte - znak, z toho vyplývá, že String - řetězec v Pascalu může být dlouhý jen 255 znaků. K posunu (aby ukazoval na další část pole) ukazatele adr použijeme proceduru PosunUkazatel.
Použití: Například jsem nahrál WWW stránku do paměti - do velkého pole znaků. Číst to pomocí read je zdlouhavé, pomocí BlockRead je to rychlejší. Potom jsem chtěl WWW stránku s modifikacemi zapsat do textového souboru. Jenže smůla, zapsat toto pole (leč je to pole znaků, nejde, je to proti syntaxi jazyka Pascalu). C++ je rozumější. Tak jsem si musel vymyslet trik jak to obejít a tím je tato procedure Zapis. Na případné dotazy Vám mohu odpovědět. Pokud bych ten soubor otevřel pro zápis jako binární, tak zase do něj nemohu z zapisovat textově, ale i to myslím by naprogramovat šlo. Co je u lidí nemožné, u Boha jest možné. Mnoho věcí dříve nemožných se stalo možnými... Bůh, jakožto nejvyšší a také nejchytřejší bytost skutečně asi nezná výraz nemožný. Pokud tedy lidé pochopí Boží myšlení, nebude pro ně ani vzkříšení nemožné... Nyní již mohu zapsat do textového souboru textově (normálně pomocí write resp. writeln), ale také binárně nějaké pole (pokud to má rozumný smysl) pomocí námi definované procedury Zapis.
Otázky a odpovědi:
  • Jaký se vrátí chybový kód, když použiju samotný příkaz Halt bez parametrů?
  • Vrátí se nula. Dále je nutné upozornit na to, že Halt má 1 parametr typu Word, ale nakonec je stejně v DOSERROR číslo typu BYTE, tzn. že v DOSERROR může být hodnota 0-255.

  • Jaký je rozdíl mezi constructor Init a procedure Init?
  • Pokud nepoužijeme virtuální metody v objektovém programování, potom je to ekvivalentní. V případě, že ovšem definujeme nějakou metodu jako virtuální, potom constructor navíc (narozdíl od samotné procedury) prý nastavuje adresy virtálních metod v tzv. virtual method table. Navíc použití constructor Init je programátorsky "hezčí" než procedure Init.

  • Jaký je rozdíl mezi destructor Done a procedure Done?
  • Nevím, ale snad je to jakýsi programátorský zvyk, když něco rušíme, potom je "hezčí" použít destructor než proceduru. Pokud se vyznáte v přeloženém Pascal programu, možná že zjistíte nějaký rozdíl v překladu.

  • Jak lze zjistit, adresu návěští, když Seg(návěští), Ofs(návěští) nebo (návěští) syntaxe Pascalu toto nedovoluje?
  • Bez Assembleru to asi nepůjde. Nejlepší je použít např. MOV registr,OFFSET návěští resp. MOV registr,SEG návěští. Instrukce se do Pascalu vkládají nejlépe mezi příkazy ASM a END;.

  • Jak lze zjistit 2. rozměr dvourozměrného pole, když 1. rozměr lze zjistit např. pomocí funkcí High(pole) resp. Low(pole)?
  • 1.rozměr: Low(pole) ... High(pole)
    2.rozměr: Low(pole[index]) ... High(pole[index])
    kde index může být libovolný index 1. rozměru (tedy z intervalu Low(pole) ... High(pole).
    Takže řešení je tedy: Low(pole[Low(pole)]) ... High(pole[Low(pole)]); Je nutné zdůraznit, že dvourozměrné pole je ekvivaletní s jednorozměrným polem, jehož prvky jsou zase pole.

  • Jak lze předat jako parametr funkce či procedury pole předem neznámé délky?
  • V proceduře či funkci napíšeme:
     procedure název(pole:Array of typ);
     var i:Longint;
     begin
       for i:=Low(pole) to High(pole) do Writeln('pole[',i,'] = ',pole[i]);
     end;
    

    Pokud je předávané pole dvourozměrné, potom nastanou vážně dost značné problémy. V tomto případě bude asi lepší předávat ukazatel na dynamicky alokované pole (např. pomocí new, kde jako parametr délky zadáme např. sirka*vyska*SizeOf(typ).
  • Jak lze něco provést před startem vlastního programu?
  • Příkazy, které chcete provést, dejte do inicializační jednotky, kterou bude Váš program používat. Potom se bude tato část automaticky volat ve všech programech používající tuto jednotku.

      unit Jmeno;
    
      interface
      ...
      ...
      ...
    
      implementation
      ...
      ...
      ...
    
      {Inicializační část:}
      begin
        {příkazy}
      end.
    

  • Jak lze něco provést po skončení vlastního programu?
  • Je důležité upozornit, že program může skončit několika způsoby:
    1. zaseknutím
    2. nekonečným cyklem
    3. RESETem (ať již studený nebo teplý start)
    4. přerušením
    5. chybou
    6. abnormální konec
    7. stiskem Break
    8. normální řádný konec

    (Ad 1)
    Tento stav je nepříjemný, systém přejde do stavu ztuhnutí (např. při zprávě: "System halted." apod.). Výhoda: Systém přestane pracovat ještě dříve, než provede něco nehezkého či nějakou destruktivní činnost. (Existuje instrukce HALT nebo HLT). V takovém případě, nezlobte se, bych nic od programu nechtěl vykonávat. Jedině snad vypsat zprávu: "Systém se v nejbližší době zasekne. Proveďte RESET." Ale jak naprogramovat, aby se toto hlášení vypsalo před zaseknutím, toť otázka...

    (Ad 2)
    Tento stav lze těžko kontrolovat, je chybou samotných programů, že takhle skončí. U normálních programů by měla být možnost program nějak přerušit, v nejhorším případě alespoň stiskem Break. U tohoto nekonečného cyklu je vždy problém rozeznat, kdy ještě počítač na něco čeká a kdy už je skutečně v nekonečném cyklu. Konec konců, nakonec to většinou vzdáme my... Jak určit ovšem dobu, od které se již čekání bude považovat za nekonečný cyklus?

    Řešení: Nastavte časovač na přípustnou dobu a po té, když program nic nevykonná (i to je někdy obtížné otestovat...), provede se přerušení od časovače, např. $1C a provedete příslušnou požadovanou činnost (např. automatický RESET PC).

    (Ad 3)
    V případě studeného startu (tlačítkem RESET nebo vypínačem na PC) je těžko něco vykonávat a při opětovném zapnutí či RESETu se vždy skočí do obslužného programu v paměti ROM. V případě teplého startu (např. CTRL+ALT+DELETE) se někdy skočí (nebo také ne) na přerušení 19H (nemusí to být pravidlo!!!).

    Řešení: Pokud je tedy u nějakého počítače ekvivalentní stisk CTRL+ALT+DELETE s provedením instrukce INT 19H, potom je možné vektor tohoto přerušení přesměrovat a provést tak vytouženou akci. Pokud se neshoduje, bude asi nutné zavést rezidentní program, který čte okamižitý kód stisknutých kláves a rozeznat stisk kláves CTRL+ALT+DEL dříve než se skutečně teplý start provede. Před tím by bylo ovšem dobré vyprázdnit klávesnicový buffer, aby se teplý start neprovedl. Praxe však ukazuje, že bude dosti problematické něco podobného řešit...

    (Ad 4)
    Při ukončení přerušení např. NMI je asi nerozumné něco provádět. Ale když je to opravdu nutné, nikdo Vám nemůže zabránit změnit vektor přerušení a tak zajistit provedení Vaší akce. Bylo by ovšem dobré zavolat původní obsluhu toho přerušení, které jste změnili. Ani zde se ovšem bez části rezidentního programu neobejdeme nebo program by musel být stále v paměti, když je na něj nastaven vektor přerušení (aby se při příchodu přerušení neskočilo někam, kde už žádné instrukce nejsou).

    (Ad 5)
    Jsou to např. dělení nulou, přetečení, podtečení, chyba zásobníku či paměti resp. parity apod. Pokud chyba není ošetřena jinak, většinou následuje HALT nebo-li zaseknutí počítače. Platí zde to stejné jako v (1). Jakákoliv akce před zaseknutím je asi nemožná...

    (Ad 6)
    Abnormální konec může nastat v případě, že systém je přetížen (mimochodem se většina systémů zasekává, takže uživatelský program se to ani nedozví, až pouze uživatel potom zoufale buší do klávesnice v domění, že se mu podaří povzbudit program k další činnosti, ale zpravidla vždy je nutný nový start počítače). Podobná chyba může nastat v případě, že by se Vám omylem podařilo zavřít standardní vstup či výstup a potom by nastaly problémy s otevíráním. Sice se to stává zřídka, ale lze si domyslet, že počítač se v takovém případě zasekne... Většinou, až možná na několik neznámých vyjímek, se situace podobá bodu (1).

    (Ad 7)
    Stisk Break je "uživatelská poslední naděje", když uživatel neví, jak program ukončit nebo když mu počítač sám nebo program začne dělat, co uživatel po něm nechce nebo pokud program již dlouho běží a uživatel již nechce čekat...
    Mnoho programů ovšem vypíná možnost přerušení Break, tak u mnoha programů není možné program tímto způsobem přerušit.
    Existují asi 3 metody ochrany před skončení stiskem Break:

    1. Přesměrují vektor přerušení Break na svou akci, spíše tedy rovnou na instrukci pro návrat z přerušení (RETI) a tevolají potom standardní obsluhu přerušení, takže se operační systém o stisku Break nedozví.
    2. Nastavením proměnné operačního systému (v DOSu v souboru CONFIG.SYS BREAK=ON resp. OFF). Těžko říci, jak je to v jiných DOSech např. v UNIXu či CP/M apod. Nastavením příslušné proměnné lze Break ignorovat. Hodnotu proměnné DOSu je možné měnit i po zavedení DOSu.
    3. Vypnutím komunikace s klávesnice nebo jiným podivným způsobem.

    Řešení:
    V některých z těchto případů je skutečně možné přesměrováním vektoru přerušní BREAK 23H (platí pouze v MS-DOSu) na obslužnou rutinu, lze skutečně provést požadovanou činnost, ikdyž problémů s tímto vektorem je poměrně dost. !!! MS-DOS tento vektor dost často obnovuje, hlavně při ukončení programu a návratu zpět do DOSu. !!! Jak již bylo řečeno, je snadné Break ignorovat, ale je dost obtížné něco provést při jeho stisku. (Přerušení 23H platí jen v MS-DOSu !!!)

    (Ad 8)
    Program se může řádně ukončit dvěmi způsoby:

    • předáním řízení MS-DOSu a uvolněním z paměti
    • ukončením jako rezidentní - část programu nebo celý zustane v paměti (jako vir) a potom provádí, co potřebuje
    Ve všech případech můžete změnit vektory všech přerušení, které se používají k ukončení programu a tak docílit provedení požadované akce před skončením svého programu (pokud skončí normálně).
    Je nutné upozornit na možnost ukončení programu přerušením MS-DOSu 21H, kde je nutné testovat registr AH jako kód požadované funkce a rozlišit, zda se skutečně jedná o konec programu.
    Jsou ovšem také programy, které vrací řízení tomu programu, který je spustil. To je většinou asi zajištěno změnou adresy, kam se má skočit při ukončení programu. !!! Je nutné upozornit, že MS-DOS má většinu pro většinu svých vektorů uschovanou svou původní hodnotu, takže musíte dát pozor, co nastavujete a kdy to nastavujete. Uvedené vektory se obnovují při ukončení Vašeho programu a při návratu do DOSu. !!!

  • Problém "Data Segement too large"
  • Toto řešení pomáhá jen někdy, co lze udělat, aby se toto hlášení již neobjevilo:
    1. deklarujte co nejméně globál. proměnných
    2. pro uchovávání proměnných používejte zásobník (lokální proměnné) nebo haldu (používejte příkazy New, Dispose, GetMem, FreeMem pro práci s dynamickými proměnnými)
    3. pokud máte v programu moc textů, vytvořte si raději soubor s těmito texty (většina PC je dnes již tak rychlá, že dobu přístupu na disk, pokud není častá, lze zanedbat). Při spuštění programu přečtěte obsah souboru a data uložte jako lokální nebo jako dynamicky alokovaná
    4. nejvíce místa zabírají řetězce - většinou jsou dlouhé (pokud nezadáte jinak) 256 Bytů, takže v deklaraci místo "var s:String;" pište "var s:String[100];" - Většinou je tato délka 100 znaků postačující a do paměti se Vám vejde více řetězců
      - Pozor na pole řetězců: "var pole:Array[1..100] of String;" -zde je délka 256*100=25600 Bytů, ale pokud zadáme kratší String[delka], můžeme ušetřit dost paměti.
      - Ostatně, když se člověk spokojí s málem, nevadí mu více. Opačné tvrzení neplatí vždy... Stejně jako v životě skromnost jistě neuškodí...
    5. vyvarujte se zbytečných údajů na obrazovku, popravdě řečeno, mnoho uživatelů to stejně nečte (a nemusí se jednat pouze o cizince). Např. když je na obrazovce někde v rohu napsáno: Nyní stiskněte ESC, tak mnoho uživatelů (s politováním, praxe skutečně někdy ukazuje, že se tací najdou a není jich málo) například čeká, nebo metodou pokusů a omylů mačkají cokoliv (třeba někteří nejsou tak zkušení a mohou existovat i takoví, kterým najití nějaké klávesy dá pěknou fušku!).
      Praxe ukazuje, že je i dosti problematické i menu, ve kterém mnoho uživatelů zmateně hledá... (někdy i já u některých programů ve Windows). Nejlepším řešením jsou ikonky s obrázky, kterým rozumí i cizinci i malé děti, které ještě neumí číst. Je dobré, když se program dá ovládat myší nebo joystickem, protože s klávesnicí jsou problémy, jednak proto, že rozložení klávesnic se od sebe mírně odlišuje, nemluvě o přehazování Y-Z, psaní písmen se znaménky atd.
      Dále je problém, že u většiny programů se najde vždy nějaká klávesa, jejíž stisk vypíše jiný znak, než je na klávesnici napsán, a nebo se stisk klávesy ignoruje.
    6. vyvarujte se tedy předefinování funkcí kláves, většina uživatelů, kterým Vaše předefinování klávesnice nevyhovuje mohou právě proto Váš program odmítnout... Popravdě to zbytečně plýtváte svým časem pro tvorbu takového programu a také zabírá dost paměti.
    7. jako čítače cyklů, pokud to jde, používejte stejné proměnné. Toto sice není tak nutné, jednoduce proměnné pro cyklus mohou být maximálně 10B veliké (a to u reálných čísel Extended).
  • Jakého typu jsou proměnné p1 a p2 v této proceduře. Překlad projde až k přiřazení p:=p1, kde ohlásí chybu:

    procedure Vymen_Byte(var p1,p2);
    var p:Byte;
    begin
    {  Writeln('Low: ',Low(p1),' High: ',High(p1));}
    { Writeln(SizeOf(p2));}
    {  p:=p1;p1:=p2;p2:=p;}
    {  Writeln(p1,' ',p2);}
    end;
    
    var a,b:Array[1..10,1..20] of Byte;
     c,d:Byte;
    begin
      Vymen_Byte(a,b);
      Vymen_Byte(c,d);
    end.
    

    Var p1,p2 v deklaraci oznamujete, že pod p1 a p2 zde mohou být skoro libovolné proměnné. S takovou proměnnou se špatně pracuje, protože je to vlastně taková novinka, ikdyž stará asi od doby Borland Pascal 7.0 :-) Upozorňuji, že konstanta není proměnná, tedy zde nemůže být. Já jsem to použil jen v jednom případě, kde se mi to hodilo. Kromě toho si myslím, že těžko půjde v Pascalu udělat univerzální proceduru pro výměnu proměnných. V C++ jsem si udělal takové univerzální makro, které prohazuje číselné proměnné.
    Problém, že nelze naprogramovat univerzální proceduru v Pascalu na prohazování obsahu proměnných, není ani v tom, že by nešlo použít SizeOf jako délku do Move, ale spíše v přísné syntaxi. Nelze deklarovat proměnnou, která je předem neznámého typu. Jediný trik bych viděl v dynamické alokaci proměnné p a potom ji používat. Stačí taková odpověď?


Jak v Pascalu v textovém souboru uchovat a obnovit pozici?
Dejme tomu, že máme překladač, který bychom chtěli naprogramovat v Pascalu a přitom použít Pascalovský typ textového souboru text a provést něco na více průchodů.
Řešením může být například tyto funkce:
var     Stare0:TTextRec;
        l0:Longint;
procedure UschovejPozici(var f:Text);
begin
 Move(f,Stare0,SizeOf(Text));
 r.ah:=$42;r.al:=1;r.bx:=Stare0.Handle;r.cx:=0;r.dx:=0;MsDos(r);
 l0:=r.dx*65536+r.ax;
end;

procedure ObnovPozici(var f:Text);
begin
 Move(Stare0,f,SizeOf(Text));
 r.ah:=$42;r.al:=0;r.bx:=Stare0.Handle;r.cx:=l0 div 65535;r.dx:=l0 mod 65536;
 MsDos(r);
end;

Poznámky: TTextRec je záznam, který odpovídá typu text. Jinými slovy: Typ text je identicky rovný typu TTextRec. Nelze je však zaměňovat, protože to syntaxe nedovolí. Kde se očekává Text, musí být proměnná typu Text. Jsou-li identické, potom můžeme zkopírovat proměnnou f typu Text do Stare0, což je proměnná typu TTextRec = záznam. Samotné uchování proměnné typu Text nestačí k uložení pozice v textovém souboru, rovněž nestačí uložení pouze ukazatele. Je nutné jednak uložit ukazatel a současně i záznam odpovídající typu Text. Ke zjištění ukazatele v souboru použijeme funkci DOSu AH = 42H. Funkce se normálně používá pro přesun ukazatele, ale my jí můžeme použít i pro zjištění aktuální pozice tak, že ukazatel v souboru posuneme o 0 (tedy nebudeme posouvat). Funkce DOSu 42H vrátí v registrech DX a AX aktuální ukazatel. Funkce požaduje jako vstupní parametr v registru BX file handle, který získáme ze struktury TTextRec nebo-li je také obsažen i v proměnné typu Text. CX (horní) a DX (dolní) jsou wordy, tvořící 32 bitový ukazatel v souboru, se kterým lze pracovat v Pascalu jako s proměnnou typu Longint (pokud uvažujeme pouze kladné hodnoty). Funkce vrátí také 2 wordy aktuálního ukazatele v registru DX (horní) a AX (dolní) word tvořící 32 bitový ukazatel v souboru.


[o úroveň výše]


WEBovský počítadlo spočítalo, že si číslo počitadlo, které navštívilo od 17.října 1999 tyto stránky uložené na serveru Volny.cz
Tato stránka byla autorem naposledy editována 07.08.2007 13:27:24,
automatický update proveden 03.09.2007 22:23:44