
                     SAMMANSATTA DATATYPER


Nr vi nu har klarat av de enkla datatyperna r det dags att brja stta sam-
man dem till strre bitar, och vi brjar med listor eller vektorer. P engel-
ska heter det arrays. En enkel lista kan best av ngra heltal, men det r 
ocks tilltet att stta samman sammansatta datatyper till listor. En lista
kan slunda best av:
                        listor
                        enkla datatyper
                        pekare
                        strukturer	
                        unioner

Eftersom dessa senare typer ocks r sammansatta typer och dessutom kan vara
sammansatta av sammansatta typer som kan vara sammansatta av sammansatta ty-
per etc s kan det bli rtt snrigt till slut. Vi gr ut lite lst med en
endimensionell lista.  

Vi brjar med en enkel liknelse. Frestll dej en lng rad av kldskp i ett
omkldningsrum. I varje skp befinner sig en fotbollsspelares grejor. Fr att
dessa skp ska kunna anvndas p rtt stt s mste arkitekten f veta hur
mnga skp som behvs till fotbollslaget och vad som ska frvaras i dem. D 
frst kan han dimensionera skpraden och frse den med den rtta inredningen.
Exakt likadant r det med C-kompilatorn. Den mste f veta hur mnga element 
vi vill stoppa in i listan och av vilken typ de kommer att vara. Lt oss p
prov dimensionera en lista med heltal:

			int listnamn[ANTAL_ELEMENT];

Det hr ser ut precis som en DIM-sats i Basic, men observera att eftersom det
r vid kompileringen som listans storlek faststlls s mste storleken anges
med ett konstantuttryck eller en symbolisk konstant. I ett interpreterande
sprk som Basic s kan man dimensionera med en variabel, bara den tidigare 
har tilldelats ett vrde. Vi provar ngra andra varianter ocks:

			int listnamn[10];
		
			int listnamn[ANTAL_ELEMENT+10];

			int listnamn[]={0,1,2}; 

Den andra varianten r tillten eftersom uttrycket som anger listlngden har
ett konstant vrde och kan berknas redan vid kompileringen. Den tredje vari-
anten bde dimensionerar listan och ger elementen startvrden. Detta r bara 
tilltet fr globala listor. Deras deklarationer mste allts st utanfr
alla funktioner, eller s mste de ges klassen static, s att de behller 
sina vrden utanfr den funktion dr de deklarerades:

			static int listnamn[]={0,1,2};

I denna version kan deklarationen st inuti en funktionsdefinition utan att 
ngot obehagligt intrffar. Om man vill deklarera och ge startvrden inuti
en funktionsdefinition och nd inte vill ha en global eller statisk lista
s fr man ge startvrdena i tilldelningssatser s hr till exempel:

			{
			int listnamn[3];
			
			listnamn[0]=0;
			listnamn[1]=1;
			listnamn[2]=2;   
			}

Av detta senare kan vi se att elementens numrering brjar med 0, och allts 
blir numret p det sista elementet alltid ett mindre n antalet element i
listan. Se upp hr, det r ltt att gra fel.

En strng r ocks en lista. Det var drfr vi vntade lite frut med att se
p strngens egenskaper. Det finns tv stt att initiera en strng. Observera
att den hr gngen gller det inte en strngkonstant, nu r den en variabel.

	char string[6]={'A','m','i','g','a','\0'};

	char string[6]="Amiga";

Bda dessa metoder ger samma resultat. Glm bara inte att stta dit nollteck-
net i den frsta varianten. Och lgg mrke till att i den andra varianten s 
mste du reservera plats fr det ven om du inte behver placera det dr 
sjlv. Det gr frsts ocks att tilldela en strng vrde, element fr ele-
ment, precis som andra listor:

			{
			char string[6];
			
			string[0]='A';
			string[1]='m';
			string[2]='i';
			string[3]='g';
			string[4]='a';
			string[5]='\0';
			}

Nr vi har kommit s hr lngt in i listornas vrld s gr det inte lngre 
att undvika begreppet pekare, s hr kommer det.



                              PEKARE


