C! Rakenteiset tietotyypit Moniulotteiset taulukot 22.2.2018
Agenda Rakenteiset tietotyypit Vilkaisu 6. kierroksen tehtäviin Moniulotteiset taulukot Esimerkki Seuraava luento to 8.3. Ilmoittautuminen ohjelmointikokeeseen aukeaa maaliskuun alussa 2
Tietotyypeistä Toistaiseksi ollaan tarkasteltu primitiivisiä tietotyyppejä ja taulukoita Esimerkiksi funktiolle välitetty kukin tyyppi erikseen Rakenteisella tietotyypillä voidaan useita muuttujia koota yhteen omaksi tietotyypikseen Rakenteista tietotyyppiä voidaan käyttää kaikkialla missä on muitakin tietotyyppejä Funktion parametrit ja paluuarvo, muuttujat Monissa muissa kielissä luokat hieman vastaavia C:n rakenteet sisältävät tietoa, ei toimintoja 3
Osoittimet tietorakenteessa #include <stdlib.h> #include <string.h> struct person { char *name; int age; }; int main(void) { struct person lady; struct person dude; const char *n1 = "Kirsi"; name asettaminen kannattaa tehdä huolella Suora sijoitus lady.name = Kirsi asettaisi viittauksen kirjoitussuojattuun muistiin (olisi ok jos const char* name) } lady.name = malloc(strlen(n1) + 1); strcpy(lady.name, n1); lady.age = 22; Tässä tapauksessa tila merkkijonolle varattava dynaamisesti 4
Tietueen kopiointi struct person lady = { "Kirsi", 22 }; struct person dude; dude = lady; // what?? lady: char *name = 0x01A5 int age = 22 0x01A5 (read-only) Kirsi\0 dude: char *name = 0x01A5 int age = 22 5
Tietorakenteen syväkopiointi struct person { char *name; int age; }; struct person personcopy(struct person orig) { struct person new; new.name = malloc(strlen(orig.name) + 1); strcpy(new.name, orig.name); new.age = orig.age; return new; } const char *name = 0xA110 int age = 22 const char *name = 0xA125 int age = 22 Kirsi\0 Kirsi\0 6
Taulukko tietueen jäsenenä Taulukko kopioituu tietueen osana myös Kätevä ominaisuus: paljasta taulukkoa ei voi kopioida sijoittamalla Tietueen osana taulukon kopiointi helpottuu voidaan myös välittää taulukko funktiolle ja palauttaa taulukko funktiosta struct person { char name[40]; int age; }; 7
Tietueen kopiointi lady: struct person { char name[40]; int age; }; struct person lady = { "Kirsi", 22 }; struct person dude; dude = lady; Tietorakenteen koko nyt isompi kuin aiemmissa esimerkeissä char name[40] = int age = 22 dude: char name[40] = int age = 22 Kirsi\0 Kirsi\0 Nyt nimeä voi muuttaa vaikuttamatta toiseen tietueeseen 8
Rakenteinen tietotyyppi osoittimena (Katso materiaali) 10
Dynaaminen taulukko Yhden alkion koko tavuina: [0] sizeof(struct oodi) [1] [2] struct oodi *array student course grade 0 struct date strlen(array[0].course) + 1 Oleellinen funktio taulukon varaamisessa: realloc 12
Linkitetty lista (tehtävä Jono ) struct queue first last - Kukin listan elementti on varattu dynaamisesti erillisellä malloc:lla - Kukin elementti sisältää muistiviittauksen seuraavaan elementtiin - Tässä: viittaukset listan alkuun ja loppuun struct student student 0 name next student name next 0 student name next 0 13
Linkitetty lista (tyhjä) struct queue first last 14
Linkitetty lista (ensimmäinen alkio) first last struct queue - Varaa muistia struct student:lle (muista name-merkkijono) - Linkitä first- ja last-osoittimet (mallocin palauttama osoite) - Next-osoitin alustetaan NULL struct student student 0 name next 15
Linkitetty lista (alkion lisääminen) struct queue first last - Varaa muisti struct studentille (muista name) - Linkitä listan viimeisen alkion next-osoitin tähän uuteen - Päivitä last-osoitin uuteen viimeiseen alkioon struct student student 0 name next struct student student 0 name next 16
Linkitetty lista (alkion poistaminen) struct queue first last - Aseta poistettavaa edeltävä next-osoitin osoittamaan poistettavaa seuraavaan alkioon (tai NULL jos viimeinen) - Vapauta muisti poistettavan alkion osalta - Huom: erikoistapaukset ensimmäinen tai viimeinen struct student student 0 name next student name next 0 student name next 0 17
Moniulotteiset taulukot Moniulotteinen taulukko C:ssä on taulukoista koostuva taulukko Kurssilla keskitytään kaksiulotteisiin taulukoihin, mutta ulottuvuuksia voi olla kuinka monta tahansa Moniulotteinen taulukko voidaan muodostaa eri tavoin Staattinen: ulottuvuudet määräytyvät määrittelyvaiheessa. Dynaaminen: koko määräytyy ajon aikana, tarvittava tila varataan malloc:lla keosta Esim: varataan taulukko, jossa osoittimet yksiulotteisiin taulukoihin Taulukko merkkijonoja, eli kaksiulotteinen taulukko merkkejä 18
Esimerkki staattisesta taulukosta #include <stdio.h> int main(void) { int matrix[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; int i,j; } // Print the matrix in rectangular format for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) { printf("%d ", matrix[j][i]); } printf("\n"); } 19
Edellinen taulukko muistissa Kaikki alkiot peräkkäisissä muistipaikoissa Periaatteessa voisi ottaa osoittimen ensimmäiseen alkioon, ja iteroida kuten yhdeksän alkion yksiulotteista taulukkoa Eli: int *p = &matrix[0][0]; kasittele(*p++); jne int matrix[3][3] 1 2 3 4 5 6 7 8 9 20
Moniulotteinen taulukko funktioparametrina Staattisen taulukon välittäminen funktiolle vaatii tarkkuutta syntaksin kanssa Taulukko taulukoita vs. taulukko osoittimia Jatkoa edelliseen esimerkkiin void printarray(int arr[][3]) { int i,j; // Print the matrix in rectangular format for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) { printf("%d ", arr[j][i]); } printf("\n"); } } int arr[][3] tai int (*arr)[3] 21
Kaksiulotteinen taulukko ja osoittimet Kaksiulotteinen taulukko voidaan esittää osoittimien avulla, esim. kolme riviä int-osoittimia int *array[3]; Kolme riviä int-osoittimia (jotka kukin osoittavat taulukon alkuun) Rivit asetettava erikseen tai esim. varattava dynaamisesti int * int 0x8a15 1 2 3 0x8a28 0x8a3a 4 5 6 7 8 9 22
Useamman asteen osoittimet int** tarkoittaa osoitinta osoittimeen, jonka päässä on kokonaisluku int ** int * int 0x1008 0x8a15 1 Näin voidaan esittää myös kaksiulotteinen taulukko Koska osoittimella voidaan viitata taulukon ensimmäiseen alkioon Myös muita käyttöjä Esim. osoittimen palauttaminen parametrin kautta 23
Useamman asteen osoittimet int** ja kaksiulotteinen taulukko int ** int * int 0x1008 0x8a15 1 2 3 0x8a28 4 5 6 0x8a3a 7 8 9 Taulukkojen koot täytyy tietää jollain tapaa (muuttuja, loppumerkki, tms.) 24
Moniulotteinen taulukko dynaamisesti varattuna 2-ulotteinen taulukko voidaan varata kokonaan dynaamisesti (mallocin avulla) Ensin varataan 1-ulotteinen taulukko osoittimia varten Sitten varataan 1-ulotteiset taulukot kullekin riville (voidaan varata myös muilla tavoin) Vapautus pitää muistaa hoitaa oikein Käänteisessä järjestyksessä Ensin vapautetaan kukin rivi Lopuksi päällimmäinen taulukko, jossa osoittimet riveihin 25
Merkkijonotaulukko Yleinen kaksiulotteisen taulukon tapaus Merkkijono on taulukko merkkejä (char) taulukko merkkijonoja on kaksiulotteinen taulukko merkkejä Taulukon eri rivit (merkkijonot) voivat olla keskenään eri mittaisia Kaksi erilaista tapaa Vakiomerkkijonot tai dynaamisesti varatut: taulukko osoittimia const char *lista[] = { Yksi, Kaksi, Kolme }; Pinosta varatut muokattavat merkkijonot: taulukko taulukoita char lista[][6] = { Yksi, Kaksi, Kolme }; 26
Esimerkki merkkijonotaulukosta Vakiomuotoiset merkkijonot: #include <stdio.h> int main(void) { char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; } for (int i = 0; i < 12; i++) { printf("%s\n", months[i]); } Muokattavat merkkijonot (varataan 20 merkkiä kullekin): char months[12][20] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; 27