Tietueet Tietueiden määrittely Tietue on tietorakenne, joka kokoaa yhteen eri tyyppistä tietoa yhdeksi asiakokonaisuudeksi. Tähän kokonaisuuteen voidaan viitata yhteisellä nimellä. Auttaa ohjelmoijaa järjestelemään ja käsittelemään tietoa joustavasti, tiedon abstrahointi korkean tason rakenteilla. C-kielessä on vaihtoehtoisia tapoja tietueiden määrittelyyn. 415
tapa 1. Tietuetyyppi määritellään avainsanalla struct. Rakenteen yleinen muoto on seuraavanlainen struct nimi { }; tietotyyppi kenttä1; tietotyyppi kenttä2;... tietotyyppi kenttän; Esimerkiksi: 416
#define NIMEN_PITUUS 11 struct tuote { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ }; Tämä esittely ei varaa vielä tilaa nimi- eikä hinta -muuttujille. struct tuote kertoo kääntäjälle, että voidaan varata tilaa muuttujille joiden sisäinen rakenne on kuvauksen mukainen, eli sisältää kentät nimelle ja hinnalle. 417
Varataan tilaa muuttujalle, joka on määriteltyä struct -tyyppiä struct tuote1; Nyt muuttuja tuote on sisäiseltä rakenteelta: 418
tuote1 nimi-kentän sisältö hinta 419
Muuttujan määrittelyssä muuttuja voidaan myös alustaa alustuslistan avulla. Kääntäjä kopioi alustuslistan sisällön vastaaviin muuttujiin. Eli merkkijono "HK_Blöö" kopioidaan nimi -merkkijonomuuttujan sisällöksi ja 2.99 kopioidaan hinta -muuttujan sisällöksi. struct tuote tuote2 = {"HK_Blöö", 2.99 }; 420
tuote2 H K _ B l ö ö \0 nimi-kentän sisältö 2.99 hinta 421
Tapa2: Tietueelle määritellään oma tietotyyppi käyttämällä typedef - määrettä. Tietuetyypin määrittelyn syntaksi: typedef struct { tietotyyppi kenttä; tietotyyppi kentta;... tietotyyppi kenttän; } Tietuetyyppi; typedef ja struct ovat C-kielen varattuja sanoja. 422
Tunnus Tietuetyyppi on määriteltävän abstraktin tietotyypin nimi. Jokainen kenttäx on lista kentän nimiä pilkulla erotettuna, kenttien tietotyypin määrää tyyppix, joka voi olla mikä tahansa standardi tietotyyppi tai itse määritelty tietotyyppi kuten toinen tietue. typedef struct { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ }TuoteTietue; 423
typedef -lause ei varaa tilaa tietokoneen muistista, se määrittelee ainoastaan tietotyypin. Nyt muuttujia voidaan varata käyttämällä tietotyypin nimeä TuoteTietue. TuoteTietue tuote3, tuote4 = {"luomumaito", 1.5 }; Muuttujan tuote3 kenttien sisältö on määrittelemätön ja tuote4 saa arvonsa alustuslistasta. 424
Tietueen kentän nimi voi esiintyä myös toisen tietueen kentän nimenä ja myös toisen muuttujan nimenä, muodostettava viittaus pitää huolen siitä, että nimisekaannuksia ei synny. Ohjelmoijan määrittelemää tietotyyppiä TuoteTietue voidaan käyttää määrittelemään yksinkertaisia muuttujia, taulukoita ja edelleen muiden tietuetyyppien kenttiä. Seuraavassa määritellään tietotyyppi TuotelajiTietue, joka on rakenteeltaan tietue ja jonka yksi kenttä on taulukko, jonka alkiot ovat tietueita tyypiltään TuoteTietue 425
typedef struct { char nimi[nimen_pituus]; double hinta; }TuoteTietue; typedef struct { char lajike[nimen_pituus]; TuoteTietue tuotteet[tuote_lkm]; }TuotelajiTietue; 426
Tietueen kenttien käsittely Yksittäiseen tietuemuuttujan (esim. tuote1) kenttään viitataan C- kielessä kenttämuuttujan avulla, joka muodostetaan tietuemuuttujan (esim. tuote1) ja kentän nimen perusteella (esim. hinta) käyttäen ns. pisteoperaattoria. Pistenotaatio: tietuemuuttuja.kentännimi Laskentajärjestyksessä pisteoperaattori on samalla tasolla funktion kutsujen ja taulukkojen indeksoinnin kanssa. 427
Kenttämuuttujaa voidaan käyttää kuten vastaavaa tyyppiä olevaa yksinkertaista muuttujaa. Esimerkiksi: TuoteTietue tuote5; Tuotteen nimeksi sijoitetaan merkkijono "Kalapuikko" ja hinnaksi 3.95 strcpy( tuote5.nimi, "Kalapuikko"); tuote5.hinta = 3.95; 428
tuote 1 K a l a p u i k k 0 \n 3.95 hinta nimi-kentän sisältö 429
Lyhyt esimerkkiohjelma tietueiden käytöstä annettujen määritysten pohjalta: #include <stdio.h> #include <string.h> #define NIMEN_PITUUS 10 #define TUOTE_LKM 5 typedef struct { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ } TuoteTietue; 430
int main(void) { TuoteTietue tuote1, tuote2 = {"luomumaito", 1.5 }; } strcpy(tuote1.nimi, "lenkki"); tuote1.hinta = 2.99; printf("\ntuoteen \"%s\" hinta on %.2lf euroa\n", tuote1.nimi, tuote1.hinta); printf("tuoteen \"%s\" hinta on %.2lf euroa\n\n", tuote2.nimi, tuote2.hinta); return(0); 431
432
Tietuetyyppinen muuttuja esimerkiksi tuote2 ilman kentän valintaa viittaa koko tietorakenteeseen. Kaikkien tietuemuuttujan kenttien arvojen kopiointi toiseen vastaavaan rakenteeseen käy helposti: tuote3 = tuote2; Huom. Tavallisten taulukoiden yhteydessä sijoitusoperaattoria voi käyttää vain muuttujien alustuksessa! 433
Tietueet funktioiden parametreina Kun tietuemuuttuja syötetään funktion parametriksi (arvoparametri) kaikki sen kentän arvot kopioidaan vastaaviin muodollisen parametrin kenttiin. Tietueen käyttöä arvoparametrina selventää seuraava funktio, joka tulostaa TuoteTietue -tyyppiä olevan muuttujan kenttien arvot. 434
#include <stdio.h> #include <string.h> #define NIMEN_PITUUS 10 typedef struct { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ } TuoteTietue; void tulostatuote(tuotetietue t); 435
int main(void) { TuoteTietue tuote1 = {"makarooni", 0.19}; tulostatuote(tuote1); return(0); } void tulostatuote(tuotetietue t){ } printf("\ntuote \"%s\" hinta %.2lf\n", t.nimi, t.hinta); 436
437
Tietueiden vertailu Vaikka C-kielessä on mahdollista kopioida kokonaisia tietueita käyttämällä sijoitusoperaatiota, kahden tietueen vertailua ei voi suorittaa yhtenä kokonaisuutena. TuoteTietue t1, t2;... if( t1 == t2) /* EI VERTAILE OIKEIN */ Jos halutan verrata ovatko kaksi tietuetta sisällöltään identtisiä, on verrattava jokaista kenttää erikseen. 438
#include <stdio.h> #include <string.h> #define NIMEN_PITUUS 20 typedef struct { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ } TuoteTietue; void tulostatuote(tuotetietue t); int vertaa(tuotetietue t1, TuoteTietue t2); 439
int main(void) { TuoteTietue tuote1 = {"makarooni", 0.19}; TuoteTietue tuote2 = {"HK_Blöö", 2.99}; TuoteTietue tuote3 = {"makarooni", 0.19}; tulostatuote(tuote1); tulostatuote(tuote2); tulostatuote(tuote3); 440
printf("\n**********\n"); if(vertaa(tuote1, tuote2)){ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote2); printf("ovat samoja"); }else{ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote2); printf("eivät ole samoja"); } 441
printf("\n**********\n"); if(vertaa(tuote1, tuote3)){ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote3); printf("ovat samoja"); }else{ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote3); printf("eivät ole samoja"); } printf("\n\n"); return(0); } /* main */ 442
void tulostatuote(tuotetietue t){ printf("\ntuote \"%s\" hinta %.2lf\n", t.nimi, t.hinta); } int vertaa(tuotetietue t1, TuoteTietue t2){ } if(strcmp(t1.nimi, t2.nimi)==0 && t1.hinta == t2.hinta) return(1); else return(0); Funktiolle vertaa syötetään kaksi muuttujaa ja se palauttaa 1 tai 0 riippuen siitä ovatko muuttujien kenttien arvot samat. 443
Tietueet palautettuina arvoina Tietueiden käsittelyssä on olennaista selvittää mitkä operaatiot ovat sallittuja tietuekokonaisuudelle. tietueen arvon voi sijoittaa toisen tietueen arvoksi tietueita ei voi verrata suoraan toisiinsa C-kielessä kaikkia tietorakenteita ei käsitellä samalla tavalla esim. taulukon nimi tarkoittaa taulukon ensimmäisen alkion osoitetta, tietueen nimi tarkoittaa kaikkien sen kenttien arvojen joukkoa. 444
Taulukoiden yhteydessä koko taulukkoa arvoineen ei voida palauttaa return-lauseen arvona. pitää käyttää epäsuoraa osoitusta, osoittimia Tietueille arvon palauttaminen funktioista sen sijaan onnistuu funktio palauttaa kaikkien kenttien arvot. 445
Esimerkkinä funktio, joka lukee käyttäjän antamia arvoja tietuemuuttujan kenttiin. tietuemuuttuja tyyppiä TuoteTietue palautettava arvo tyyppiä TuoteTietue funktiolle ei anneta parametreja kutsussa 446
#include <stdio.h> #include <string.h> #define NIMEN_PITUUS 20 typedef struct { char nimi[nimen_pituus]; /* kenttä nimelle */ double hinta; /* kenttä hinnalle */ } TuoteTietue; void tulostatuote(tuotetietue t); int vertaa(tuotetietue t1, TuoteTietue t2); TuoteTietue luetuote(void); 447
int main(void) { TuoteTietue tuote1; TuoteTietue tuote2; tuote1 = luetuote(); tuote2 = luetuote(); tulostatuote(tuote1); tulostatuote(tuote2); printf("\n**********\n"); 448
if(vertaa(tuote1, tuote2)){ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote2); printf("ovat samoja"); }else{ printf("tuotteet:\n"); tulostatuote(tuote1); printf("ja"); tulostatuote(tuote2); printf("eivät ole samoja"); printf("\n\n"); } return(0); } 449
void tulostatuote(tuotetietue t){ printf("\ntuote \"%s\" hinta %.2lf\n", t.nimi, t.hinta); } int vertaa(tuotetietue t1, TuoteTietue t2){ if(strcmp(t1.nimi, t2.nimi)==0 && t1.hinta == t2.hinta) return(1); else return(0); } 450
TuoteTietue luetuote(void){ TuoteTietue apu; printf("\nanna tuotteen nimi >"); scanf("%s", apu.nimi); printf("\nanna tuoteen \"%s\" hinta >", apu.nimi); scanf("%lf", &apu.hinta); } return(apu); 451
Tietuetaulukot Tietuetyypi voi olla myös taulukon tyyppi. Esimerkiksi: Tarvitaan ohjelma joka tallettaa opiskelijan opintosuorituksia. Yhden opiskelijan tiedot mallinnetaan tietuella: typedef struct { char nimi[nimen_pituus]; int opiskelijakortinnumero; int johdatusohjelmointiin; /* kurssin arvosana */ }OpiskelijaTietue; 452
Halutaan käsitellä n opiskelijan nimi, opiskelijakortin numero ja Johdatus ohjelmointiin kurssin arvosana. Luodaan taulukko, joka on tyyppiä OpiskelijaTietue. #include <stdio.h> #include <string.h> #define NIMEN_PITUUS 25 #define OPISKELIJOIDEN_LKM 10 typedef struct { char nimi[nimen_pituus]; int opiskelijakortinnumero; int johdatusohjelmointiin; /* kurssin arvosana */ }OpiskelijaTietue; 453
OpiskelijaTietue lueopiskelijantiedot(void); void tulostaopiskelija(opiskelijatietue op); int main(void) { OpiskelijaTietue rekisteri[opiskelijoiden_lkm]; int i; /* luetaan tiedot taulukkoon */ for(i=0; i < OPISKELIJOIDEN_LKM; i++) { rekisteri[i] = lueopiskelijantiedot(); } } /* tulostetaan opiskelijatiedot */ for(i=0; i < OPISKELIJOIDEN_LKM; i++){ tulostaopiskelija(rekisteri[i]); } return(0); 454
/* lukee yhden opiskelijan: - nimen, - opiskelijakortin numeron ja - johdatus ohjelmointiin -kurssin arvosanan ja palautta tiedot OpiskeijaTietue-typpisenä arvona */ OpiskelijaTietue lueopiskelijantiedot(void){ OpiskelijaTietue op; printf("\nanna opiskelijan nimi >"); scanf("%s", op.nimi); printf("anna opiskelijakorin numero >"); scanf("%d", &op.opiskelijakortinnumero); printf("anna \"Johdatus ohjelmointiin\"-kurssin arvosana >"); scanf("%d", &op.johdatusohjelmointiin); } return(op); 455
/* tulostaa opiskelijan: - nimen - opiskelijakorin numeron ja - Johdatus ohjelmointiin -kurssin arvosanan */ void tulostaopiskelija(opiskelijatietue op){ printf("\n****************************\n"); printf("nimi: %s\n", op.nimi); printf("kortin numero: %d\n", op.opiskelijakortinnumero); printf("jo-arvosana: %d\n", op.johdatusohjelmointiin); printf("\n****************************\n"); } 456
Lueteltu tyyppi Luetellun tyypin avulla voidaan antaa kokonaisluvuille nimi ja näin rajoittaa lukualuetta Listan ensimmäinen nimi saa kokonaislukuarvon 0, seuraava arvon 1 jne. Lueteltu tyyppi määritellään avainsanalla enum, yleinen muoto: enum nimi { luettelo... } muuttujannimi; 457
Esimerkiksi: enum viikonpaivat { ma, ti, ke, to, pe, la, su } paiva; Nyt muuttuja paiva voi saada arvoja ma - su eli 0-6. paiva = ti; /* arvo on nyt 1 */ Luetellun tyypin yhteydessä voidaan käyttää myös sanaa typedef, jolloin voidaan luetellulle tyypille antaa nimi, jonka avulla määritellään muuttujia. typedef enum {ma, ti, ke, to, pe, la, su } Viikonpaiva; 458
Muuttujan määrittely Viikonpaiva pv; pv = ke; /* pv saa arvon 2 */ Symbolien arvon voi määritellä alustuksen avulla. typedef enum {ma = 1, ti, ke, to, pe, la, su } Viikonpv; tällöin ma vastaa arvoa 1 ja ti arvoa 2 jne. 459
#include <stdio.h> typedef enum {ma = 1, ti, ke, to, pe, la, su } Viikonpaiva; int main ( void ) { Viikonpaiva pv; char pvnimet[7][12] = { "", "Maanantai", "Tiistai", "Keskiviikko", "Torstai", "Perjantai", "Lauantai", "Sunnuntai"}; } for( pv = ma; pv <=su; pv++) { printf("%2d %s\n", pv, pvnimet[pv] ); } return (0); 460
461
Kertaus C-kielessä käyttäjä voi itse määritellä omia tietotyyppejä, etenkin tietuerakenteet ovat hyödyllisiä. Tietueen kenttiin viitataan pisteoperaattorin (.)joka sijoitetaan tietuemuuttujan ja kentän nimen väliin. Tietueita voi käyttää funktion palautettavina arvoina, kuten standardeja tietotyyppejä. Tietueille on käytössä tavallinen sijoitusoperaatio toisin kuin taulukoille, mutta ei tietueiden vertailua. 462