Var och en som har sett en listning av ett C-program har nog lagt mrke till
alla dessa asterisker. Deras funktion r bland annat att markera att den va-
riabel som de str framfr r pekare. En pekare r frsts ngot som pekar p
ngot. Precis som ett barn i en leksaksaffr s kan man i C peka p nstan 
allt. Hr ska vi inskrnka oss till att peka p listor. Pekaren r i sjlva 
verket bara ett stt att ange en adress med en symbol i stllet fr siffror.
Adressen till ngot i minnet r frsts en konstant, eftersom data inte bru-
kar flyta omkring hur som helst utan som regel stannar dr man skriver det.
Men man kan frsts ge en variabel vrdet av en pekare, och d har man allts
ftt en pekarvariabel. Om den pekar p en lista med tecken, allts en strng,
s r det frsts en pekare till typen char och deklareras s hr:

		char *lista2 = "Amiga";

Tidigare sg vi att man kunde deklarera och initiera en lista s hr:

		char lista1[] = "Amiga";

Det r faktiskt s att i bda fallen s r listnamnet en pekare, nmligen 
adressen till det frsta elementet i listan, och vi var ju nyss p det klara 
med att en pekare var ett stt att skriva en adress. I det senare fallet kan
vi ocks konstatera att lista1 r en pekarkonstant, eftersom listans adress 
aldrig kan ndras. Det gr naturligtvis att addera ett heltal till pekarkon-
stanten fr att peka p ett annat element i listan. Slunda pekar lista1+1 
frsts p det andra elementet. Dremot gr det inte att frndra vrdet p
lista1. Den frsta versionen, pekarversionen, r nstan detsamma, men med den
viktiga skillnaden att kompilatorn inte bara skapar en lista och initierar 
den utan ocks reserverar minne fr pekarvariabeln lista2. Frn brjan pekar
den frsts p strngens frsta element, men eftersom den r en variabel s
r det mjligt att ndra dess vrde s att den pekar p ngot helt annat. Det
r till exempel tilltet att inkrementera den s att den pekar p nsta ele-
ment. Den skulle ocks kunna tilldelas vrdet av en annan pekarkonstant s 
att den sedan pekar p en helt annan strng. Den ursprungliga strngen ligger
frsts kvar i minnet, men adressen till den r frsvunnen. Lite eftertanke 
ger vid handen att pekarhantering r ett mycket kraftfullt instrument fr att
flytta stora mngder data eftersom man inte behver ndra dess fysiska place-
ring i minnet utan bara manipulera adressen.

Efter denna lilla utvikning s tergr vi till listorna och ser p hur de kan
utvidgas till flera dimensioner.



			FLERDIMENSIONELLA LISTOR


Lt oss i varje skp som vi byggde frut bygga in ngra mindre skp. D skul-
le vi ocks kunna ta bort de ursprungliga drrarna och s str vi dr med n-
got som liknar en hel vgg med bankfack. Vad vi gjorde var att vi sg till 
att varje element i den ursprungliga listan blev en egen lista. Vi skapade 
allts en lista av listor, och det visade sig d att vi fick fram en tvdi-
mensionell lista. Om vi vill stoppa ett heltal i varje fack s mste vi fr-
sts deklarera en tvdimensionell lista av typen int:

		int listnamn [11][7];

Hr har vi allts plats till 77 heltal, och denna form av lista ska man fr-
sts anvnda om ens data har ngon sorts gruppvis samhrighet. Annars skulle
det vara minst lika bra med en endimensionell lista med 77 element.

Nyss sg vi att en strng var detsamma som en lista av tecken. En lista med
strngar blir allts detsamma som en lista med listor av tecken, allts en
tvdimensionell teckenlista. Lt oss anta att i stllet fr att var och en av
spelarna stoppar ett par fotbollsskor i vart och ett av sina sju fack, s
vill som spara sina kompisars namn och adress i en lista av strngar. Fr 
att inte behva en tredimensionell lista s njer vi oss med en spelare:

		char adress[5][18];
		
		adress[0] = "Pelle";
		adress[1] = "Plutt";
		adress[2] = "gatan 8";
                   etc etc

