int21h

Pole s proměnnou velikostí prvku

Občas se stane, že bychom rádi uložili do jednoho pole hodnoty různých typů, které se velmi výrazně liší svou velikostí (třeba jeden prvek word, druhý string apod.). Bylo by plýtvání pamětí udělat prvky jako variantní záznamy, které mají vždy velikost podle největšího z možných typů. Můžeme tedy použít jakousi beztypovou datovou strukturu, kde k jednotlivým datům budeme přistupovat pomocí přetypovaných ukazatelů.

Tento postup se hodí pro data, která se nemění - potřebujeme je jen číst. Pokud by se totiž změnila velikost některého prvku, muselo by se s celým polem složitě šoupat.

Deklarujeme si nějaké pole, třeba var pole:array[0..něco] of byte, protože s tím se nejlíp pracuje. Může být samozřejmě dynamické (vlastně by mělo být - ať se pro něj naalokuje jenom tolik paměti, kolik je podle celkové velikosti dat potřeba).

Pak mějme proměnnou var pozice:word, která nám bude udávat, kde se zrovna v poli nacházíme.

Budeme samozřejmě potřebovat příslušné datové typy, takže např.:

tPlaneta = record
           jmeno:string[30];
           VzdalenostOdSlunce:real;
           hmotnost,prumer,g:real;
           PocetObyvatel:longint;
           end;

tAsteroid = record
            kod:string[10];
            hmotnost:real;
            end;

A také ukazatele na tyto typy:

uPlaneta = ^tPlaneta;
uAsteroid = ^tAsteroid;

Představme si, že máme pole naplněné daty těchto dvou typů. Abychom je od sebe rozeznali, umístíme před každou planetu třeba znak 'P' (velikost 1B) a před asteroid 'A'. Což je vlastně třetí typ dat, jaký se bude v poli nacházet - identifikační kódy.

Nyní se např. nacházíme na pozici 100 (proměnná Pozice má hodnotu 100, což znamená 101. byte od začátku pole, první má index 0) a přečteme si, že char(pole[pozice])='A'. Takže víme, že následující data jsou typu tAsteroid.

Pro přístup k datům potřebujeme nejdřív zjistit jejich skutečnou adresu v paměti, tedy vlastně ukazatel na ně, a tento ukazatel přetypovat na ukazatel na příslušný typ. Přímo data přetypovávat nemůžeme, protože velikost obou typů, mezi kterými přetypováváme, musí být stejná, což v tomto případě není.

Data k asteroidu jsou:

uAsteroid(adresa)^

Adresa je:

@pole[index]

Nebo pro dynamické pole:

@uPole^[index]

Index začátku dat je:

Pozice+1

Data začínají za identifikačním znakem, na kterém se proměnná Pozice zrovna nachází. Po dosazení výše uvedeného dohromady se např. k hmotnosti asteroidu dostaneme takto:

uAsteroid(@pole[Pozice+1])^.hmotnost

Asteroid jsme si prohlédli, teď se podíváme, co je v poli dál:

inc(pozice,sizeof(tAsteroid)+1);

Jednička, kterou jsme ještě přičetli, je velikost identifikačního znaku.

Nyní jsme na prvním bytu za dříve přečtenou položkou typu tAsteroid. Měl by to být identifikační znak, tak ho přečteme stejným způsobem jako minule - vyjde nám třeba 'P'. Následuje tedy planeta, takže ukazatel na data budeme přetypovávat na typ uPlaneta.

Tímto způsobem se s polem dat pracuje vlastně podobně jako s beztypovým souborem - další položku najdeme za koncem předchozí. Pokud bychom potřebovali kdykoli přístup k libovolné položce, stačí si vytvořit ještě jedno pole:

var adresy:array[1..počet položek] of word;

ve kterém budeme mít uložené indexy jednotlivých datových položek v hlavním poli. Např. typ čtvrté datové položky (identifikační znak) bychom určili takto:

znak:=pole[adresy[4]];

Podle toho znaku se rozhodneme, na co přetypovat, a potom třeba počet obyvatel bude:

pocetobyvatel:=uPlaneta(@pole[adresy[4]+1])^.pocetobyvatel;

(pokud tedy znak byl 'P' a tato položka je typu tPlaneta)

A praktické využití? Já jsem tímto způsobem vyřešil např. uložení vektorového fontu v paměti. Každý znak je tvořen textovým řetězcem, samozřejmě pokaždé jinak dlouhým. Všechny je mám jeden za druhým nasypané v jednom poli (array of char) a ve druhém (array of word) mám jejich adresy. Jako identifikační znaky fungují přímo nulté znaky řetězců určující jejich délku. Ve výsledku tento systém spotřeboval méně paměti, než kdyby každý znak byl jeden dynamicky alokovaný řetězec (zbytečné 4 B na každý ukazatel plus ztráty zarovnáním na 16 B, což v poli oboje odpadá).

Pro datové struktury, ve kterých potřebujeme často přidávat, mazat a přepisovat, se to ale nehodí, lepší je spojový seznam (raději obousměrný).

2006-11-30 | Mircosoft
Reklamy: