310 likes | 550 Views
Sisältö Taulukot Osoittimet ja viitevälitys Tietueet. Rakenteinen ohjelmointi. 1. Taulukko. Taulukko on rakenteellinen tietotyyppi, johon voidaan tallettaa melkein minkälaisia tietotyyppejä tahansa. Kuitenkin yksi taulukko voi sisältää vain yhdentyyppisiä alkioita.
E N D
Sisältö Taulukot Osoittimet ja viitevälitys Tietueet Rakenteinen ohjelmointi
1. Taulukko Taulukko on rakenteellinen tietotyyppi, johon voidaan tallettaa melkein minkälaisia tietotyyppejä tahansa. Kuitenkin yksi taulukko voi sisältää vain yhdentyyppisiä alkioita. Sisäisesti taulukko on tiettyyn muistiosoitteeseen varattu sarja peräkkäisiä samantyyppisiä muistipaikkoja. Paikkoihin viitataan taulukon indekseillä, jotka juoksevat arvosta 0 arvoon koko-1 ja jotka ympäröidään hakasulkeilla. Alla esimerkiksi char-tyypin taulukko, jonka koko = 5 Osoite 0101110 01010001 00101001 00000000 00011100 indeksi [0] [1] [2] [3] [4]
1. Taulukko Taulukko voi olla myös useampiulotteinen (dimensioinen). Tällöinkin se voi sisältää vain yhden tyyppisiä alkioita Alla 2-ulotteinen 2x3 ulotteinen char taulukko, joka sisäisesti muodostuu 1-ulotteisesta taulukosta, jonka alkiot ovat kolmen pituisia taulukoita. Visuaaliseti ajatellaan, että taulukko koostuu kahdesta alakkaisesta rivistä, jolloin voidaan “nähdä” 2 riviä ja 3 saraketta.. Osoite 0101110 01010001 00101001 00000000 00011100 00010110 indeksi [0][0] [0][1] [0][2] [1][0] [1][1] [1][2] 2 riviä ja 3 saraketta [0][0] [0][1] [0][2] [1][0] [1][1] [1][2]
1. Taulukko Taulukon esittely koodissa Syntaksi Semantiikka 1. rivillä esitellään 1-ulotteinen taulukko, jolle varataan koko peräkkäistä muistipaikkaa. 2. rivillä esitellään 2-ulotteinen taulukko, jolle varataan riv*sar peräkkäistä muistipaikkaa. Rivien lukumäärä = riv ja sarakkeiden lukumäärä = sar tyyppi taulu1 [koko]; tyyppi taulu2 [riv] [sar]; Esimerkiksi 1. rivillä esitellään 5-alkioinen merkkitaulukko 2. rivillä esitellään 2-ulotteinen kokonaislukutaulukko, jossa rivien lukumäärä = 10 ja sarakkeiden lukumäärä = 5 char merkit[5]; int luvut [10] [5];
1. Taulukko Taulukon alustaminen koodissa • Koodissa taulukko voidaan alustaa • antamalla listassa arvot. • Viereisissä alustuksissa tulee • taulukkoon alkiot: • 1) 12, 4 ja 5 • 2) 0, 0 ja 0 • 12, 0 ja 0 • 1. rivi 1, 4 ja 5 • 2. rivi 8, 2 ja 0 1) inttaulu[3]={12,4,5}; 2) inttaulu[3]={0}; 3) inttaulu[3]={12}; 4) inttaulu[2][3]={{1,4,5},{8,2,0}};
1. Taulukko 1-ulotteisen taulukon läpikäyminen Taulukkoja operoidaan peräkkäiskäsittelyllä käyttämällä toistolauseita for( i=0; i < koko; i++) taulu[i]=i*i; for( i=0; i < koko; i++) printf(”%i\n”,taulu[i]); 1. esimerkissä lasketaan taulukkoon, jossa on koko alkiota, indeksien 0, …, koko-1 neliöt 2. esimerkissä tulostetaan taulukon luvut kuvaruudulle alakkain
1. Taulukko 2-ulotteisen taulukon läpikäyminen 2-ulotteisia taulukkoja operoidaan kahdella sisäkkäisellä toistolauseella, joissa 1. laskurimuuttuja (i) on rivien ja 2. laskurimuuttuja (j) on sarakkeiden indeksi for( i=0; i < rivit; i++) for( j=0; j < sarakkeet; j++) taulu[i][j]=i+j; for( i=0; i < rivit; i++) { for( j=0; j < sarakkeet; j++) printf(”%i ”,taulu[i][j]); printf(”\n”); } 1. esimerkissä lasketaan 2-ulotteiseen taulukkoon indeksien summat 2. esimerkissä tulostetaan 2-ulotteisen taulukon rivit kuvaruudulle alakkain (jokaisen rivin jälkeen tulostetaan rivinvaihto)
1. Taulukko Taulukon välittäminen funktiolle Taulukot välitetään funktioille C-kielessä automaattisesti viitevälityksellä (kts. seur. osio). Tällöin funktion rungossa käsitellään aina kutsuvan ohjelmanosan taulukkoa eikä sen kopiota void nelioi(int taulu[ ], int koko) { inti; for(i=0;i<koko;i++) taulu[i]=taulu[i]*taulu[i]; } Funkion otsikossa muodollinen taulukkomuuttuja määritellään hakasulkeilla. Funktion rungossa taulukon kenttien arvoihin viitataan aivan tavallisesti hakasulkeissa olevilla indekseillä Huom! Funktio käsittelee nyt kutsuvan ohjelmanosan taulukkoa ja neliöi sen alkiot
1. Taulukko Merkkitaulukot (C-kielen merkkijonotyyppi) Esitellään 6-alkioinen merkkitaulukko ja alustetaan se kolmella C-kielen merkkityypin alkiolla char merkit[5+1] =”Kalle”; Sisäisesti merkkitaulukko näyttää nyt tältä Huomaa, että kääntäjä laittaa automaattisesti merkkijonon lopetusmerkin ’\0’. Merkkitaulukon koko varataan muodossa 5+1. Tällöin koodista nähdään suoraan merkkijonon maksimipituus 5 ja yksi ”lokero” jää lopetusmerkille
1. Tauluko Merkkijonojen käsittelyfunktiot kirjastossa Standardikirjaston otsikkotiedostossa string.h esitellään hyödyllisiä merkkijonojen käsittelyfunktioita. Ohessa pari esimerkkiä: #include <string.h> char*strcpy(char kohde[ ],char lähde[ ]) intstrcmp(char jono1[ ],char jono2[ ]) strcpy kopioi lähde-merkkijonon kohde-merkkijonoon. Palautetaan kohdemerkkijonon osoite strcmp vertaa kahta merkkijonoa aakkostuksellisesti. Vertailu päättyy ensimmäiseen eroavaan merkkiin tai merkkijonon loppuun, jos ne ovat samat. Palautetaan 0, jos merkkijonot ovat samat, 1, jos jono1 > jono2 tai -1, jos jono1 < jono2.
1. Taulukot Merkkijonon lukeminen näppäimistöltä Standardikirjaston (stdio.h) funktio gets lukee komentoriviltä syötetyn merkkijonon, joka voi sisältää myös välimerkkejä ja erillisiä sanoja. #include <stdio.h> … char nimet[32+1]; printf(”Kirjoita nimesi (max 32 merkkiä) \n”); gets(nimet); gets lukee komentoriviltä syötetyn merkkijonon ja sijoittaa merkit parametrina annettuun merkkitaulukkoon. Huom! Funktio ei tarkista kohdetaulukon kokoa, vaan se on ohjelmoijan vastuulla. Taulukon ylivuoto saa ohjelman toimimaan täysin odottamattomasti.
2. Osoittimet ja viitevälitys Osoittimien käyttö on keskeinen osa C-ohjelmointia. Ohjelmoijan tulee ehdottomasti ymmärtää osoittimien käytön periaatteet. Jokaiselle muuttujalle ohjelmassa varataan kaksi vierekkäistä muistilohkoa: ensimmäinen sisältää muuttujan osoitteen (LVALUE) ja toinen sen arvon (RVALUE). Osoitinmuuttuja on muuttuja, jonka arvo (RVALUE) on toisen muuttujan osoite (LVALUE). Sanotaan, että se osoittaa jotain muuttujaa. Tavallinen merkkimuuttuja, jonka arvo on ’A’ Osoitinmuuttuja Osoite_ptr Osoite1 Osoite1 ’A’ LVALUE RVALUE LVALUE RVALUE
2. Osoittimet ja viitevälitys Osoitinmuuttujan esittely ja asetus Tavallinen merkkimuuttuja, jonka arvo on ’A’ Osoitinmuuttuja Osoite_ptr Osoite1 Osoite1 ’A’ char merkki = ’A’; char *merkki_ptr; // tai pMerkki merkki_ptr =&merkki; • rivi: Esitellään tavallinen merkkimuuttuja merkki ja alustetaansen arvoksi ’A’ • rivi: Esitellään osoitinmuuttuja merkki_ptr, joka voi osoittaa merkkityyppiin. • rivi: Asetetaan merkki_ptr osoittamaan merkkimuuttujaan merkki. * muuttujan nimen edessä osoitimuuttuja & muuttujan nimen edessä palautetaan muuttujan osoite
2. Osoittimet ja viitevälitys Muuttujan arvon syöttö ja lukeminen osoitinmuuttujalla Tavalliset merkkimuuttujat Osoitinmuuttuja merkki1 Osoite1 ’A’ Osoite_ptr merkki2 Osoite2 ’B’ charmerkki1 = ’A’, merkki2 = ’B’; char *merkki_ptr; merkki_ptr = &merkki2; *merkki_ptr = ’C’; merkki1=*merkki_ptr; Muista! ???????? Osoite_ptr nimi_ptr Sijoitetaan tai luetaan osoittimen arvokenttää (osoitteita) Osoite_ptr Osoite2 *nimi_ptr Sijoitetaan tai luetaan osoittimen osoittaman muuttujan arvokenttää Osoite2 ’C’ Osoite1 ’C’
2. Osoittimet ja viitevälitys Tiedonvälitys funktioiden välillä Funktioiden tiedonvälitys tapahtuu parametrien avulla. Periaatteessa on kaksi erilaista tapaa välittää tietoa funktioille arvovälitys ja viitevälitys. Arvovälityksessä funktiota kutsuvasta ohjelmanosasta välitetään kutsuparametrienarvot (RVALUE), joista otetaan kopiot funktion muodollisille parametreille. Viitevälityksessä funktiota kutsuvasta ohjelmanosasta välitetään kutsuparametrienosoitteet (LVALUE), jolloin funktio voi käsitellä kutsuvan ohjelmanosan kutsuparametrien arvokenttiä (RVALUE)
2. Osoittimet ja viitevälitys Viitevälityksen määrittely ja kutsu intluku1 = 3, luku2 = 5; vaihda(&luku1,&luku2); Kutsuvassa ohjelmanosassa määritellään ja alustetaan kokonaislukumuuttujat arvoilla 3 ja 5. Lisäksi kutsutaan funktiota vaihda, jolle välitetään edellisten muuttujien osoitteet, void vaihda(int *eka, int *toka) { inttemp; temp = *eka; *eka=*toka; *toka=temp; } Funkiossa viitevälitys määritellään esittelemällä muodolliset parametrit osoittimina. Funktio vaihda() vaihtaa kutsuvan ohjelmanosan muuttujien (luku1 ja luku2) arvokenttien sisällöt käyttämällä paikallista apumuuttujaa temp.
2. Osoittimet ja viitevälitys Esimerkki, jossa arvovälitys ei toimi intluku1 = 3, luku2 = 5; vaihda(luku1,luku2); Tässsä on edellisen sivun esimerkki toteutettu arvovälityksellä. Nyt kutsuvan ohjelman muuttujat luku 1 ja luku2 eivät muutu funktion kutsussa, koska niistä annetaan funktioille vain kopiot niiden arvoista (RVALUE). void vaihda(inteka, inttoka) { inttemp; temp = eka; eka=toka; toka=temp; } Funkiossa määritelty arvovälitys, jolloin muodolliset parametrit saavat arvokseen kopiot kutsuparametrien arvoista. Nyt funktion toiminta rajoittuu paikallisten muuttujien eka ja toka arvojen vaihtoon ja siten funktio ei toimi siten kuin on sen tarkoitus.
2. Osoittimet ja viitevälitys Taulukon välittäminen funktiolle Taulukot välitetään funktioille C-kielessä automaattisesti viitevälityksellä. Tällöin *- operaattoria ei tarvita parametrien esittelyssä eikä taulukon käsittelyssä funktion rungossa void nelioi(int taulu[ ], int koko) { inti; for(i=0;i<koko;i++) taulu[i]=taulu[i]*taulu[i]; } Funkion otsikossa taulukon viitevälitys määritellään esittelemällä muodollinen taulukkomuuttuja hakasulkeilla. Funktion rungossa taulukon kenttien arvoihin viitataan aivan tavallisesti hakasulkeissa olevilla indekseillä Huom! Funktio käsittelee nyt kutsuvan ohjelmanosan taulukkoa ja neliöi sen alkiot
2. Osoittimet ja viitevälitys Taulukon välittäminen funktiolle Taulukon nimi on C-kielessä määritelty vastaamaan taulukon osoitetta (LVALUE), jolloin se voi toimia kutsuparametrina viitevälityksessä. int summa, taulu[5]={3,6,1,0,9}; … nelioi(taulu,5); … void nelioi(int taulu[ ], int koko) { … Kutsuvassa ohjelmanosassa taulukko välitetään funktiolle kirjoittamalla kutsuparametriksi pelkkä taulukon nimi. Funktiokutsun jälkeen kutsuvan ohjelman osan taulukko on muuttunut ja se sisältää nyt arvot {9,36,1,0,81}
2. Osoittimet ja viitevälitys Osoitinaritmetiikkaa Taulukon välityksessä funktiolle ja sen käsittelyssä funktion rungossa voidaan käyttää myös osoitinta taulukon yksittäisiin alkioihin. Alla edellinen neliöintiesimerkki Paikallinen osoitinmuuttuja ptr saa kutsuparametrin osoitearvon void nelioi(int *alku, int koko) { int *ptr = alku; while(ptr-alku<koko) { *ptr=(*ptr)*(*ptr); ptr=ptr+1; } } Osoitteet ovat peräkkäisissä muistipaikoissa [alku] … [alku+koko] Osoitetun muuttujan (taulukon alkion) arvoa käsitellään *-operaattorilla Osoitinaritmetiikka = osoitinta siirretään muistipaikasta seuraavaan lisäämällä osoittimen arvokenttään (osoitteeseen) 1
2. Osoittimet ja viitevälitys Osoittimet ja merkkijonot Osoittimien avulla voidaan käsitellä myös merkkitaulukkoja eli merkkijonoja. Seuraava funktio laskee annetun merkkijonon pituuden Paikallinen osoitinmuuttuja ptr saa kutsuparametrin osoitearvon unsigned pituus(char *alku) { char *ptr = alku; while(*ptr != ’\0’) ptr=ptr+1; return ((unsigned)(ptr-alku)); } Testataan onko osoitetun muuttujan arvo merkkijonon lopetusmerkki osoitinta siirretään muistipaikasta seuraavaan lisäämällä osoittimen arvokenttään 1 Palautetaan viimeisen merkin osoitearvo – 1. merkin osoitearvo
3. Tietueet Tietuetyyppi ja tietuemuuttuja Tietue struct on rakenteellinen tietotyyppi, joka toisin kuin taulukko voi sisältää useampia tietotyyppejä eli tietokenttiä. Tietueita käsitellään tietuetyypin muuttujilla ja tietueen kenttiä muuttujaan liitettävällä pisteoperaattorilla (.) Huom! Tietueen kentät eivät välttämättä ole peräkkäisissä muistipaikoissa, vaan tilanvaraukset ovat laitekohtaisia Osoite1 120030 LVALUE ”Kalle Virtanen” 32 ”Jokiväylä 11” RVALUE
3. Tietueet Tietuemuuttuja ja tietuetyyppi • Tietuemuuttuja voidaan määritellään kahdella eri tavalla • Kuvataan tietueen rakenne muuttujan esittelyn yhteydessä • Määritellään ensin globaalisti uusi tietuetyyppi, jonka yhteydessä kuvataan tietueen rakenne. Jatkossa funktioissa tämän tietuetyypin muuttujat esitellään käyttämällä niiden tyyppinä määriteltyä uutta tietuetyyppiä.
3. Tietueet Tietuemuuttujan määrittely ilman tietuetyyppiä • Kuvataan tietueen rakenne muuttujan esittelyn yhteydessä • struct {kenttien_määrittely}muuttujanimi; int main(void) { struct { intid_nro; char nimi[32+1]; unsigned ika; } henkilo ; … Tietuemuuttujan henkilo esittely Esittelyssä kuvataan tietueen rakenne. Kentät esitellään kuten paikalliset muuttujat, mutta mitään alkuarvoja ei kenttiin voi rakenteen esittelyssä määrätä Tämä tapa on hyvä, jos muita samanlaisia tietuemuuttujia ei tarvita
3. Tietueet Tietuetyypin määrittely • Määritellään ensin uusi globaalinen tietuetyyppi, jota käytetään tietuemuuttujan esittelyssä. • struct TYYPPINIMI {kenttien_määrittely}; HENKILO tietuetyypin määrittely globaalisti (yleensä heti esikääntäjädirektiivien jälkeen) Funktiossa tietuetyyppiä HENKILO olevien tietuemuuttujien isa ja aiti esittely Tämä tapa on hyvä, jos tarvitaan useampia saman tyyppisiä tietuemuuttujia Huomaa puolipiste tietuetyypin määrittelyn jälkeen structHENKILO { intid; char * nimi[32+1]; } ; int main(void) { struct HENKILO isa, aiti; …
3. Tietueet Tietueen alustaminen koodissa ja kenttien käsittely int main(void) { struct HENKILO isa, aiti; aiti.id=123; strcpy(aiti.nimi, ”Maija”); isa={321,”Matti”};/* C++*/ … Paikallisen tietuemuuttujan tietueen kenttiä käsitellään pistenotaatiolla. Tietueen kenttien alustaminen voidaan kääntäjästä (C++) riippuen tehdä myös aaltosulkeilla rajattuna listana
3. Tietueet Tietueen kopioiminen int main(void) { struct HENKILO kalle1, kalle2; kalle1.id=123; strcpy(kalle1.nimi, ”Kalle”); kalle2 = kalle1; … Tietuemuuttujan kenttien sisältö voidaan kopioida toiselta samantyyppiseltä tietuemuuttujalta sijoitusoperaattorilla
3. Tietueet Tietue taulukon alkiona int main(void) { struct HENKILO hlot[2]; hlot[0].id_nro=111; strcpy(hlot[0].nimi,”Kalle”); hlot[1].id_nro=222; strcpy(hlot[1].nimi,”Ville”); … Määritellään 2-paikkainen taulukkomuuttuja hlot, jonka alkiot ovat HENKILO tyyppisiä tietueita Taulukon alkiona olevaa tietuetta käsitellään siten, että indeksoitu taulukon alkio toimii tietueen nimenä.
3. Tietueet Tietue tietueen kenttänä int main(void) { struct PISTE {float x; float y;} p1; struct {struct PISTE kp; float sade;} ympyra; p1.x = 0; p1.y = 0; ympyra.sade=5.3; ympyra.kp.x=5; ympyra.kp.y=2; Ensimmäisellä rivillä esitellään samalla kertaa sekä tietuetyyppi PISTE että samantyyppinen tietuemuuttuja p1. Seuraavalla rivillä esitellään tietuemuuttuja ympyrä, jonka rakenteeseen kuuluu PISTE tyyppinen tietuemuuttuja kp sekä float tyyppinen muuttuja sade. Ympyrän kenttiin viitataan pistenotaatiolla, keskipisteen tapauksessa myös sisäisen tietueen kenttiin.
3. Tietueet Tietuetaulukon välittäminen funktiolle int main(void) { struct HENKILO hlot[2]; /* taulukon tietueiden alustus */ tulosta (hlot, 2); } void tulosta (struct HENKILO hlot[ ], int koko) { int i; for(i=0;i<koko;i++) printf("%s %i\n",hlot[i].nimi, hlot[i].id_nro); } Tietuetaulukko välitetään funktioille samalla periaatteella kuin muutkin taulukkotyypit eli viitevälityksellä. Funktion rungossa tietueita käsitellään samalla pistenotaatiolla kuin kutsuvassa ohjelmassa, missä taietueet on luotu.
3. Tietueet Tietueosoitin int main(void) { struct HENKILO jokuHlo; /* taulukon tietueiden alustus */ alustaTietue(&jokuHlo); … } void alustaTietue(struct HENKILO * hlo) { strcpy(hlo->nimi,""); hlo->id_nro=0; } Kun tietuetta käsitellään osoitintyyppisellä muuttujalla, tietueen kenttiä käsitellään pisteoperaattorin sijasta nuolioperaattorilla ->. Tässä alustetaan tietue ”nollahenkilöksi” eli henkilöksi, jolla ei ole todellisia henkilötietoja, mutta kentät on kuitenkin alustettu, joten niitä voidaan lukea ilman ohjelman kaatumista (testausvaiheessa voi tarvita).