Hr ser vi att vi slsar med minne eftersom inte alla strngarna r s lnga
att de fyller ut det reserverade minnesutrymmet. nd mste vi reservera s
mycket att den lngsta strngen fr plats verallt. Ett stt att spara ut-
rymme r att i stllet fr en lista med strngar skapa en lista med pekare 
till strngar:
		
		char *adress[5];

		adress[0] = "Pelle";
		adress[1] = "Plutt";
  		   etc etc

Nu reserverar kompilatorn bara plats fr de verkliga strnglngderna. Nack-
delen som du mste kpa r att en lista med pekare inte kan initieras sta-
tiskt. Om du ger nyckelordet static i det senare fallet s kommer visserligen
pekarna att lagras i statiskt utrymme, men strngarna r externa konstanter.
I det frsta fallet kommer hela listan av teckenvariabler att hamna i lokalt,
statiskt utrymme.  

Innan vi trasslar in oss fr mycket bland pekare och strngar, om vilka det 
skulle g att skriva en hel bok, s byter vi raskt mne och kastar oss ver
strukturer.


                             DATASTRUKTURER

        
Nstan allt i en Amiga arbetar med den sammansatta datatypen struktur. Det 
gr att gra strukturer av praktiskt taget allt i datavg. En struktur pmin-
ner lite om en lista med den stora skillnaden att i strukturen kan man blanda
alla frekommande datatyper och p ett bekvmt stt sammanfra data som hr 
ihop till en enda variabel, en strukturvariabel. Sedan kan vi arbeta med an-
tingen strukturen eller en pekare till strukturen utan att f skrivkramp. Fr
att inte jag ska f sdana problem nu s tar vi ett enkelt exempel ur Amiga-
vrlden, nmligen struct image, som anvnds fr att verfra grafikdata i en
del enklare sammanhang:

		struct image
		{
		   SHORT	LeftEdge;
		   SHORT 	TopEdge;
		   SHORT	Width;
		   SHORT	Height,Depth;
		   USHORT	*ImageData;
		   UBYTE	PlanePick,PlaneOnOff;
		   struct 	Image *NextImage;
		}min_bild;

Hr hittar vi, frutom fem vrden av typen kort heltal, en pekare till ett 
icke teckensatt heltal, tv bytes utan tecken och en pekare till en struktur.
Allt detta kan sedan behandlas som en enda variabel. Hur stor vinsten blir
inser man efter att ha lst igenom ngra strukturdefinitioner i Amigamanua-
lerna. Det r inte alltid som det rcker med en sida fr en struktur.  

Frutom sjlva strukturdefinitionen s har jag ocks lagt in deklarationen av
en variabel av typen image som heter min_bild. Det gr ocks bra att initiera
den med startvrden direkt ungefr som med en lista om den str utanfr alla
funktioner. Ska den initieras inuti en funktion s mste den deklareras som 
static. 

Det gr att komma t vrdet p ett enstaka element i en struktur genom ele-
mentoperatorn, som ser ut som en vanlig punkt. Slunda innebr uttrycket
min_bild.LeftEdge = 100; att elementet LeftEdge i imagestrukturen min_bild
tilldelas vrdet 100. Om man i stllet fr namnet p strukturen arbetar med
en pekare till strukturen, lt oss kalla den fr min_pekare, s mste man
anvnda den indirekta elementoperatorn som bestr av en pil, gjord av ett 
bindestreck och ett strre ntecken: ->. Om allts min_pekare pekar p struk-
turen min_bild s r min_bild.LeftEdge detsamma som min_pekare->LeftEdge.


	
			FLT, EN MINISTRUKTUR


