Andmestruktuurid

Massiivide abil võite salvestada vaid teatud hulga üht tüüpi muutujaid. Keerukamate andmestruktuuride salvestamiseks tuleks aga luua uus tüüp. Selleks võite kasutada võtmesõnu struct ja union. Uue tüübi loomisega taotletakse enamasti kokkukuuluvate andmete üheskoos salvestamist. Andmestruktuuri defineerimiseks kasutatakse järgmist sõnastust:

struct <uue tüübi nimi> {
  <väljad koos tüüpidega>;
  <seda tüüpi muutujate nimed>;
}

Näiteks:

struct Isik {
  char   Eesnimi[10];          /* isiku nimi ja perekonnanimi */
  char   Perekonnanimi[20];
  unsigned long Telefon;       /* telefoninumber */
  char   Aadress[30];          /* aadress - 29 sümbolit */
}
Toomas, Peeter; /* kaks isikut kõigi loetletud andmetega */
...
struct Isik  Perekond[5]; /* struktuur viie elemendiga */

Eelmise lõiguga defineeriti uus tüüp Isik, mis on 10 + 20 + 4 + 30 = 64 baidi pikkune. Uue tüübi defineerimisel ei ole ilmtingimata vaja määrata tüübi nime, kuid kui te seda ei tee, siis ei saa te ka loodud tüüpi hiljem uute muutujate defineerimisel kasutada. Sel juhul jääks vaid võimalus luua kohe tüübi defineerimisel paar seda tüüpi muutujat, nagu siinloodud muutujad Toomas ja Peeter. Looksulgude vahel sisestatakse üksteise järel kõik antud andmestruktuuri elemendid. Need elemendid salvestatakse arvuti mälus samas järjekorras. Sellist tüüpi muutuja erinevate elementide väärtuste muutmiseks või lugemiseks tuleb kasutada operaatorit . Näiteks:

strcpy(Peeter.Eesnimi, "Peeter");
Peeter.Telefon = 523467;
strcpy(Toomas.Aadress, "Tallinn EE-0026, Akadeemia 15-42");

Kui te aga defineerite viida sellisele andmestruktuurile, siis tuleb teil kasutada erinevate elementide muutmiseks või lugemiseks operaatorit ->. Näiteks:

struct Isik *minu_tuttav;
unsigned long telefon;
...
minu_tuttav = &Peeter; /* viit muutujale Peeter */
telefon = minu_tuttav->Telefon; /* telefon on nüüd 523467 */

Tüübi Isik defineerimise järel defineerisime ka viie elemendiga struktuuri Perekond. See on struktuur, mille iga element on 64 baidi pikkune ja sisaldab omakorda elemente. Iga elemendi muutmiseks tuleb kõigepeal kasutada elemendi järjekorranumbrit ja seejärel peale punkti (või noolt) soovitud elemendi nime, näiteks:

struct Isik tuttavad;
char nimi[10];
...
strcpy(Perekond[4].Eesnimi, "Toomas");
tuttavad = &Perekond;
strcpy(nimi, (tuttavad+4)->Eesnimi);  /* nimi on nüüd "Toomas" */

Loodud andmetüübi suuruse määramiseks kasutage operaatorit sizeof(), näiteks sizeof(Isik).

Uue andmetüübi defineerimisel võib peale põhiliste andmetüüpide kasutada veel nn. bitivälju (bitfields). Bitiväli defineeritakse järgmiselt:

<tüübi nimi> <välja nimi> : <laius bittides>

Tüübi nimi määrab selle põhitüübi, mida kasutatakse bitivälja salvestamiseks. Võimalik on kasutada vaid tüüpe char, unsigned char, int ja unsigned int. Kui välja nime ei määrata, siis see väli küll luuakse, kuid tema väärtust ei saa kasutada. Seda kasutatakse sageli bitivälja defineerimise puhul protsessori registrite jaoks juhul, kui osa bitte ei oma mingit tähendust. Laius määrab elemendi laiuse bittides. Bitiväli võib olla 1 kuni 16 biti laiune. Näiteks:

struct bitid {
  int  esimene_bitt : 1;   /* üks bitt */
  int   : 3;               /* midagi ebahuvitavat */
  int muud_bitid : 12;     /* vajalikud 12 bitti */
};

Andmestruktuurid võivad sisaldada ka omakorda struktuure. Sel juhul tuleb need struktuurid defineerida enne neid kasutavat struktuuri. Näiteks:

struct Aadress{
  char Taenav[10], Linn[10], Riik[10];
  unsigned long telefon;
  unsigned maja, korter;
};
...
struct Isik {
  char  Eesnimi[10], Perekonnanimi[20];
  struct Aadress  aadress;
} Peeter;

Peale võtmesõna struct on võimalik uut andmestruktuuri luua ka võtmesõnaga union. Sel puhul kehtivad kõik samad reeglid, ainult et loodud andmetüübi pikkus ei ole enam võrdne kõigi tema elementide pikkuste summaga, vaid kõige suurema elemendi pikkusega. Näiteks:

struct LihtnePunkt {
  char  taepne;   /* FALSE (<0) kui lihtsad koordinaadid */
  unsigned  x, y; /* suvalise ekraanipunkti koordinaadid */
};

struct TaepnePunkt {
  char taepne;           /* TRUE (>0) kui täpsed koordinaadid */
  unsigned long  x, y;   /* täpsed koordinaadid nagu näiteks 
                            tehnilisel joonisel */
};

union Koodinaadid {
  struct LihtnePunkt  lp;   /* millist neist kasutame, sõltub */
  struct TaepnePunkt  tp;   /* olukorrast */
} Punkt1, Punkt2;

union Koordinaadid   k;
...
if(k.taepne < 0) {
  k.x = 3;
  k.y = 4;
}
else {
  k.x = 3.56;
  k.y = 4.23;
}

Tüübi Koordinaadid pikkuseks on praegu 9 baiti, mis on võrdne ka tüübi TaepnePunkt pikkusega. Vastavalt olukorrast võime nüüd kasutada üht neist tüüpidest. Paraku reserveeritakse sellisele muutujale alati 9 baiti mälu. Selleks, et teada, millist tüüpi praegu kasutame, sisaldavad eri tüübid lippu taepne. Näidatud on ka, kuidas sellist muutujat kasutada.