Sisält ltö C-ohjelmointi Luento 6: tietueet ja joukot 23.9.2008 Tietueet (struct( struct) Määrittely Rakenne Käyttö Lueteltu tyyi (enum( enum) Union Määrittely Rakenne Käyttö Linkitetty lista 1 2 Tietueet (struct( struct) Tietue - Määrittely Tietueessa voi olla vain datakenttiä (vrt. luokka) Tietueen kentät voivat olla keskenää ään erilaisia. Kenttiä ei voi iilottaa, vaan ne kaikki näkyvät (vrt. ublic). Ajatellaan hetkinen Javan luokkatoteutusta Pikkuvarasto (A.Wiklan Java-kurssi kurssi). Tällä luokalla on metodi vievarastoon. Metodikutsu x.vievarastoon(y) koskettaa kahta oliota: This, tässä x ja y, joka välitettiin arametrina C:ssä voi vastaavaa toimintaa jäljitellä, mutta tuo this on välitettävä funktiolle arametrina, joten C:ssä vastaava funktio olisi muotoa vievarastoon(x,y); struct info char Name[20]; ; struct info i1, i2; Tietueen kenttiin viitataan muodolla nimi.kenttänimi nimi: 1.age = 18; rintf("%s\n", ", i2.name); tyedef struct InfoT char Name[20]; InfoT; InfoT 1; Suositeltavin taa struct info char Name[20]; k1, k2; 3 4 Tietue - Rakenne Tietueelle varataan yhtenäinen muistialue. Kuitenkin muistialueen koko voi olla eri kuin kenttien yhteenlasketut koot, koska viittausten yksinkertaistamiseksi kenttien välejä saatetaan jättää käyttämättä. Esim: sizeof(infot) ) >= 40*sizeof(char)+sizeof(int) Tietue - Käyttö InfoT i1, i2; Tietueen sijoittaminen toiselle i1 = i2 koioi tietueen sisäll llön bitti kerrallaan täsmälleen samanlaisena, mutta ei seuraa kentissä mahdollisesti olevia osoittimia. Tietueita EI voi verrata suoraan: i1 == i2 vaan vertailu on tehtävä itse: strcm(i1.name, i2.name) == 0 && strcm(i1.lastname, i2.lastname) == 0 && i1.age == i2.age 5 6 1
e1: Sisäkk kkäiset tietueet Tietueen kenttinä voi olla myös s tietueita info: salary: Name: lastname: age: e1.info.age = 21; e1.salary = 125.6; tyedef struct char Name[20]; InfoT; tyedef struct InfoT info; double salary; EmloyeeT; EmloyeeT e1; 7 Tietueita käsitellk sitellään usein osoitinmuuttujan kautta: (*).x tai ->x PairT w; PairTP q; PairTP = &w; Osoitin tietueeseen tyedef struct air double x; double y; PairT,, *PairTP* PairTP; PairT x; PairTP ; if((q = malloc(sizeof(pairt))) == ) if((q = malloc(sizeof(struct air))) == ) w.x = 2; ->x = 1; (*).x = 1; *.x = 1; q->y = 3.5; 8 tietue viitearametrina void constructorp(pairtp this, double x, double y) this->x = x; this->y = y; Funktio saa osoittimen Kutsujan aiemmin varaaman tietueen PairT w; muistialueeseen. PairTP ; constructorp(&w,, 1, 2); if(( = malloc(sizeof(pairt))) == ) error; constructorp(,, 1, 2); tietue aluuarvona Funktio voi alauttaa kokonaisen tietueen. Silloin kutsujan on tehtävä alautetusta tietueesta koio, koska alkueräinen vaautuu inosta funktiosta alattua. PairT constructorfunc(double x, double y) PairT ;.x = x;.y = y; Pinossa alautettu return ; tietue on koioitava heti talteen. PairT w = constructorfunc(1, 2.2); /* koio */ 9 10 osoitin aluuarvona Funktio varaa tilan tietueelle, jolloin kutsujan vastuulle jääj vaauttaa tuo varattu tila. PairTP constructor(double x, double y) /* client resonsible for deallocation */ PairTP ; if(( = malloc(sizeof(pairt))) == ) return ; ->x = x; Funktio varaa tilan tietueelle ->y = y; Ja alauttaa osoittimen siihen return ; Kutsuja vaauttaa tilan myöhemmin. PairTP 1 = constructor(1, 2); free(1); 11 osoitin aluuarvona Käyttö toisen funktion arametrina: int comare(const PairTP, const PairTP q) return ->x == q->x q && ->y == q->y; q PairTP 2 = constructor(1, 3); PairTP 3 = constructor(2, 6); int i = comare(3, 2); free(2); free (3); Vältä muistivuotoa! i = comare(1, constructor(3.5, 7)); Tässä kadotetaan osoitin tietueeseen, joten muisti jää vaauttamatta 12 2
Muistilohko tietueista Muistilohkon käsittely k ei riiu lohkon alkioiden tyyistä. Tietueen omiin alkioihin viittaus kuten itsenäisiss isissäkin tietueissa. PairTP rectangle; PairTP aux; double x, y; rectangle if((rectangle= = malloc(4*sizeof(pairt sizeof(pairt)))== )))==) )error; for(aux = rectangle; aux < rectangle + 4; aux++) rintf("enter two double values:"); if(scanf("%lf%lf", ", &x, &y)!= 2) /* error */ break; constructorp(aux,, x, y); air air air air 13 Osoitinlohko tietueisiin Mahdolliset viittaustavat: rectangle[1][0].x rectangle[1]->x (rectangle+1)->x rectangle PairTP *rectangle; if((rectangle= = malloc(4*sizeof(pairtp sizeof(pairtp)))== )))==) )error; for(i = 0; i < 4; i++) rintf("enter two double values:"); if(scanf("%lf%lf", ", &x, &y)!= 2) error; if((rectangle[i] ] = constructor(x,, y)) == ) error; for(i = 0; i < 4; i++) rintf("vertex %d = (%f %f)% f)\n", i, rectangle[i][0].x, rectangle[i][0].y); air 1 x y air air 14 Lueteltu tyyi (enum( enum) Luetellun tyyin määrittelemät järjestetyt vakiot vastaavat järjestyksessä kokonaislukuja 0,1,2 jne. Numeroinnin voi myös aloittaa haluamastaan arvosta tyedef enum ocodes lvalue, rvalue, ush, lus OcodesT; enum ocodes e; OcodesT f; int i = (int)rvalue( int)rvalue; ; /*i=1*/ enum ocodes lvalue = 1, rvalue, ush, lus ; enum ocodes e = lvalue; if(e == ush) int i = (int)rvalue( int)rvalue;/*i=2*/ 15 Lueteltu tyyi funktion aluuarvona Lueteltua tyyiä voi käyttää virhetiedon käsittelyssä Virheilmoitustekstit kootaan taulukkoon, jota indeksoidaan luetellun tyyin alkioita vastaavilla kokonaislukuarvoilla FoerT rocess(); tyedef enum FOPEN, FCLOSE, FOK FoerT; #define TOINT(f) ) ((int)(f int)(f)) )) char *Messages[] = "File can not be oened", "File can not be closed", "Successful oeration", "This can not haen" ; rintf("result of calling rocess() is %s\n",% Messages[TOINT(rocess())]; ())]; 16 Tietue Alkiot eräkk kkäin eli kaikki käytettävissä struct intanddouble double d; ; int double intanddouble Union Union (vrt. Pascalin vaihtuvan- mittaiset tietueet) Alkiot ää äällekkäin eli vaihtoehtoisia int double intordouble union intordouble double d; ; Käyttö yleensä tietueen osana Tietueessa kenttä (tag), joka kertoo miten union- rakenne itää tulkita Käytetään n aljon tietoliikennerotokollissa säästämässä tilaa Kenttiin viitataan samalla istenotaatiolla kuin tietueidenkin kenttiin Union - Käyttö tyedef enum integer, real TagTyeT; tyedef struct TagTyeT tag; union double d; value; TaggedValueT; TaggedValueT v; if(v.tag == integer) v.value.i ; v.value.d ; 17 18 3
Linkitetyt tietorakenteet Abstraktit tietorakenteet Käsitelty Tietorakenteiden kurssilla Kannattaa kerrata kaikki rakenteet sieltä Pino : oeraatiot ush,, o ja emty Jono: oeraatiot enqueue, dequeue ja emty Linkitetty lista rakenteesta ää äätettävä Yhteen suuntaan vai kahteen suuntaan Rengas vai ei Tunnussolmu vai ei Järjestetty vai järjestj rjestämätön Alkiot erilaisia vai sallitaan myös s samanlaiset alkiot Linkitetty lista - Määrittely Tunnussolmullinen yhteensuuntaan linkitetty arvoa käytetään aina listan loun osoittamiseen Huomaa seuraavaan alkioon osoittavan kentän määrittely! tyedef double DataTye; tyedef struct elem DataTye value; struct elem *;,, *P* P; tyedef struct P ;,, *P* P; P ; 19 20 Linkitetty lista Tunnussolmun luonti ja oisto Listan luonti P construct(void) ) Tehdää ään n vain P ; tunnussolmu ja if(( = malloc(sizeof())) asetetaan linkit == ) return ; -> = ; return ; Listan oistaminen Alkioiden oistoon void destruct(p *this) oma funktio clear(*this); /* alkiot ois */ Tässä vain free(*this); tunnussolmun *this = ; tilan vaautus Linkitetty lista - Käyttö Kaikkien listaa käsittelevien toimintojen täytyy säilyttää seuraavat invariantit: Tyhjälle listalle ätee -> on. Eätyhj tyhjälle listalle ätee, että viimeisen alkion - kentän arvo on. 21 22 Linkitetty lista - Käyttö Oeraatioita: Listan läikl ikäynti Aina linkien kulkusuuntaan Listaan lisää ääminen Alkuun, louun, keskelle Listasta oistaminen Alusta, lousta, keskeltä 23 Listan läikl ikäynti aux void rintall(const P this) P aux; for(aux=this =this->; aux!=; aux=aux aux->) rintf("%f\n", ", aux->value); /* idetään aux edellisessä alkiossa */ If ((->!= ) && (->->!=)) for(aux = ->; aux->->!= ; aux = aux->)... 24 4
Lisäys listan alkuun Viimeisen alkion oisto listasta value 3.5 int insertfront(p this, DataTye d) P aux; if((aux = malloc(sizeof())) == ) aux-> = this->; /* save state */ aux->value = d; this-> = aux; 25 int deletelast(p this, DataTye *value) P aux; if(this-> == ) /* emty list */ if(this-> >-> > == ) /* single */ *value = this-> >->value; >value; free(this->); this-> = ; for(aux = this->; aux-> >->!= ; aux = aux->) ; /* aux viimeistä edelliseen */ *value = aux-> >->value; >value; free(aux->); aux-> = ; 26 Koko listan tuhoaminen Tuhotaan alkio kerrallaan edetää ään n lista alusta louun int deletefirst(p this) P aux = this->; if(aux == ) /* emty list */ this-> = aux->; free(aux); void clear(p this) while(deletefirst(this)) ; this-> = ; 27 Moduuli List Tehdää ään oma käännösyksikkö (moduuli), joka sisält ltää listan määrittelyt ja käsittelyfunktiot Tässä tehtäväll llä moduulilla on seuraavat iirteet Moduuli ystyy käsittelemään useita eri listoja Moduuli käsittelee void-tyyist tyyistä dataa, joten eri listoihin voi sijoittaa eri tyyistä tietoa Listan sisäinen inen toteutus ei näy käyttäjälle Listan käyttäjä vastaa datan rakenteesta ja käsittelystä Tyyin määm äärittelyt: ListItem ja List Funktiot: CreateList, CreateItem, AddTail, AddHead, ListLength, DeleteList, PrintList, EmtyList Lähde: Jan Lindströmin verkkomoniste 28 Moduulin tarjoama rajainta: list.h #ifndef MY_LIST_LIBRARY #define MY_LIST_LIBRARY /* Määritellään listatyyit */ tyedef struct listitem struct listitem *Next; /* Seuraava alkio listassa */ struct listitem *Prev; /* Edellinen alkio listassa */ void *Data; /* Tietoalkio */ unsigned long Size; /* Tietoalkion koko */ ListItem; tyedef struct ListItem *Head; /* Listan alku */ ListItem *Tail; /* Listan lou */ unsigned long Items; /* Listan alkioiden lkm */ List; 29 List.h jatkuu /* Listakirjaston tukemat funktiot */ extern List *CreateList(void); /* Luo uusi lista */ extern ListItem *CreateItem(void *Data, unsigned long Size); /* Luo lista-alkio */ extern int AddTail(List *,ListItem *); /* Lisää listan louun */ extern int AddHead(List *,ListItem *); /* Lisää listan alkuun */ extern unsigned long ListLength(List *); /* Laske listan ituus */ extern void DeleteList(List *); /* Tuhoa lista */ extern void PrintList(List *); /* Tulosta listan sisältö */ extern int EmtyList(List *); /* Tarkista onko lista tyhjä */ #endif 30 5
List.c : CreateList /* Varataan muistia uudelle listalle ja alustetaan kentät */ List *CreateList(void) List *uusi; if(!(uusi = (List *)malloc(sizeof(list)))) return ; uusi->head = ; uusi->tail = ; uusi->items = 0; return uusi; 31 List.c: CreateItem /* Varataan muistia uudelle listan alkiolle ja alustetaan kentät */ ListItem *CreateItem(void *Data,unsigned long size) ListItem *uusi; /* Jos järkevää dataa ei ole annettu oistu */ if (Data == ) return ; if(!(uusi = (ListItem *)malloc(sizeof(listitem)))) return ; if(!(uusi->data = (void *)malloc(size))) return ; uusi->next = ; uusi->prev = ; memcy(uusi->data,data,size); uusi->size = size; return uusi; 32 List.c: AddTail List.c: AddHead /* Lisätään alkio listan louun */ extern int AddTail(List *lista,listitem *item) if (lista == item == ) if ( lista->head == ) lista->head = lista->tail = item; lista->tail->next = item; item->prev = lista->tail; lista->tail = item; lista->items++; 33 /* Lisätään alkio listan alkuun */ extern int AddHead(List *lista,listitem *item) if (lista == item == ) if ( lista->head == ) lista->head = lista->tail = item; lista->head->prev = item; item->next = lista->head; lista->head = item; lista->items++; 34 List.c: ListLength ja EmtyList List.c:DeleteList /* 'Lasketaan' listan ituus */ unsigned long ListLength(List *lista) if ( lista == ) return lista->items; /* Tarkista onko lista tyhjä */ int EmtyList(List *lista) if ( lista == ) if (lista->head == ) /* Tuhotaan koko lista */ void DeleteList(List *lista) ListItem *tm; if ( lista == ) return; tm = lista->head; while(tm!= ) tm = lista->head->next; free(lista->head->data); free(lista->head); lista->head = tm; free(lista); 35 36 6
List.c: PrintList /* Tulostetaan koko lista */ void PrintList(List *lista) ListItem *tm = lista->head; rintf(" "); while(tm!= ) rintf("%s ",(char *)tm->data); tm = tm->next; rintf(" -\n"); Tässä oletetaan, että listassa on merkkijonoja. Paremi ratkaisu olisi käyttää funktioarametria data-alkioiden käsittelyyn. Käyttäjä joutuisi silloin itse ohjelmoimaan alkioiden käsittelyn, mutta listan käyttöalue kasvaisi. 37 Harjoitustyö Käytettävät t iirteet: Linkitetty tietorakenne osoittimilla (ino, lista, uu, hajautustaulu, ), mikäli tehtävä sallii Tiedosto (tekstitiedosto tai binää ääritiedosto) Komentoriviarametrit (jos ei muuta järkevj rkevää,, niin ainakin h h oastus) Funktioita arametreineen mielekkää äästi Käännyttävä laitoksen Linux-ym ymäristössä gcc:n arametreilla ansi edantic ja Wall ilman varoituksia Vähintään n kaksi kääk äännösyksikköä ja make Koodi dokumentoitava järkevj rkevästi Erillinen lyhyt rakennedokumentti ja käyttk yttöohje 38 Aiheet Aiheet käsitellk sitellään n seuraavalla luennolla. Aiheen valinta Valitse aihe e 3.10 mennessä Valinnan voi kertoa sähks hköostilla: Paivi.Kuuelomaki@cs.helsinki.fi Tai lokakuun ensimmäisen isen viikon luennoilla tai harjoituksissa. Omista (varsinkin simulointityyisistä) aiheista voi neuvotella luennoijan kanssa 39 40 7