Ofta frekommer det data som kan anta bara ett ftal olika vrden, kanske 
bara tv, flaggvariabler till exempel. Att d behva ta till en hel byte fr
ett vrde som kan rymmas i en bit knns ondigt. I C finns det en mjlighet
att definiera en struktur dr de olika elementen kan vara ner till en bit 
lnga. Dessa element kallas fr flt, och bestr helt enkelt av ett antal p
varandra fljande bitar inom en unsigned int. Det finns i sprkets definition
inga begrnsningar p typen fr flten, men  andra sidan s behver en C-
kompilator bara klara typen unsigned int, s om man vill ha flyttbar kod r 
det bst att begrnsa sig till sdana flt. Listor av flt r inte tilltna,
och inte heller kan man adressera flt via pekare. Adressoperatorn &, som vi
kommer till senare, kan inte anvnds p flt. Vi testar ett enkelt exempel p
en deklaration av ngra flt:

                struct provflt
                {
                   unsigned FLAGGA1 : 1;
                   unsigned FLAGGA2 : 1;
                                    : 2;
                   unsigned FLAGGA3 : 1;
                   unsigned KOD1    : 4;
                                    : 0;
                   unsigned KOD2    : 7;
                }test;

Inlagt i strukturen ligger tv namnlsa flt, det frsta med lngden 2, och
det kommer att innebra att det uppstr ett mellanrum mellan FLAGGA2 och 
FLAGGA3 om precis tv bitar. Om man i stllet anger lngden 0 p det namn-
lsa fltet s medfr det att resten av den int som innehller flten blir
tom och att nsta flt kommer att placeras i nsta int. Ytterligare en kom-
plikation som kan uppst r om de angivna flten inte gr jmnt ut i en int.
Det r inte tilltet fr kompilatorn att lta ett flt passera grnsen mellan
tv int, utan i stllet kommer det flt som skulle behva delas att i sin 
helhet fras ver till nsta int. 

Tilldelning av vrden till flt sker precis som till element i en struktur. 
Se bara upp s att vrdena verkligen fr plats i fltet.

		test.FLAGGA1 = 1;
		test.FLAGGA2 = 0;
		test.FLAGGA3 = 1;
		test.KOD1 = 9;
		test.KOD2 = 12;

S hr fungerar det bra, men frsk inte att tilldela ngon av flaggorna vr-
det 2 eller KOD1 ngot strre n 15.



			      UNION


Ettv annat anvndbart stt att spara utrymme i C-program r att lta olika 
data samsas om samma utrymme, vilket gr jttebra om det bara r s att de
inte behvs samtidigt. Unioner frekommer i ett par fall i Amigas system, 
de r motiverat att anvnda demi hantering av devices, eftersom  det dr kan 
uppst ganska lnga ker av kommandon som vntar p att utfras. Det r d
vrdefullt att kuna spara ett par bytes i varje struktur. Vi njer oss med 
ett enkelt exempel som visar hur det r mjligt att spara antingen ett flyt-
tal eller ett heltal i samma minnesutrymme:

		union flyt_int {
		   int   heltal;
		   float deltal;
		} test;

Om vi nu anvnder denna union i ett program s kan vi tilldela variabeln 
heltal eller deltal ett vrde. Observera att det r samma minnescell som vi
placerar ett tal i antingen vi vljer int eller floatvarianten. Vi provar:

		test.heltal = 4444;

Nu kommer kruxet! Man mste sjlv hlla reda p vilken variabel man har ini-
tierat! Dummar man sig och frsker lsa test.deltal s kommer programmet
lydigt att g till rtt minnescell och lsa det som har stoppats dit som 
test.heltal. Dr str d ett visst bitmnster. Nu tolkas det som ett flyttal
och det bestr ju av bde en brkdel och en exponent. Resultatet blir ngot
helt annat n vad man vntat sig. I Lattice C p IBM blir det ngot i stil
med 4.3387349787e-29. och det r ju inte ens i nrheten av 4444. 

Det r tilltet att skapa unioner av samma typer som strukturer. Det gr 
till och med att skapa unioner av strukturer, och unioner fr ing som en del
i en struktur. Inte heller r man begrnsad till tv olika variabler, utan 
det gr bra att lta mnga olika typer samsas om platsen. Om de olika elemen-
ten har olika utrymmesbehov, s reserverar kompilatorn s mycket utrymme att
det strsta elementet fr plats.
 

 
 			   TYPEN ENUM				


Typen enum finns inte med i den ursprungliga definitionen av C men finns i 
mnga kompilatorer i alla fall. Den r ett stt att definiera variabler som 
bara kan anta vrden inom en begrnsad, upprknad mngd. Variablerna behand-
las som int, men kan bara anta de upprknade vrdena. Ett typexempel r en
variabel som betecknar veckodag. Vi definierar den s hr:

        enum dag { man, tis, ons, tor, fre, lor, son }; 

        enum dag dag1, dag2;

Den frsta raden talar om att det finns en typ som heter dag, och att den kan
anta de vrden som str inom definitionens klammer. Dessa vrden r konstan-
ter av typen dag. Nsta sats deklarear tv variabler av typen dag, nmligen 
dag1 och dag2. De kan tilldelas vrden av typen dag men inga andra vrden.

        dag1 = man;
        dag2 = fre;

Enumkonstanter lagras i datorn som om de vore heltal. Om man inte gr ngot
t det s kommer de att vara synonymer med heltalen 0 till 6 i det hr fal-
let. Det gr att tvinga p dem andra vrden genom att specificera dem i de-
klarationen:

        enum dag { man=2, tis, ons, tor, fre, lor=11, son };

Nu r man allts 2, men vad blir tis? Jo helt enkelt 3, ons blir 4 och s 
vidare. Lor blir 11 och son 12.

Enumtyper har mycket begrnsat anvndningsomrde, endast fljande operationer
r tilltna:

        Man kan tilldela en enumvariabel vrdet av en enumkonstant:

        dag1 = man;

        Inga andra tilldelningsoperatorer kan anvndas.

        Man kan jmfra men bara fr likhet eller inte likhet:

        if( dag2 == man );    if( dag1 != tis );

        Inga andra relationsoperatorer kan anvndas.

        Aritmetiska operatorer kan anvndas p enumkonstanter men inte p
        enumvariabler:

        dag1 = man + tis;    dag2 = ons - man;

        Resultatet mste frsts hamna inom det tilltna omrdet, s oftast
        r det knappt meningsfullt med sdana operationer.

        Det gr inte att addera heltal till enumkonstanter, men det kan man
        ordna med typkonvertering enligt nsta avsnitt, fast d kan man lika
        grna anvnda int frn brjan.

Varfr ska man d anvnda ngot s begrnsat som enumtyper? Jo det finns ett
par frdelar. Fr det frsta s blir programmet lttlst, nrmast sjlvdoku-
menterande, och fr det andra s fr man en typkontroll s att man inte av 
misstag brjar lgga ihop pplen med pron och fr det till 18 krusbr.

 

                     TYPER OCH TYPKONVERTERING

 
Vi berrde tidigare de olika standardtyperna i C, men man kan ju ocks sjlv
definiera egna typer utgende frn standarden. I Amigasystemet finns det 19
olika egendefinierade enkla typer, som har tillkommit nrmast fr att gra
programmen mer lttfrsteliga och lttskrivna.

Amiga     Standard          kommentar

LONG      long              32 bitars integer med tecken
ULONG     unsigned long     32 bitars integer utan tecken
LONGBITS  unsigned long     32 bitar som manipuleras individuellt 
WORD      short             16 bitar integer med tecken
UWORD     unsigned short    16 bitars integer utan tecken
WORDBITS  unsigned short    16 bitar som manipuleras individuellt
BYTE      char              8 bitar med tecken
UBYTE     unsigned char     8 bitar utan tecken
BYTEBITS  unsigned char     8 bitar som manipuleras individuellt
STRPTR    unsigned char *   pekare till strng
APTR      unsigned char *   pekare till absolut minnesadress
SHORT     short             detsamma som WORD, anvnd WORD istllet
USHORT    unsigned short    detsamma som UWORD, anvnd UWORD istllet
FLOAT     float             flyttal, enkel precision 
DOUBLE    double            flyttal, dubbel precision
COUNT     short             anvnd som rknare
UCOUNT    unsigned short    anvnd som rknare
BOOL      short             anvnd som Boolesk variabel (TRUE/FALSE)
TEXT      unsigned char     ASCII-tecken

Som synes s finns det flera stt att ange samma typ av storhet, och vilken
som br anvndas framgr av dess betydelse. De enda av dessa typer som inte
har lngden klar direkt genom definitionen r de bda pekartyperna. Dessa
omfattar 4 bytes, dvs samma lngd som integer i Lattice. 

Det finns ocks ett par andra definitioner som r bra att knna till som 
gller variabeltyper:

GLOBAL    extern            extern variabel nr den deklareras
IMPORT    extern            extern variabel nr det refereras till den
STATIC    static            lokal statisk variabel
REGISTER  register          en registervariabel, frhoppningsvis
VOID      void              funktion som inte lmnar ngot vrde

Det kan frefalla tokigt att definiera om en typ till densamma fast med stora
bokstver, men det r frsts fr att ge likformighet, eftersom man i C br
skriva egendefinierade storheter med stora bokstver. Ordet frhoppningsvis
i kommentaren till registervariabeln kommer att f sin frklaring nr vi 
snart ska behandla lagringsklasser.

Om det nu skulle vara s att en variabel har en typ som inte passar i ett 
visst sammanhang s finns det i C till skillnad frn Pascal och en del andra
sprk mjlighet att gra en typkonvertering. Antingen kan denna ske automa-
tiskt eller s kan man styra den sjlv. I Amigasystemet med sin rika flora av
typer, srskilt alla dessa pekare till olika strukturer, r det ofta ndvn-
digt att typkonvertera i samband med funktionsanrop fr att f tillbaka ett
vrde av rtt typ. 

Nr man typkonverterar, antingen det sker automatiskt eller manuellt, s 
gller det att veta vad man vill, annars kan det g illa. 

Vid alla operationer som sker med tv typer, omvandlas det vrde som har den
lgsta typen till samma typ som den hgsta, uppklassning. Att en typ r hgre
innebr att den kan anta strre vrden. Hgst blir d double, drefter fljer
float, long, int, short och char. Om en typ r unsigned s r den hgre n 
samma grundtyp med tecken. Uppklassning leder knappast till ngra problem. 
Dremot intrffar ocks den motsatta operationen, nerklassning. Detta sker
om ett resultat ska tilldelas en variabel, om denna har lgre typ n resul-
tatet. Om det skulle vara s att resultatet r s stort att det inte fr 
plats i en variabel av den angivna typen kommer det att trunkeras (skras av)
p ngot stt och ge ett resultat som knappast kan frutses. 

Kompilatorn gr faktiskt ocks en annan typkonvertering utan att tala om det.
Vid flyttalsberkningar omvandlas alla float till double vid berkningarna 
fr att inte i ondan tappa noggrannheten. Sedan omvandlas resultatet till-
baka till float om det r deklarerat som sdant. Den hr konverteringen r
helt riskfri, men det r sknt att veta att kompilatorn r s omtnksam.

Att gra en styrd typkonvertering r enkelt. Det r bara att framfr sin
variabel (eller konstant) skriva en styroperator, som bestr av den nskade
typens namn inom parentes. I Amigaprogrammering frekommer det mest vid funk-
tionsanrop nr man vill att funktionen ska returnera ett vrde av en viss 
typ. Normalt s r alla funktioner i C av typen int, dvs returnerar en int, 
om de inte deklareras annorlunda. Ett bra exempel p en situation dr typkon-
vertering mste anvndas r nr man ska ppna ett library, eftersom man d
ska f tillbaka en pekare av rtt typ, men funktionen anvnds till alla sor-
ters library, s den kan inte deklareras till rtt typ.

IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",1)

Hr ser vi att funktionens typ omvandlas till en pekare till en IntuitionBase
struktur genom styroperatorn, och allt r frid och frjd. Om inte denna typ-
konvertering grs kommer kompilatorn att protestera med ett felmeddelande i 
stil med "Type mismatch in function call line nnn".   

Mnga lrobcker i C avrder frn typkonverteringar, men vi Amigaprogramme-
rare mste lra oss den konsten ordentligt.
