Ohjelmointi funktioiden avulla

Koko: px
Aloita esitys sivulta:

Download "Ohjelmointi funktioiden avulla"

Transkriptio

1 Ohjelmointi funktioiden avulla 8 Ohjelmointi funktioiden avulla Ohjelman jakaminen hallittaviin osiin on idea, joka on perustana kaikille ohjelmointikielille. Funktio on kaikkien C++-ohjelmien perusosa. Tähän mennessä olemme käyttäneet joitakin standardikirjaston funktioita, mutta itse olemme kirjoittaneet vasta main()-funktioita. Tässä luvussa käsitellään omien funktioiden määrittelyä. Luvun aiheita ovat: Mikä on funktio ja miksi ohjelma tulisi jakaa funktioihin Miten funktioita esitellään ja määritellään Miten parametrejä välitetään funktiolle ja kuinka funktio palauttaa paluuarvon Mitä ovat arvoparametrit Mitä parametrin määritteleminen osoittimeksi vaikuttaa parametrinvälitykseen Mitä const-avainsanan käyttäminen parametrin tyypin yhteydessä vaikuttaa funktion toimintaan Mitä ovat viittausparametrit ja miten viittaukset määritellään ohjelmassa Miten funktiosta palautetaan paluuarvo Mitä tarkoittaa avoin funktio Mitä vaikutusta on funktion muuttujan esittelyllä static-avainsanalla Ohjelman jakaminen osiin Kaikki tähän saakka kirjoittamamme ohjelmat ovat koostuneet vain yhdestä funktiosta, main()- funktiosta. Kuten tiedät, kaikissa C++-ohjelmissa tulee olla main()-funktio - ohjelman suoritus alkaa siitä. C++ sallii kuitenkin haluamasi määrän funktioita ohjelmassa ja olemmekin jo käyttäneet useita standardikirjaston funktioita esimerkeissämme. Omien funktioiden määrittely ja käyttäminen on yhtä helppoa. Kuvitteellisen ohjelman jakaantuminen funktioihin on esitetty seuraavassa kaaviossa: 277

2 C++ Ohjelmoijan käsikirja int main()... teetama();... teetuo();... return 0; kutsu paluu kutsu paluu void teetama()... y = lasketama();... m = lasketuo();... return ; void teetuo()... n = lasketuo();... t = lasketoinen();... return ; paluu kutsu paluu kutsu kutsu paluu kutsu paluu double lasketama()... return arvo; long lasketuo()... return kokonaisluku; double lasketoinen()... return z; Yleisesti ottaen, kun kutsut ohjelmassasi olevaa funktiota, funktion sisältämä koodi suoritetaan; kun se on suoritettu, ohjelman suoritus jatkuu funktion kutsukohtaa seuraavalta riviltä. Mikä tahansa funktio voi kutsua mitä tahansa toista funktiota (tätähän main()-funktiokin tekee), joka saattaa edelleen kutsua uutta funktiota, joten yhden funktion kutsu saattaa saada aikaan useamman funktion suorituksen. Kun funktio kutsuu toista funktiota, joka puolestaan kutsuu uutta funktiota ja niin edelleen, ohjelmasi on tilanteessa, jossa on useampi funktio aktiivisina ja jokainen funktio odottaa paluuta kutsumastaan funktiosta. Yllä olevassa kaaviossa main() kutsuu teetama()-funktiota, joka kutsuu lasketama()-funktiota, joten kaikki kolme ovat aktiivisina yhtä aikaa. Kun lasketama() on suorituksessa, funktio teetama() odottaa paluuta ja main() odottaa teetama()-funktion paluuta. Jotta funktio voidaan suorittaa, sen koodin tulee sijaita jossain kohtaa muistissa. Tässä on helppo sanoa, että yksi funktio odottaa toisen funktion paluuta, mutta jotta tämä kaikki toimii, jonkun täytyy pitää kirjaa, mistä kohdasta muistia kutsu tehtiin ja mihin funktion tulee palata. Nämä tiedot talletetaan ja niitä ylläpidetään kutsupinossa. Kutsupino sisältää kaikki tiettynä hetkenä aktiivisina olevat funktion kutsut sekä kullekin funktiolle välitetyt parametrit. Useimpien C++kehitysympäristöjen mukana tulevissa debuggereissa on mahdollista seurata kutsupinoa ohjelman suorituksen aikana. 278 Miksi ohjelmat tulee jakaa osiin Ohjelmien jakamiseksi on useita syitä. Ensiksikin se tekee ohjelmien lukemisen ja hallinnan helpommaksi. On helppo kuvitella, että main()-funktio saattaisi tulla hallitsemattoman kokoiseksi. Useat nykypäivän ohjelmista sisältävät useista sadoista tuhansista koodiriveistä aina miljooniin riveihin saakka. Voit vain kuvitella, kuinka main()-funktiota ylläpidettäisiin, jos siinä olisi riviä koodia! Ohjelman logiikan selvittäminen saattaisi jo olla lähes mahdotonta, puhumattakaan virheen etsimisestä. Tällaisen ohjelman jakaminen osiin on aivan välttämätöntä.

3 Ohjelmointi funktioiden avulla Toiseksi, funktioita voidaan käyttää uudelleen. Saatat esimerkiksi kirjoittaa yhteen ohjelmaan funktion, joka lajittelee merkkijonoja. Voit myöhemmin käyttää samaa funktiota muissa ohjelmissa. Standardikirjasto on hyvä esimerkki funktioiden uudelleenkäytöstä. Standardikirjaston ohjelmointiin ja testaukseen on käytetty tuhansia tunteja, mutta sinulla on kaikki sen toiminnot heti käytettävissä. Kolmanneksi, ohjelman jakaminen useisiin funktioihin voi vähentää ohjelman suorittamiseksi tarvittavaa muistia. Useimmat sovellukset sisältävät laskentaa, joka suoritetaan toistuvasti. Jos laskennan suorittava koodi sijoitetaan funktioon, jota kutsutaan aina tarvittaessa, koodi kirjoitetaan vain kerran. Ilman funktioita koodi pitäisi toistaa aina tarvittaessa; eli käännetty ohjelma olisi suurempi. Funktioiden perusteet Olemme tähän saakka kirjoittaneet varsin yksinkertaisia main()-funktioita, joten sinulla pitäisi olla jo varsin hyvä kuva funktioista, mutta käymme läpi perusideat, jotta kaikki olisi aivan selvää. Katsotaan ensiksi funktioiden toiminnan perusteita: Funktio on itsenäinen, tiettyä tehtävää varten tehty koodilohko. Tästä on suorana seurauksena se, että sinulla tulee olla suhteellisen kirkas ajatus ohjelmasi jakamisesta toiminnallisiin osiin. C++:ssa tähän sisältyy muutakin, mutta ohitamme tämän, kunnes käsittelemme tietotyyppien määrittelyä luvussa 11. Huomaat, että tällä on syvällinen vaikutus siihen, miten suunnittelet ohjelmasi rakenteen. Katsotaan nyt, miten funktio rakennetaan. Jatkossa on hyvä pitää seuraava esimerkki mielessä: Funktion paluuarvon tyyppi Funktion nimi Parametrit määrittelevät funktiolle välitettävien arvojen tyypit ja ne ovat parametrien tunnisteet funktion rungossa Funktion runko on aaltosulkeiden välissä double potenssi( double x, int n) double tulos = 1.0; if(n >= 0) for(int i = 1 ; i <= n ; i++) tulos *= x; else for(int i = 1 ; i <= -n ; i++) tulos /= x; return tulos; Funktio tunnistetaan yksikäsitteisesti sen nimen ja parametrien perusteella 279

4 C++ Ohjelmoijan käsikirja Funktion määrittely Funktion toiminta määritellään funktion määrittelyssä. Äskeisessä kaaviossa on funktion koko määrittely. Määrittelyssä on kaksi osaa: funktion otsikko, joka on määrittelyn ensimmäinen rivi ennen ensimmäistä aaltosulkua sekä funktion runko, joka on aaltosulkeiden sisällä. Kuten tiedät main()-funktion käyttökokemuksesta, funktion runko sisältää koodin, joka suoritetaan, kun funktiota kutsutaan. Funktion otsikko Käsitellään ensiksi esimerkkifunktion otsikkoa. Seuraavassa on funktion ensimmäinen rivi: double potenssi(double x, int n); Se koostuu kolmesta osasta: paluuarvon tietotyyppi (joka on double tässä tapauksessa); funktion nimi, potenssi ja sulkeiden sisällä oleva funktion parametriluettelo. Parametrien nimet ovat tässä x ja n ja näitä nimiä käytetään funktion lauseissa. Ne vastaavat funktiolle kutsussa välitettyjä parametrejä. Parametri x on double-tyyppinen ja n on int-tyyppinen, joten funktiota kutsuttaessa sille tulee välittää tämän tyyppiset parametrit. Huomaa, että funktion otsikon perässä ei ole puolipistettä. Funktion otsikon yleinen muoto Funktion otsikon yleinen muoto voidaan kirjoittaa seuraavasti: paluuarvon_tyyppi FunktionNimi(parametriluettelo) FunktionNimi on nimi, jota käytetään funktiota kutsuttaessa. On hyvä tapa antaa kaikille ohjelman funktioille oma nimi, mutta kuten näit joidenkin standardikirjaston funktioiden yhteydessä, useilla funktioilla voi olla sama nimi, kunhan niillä on erilaiset parametriluettelot. Funktion nimiin pätee samat säännöt kuin muuttujien nimiinkin. Eli funktion nimi muodostuu kirjaimista ja numeroista, ensimmäisen merkin tulee olla kirjain ja alaviiva vastaa kirjainta. Funktion nimen tulisi vastata jotenkin sen suorittamaa tehtävää, joten funktion, joka laskee papujen määrää, nimi voisi olla vaikka LaskePavut(). Funktion, joka laskee luvun potenssin, nimi voisi olla potenssi(). Paluuarvon_tyyppi on funktion palauttaman arvon tietotyyppi. Se voi olla mikä tahansa tietotyyppi - mukaan lukien kaikki itse luomasi tietotyypit. Jos funktio ei palauta mitään paluuarvoa, paluuarvon tyyppi määritellään avainsanalla void. Parametriluettelo kertoo, mitä tietoja funktiolle voidaan välittää kutsuvasta funktiosta. Se määrittelee kunkin parametrin nimen ja tyypin. Parametrin nimeä käytetään funktion rungossa viittaamaan kutsuvasta funktiosta tuotuun tietoon. Funktiolla ei tarvitse olla yhtään parametriä. Tätä kuvataan tyhjällä parametriluettelolla tai avainsanalla void. Funktion, jolla ei ole parametrejä eikä palauta mitään paluuarvoja, otsikko on siis: 280

5 Ohjelmointi funktioiden avulla void OmaFunktio() Tai vaihtoehtoisesti: void OmaFunktio(void) Huomaa, että parametrin tyyppi ei voi olla void. Koska funktio, jonka paluuarvon tyyppi on void, ei palauta arvoa, sitä ei voi kutsua lausekkeesta. Funktion arvona ei ole mitään, joten sen arvon vertaamisella tai sijoittamisella ei ole mitään merkitystä. Tällaisen funktion käyttäminen tällaisessa yhteydessä saa aikaan kääntäjän virheilmoituksen. Funktion runko Funktio suorittaa tehtävänsä suorittamalla sen rungossa olevat lauseet. Esimerkissämme funktion rungon ensimmäisellä rivillä esitellään double-tyyppinen muuttuja tulos, jonka alkuarvoksi asetetaan 1.0. tulos on automaattinen muuttuja: se on määritelty paikalliseksi funktiossa ja se on olemassa vain funktion rungossa. Tämä tarkoittaa sitä, että tulos häviää funktion suorituksen jälkeen. Laskenta suoritetaan jommassa kummassa for-silmukassa, riippuen n:n arvosta. Jos n on suurempi tai yhtä suuri kuin nolla, ensimmäinen for-silmukka suoritetaan. Jos n:n arvo on nolla, silmukkaa ei suoriteta kertaakaan, koska silmukan ehtolausekkeen arvo on heti false. Tällöin tuloksen arvoksi jää 1.0. Muutoin silmukan kontrollimuuttuja i saa arvot 1 - n ja muuttuja tulos kerrotaan x:llä jokaisella silmukan kierroksella, jolloin tuloksena on haluamamme arvo. Jos n on negatiivinen, toinen for-silmukka suoritetaan, jolloin tulos jaetaan x:llä jokaisella silmukan kierroksella. Muista aina, että funktion rungossa esitellyt muuttujat ja funktion parametrit ovat paikallisia kyseisessä funktiossa. Muuttujien ja parametrien nimiä voi käyttää toisissa funktioissa halutessasi erilaisiinkin tarkoituksiin. Funktiossa esiteltyjen muuttujien näkyvyysalue määritellään samaan tapaan kuin olemme jo nähneet: muuttuja luodaan siinä kohdassa, jossa se määritellään ja se häviää määrittelyn sisältävän lohkon päättyessä. Ainoana poikkeuksena tähän on static-avainsanalla määritellyt muuttujat. Käsittelemme niitä myöhemmin tässä luvussa. Ennen kuin käsittelemme parametrejä ja paluuarvoja syvällisemmin, sijoitetaan funktiomme potenssi() ohjelmaan. Kokeile itse - Funktion käyttö Seuraavassa esimerkissä käytetään potenssi()-funktiota: // Esimerkki Luvun potenssin laskenta #include <iostream> #include <iomanip> using namespace std; 281

6 C++ Ohjelmoijan käsikirja // Funktio laskee x potenssiin n double potenssi(double x, int n) double tulos = 1.0; if(n >= 0) for(int i = 0 ; i < n ; i++) tulos *= x; else for(int i = 0 ; i < -n ; i++) tulos /= x; return tulos; int main() cout << endl; // Lasketaan 8 potenssit for(int i = -3 ; i <= 3 ; i++) cout << setw(10) << potenssi(8.0, i); cout << endl; return 0; Ohjelman tulostus näyttää seuraavalta: Kuinka se toimii Kaikki toiminta tapahtuu main()-funktion for-silmukassa: for(int i = -3 ; i <= 3 ; i++) cout << setw(10) << potenssi(8.0, i); Funktiota potenssi() kutsutaan seitsemän kertaa. Niissä jokaisessa on ensimmäisenä parametrinä luku 8.0, mutta toisena parametrinä on i:n arvot Näin ollen seitsemän arvoa tulostetaan, eli arvot 8-3, 8-2, 8-1, 8 0, 8 1, 8 2 ja 8 3. Parametrit Funktiolle välitetään tietoa parametreissä, jotka määrittelet funktiota kutsuessasi. Ne sijoitetaan funktion nimen perässä olevien sulkeiden sisään, kuten edellisessä esimerkissä: cout << setw(10) << potenssi(8.0, i); Kutsussa käytetyt parametrit korvaavat funktion määrittelyssä käytetyt parametrit. Funktio suoritetaan aivan kuin se olisi kirjoitettu siten, että syötteenä saadut arvot olisivat funktion parametreissä. Funktion kutsussa käytettyjen parametrien ja funktion määrittelyssä käytettyjen parametrien välistä suhdetta kuvaa seuraava kaavio: 282

7 Ohjelmointi funktioiden avulla cout << potenssi( 3.0, 2 ); Funktion kutsun parametrit vastaavata funktion määrittelyn parametrejä Arvo 9.0 palautetaan, kun funktion suoritys päättyy double potenssi( double x, int n) double tulos = 1.0; if(n >= 0) for(int i = 1 ; i <= n ; i++) tulos *= x; else for(int i = 1 ; i <= -n ; i++) tulos /= x; return tulos; Funktion rungon koodi suoritetaan aivan kuin parametreissä olisi funktion kutsussa välitetyt arvot Funktion kutsun parametrien ja funktion määrittelyn parametrien välillä oleva ero on hyvin pieni. Funktion määrittelyn parametri esiintyy funktion määrittelyssä ja määrittelee funktion odottaman tietotyypin. Funktion kutsun parametri on se todellinen arvo, joka välitetään funktiolle sitä kutsuttaessa. Funktion kutsun parametrien järjestyksen tulee olla sama kuin funktion määrittelyn parametrien. On myös hyvä idea varmistaa, että kutsun parametrit ovat samaa tyyppiä kuin määrittelyn parametrit vaativat: kääntäjä ei välttämättä ilmoita, jos sen täytyy käyttää automaattista tyypinmuunnosta, jolloin saatat menettää tietoa. C++ sallii samassa ohjelmassa määriteltävän monta funktiota samalla nimellä. Saman nimisillä funktioilla tulee olla parametriluettelo. Funktion nimeä ja parametriluetteloa kutsutaan yhdessä funktion allekirjoitukseksi. Jos ohjelmassa on useita saman nimisiä funktiota, kääntäjä käyttää funktion allekirjoitusta päätellessään, mitä funktiota on kutsuttu. Tästäkin syystä on hyvä varmistaa, että kutsun ja määrittelyn parametrit vastaavat toisiaan. Paluuarvot Yleisesti ottaen funktion, jonka paluuarvon tyyppi on jokin muu kuin void, tulee palauttaa yksi paluuarvo, jonka tyyppi on määritelty funktion otsikossa. Paluuarvo lasketaan funktion rungossa ja palautetaan, kun funktion suoritus päättyy. Eli kun funktiota potenssi() kutsutaan lausekkeessa, potenssi() toimii double-tyyppisenä arvona. Paluuarvon palauttamaa funktiota ei kuitenkaan tarvitse kutsua lausekkeesta. Voit kutsua myös pelkkää funktiota: potenssi(10.5, 2); Tässä funktio suoritetaan normaalisti, mutta paluuarvoa ei käytetä mihinkään. Funktion potenssi() kutsuminen tällä tavalla ei ole järkevää, mutta monien muiden funktioiden kohdalla näin saattaisi ollakin. Sinulla voi olla esimerkiksi funktio, joka kopioi tiedoston ja paluuarvo vain välittää lisätietoa funktion suorituksesta (esimerkiksi onnistumisesta tai epäonnistumisesta), jota et ehkä aina tarvitse. Samaan tapaan kutsutaan tietysti funktioita, joiden paluuarvon tyyppi on void. 283

8 C++ Ohjelmoijan käsikirja Se, että funktio voi palauttaa vain yhden paluuarvon, saattaa nyt tuntua rajoitteelta, mutta näin ei ole. Palautettava arvo voi olla osoitin mihin tahansa: taulukkoon tai jopa osoitintaulukkoon. Tietty funktio palauttaa aina tietyntyyppisen paluuarvon. return-lause Esimerkkimme return-lause palauttaa muuttujan tulos arvon. Saatat nyt hieman ihmetellä, sillä väitimmehän juuri, että muuttuja tulos häviää funktion suorituksen päättyessä, eli mitä oikein palautetaan? Vastaus on, että palautettavasta arvosta tehdään automaattisesti kopio, joka on käytettävissä funktion kutsukohdassa. return-lauseen rakenne on: return lauseke; Tässä lausekkeen arvon tulee vastata funktion otsikossa määriteltyä funktion paluuarvon tyyppiä. Lauseke voi olla vaikka kuinka monimutkainen, kunhan tuloksena on palautettava tyyppi. Se voi sisältää funktion kutsuja ja se voi sisältää myös kutsun samaan funktioon, jossa tämä return-lause on. Tällaiseen tilanteeseen palaamme seuraavassa luvussa. Jos paluuarvon tyypiksi on määritelty void, return-lauseessa ei voi olla lauseketta. Eli se täytyy kirjoittaa seuraavasti: return; Jos funktion rungon päättävä aaltosulku kohdataan suorituksen aikana, se vastaa return-lauseen kutsumista ilman lauseketta. Itse asiassa, jos funktion paluuarvon tyyppi on void, tämä on suositeltava tapa toimia. Jos funktion paluuarvon tyyppi on eri kuin void, tämä olisi tietysti virhe, eikä funktio kääntyisi. Funktion esittely Edellinen esimerkkimme, esimerkki 8.1, toimi aivan hyvin, mutta yritetään nyt muuttaa koodia siten, että main()-funktio on potenssi()-funktion edellä. Ohjelman koodi näyttäisi seuraavalta: // Esimerkki Luvun potenssin laskenta - uusi järjestys #include <iostream> #include <iomanip> using namespace std; int main() cout << endl; // Lasketaan 8 potenssit for(int i = -3 ; i <= 3 ; i++) cout << setw(10) << potenssi(8.0, i); cout << endl; return 0; 284

9 Ohjelmointi funktioiden avulla // Funktio laskee x potenssiin n double potenssi(double x, int n) double tulos = 1.0; if(n >= 0) for(int i = 0 ; i < n ; i++) tulos *= x; else for(int i = 0 ; i < -n ; i++) tulos /= x; return tulos; Jos yrität kääntää tämän version, se ei onnistu. Kääntäjällä on ongelma, koska funktiota potenssi() ei ole määritelty kääntäjän käsitellessä funktiota main(). Voimme tietysti hylätä tämän version ja käyttää aina edellistä versiota, mutta sehän ei ratkaise ongelmaamme. Tähän liittyy kaksi asiaa. Ensinnäkin, ohjelma saattaa koostua useammista lähdetekstitiedostoista, kuten näemme myöhemmin. Tällöin saattaa olla, että kutsuttava funktio ei ole kutsuvan funktion edellä - se voi olla jopa aivan eri tiedostossakin. Toiseksi, oletetaan, että meillä on funktio A(), joka kutsuu funktiota B(), joka puolestaan kutsuu funktiota A() uudelleen. Jos nyt sijoitamme funktion A() ennen funktiota B(), ohjelma ei käänny, koska A() kutsuu funktiota B(); sama ongelma esiintyy, jos ensiksi määritellään B(), koska se kutsuu funktiota A(). Kuten arvata saattaa, tällaiseen tilanteeseen on ratkaisu. Voimme esitellä funktion ennen kuin käytämme sitä tai määrittelemme sen. Tämä tapahtuu funktion prototyypin avulla. Funktion prototyyppi Funktion prototyyppi on lause, joka kuvaa funktion riittävän tarkasti, jotta kääntäjä voi kääntää sen kutsut. Se esittelee funktion nimen, paluuarvon tyypin ja parametrien tyypit. Funktion prototyyppiä kutsutaan usein funktion esittelyksi ja on samanlainen kuin muuttujan esittelykin - funktiota ei voida kutsua lähdetekstitiedostossa, ellei kutsun edellä ole funktion esittelyä. Funktion määrittely on myös funktion esittely, joten emme tarvitse funktion prototyyppiä funktiosta potenssi() esimerkissä 8.1. Voimme kirjoittaa funktion potenssi() prototyypin seuraavasti: double potenssi(double x, int n); Jos sijoitat funktion prototyypin sen lähdetekstitiedoston alkuun, jossa funktiota kutsutaan, kääntäjä pystyy kääntämään koodin riippumatta, missä funktion määrittely on. Jotta esimerkki 8.2 kääntyisi, sijoitamme funktion potenssi() prototyypin ennen main()-funktiota: // Esimerkki Luvun potenssin laskenta - uusi järjestys #include <iostream> #include <iomanip> using namespace std; double potenssi(double x, int n); 285

10 C++ Ohjelmoijan käsikirja int main() //main()-funktio kuten ennenkin double potenssi(double x, int n) //potenssi()-funktio kuten ennenkin Tässä funktion prototyyppi on sama kuin funktion otsikko lisättynä puolipisteellä. Funktion prototyyppi päättyy aina puolipisteeseen, mutta yleisesti ottaen sen ei tarvitse olla samanlainen kuin funktion otsikko. Voit käyttää parametreissä eri nimiä (mutta ei eri tyyppejä) kuin funktion otsikossa. Esimerkiksi: double potenssi(double arvo, int eksponentti); Tämä toimii aivan yhtä hyvin. Näiden nimien käytön hyöty on minimaalinen, mutta selventää, että usein on hyödyllistä käyttää pidempiä nimiä parametreistä funktion prototyypissä. Koska kääntäjän tarvitsee tietää vain parametrien tyyppi, voit jättää parametrien nimet funktion prototyypistä kokonaan pois, kuten seuraavassa: double potenssi(double, int); Funktion protyypin kirjoittamisesta tällä tavalla ei ole mitään hyötyä ja se on vähemmän informatiivinen kuin parametrien nimien käyttö. Jos molemmat parametrit ovat samaa tyyppiä, tällainen protyyppi ei kerro mitään siitä, mikä parametri on mikäkin. Suosittelen, että käytät parametrien nimiä aina funktion prototyypeissä. Funktion prototyypin käyttö Kannattaa ottaa tavaksi kirjoittaa lähdetekstitiedoston alkuun kaikkien tiedostossa olevien funktioiden prototyyppi - poislukien main()-funktio, joka ei koskaan tarvitse prototyyppiä. Näin vältetään kääntäjän virheilmoitukset siitä, että funktioiden kutsujärjestys ei ole sallittu. Lisäksi muut ohjelmoijat saavat näin paremmin kuvan ohjelmasi toiminnasta. Olemme käyttäneet kirjastofunktiota varsin usein, mutta missä ovat näiden funktioiden prototyypit? Ne ovat itse asiassa standardiotsikkotiedostoissa, jotka olemme sisällyttäneet ohjelmiimme. Otsikkotiedostojen yksi tärkeimmistä tehtävistä on koota yhteen sopiva joukko funktioiden prototyyppejä. Käsittelemme lisää tätä aihetta luvussa

11 Parametrien välitys funktiolle Ohjelmointi funktioiden avulla On erittäin tärkeää ymmärtää, miten funktiolle välitetään parametrejä, koska se vaikuttaa siihen, miten kirjoitat funktioitasi ja viimekädessä niiden toimintaan. Lisäksi olemassa on useita sudenkuoppia, joita tulee välttää. Kuten olemme jo nähneet, funktion kutsussa olevien parametrien tyyppien tulee yleensä vastata funktion määrittelyssä oleviin parametreihin. Näin ei kuitenkaan aina tarvitse olla. Jos funktion kutsussa oleva parametrin tyyppi ei vastaa funktion esittelyssä olevaa parametrin tyyppiä, kääntäjä suorittaa (jos mahdollista) automaattisen tyypinmuunnoksen. Tämän automaattisen tyypinmuunnoksen säännöt ovat samat kuin luvussa 3 käsittelemämme sijoituslauseenkin yhteydessä. Jos automaattinen tyypinmuunnos ei ole mahdollista, saat kääntäjän virheilmoituksen. C++:ssa on kaksi mekanismia parametrien välittämisessä funktioille. Ensimmäinen on parametrien välittäminen arvoparametreinä ja toinen on parametrien välittäminen viittausparametreinä. Tutustumme ensin arvoparametreihin ja myöhemmin viittausparametreihin. Arvoparametrit Tässä mekanismissa funktion parametreinä olevia muuttujia tai vakioita ei itse asiassa välitetä funktiolle lainkaan. Parametreistä muodostetaan kopiot, samaan tapaan kuin paluuarvonkin yhteydessä, ja nämä kopiot välitetään funktiolle. Tätä havainnollistetaan seuraavassa kaaviossa, jossa asia esitetään potenssi()-funktiollamme: double arvo = 20.0; int indeksi = 3; double tulos = potenssi(arvo, indeksi); indeksi arvo Indeksin kopio 3 Arvon kopio 20.0 double potenssi( double x, int n) Tämä koodi ei pääse... käsiksi muuttujien indeksi ja arvo alkuperäisiin arvoihin Joka kerta, kun kutsut potenssi()-funktiota, kääntäjä muodostaa parametreistä väliaikaiset kopiot aikaisemmin tässä luvussa mainitsemaani pinoon. Funktion suorituksen aikana kaikki viittaukset parametreihin yhdistetään näihin parametrien väliaikaisiin kopioihin. Kun funktion suoritus päättyy, parametrien kopiot hävitetään. 287

12 C++ Ohjelmoijan käsikirja Voimme havainnollistaa vaikutuksia yksinkertaisella esimerkillä. Kokeile itse - Parametrien välitys funktiolle Voimme kirjoittaa funktion, joka yrittää muuttaa parametrinsä arvoa, mutta epäonnistuu surkeasti: // Esimerkki Alkuperäisen parametrin muuttaminen ei onnistu #include <iostream> #include <iomanip> using namespace std; double muutase(double se); // Funktion prototyyppi int main() double se = 5.0; double tulos = muutase(se); cout << "Funktion suorituksen jälkeen, se = " << se << endl << "Palautettu arvo on " << tulos << endl; return 0; // Funktio yrittää muuttaa alkuperäistä parametriä double muutase(double se) se += 10.0; // Tämä muuttaa alkuperäisen kopiota cout << endl << "Funktiossa, se = " << se << endl; return se; Esimerkin tulostus näyttää seuraavalta: Funktiossa, se = 15 Funktion suorituksen jälkeen, se = 5 Palautettu arvo on 15 Kuinka se toimii Tulostuksesta voit huomata, että luvun 10 lisäämisellä funktiossa muutase() muuttujaan se ei ole vaikutusta funktion main() muuttujaan se. Muuttuja se on paikallinen funktiossa muutase() ja viittaa funktiota kutsuttaessa käytetyn parametrin kopioon. 288

13 Ohjelmointi funktioiden avulla Muuttuja se funktiossa main() 5 Kopio muodostetaan, kun funktiota kutsutaan se:n kopio 5 double tulos = muutase(se); Funktiossa main() käytetään paluuarvon kopiota. Funktion muutase() muuttuja se on hävitetty eikä ole enää olemassa. Tämä lisää kopion arvoon luvun 10. se vaiittaa aina parametrin kopioon. double muutase(double se) it += 10.0; cout << endl << "Funktiossa, se = " << se << endl; return se; Palautettavasta arvosta tehdään kopio 15 Muuttujan se arvo on 15, kun kopio tehdään Koska muuttujan se paikallinen arvo funktiossa muutase() palautetaan, kopio tehdään se:n senhetkisestä arvosta, joka sitten palautetaan kutsuvalle ohjelmalle. Arvoparametrien käyttö on varsin turvallista kutsuvalle ohjelmalle, mutta mitä jos haluaisimme muuttaa alkuperäisiä arvoja? Voimmeko tehdä sen? Totta kai voimme - yksi tapa tehdä se on käyttää osoittimia. Osoittimen välitys funktiolle Kun käytät osoitinta parametrinä, parametrin välitys arvoparametrinä toimii aivan samaan tapaan kuin edelläkin. Osoitin sisältää kuitenkin toisen muuttujan osoitteen ja osoittimen kopio osoittaa tähän samaan muuttujaan. Voimme muuttaa muutase()-funktiomme määrittelyä siten, että sille voidaan välittää double*- tyyppinen parametri. Funktiossa main() voimme sitten välittää kutsussa muuttujan se osoitteen. Tietysti myös funktion muutase() koodia tulee muuttaa, jotta se käsittelee osoittimen osoittamaa arvoa. Tämä toimii seuraavalla tavalla: Muutujan se osoite funktiossa main() 64FDEC Kopio muodostetaan, kun funktiota kutsutaan se:n kopio 64FDEC 5 Tämä kasvattaa se:n alkuperäistä arvoa luvulla 10. double tulos = muutase(&se); Muuttujan se arvo on 15, kun kopio muodostetaan Funktiossa main() käytetään paluuarvon kopiota. Funktion muutase() muuttuja se on hävitetty eikä ole enää olemassa. double muutase(double *pse) *pse += 10.0; cout << endl << "Funktiossa, *pse = " << *pse << endl; return *pse; Palautettavasta arvosta tehdään kopio

14 C++ Ohjelmoijan käsikirja Koska funktiolla muutase() on nyt alkuperäisen muuttujan se osoite, se voi muuttaa muuttujan arvoa suoraan. Katsotaan kuinka tämä toimii. Kokeile itse - Osoittimen välitys Voimme muuttaa edellistä esimerkkiä käyttämään osoittimia: // Esimerkki Parametrin alkuperäisen arvon muuttaminen #include <iostream> #include <iomanip> using namespace std; double muutase(double* se_osoitin); // Funktion prototyyppi int main() double se = 5.0; double tulos = muutase(&se); // Nyt välitetään osoite cout << "Funktion suorituksen jälkeen, se = " << se << endl << "Palautettu arvo on " << tulos << endl; return 0; // Funktio muuttaa parametriä ja palauttaa sen double muutase(double* pse) *pse += 10.0; // Tämä muuttaa alkuperäistä cout << endl << "Funktiossa, *pse = " << *pse << endl; return *pse; Tämän esimerkin tulostus näyttää seuraavalta: Funktiossa, *pse = 15 Funktion suorituksen jälkeen, se = 15 Palautettu arvo on 15 Kuinka se toimii Koska parametrin tyyppi on muutettu funktion muutase() määrittelyssä, olemme muuttaneet myös funktion prototyyppiä: double muutase(double* se_osoitin); // Funktion prototyyppi 290 Olemme myöskin käyttäneet eri parametrin nimeä kuin funktion määrittelyssä - ihan vain todistaaksemme, että voimme näin tehdä! Funktiossa main(), muuttujan se esittelyn ja alustuksen jälkeen, funktiolle muutase() välitetään kutsussa muuttujan osoite: double tulos = muutase(&se); // Nyt välitetään osoite

15 Ohjelmointi funktioiden avulla Emme tarvitse osoitinmuuttujaa muuttujan se osoitetta varten. Koska tarvitsemme osoitetta vain funktion parametrinä, voimme käyttää osoite-operaattoria funktion kutsussa. Funktion muutase() uusi versio käyttää osoitus-operaattoria lisätessään luvun 10 funktiossa main() määritellyn muuttujan se sisältöön: *pse += 10.0; // Tämä muuttaa alkuperäistä Funktio palauttaa muutetun arvon myös lauseella: return *pse; Palautettavasta arvosta tehdään nytkin kopio; se tapahtuu aina automaattisesti funktiosta palautettavan arvon yhteydessä. Funktion muutase() tämä versio vain havainnollisti, kuinka osoitinparametrin avulla voidaan muuttaa kutsuvan funktion muuttujia - se ei ole malli siitä, miten funktioita tulee kirjoittaa. Koska muutamme se:n arvoa suoraan, sen palauttaminen on aivan turhaa. Taulukoiden välitys funktiolle Koska taulukon nimeä voidaan käsitellä osoitteena, voit välittää myös taulukon funktiolle käyttämällä taulukon nimeä. Tällöin taulukon osoite kopioidaan ja välitetään funktiolle. Tästä on useita etuja. Ensinnäkin, taulukon osoitteen välittäminen on tehokkaampaa kuin taulukon arvojen välitys, koska suurten taulukoiden kopiointi voi olla hyvinkin hidasta. Itse asiassa taulukoita ei voi välittää arvoina. Toiseksi, ja vielä tärkeämpänä etuna on se, että koska funktio ei käsittele alkuperäistä osoitetta vaan sen kopiota, funktio voi käyttää osoitetta tehokkaasti - mukaan lukien osoitteen muuttaminen. Katsotaan ensin kaikkein suoraviivaisinta esimerkkiä. Kokeile itse - Taulukon välitys funktion parametrinä Voimme havainnollistaa tätä kirjoittamalla funktion, joka laskee sille välitetyn taulukon alkioiden keskiarvon: // Esimerkki Taulukon välitys #include <iostream> using namespace std; double keskiarvo(double taulukko[], int lkm); // Funktion prototyyppi int main() double arvot[] = 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0; cout << endl << "Keskiarvo = " << keskiarvo(arvot, (sizeof arvot) / (sizeof arvot[0])) << endl; 291

16 C++ Ohjelmoijan käsikirja return 0; // Funktio, joka laskee keskiarvon double keskiarvo(double taulukko[], int lkm) double summa = 0.0; // Tähän lasketaan summa for(int i = 0 ; i < lkm ; i++) summa += taulukko[i]; // Summataan taulukon alkiot return summa / lkm; // Palautetaan keskiarvo Esimerkin tulostus on varsin lyhyt: Keskiarvo = 5.5 Kuinka se toimii Funktio keskiarvo() toimii minkä kokoisen taulukon kanssa tahansa. Kuten prototyypistä näet, sille välitetään kaksi parametriä: taulukko ja taulukon alkioiden lukumäärä: double keskiarvo(double taulukko[], int lkm); // Funktion prototyyppi Ensimmäinen parametri on taulukko, jonka alkiot ovat double-tyyppisiä. Taulukon kokoa ei voi tässä määritellä hakasulkeiden sisällä. Taulukon koon kirjoittamisella ei ole tässä mitään merkitystä, koska taulukon ensimmäinen ulottuvuus ei ole osa taulukon tyyppiä (muistat samankaltaisen tilanteen, kun käsittelimme osoittimia moniulotteisiin taulukoihin). Itse asiassa voit välittää tälle funktiolle minkä kokoisen yksiulotteisen taulukon tahansa. Funktio luottaa toisena parametrinä saamaansa arvoon lkm, joka kertoo taulukon alkioiden lukumäärän. Funktion keskiarvo() rungossa laskenta suoritetaan tavalliseen tapaan: double keskiarvo(double taulukko[], int lkm) double summa = 0.0; // Tähän lasketaan summa for(int i = 0 ; i < lkm ; i++) summa += taulukko[i]; // Summataan taulukon alkiot return summa / lkm; // Palautetaan keskiarvo Tällä funktiolla ei ole mitään merkittävää eroa siihen nähden, että olisimme kirjoittaneet laskennan suoraan main()-funktioon. Funktiolla ei ole mitään mahdollisuuksia tarkistaa, että taulukossa on lkm alkiota ja se käsittelee tyytyväisenä alkuperäisen alkion ulkopuolellakin olevia muistiosoitteita, jos lkm on suurempi kuin taulukon alkioiden lukumäärä. Meidän vastuullamme on, että näin ei tapahdu! 292

17 Ohjelmointi funktioiden avulla Funktiota kutsutaan main()-funktion tulostuslauseessa: cout << endl << "Keskiarvo = " << keskiarvo(arvot, (sizeof arvot) / (sizeof arvot[0])) << endl; Tässä ensimmäinen parametri keskiarvo()-funktiolle on taulukon nimi, arvot, ja toinen parametri on lauseke, jonka arvo on taulukon alkioiden lukumäärä. Tässä esimerkissä funktiolle keskiarvo() välitetyn taulukon alkioita käsiteltiin normaalissa indeksimuodossa. Kuten sanottua, voimme käsitellä funktiolle välitettyä taulukkoa myös osoittimena ja käyttää osoitinta laskennassa. Kokeillaan nyt tätä. Kokeile itse - Osoitinmuodon käyttö taulukon välityksessä Voimme muuttaa esimerkin 8.5 funktiota siten, että se käyttää osoitinmuotoa, vaikka käytämme taulukkoa. Muutamme funktion prototyyppiä ja otsikkoa osoitinmuodon (indeksimuodon sijaan) käyttöä varten ensimmäisen parametrin osalta, vaikka se ei olekaan aivan välttämätöntä. Voisit käyttää osoitinmuotoa funktion rungossa, vaikka ensimmäinen parametri olisikin määritelty taulukkona. Tässä on uusi versio ohjelmasta: // Esimerkki Taulukkoparametrin käsittely osoittimena #include <iostream> using namespace std; double keskiarvo(double* taulukko, int lkm); // Funktion prototyyppi int main() double arvot[] = 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0; cout << endl << "Keskiarvo = " << keskiarvo(arvot, (sizeof arvot) / (sizeof arvot[0])) << endl; return 0; // Funktio, joka laskee keskiarvon double keskiarvo(double* taulukko, int lkm) double summa = 0.0; // Tähän lasketaan summa for(int i = 0 ; i < lkm ; i++) summa += *taulukko++; // Summataan taulukon alkiot return summa / lkm; // Palautetaan keskiarvo Tämän esimerkin tulostus on aivan sama kuin edellisenkin esimerkin. 293

18 C++ Ohjelmoijan käsikirja Kuinka se toimii Kuten huomaat ohjelmaan ei ole tarvinnut tehdä suuriakaan muutoksia, jotta se toimisi siten, että taulukkoa käytetään osoittimena. Funktion prototyyppi ja otsikko ovat muuttuneet, vaikkakaan kumpkiaan näistä muutoksista ei ole välttämätön. (Kokeile muuttaa tämän esimerkin funktion prototyyppi ja otsikko esimerkin 8.5 mukaiseksi, mutta jätä funktion runko osoitinmuotoiseksi. Huomaat, että se toimii yhtä hyvin myös nyt.) Tämän version kaikkein kiinnostavin osa on for-silmukkassa oleva lause: summa += *taulukko++; // Summataan taulukon alkiot Näyttäisi siltä, että rikomme tässä sääntöämme emme saa muuttaa taulukon nimen osoitetta, koska kasvatamme muuttujaan taulukko talletettua osoitetta. Oikeastaan emme riko tätä sääntöä lainkaan. Palauta mieleen, että arvoparametrin yhteydessä alkuperäisen taulukon osoitteesta tehdään kopio, joka sitten välitetään funktiolle. Eli muutamme tässä kopion sisältöä ja alkuperäisen taulukon osoite säilyy muuttumattomana. Yleisesti ottaen, aina kun välitämme funktiolle yksiulotteisen taulukon, voimme käyttää välitettyä osoitetta vapaasti haluamallamme tavalla. const-tyyppiset parametrit Joskus saattaa olla tarpeen varmistaa, että funktio ei vahingossa muuta sille välitetyn taulukon alkioita. Tällöin tulee käyttää const-avainsanaa parametrin tyypin yhteydessä. Jos esimerkiksi haluaisimme, että keskiarvo()-funktio ei muuttaisi ensimmäisen parametrinsä alkioita, voisimme kirjoittaa funktion seuraavasti: double keskiarvo(const double* taulukko, int lkm) double summa = 0.0; // Tähän lasketaan summa for(int i = 0 ; i < lkm ; i++) summa += *taulukko++; // Summataan taulukon alkiot return summa / lkm; // Palautetaan keskiarvo Nyt kääntäjä varmistaa, että taulukon alkioita ei muokata funktion rungossa. Luonnollisesti sinun tulee muuttaa myös funktion prototyyppiä vastaamaan uutta ensimmäisen parametrin tyyppiä; muista, että const-tyypit ovat aivan eri kuin muut kuin const-tyypit. Kun esittelet osoitinparametrin const-tyyppiseksi, kerrot kääntäjälle, että Kun tätä funktiota kutsutaan, se mihin osoitinparametri osoittaa, tulee käsitellä vakiona. Tästä on kaksi seurausta: kääntäjä varmistaa, että funktion rungon koodissa noudatetaan käskyäsi ja että et yritä muuttaa osoittimen osoittamaa tietoa. Se sallii myöskin funktiota kutsuttavan parametrillä, joka osoittaa vakioon. Huomaa, että ei ole mitään tarvetta esitellä tavallisen tyyppistä (kuten int) parametriä consttyyppiseksi. Koska arvoparametrien yhteydessä alkuperäisestä parametristä tehdään kopio funktiota kutsuttaessa, et pysty muuttamaan alkuperäistä arvoa funktion rungossa. 294

19 Moniulotteisen taulukon välittäminen funktiolle Ohjelmointi funktioiden avulla Edellä oleva huomioiden, moniulotteisen taulukon välittäminen funktiolle on varsin suoraviivaista. Jos olemme esitelleet kaksiulotteisen taulukon seuraavasti: double pavut[2][4]; Voisimme nyt kirjoittaa kuvitteellisen funktion tuotanto() prototyypin seuraavasti: double tuotanto(double pavut[2][4]); Olemme tässä eksplisiittisesti määritelleet taulukon kummatkin ulottuvuudet, mutta meillä ei ole mitään keinoa tarkistaa, että ensimmäisen ulottuvuuden koko on 2. Taulukkojen ensimmäisen ulottuvuuden koko ei ole osa taulukon tyyppiä, joten pavut-taulukon tyyppi on itse asiassa double[][4]. Kaikki taulukot, joiden toisen ulottuvuuden koko on 4, voidaan välittää tälle funktiolle parametrinä.! Miten kääntäjä tietää, että tämä rivi määrittelee parametrissä olevan kokoisen taulukon, eikä vain yksittäistä alkiota? Vastaus on yksinkertainen: et voi kirjoittaa taulukon yksittäistä alkiota parametriksi funktion otsikossa tai prototyypissä, mutta luonnollisestikin voit välittää funktiolle yksittäisen alkion. Funktio, jolle voidaan välittää parametrinä taulukon yksittäinen alkio, parametrin tyypin tulee olla sama kuin tämän alkion tyyppi. Taulukkomuotoa ei voi käyttää. Kun määrittelet funktion parametriksi moniulotteisen taulukon, sinun tulisi jättää ensimmäisen ulottuvuuden koko pois (ellet halua informoida, että funktion on tarkoitus toimia kiinteän kokoisen taulukon yhteydessä.). Funktio tarvitsee tällöinkin tiedon ensimmäisen ulottuvuuden koosta. Tähän voit käyttää samaa tapaa kuin aikaisemminkin: lisää toinen parametri, joka kertoo ensimmäisen ulottuvuuden koon: double tuotanto(double pavut[][4], int indeksi); Jos mietit voisitko korvata tämän lisäparametrin käyttämällä funktiossa sizeof()-funktiota taulukon koon selville saamiseksi, voin kertoa, että et voi niin tehdä. Funktion sizeof() käyttö funktion rungossa palauttaa sen osoittimen koon, jota taulukon nimi vastaa. Kokeillaan nyt kaksiulotteisen taulukon välittämistä funktiolle esimerkin avulla. Kokeile itse - Moniulotteisen taulukon välittäminen funktiolle Voimme toteuttaa tuotanto()-funktion, joka laskee sille välitetyn taulukon alkioiden summan: // Esimerkki Kaksiulotteisen taulukon välittäminen funktiolle #include <iostream> using namespace std; 295

20 C++ Ohjelmoijan käsikirja double tuotanto(double arvot[][4], int n); int main() double pavut[3][4] = 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ; cout << endl << "Tuotanto = " << tuotanto(pavut, sizeof pavut / sizeof pavut[0]) << endl; return 0; // Funktio, joka laskee kokonaistuotannon double tuotanto(double taulukko[][4], int lkm) double summa = 0.0; for(int i = 0 ; i < lkm ; i++) // Käydään läpi rivit for(int j = 0 ; j < 4 ; j++) // Käydään läpi rivin alkiot summa += taulukko[i][j]; return summa; Ohjelman tulostus on: Tuotanto = 78 Kuinka se toimii Funktion tuotanto() ensimmäinen parametri on taulukko, jossa on mielivaltainen määrä rivejä, joissa jokaisessa on neljä alkiota. Kun kutsumme funktiota, ensimmäinen parametri on taulukko pavut, jossa on kolme riviä. Toinen parametri saadaan jakamalla taulukon koko pituus (tavuissa) ensimmäisen rivin koolla. Tämä tietysti tarkoittaa taulukon rivien lukumäärää. Funktion laskenta suoritetaan yksinkertaisesti sisäkkäisissä for-silmukoissa, joista sisempi laskee yhden rivin alkiot ja ulompi toistaa sitä kullekin riville. Sellaisen funktion yhteydessä, jolla on parametrinä moniulotteinen taulukko, ei oikein ole järkevää käyttää osoitinmuotoista taulukon käsittelyä. Edellisessä esimerkissä, kun taulukko välitetään, välitetään osoite, joka osoittaa taulukkoon, jossa on neljä alkiota (rivi). Tämä ei johda helppoon osoittimen käsittelyyn ja silmukoiden sisällä oleva lause tulisikin muuttaa varsin monimutkaiseen muotoon: summa += *(*(taulukko+i)+j); Olen varma, että taulukkomuotoinen esitystapa on havainnollisempi ja yksinkertaisempi! 296

21 Viittausparametrit Ohjelmointi funktioiden avulla Viittaus on yksinkertaisesti toisen muuttujan peitenimi (alias). Kun määrittelet funktion parametrin viittaustyyppiseksi, funktiosi käyttää viittausmekanismia parametrin välityksessä. Kun funktiota kutsutaan, viittausparametriä ei kopioida, koska parametrin nimestä tulee alkuperäisen parametrin peitenimi. Aina kun parametrin nimeä käytetään funktion rungossa, käsitellään itse asiassa kutsuvan funktion alkuperäistä parametrin arvoa. Määritelläksesi parametristä viittausparametrin, kirjoitat & tyypin nimen perään. Esimerkiksi, jos haluat määritellä viittauksen int-tyyppiin, kirjoitat int&. Seuraava kaavio selventää, miten funktion kutsu viittausparametreillä toimii: Kutsuva funktio Funktio, jossa on viittausparametri int main() int numero = 20;... cout << muutase(numero);... return 0; Viittausparametri arvo alustetaan muuttujan numero arvolla, joten arvo:sta tulee numero:n peitenimi.. tämä viittaa numeroon suoraan Paluuarvon tyyppi on int, joten kopio, eli tässä tapauksessa numero:n kopio palautetaan kutsuvalle funktiolle. int muutase(int& arvo) arvo += 10; return arvo; Aina kun funktiota muutase() kutsutaan, viittausparametri, arvo, alustetaan alkuperäisellä parametrillä. Eli kun funktio muutase() on suorituksessa, arvo on peitenimi muuttujalle, joka sille välitettiin parametrinä, eli muuttujalle numero. Jos kutsut funktiota uudelleen eri muuttujalla, parametristä arvo tulee tämän muuttujan peitenimi. Funktion parametrin määrittelyllä viittaukseksi on kaksi pääseurausta. Ensinnäkin, alkuperäistä parametriä ei kopioida, joten sitä voidaan käsitellä funktiossa suoraan. Toiseksi, koska kopiointia ei suoriteta, funktion kutsu tapahtuu nopeammin. Nämä, sekä muutamat muut, ovat päällimmäiset asiat, joita meidän tulee miettiä, kun välitämme olioita funktiolle. Käsitellään nyt kuitenkin viittausparametrejä ensin tarkemmin. Lisätehokkuuden huumassa tietysti ryntäät määrittelemään kaikki funktiosi parametrit viittausparametreiksi. Tällöin on kuitenkin helppo unohtaa niiden toiminnan eräs asia: voit muokata alkuperäistä muuttujaa funktiossa ja viittauksen syntaksi tekee siitä vähemmän havainnollisen kuin jos käyttäisit osoittimia. Tässä piilee suuri virheiden vaara. Voit vahingossa muuttaa alkuperäisen muuttujan arvoa. Käytössä on turvallisempiakin tapoja muuttaa alkuperäisen muuttujan arvoa funktiossa. 297

22 C++ Ohjelmoijan käsikirja Funktio, jossa parametrinä osoitin Funktio, jossa viittauparametri int muutase(int* arvo) *arvo += 10; return *arvo; Funktiolle välitetään osoite, mikä selvästi kertoo, että funktiossa saatetaan muuttaa muuttujan arvoa. int muutase(int& arvo) arvo += 10; return arvo; Arvo välitetään funktiolle viittauksena; tämä ei mitenkään kerro, että muuttujan arvoa saatetaan muuttaa funktiossa. int numero = 20; cout << muutase(&numero); int numero = 20; cout << muutase(numero); Jos määrittelet parametrin, jonka sisältöä ajattelet muokata, osoittimeksi, funktiolle tulee välittää osoite ja kutsuvassa ohjelmassa on selvempää, että funktiossa saatetaan muuttaa muuttujan arvoa. Viittausparametrinä välitetty muuttuja on vain kyseisen muuttujan peitenimi, eikä muuttamisen mahdollisuus näy niin selvästi. Yllä oleva kaavio selventää näiden kahden tilanteen eroja. Kuinka sitten hyödynnämme tämän tehokkuuden ilman, että heikennämme parametrien turvallisuutta? Vastaus on const-tyyppisten viittausten käyttö. Tarkastellaan tätä seuraavalla esimerkillä. Kokeile itse - Viittausparametrit Kokeillaan yksinkertaista ohjelmaa, jossa käytetään funktiota suurempi(), joka palauttaa suuremman kahdesta luvusta. Käytämme tätä funktiota eri muodoissa, joilla havainnollistamme, kuinka viittausparametrien käyttö voi vaikuttaa funktion toimintaan. Tässä on ensimmäinen versio: // Esimerkki Viittausparametrien käyttö #include <iostream> using namespace std; int suurempi(int& m, int& n); int main() int arvo1 = 10; int arvo2 = 20; cout << endl << suurempi(arvo1, arvo2) << endl; return 0; // Funktio, joka palauttaa suuremman kahdesta kokonaisluvusta 298

23 Ohjelmointi funktioiden avulla int suurempi(int& m, int& n) return m > n? m : n; // Palauttaa suuremman arvon Tämän esimerkin tulostus on vain luku 20, kuten varmasti odotitkin. Kuinka se toimii Koska funktion suurempi() parametrit ovat viittausparametrejä, se käyttää muuttujien arvo1 ja arvo2 alkuperäisiä arvoja, kun sitä kutsutaan main()-funktiosta lauseella: cout << endl << suurempi(arvo1, arvo2) << endl; Jos et oikein usko tätä, niin kokeile lisätä funktion suurempi() runkoon seuraava lause, joka muuttaa toisen parametrin arvoa: int suurempi(int& m, int& n) n = 30; return m > n? m : n; // Palauttaa suuremman arvon Jos nyt tulostat muuttujan arvo2 arvon suurempi()-funktion kutsun jälkeen, huomaat, että sen arvo on muuttunut. Oletetaan, että olisimme halunneet verrata arvo1:tä vakioarvoon, esimerkiksi arvoon 15 ja sitten palauttaa suuremman arvon. Voisimme kokeilla lisätä seuraavan lauseen main()-funktioon: int main() int arvo1 = 10; int arvo2 = 20; cout << endl << suurempi(arvo1, arvo2) << endl; cout << endl << suurempi(arvo1, 15) << endl; return 0; Nyt ohjelma ei enää käänny. Kääntäjä ei salli viittauksen muodostusta vakioon 15, koska se tietää, että kutsuttavalla funktiolla on suora pääsy muuttujaan ja funktio saattaa muuttaa sitä. Tästä voimme muodostaa seuraavan päätelmän: Et voi välittää funktiolle parametrinä vakiota, jos kyseinen parametri on määritelty eiconst-tyyppisenä viittauksena. const-viittausten käyttö Pääsemme eroon edellä olleesta ongelmatilanteesta, jos määrittelemme funktion suurempi() parametrit const-viittauksiksi. Funktion prototyyppi olisi nyt: 299

24 C++ Ohjelmoijan käsikirja int suurempi(const int& m, const int& n); Nyt funktio toimii muuttujien ja vakioiden yhteydessä. Jos tarkoituksenasi ei ole muuttaa parametrinä välitettyä arvoa, voit aina määritellä parametrin const-viittaukseksi. Näin voimme suoraan käyttää alkuperäistä muuttujaa (eli vältämme kopioinnin), kääntäjä tarkistaa, ettemme yritä muuttaa niiden arvoa ja funktio toimii sekä muuttujien että vakioiden yhteydessä. Käyttämällä const-viittausta voimme yhdistää viittausparametrien paremman tehokkuuden arvoparametrien turvallisuuteen. Viittaukset vs. osoittimet Useimmissa tapauksissa viittausparametrien käyttö on suositeltavampaa kuin osoittimien käyttö. Viittausparametrit tulisi määritellä const-viittauksiksi aina kun mahdollista, koska tämä takaa alkuperäisten muuttujien turvallisuuden. Tietysti, jos tarkoituksenasi on muuttaa alkuperäisen muuttujan arvoa, et voi määritellä parametriä const-viittaukseksi, mutta kannattaa miettiä, olisiko osoitinparametri parempi ratkaisu. Kutsuvalle funktiolle on näin aina selvää, milloin funktio voi muuttaa muuttujia suoraan. Tärkeä ero osoittimen ja viittauksen välillä on se, että osoitin voi olla null, mutta viittauksen täytyy aina viitata johonkin. Jos parametri voi olla null, ainoa mahdollisuus on osoitinparametri. Luvussa 12 huomaamme, että viittausparametreillä on tiettyjä lisäominaisuuksia luokkien yhteydessä ja joissakin tapauksissa niillä voidaan saada aikaan tuloksia, jotka muuten olisivat mahdottomia. Viittausten esittely Viittauksia ei esiinny pelkästään funktion parametreissä. Viittaus voi olla olemassa myös sellaisenaan, toisen muuttujan peitenimenä. Oletetaan, että esittelemme muuttujan seuraavasti: long numero = 0; Voimme nyt esitellä viittauksen tämän muuttujaan seuraavalla esittelylauseella: long& rnumero = numero; //Esitellään viittaus muuttujaan numero Tyypin perässä oleva & ilmaisee, että esitellään viittausta, ja yhtäsuuruusmerkkien oikealla puolella olevaa muuttujaa numero käytetään alkuarvona. Täten rnumero on tyyppiä viittaus tyyppiin long. Aivan samoin kuin osoittimienkin kohdalla, voit sijoittaa &-merkin myös muuttujan nimen yhteyteen, joten voisimme kirjoittaa edellisen lauseen seuraavasti: long &rnumero = numero; //Esitellään viittaus muuttujaan numero Kääntäjä ei valita kumpaa näistä käytätkin; tässä kirjassa käytämme ensimmäistä muotoa. &-merkin käyttö viittauksen esittelyssä saattaa aluksi tuntua hieman hämmentävältä. Sen käyttö viittausten yhteydessä näyttää samanlaiselta kuin olemme nähneet aikaisemmin muuttujan osoitteen lukemisessa. Pienellä harjoituksella kuitenkin erotat nämä kaksi tilannetta. 300 Kun esittelet viittauksen, se tulee aina alustaa sen muuttujan nimellä, jonka peitenimi se on. Viittausta ei voi luoda alustamatta sitä. Lisärajoituksena on, että viittaus on kiinteä - et voi muuttaa viittausta, kun se kerran on esitelty; se on aina saman muuttujan peitenimi. Syy, miksi

25 Ohjelmointi funktioiden avulla funktion viittausparametri voi aina viitata eri muuttujaan, on se, että viittausparametri luodaan uudelleen ja alustetaan uudelleen jokaisella tämän funktion kutsulla. Viittausta voidaan aina käyttää alkuperäisen muuttujan nimen tilalla. Esimerkiksi: rnumero += 10; Tämä lause lisää muuttujan numero arvoon luvun 10, koska rnumero on viittaus siihen. Jotta varmasti huomaat eron, verrataan viittausta rnumero osoittimeen pnumero, joka on esitelty lauseella: long* pnumero = &numero; //Alustetaan osoitin osoitteella Tämä esittelee osoittimen pnumero ja alustaa sen muuttujan numero osoitteella. Muuttujan arvoa voidaan nyt kasvattaa esim. seuraavalla lauseella: *pnumero += 10; //Kasvatetaan arvoa osoittimella Tässä on merkittävä ero osoittimen ja viittauksen käytön välillä: osoittimen yhteydessä täytyy käyttää *-operaattoria, jotta osoitetun muuttujan sisältöön päästään käsiksi. Viittauksen kohdalla *-operaattoria ei tarvita. Viittaus on aivan kuin osoitin, jolle on jo sovellettu *-operaattoria, vaikka sitä ei voidakaan muuttaa viittaamaan toiseen muuttujaan. Viittaus on täydellinen vastine muuttujasta, johon se viittaa. Tässä oli kaikki, mitä meidän täytyy nyt käsitellä viittauksista. Aihe ei kuitenkaan pääty vielä tähän. Käytämme viittauksia runsaasti, kun aloitamme luokkien käsittelyn luvusta 12. main()-funktion parametrit Voit määritellä myös funktiolle main() parametrejä, jotka sitten syötetään komentoriville suorittaessasi ohjelmaa. Parametrien tyypit on standardisoitu: voit määritellä funktion main() joko ilman parametrejä, kuten olemme tähän saakka tehneet, tai voit määritellä main()-funktion muodossa: int main(int argc, char* argv[]) //main()-funktion koodi Ensimmäinen parametri, argc, on komentoriville syötettyjen merkkijonojen lukumäärä. Toinen parametri, argv, on taulukko, joka sisältää osoittimet jokaiseen komentorivillä syötettyyn merkkijonoon, mukaan lukien itse ohjelman nimi. Taulukon argv viimeinen alkio (eli argv[argc]) on aina 0 ja taulukon argv alkioiden lukumäärä on argc + 1. Katsotaan muutamaa esimerkkiä. Oletetaan, että suorittaaksesi ohjelman kirjoitat seuraavaa komentoriville: Omaohjelma Tässä tapauksessa argc on 1 ja argv[] sisältää kaksi alkiota. Ensimmäinen alkio sisältää merkkijonon Omaohjelma ja toinen alkio on

26 C++ Ohjelmoijan käsikirja Jos kuitenkin syötät komentoriville Omaohjelma Rip Van Wincle argc sisältää arvon 4 ja argv[] sisältää viisi alkiota. Ensimmäiset neljä osoittavat merkkijonoihin Omaohjelma.exe, 2, 3.5 ja Rip Van Wincle. Alkio argv[4] on 0. Se mitä teet komentorivin parametreillä, riippuu pelkästään sinusta. Havainnollistetaan komentorivin parametrien käyttöä seuraavanlaisella main()-funktiolla: #include <iostream> using namespace std; int main(int argc, char* argv[]) for (int i = 0 ; i < argc ; i++) cout << endl << argv[i]; cout << endl; return 0; Tämä yksinkertaisesti luettelee kaikki komentorivin parametrit, ohjelman nimen mukaan lukien. Komentorivin parametrit voivat olla mitä tahansa: esimerkiksi tiedostonimiä kopiointiohjelmille tai yhteystiedoista etsittävän henkilön nimi. Toisin sanoen mitä tahansa, joka on ohjelmallesi hyödyllistä. Parametrin oletusarvot Monissa tapauksissa olisi hyödyllistä määritellä yhdelle tai useammalle parametrille oletusarvo. Tällöin funktiolle täytyy välittää vain ne parametrit, jotka eroavat oletusarvoista. Yksinkertainen esimerkki voisi olla esimerkiksi standardin virheilmoituksen tulostaminen. Useimmissa tapauksissa oletusviesti olisi riittävä, mutta tietyissä tapauksissa haluaisit käyttää toista viestiä. Voit tehdä tämän määrittelemällä parametrin oletusarvon funktion prototyyppiin. Voisimme kirjoittaa viestin tulostavan funktion seuraavasti: void naytavirhe(const char* viesti) cout << endl << viesti << endl; Jos haluamme määritellä oletusviestin, kirjoitamme parametrin oletusarvon funktion prototyyppiin: void naytavirhe(const char* viesti = Ohjelmavirhe ); 302

27 Jos haluat tulostaa oletusviestin, voit kutsua funktiota ilman parametrejä: Ohjelmointi funktioiden avulla naytavirhe(); //Tulostetaan oletusviesti Tämä tulostaa: Ohjelmavirhe Kun haluat tulostaa tietyn viestin välittämällä funktiolle parametrin: naytavirhe( Mikään ei toimi! ); Voisimme määritellä naytavirhe()-funktion parametrin myös string-tyyppiseksi C-tyylisen merkkijonon sijaan. Tällöin funktion prototyyppi kirjoitettaisiin seuraavasti: void naytavirhe(const string viesti = Ohjelmavirhe ); Tietysti tulee muistaa sisällyttää ohjelmaan otsikkotiedosto string, jotta string-tyyppi on käytettävissä. Parametrin oletusarvon määrittely tällä tavalla voi helpottaa funktioiden käyttöä, eikä niiden käyttöä ole rajoitettu vain yhteen parametriin - oletusarvo voi olla useammallakin parametrillä. Tutkitaan tätä hieman lisää. Useiden parametrien oletusarvot Kun kirjoitat funktiota, jonka parametreillä on oletusarvoja, nämä parametrit tulee sijoittaa parametriluettelon loppuun. Syykin on varsin selvä. Jos haluat käyttää oletusarvoa funktiota kutsuessasi, jätät välittämättä arvon tälle parametrille. Jos arvo voitaisiin jättää pois parametriluettelon keskellä olevalta parametriltä, kääntäjä ei millään enää tietäisi, mikä välitetyistä arvoista kuuluu millekin parametrille! Kääntäjä olettaa, että poisjätetty alkuperäinen parametri vastaa funktion oikeanpuoleisinta parametriä. Kokeillaan esimerkkiä funktiosta, jossa on useita parametrien oletusarvoja. Oletetaan, että olemme kirjoittaneet funktion, joka tulostaa yhden tai useamman arvon usealle riville: void naytadata(const int data[], int lkm, const string& otsikko, int leveys, int riville) cout << endl << otsikko; // Tulostetaan otsikko // Tulostetaan tiedot for(int i = 0 ; i < lkm ; i++) if(i % riville == 0) cout << endl; cout << setw(leveys) << data[i]; cout << endl; // Uusi rivi ennen ensimmäistä // ja riville jälkeen // Tulostetaan tietoalkio 303

28 C++ Ohjelmoijan käsikirja Parametri data sisältää tulostettavat arvot ja lkm ilmaisee, montako arvoja on. Kolmannen parametrin tyyppi on const string& ja ilmaisee tulostuksen otsikon. Neljäs parametri ilmaisee kunkin arvon kentän leveyden tulostuksessa ja viimeinen parametri on yhdelle riville tulostettavien arvojen lukumäärä. Tämä parametriluettelo on jo varsin pitkä - kuvittele, jos meidän tulisi aina kirjoittaa kaikki viisi parametriä, vaikka tulostettavia arvoja olisi vain yksi! Voimme käyttää joidenkin parametrien oletusarvoja helpottamaan tilannetta. Kokeile itse - Useiden parametrien oletusarvojen käyttö Seuraavassa on esimerkki, miten voimme käyttää kaikkien muiden paitsi ensimmäisen parametrin oletusarvoja: // Esimerkki Useiden parametrien oletusarvojen käyttö #include <iostream> #include <iomanip> #include <string> using namespace std; void naytadata(const int data[], int lkm = 1, const string& otsikko = "Datan arvot", int leveys = 10, int riville = 5); int main() int esimerkit[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; int tieto = 99; naytadata(&tieto); tieto = 13; naytadata(&tieto, 1, "Epäonnea jollekin!"); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0]); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit"); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit", 14); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit", 14, 4); return 0; // Funktio, joka tulostaa yhden tai useamman kokonaislukuarvon void naytadata(const int data[], int lkm, const string& otsikko, int leveys, int riville) cout << endl << otsikko; // Tulostetaan otsikko // Tulostetaan tiedot for(int i = 0 ; i < lkm ; i++) if(i % riville == 0) cout << endl; // Uusi rivi ennen ensimmäistä // ja riville jälkeen 304

29 Ohjelmointi funktioiden avulla cout << setw(leveys) << data[i]; cout << endl; // Tulostetaan tietoalkio Ohjelman tulostus on seuraava: Datan arvot 99 Epäonnea jollekin! 13 Datan arvot esimerkit esimerkit esimerkit Kuinka se toimii Funktion naytadata() prototyyppi määrittelee oletusarvot kaikille muille parametreille paitsi ensimmäiselle: void naytadata(const int data[], int lkm = 1, const string& otsikko = "Datan arvot", int leveys = 10, int riville = 5); Kuten huomaat kolmannesta parametristä, voit käyttää oletusarvoja niin viiteparametrien kuin muidenkin parametrien yhteydessä. Koska neljällä viimeisellä parametrillä on oletusarvo, voimme kutsua funktiota viidellä eri tavalla: voimme välittää kaikki viisi parametriä, jättää viimeisen pois tai kaksi viimeistä pois tai kolme viimeistä tai neljä viimeistä. Muista, että voimme jättää arvot välittämättä vain luettelon loppupäästä päin. Et voi esimerkiksi jättää pois toista ja viidettä parametriä: naytadata (esimerkit,, esimerkit, 15) //VIRHE! main()-funktiossa määrittelemme taulukollisen kokonaislukuja: int esimerkit[] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; 305

30 C++ Ohjelmoijan käsikirja Taulukkoa käytetään naytadata()-funktiota kutsuttaessa. Funktion ensimmäinen kutsukerta tulostaa yhden arvon: int tieto = 99; naytadata(&tieto); Koska ensimmäinen parametri on int-tyyppinen taulukko, meidän tulee välittää funktiolle muuttujan tieto osoite. Parametrin lkm oletusarvo 1 käy tässä tilanteessa. Samaten käyvät kaikki muutkin oletusarvot. Seuraavaksi tulostamme muuttujan tieto muuttuneen arvon sekä otsikon: tieto = 13; naytadata(&tieto, 1, "Epäonnea jollekin!"); Haluamme tässä tulostaa vain uuden otsikon, mutta koska otsikko on kolmas parametri, meidän tulee välittää funktiolle myöskin toinen parametri. Parametrin lkm arvo on sama kuin oletusarvon, koska tulostamme yhä yhden arvon. Tämän jälkeen tulostamme taulukon esimerkit alkiot neljä kertaa seuraavilla neljällä lauseella: naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0]); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit"); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit", 14); naytadata(esimerkit, sizeof esimerkit / sizeof esimerkit[0], "esimerkit", 14, 4); Taulukoiden yhteydessä meidän tulee välittää arvo myös parametrille lkm; muutoin naytadata() tulostaa vain taulukon ensimmäisen alkion. Kussakin edellä olevassa lauseessa kutsutaan funktiota aina uusilla parametreillä. Kun käytät useita parametrejä, joilla on oletusarvo, parametrien järjestys kannattaa harkita tarkkaan. Ne tulee järjestää siten, että parametri, jolle harvimmin välitetään arvo, tulee luettelossa viimeiseksi. Tämän järjestyksen valinta voi kuitenkin olla hyvinkin vaikeaa. Arvon palauttaminen funktiosta Kuten jo tiedätkin, voit palauttaa funktiosta minkä tyyppisen arvon tahansa. Arvon palauttaminen on varsin suoraviivaista, kun palautat perustietotyyppiä olevan paluuarvon, mutta osoittimien palauttamisessa on joitakin sudenkuoppia. 306 Osoittimen palauttaminen Kun palautat osoittimen funktiosta, sinun tulee olla ehdottoman varma, että osoittimen osoite on joko 0 tai muistiosoite, joka on olemassa myös kutsuvassa funktiossa. Toisin sanoen, osoitettavan muuttujan näkyvyysalueen tulee olla voimassa palattaessa kutsuvaan funktioon. Tästä muotoutuukin seuraava kultainen sääntö: Älä koskaan palauta funktion paikallisen muuttujan osoitetta.

31 Ohjelmointi funktioiden avulla Katsotaan esimerkkiä. Oletetaan, että haluaisimme kirjoittaa funktion, joka palauttaa sen muuttujan osoitteen, joka on suurempi kahdesta arvosta. Tätä funktiota saatetaan käyttää sijoitusoperaattorin vasemmalla puolella siten, että voit muuttaa suuremman muuttujan arvoa: *suurempi(arvo1, arvo2) = 100; //Muutetaan suuremman arvoksi 100 Tällainen saattaa johtaa sinut helposti harhaan. Seuraavassa on toteutus, joka ei toimi: int* suurempi(int a, int b) if(a > b) return &a; else return &b; // Väärin! // Väärin! On varsin helppo nähdä, mikä tässä on väärin: a ja b ovat paikallisia muuttujia. Alkuperäisten muuttujien kopiot välitetään paikallisiin muuttujiin a ja b, mutta kun palautamme osoitteet &a ja &b, näissä osoitteissa olevien muuttujien näkyvyysalue ei ole voimassa kutsuvassa funktiossa. Kääntäjäsi pitäisi antaa ilmoitus, jos yrität kääntää funktion tämän version. Se mitä voimme tehdä, on parametrien muuttaminen osoittimiksi: int* suurempi(int* a, int* b) if(*a > *b) return a; else return b; // OK // OK Voimme kutsua tätä funktiota seuraavanlaisella rivillä: *suurempi(&arvo1, &arvo2) = 100; //Muutetaan suuremman arvoksi 100 Kahdesta arvosta suuremman osoitteen palauttava funktio ei varmastikaan ole kovin hyödyllinen, mutta funktio, joka palauttaa taulukon suurimman alkion osoitteen, on mielenkiintoisempi. Kokeile itse - Osoittimen palauttaminen Voimme laajentaa edellistä ideaa hieman ja kirjoittaa kaksi funktiota: funktion, joka palauttaa taulukon pienimmän alkion osoitteen ja toisen funktion, joka palauttaa taulukon suurimman alkion osoitteen. Voimme myöskin käyttää naytadata()-funktiota. Tämä ohjelma muokkaa double-tyyppistä taulukkoa siten, että alkioiden arvot ovat välillä 0 ja 1. Voit tehdä tämän, jos olet kiinnostunut vain arvojen suhteellisista arvoista - arvojen skaalaaminen kiinteälle arvovälille helpottaa esimerkiksi niiden esittämistä graafisesti. // Esimerkki Osoittimen palauttaminen #include <iostream> #include <iomanip> #include <string> 307

32 C++ Ohjelmoijan käsikirja using namespace std; void naytadata(const double data[], int lkm = 1, const string& otsikko = "Datan arvot", int leveys = 10, int riville = 5); double* suurin(double data[], int lkm); double* pienin(double data[], int lkm); int main() double esimerkit[] = 11.0, 23.0, 13.0, 4.0, 57.0, 36.0, 317.0, 88.0, 9.0, 100.0, 121.0, 12.0 ; const int lkm = sizeof esimerkit / sizeof esimerkit[0]; naytadata(esimerkit, lkm, "Alkuperäiset arvot"); int min = *pienin(esimerkit, lkm); // Muutetaan arvojen väli siten, että pienin on nolla for(int i = 0; i < lkm ; i++) esimerkit[i] -= min; int max = *suurin(esimerkit, lkm); // Normalisoidaan arvoväliksi 0-1 for(int i = 0; i < lkm ; i++) esimerkit[i] /= max; naytadata(esimerkit, lkm, "Normalisoidut arvot", 12); return 0; // Funktio, joka etsii double-tyyppisen taulukon suurimman alkion double* suurin(double data[], int lkm) int indeksimax = 0; for(int i = 1; i < lkm; i++) if(data[indeksimax] < data[i]) indeksimax = i; return &data[indeksimax]; // Funktio, joka etsii double-tyyppisen taulukon pienimmän alkion double* pienin(double data[], int lkm) int indeksimin = 0; for(int i = 1; i < lkm; i++) if(data[indeksimin] > data[i]) indeksimin = i; return &data[indeksimin]; 308

33 // Funktio, joka tulostaa double-tyyppisen taulukon alkiot void naytadata(const double data[], int lkm, const string& otsikko, int leveys, int riville) cout << endl << otsikko; for(int i = 0 ; i < lkm ; i++) if(i % riville == 0) cout << endl; cout << setw(leveys) << data[i]; cout << endl; Ohjelmointi funktioiden avulla Ohjelman tulostus näyttää seuraavalta: Alkuperäiset arvot Normalisoidut arvot Kuinka se toimii Tässä ohjelmassa käytetään kahta varsin samanlaista funktiota suurin() ja pienin(), jotka palauttavat double-tyyppisen taulukon suurimman ja pienimmän arvon. Funktion suurin() koodi on seuraava: double* suurin(double data[], int lkm) int indeksimax = 0; for(int i = 1; i < lkm; i++) if(data[indeksimax] < data[i]) indeksimax = i; return &data[indeksimax]; Funktio aloittaa olettamalla, että taulukon ensimmäinen alkio on suurin, määrittelemällä muuttujan indeksimax arvoksi 0. Indeksissä indeksimax olevaa alkiota verrataan muihin alkioihin for-silmukassa. Jos indeksimax alkio on pienempi kuin silmukan nykyinen alkio, indeksimax:n arvoksi sijoitetaan silmukan nykyinen indeksi. Funktio palauttaa suurimman alkion osoitteen, joka saadaan selville lausekkeella &data[indeksimax]. Funktio pienin() eroaa vain if-lauseessa käytettävän vertailuoperaattorin perusteella. Funktiossa main() määrittelemme taulukon esimerkit. Tämän jälkeen sijoitamme taulukon alkioiden lukumäärän muuttujaan lkm. Näin meidän ei tarvitse joka kerta laskea sitä: const int lkm = sizeof esimerkit / sizeof esimerkit[0]; 309

34 C++ Ohjelmoijan käsikirja Tulostamme taulukon alkuperäiset alkiot lauseella: naytadata(esimerkit, lkm, "Alkuperäiset arvot"); Ensimmäistä parametriä lukuun ottamatta, naytadata()-funktion määrittely on samanlainen kuin olemme jo nähneet aikaisemmin. Funktion prototyyppi määrittelee oletusarvot neljälle viimeiselle parametrille ja käytämme kahden viimeisen oletusarvoja tässä kutsussa. Käytämme pienin()-funktiota löytääksemme esimerkit-taulukon pienimmän arvon: int min = *pienin(esimerkit, lkm); Pienimmän alkion arvon selville saamiseksi käytämme *-operaattoria funktion palauttamalle osoitteelle. Voimme muuttaa arvojen arvovälin sellaiseksi, että pienin arvo on 0 vähentämällä pienimmän arvon kaikista taulukon alkioista. Tämän teemme for-silmukassa: for(int i = 0; i < lkm ; i++) esimerkit[i] -= min; Nyt kun arvojen arvoväli on nollasta johonkin suurimpaan arvoon, voimme muokata arvoja siten, että ne ovat kaikki välillä 0-1 jakamalla ne suurimmalla arvolla, joka saadaan selville suurin()- funktiolla: int max = *suurin(esimerkit, lkm); Tämän jälkeen muokkaamme arvoja edelleen toisessa for-silmukassa jakamalla ne suurimmalla arvolla: for(int i = 0; i < lkm ; i++) esimerkit[i] /= max; Lopuksi tulostamme uudet arvot: naytadata(esimerkit, lkm, "Normalisoidut arvot", 12); Nyt emme halua käyttää leveyden oletusarvoa, koska haluamme, että kunkin arvon desimaalit mahtuvat mukaan tulostukseen. Lopuksi on hyvä mainita, että koska funktiot suurin() ja pienin() palauttavat osoitteen, voimme käyttää tätä arvoa sijoitusoperaattorin vasemmalla puolella käyttämällä *-operaattoria: *suurin(esimerkit, lkm) *= 2.0; Tämä kaksinkertaistaa esimerkit-taulukon suurimman alkion arvon. 310

35 Viittauksen palauttaminen Ohjelmointi funktioiden avulla Osoittimen palauttaminen funktiosta on joskus paikallaan, mutta siinä on ongelmansakin. Erityisesti silloin, kun käytät funktiota sijoitusoperaattorin vasemmalla puolella. Osoittimen arvo voi olla null, ja yritys käyttää null-osoitinta saa aikaan virheen ohjelmassasi. Ratkaisuna on, kuten varmasti osaat kappaleen nimestäkin aavistaa, on viittauksen palauttaminen funktiosta. Koska viittaus on toisen muuttujan peitenimi, voimme kirjoittaa viittausten kultaisen säännön, joka on aivan samanlainen kuin osoittimienkin kultainen sääntö: Älä koskaan palauta viittausta funktion paikalliseen muuttujaan. Viittauksen palauttaminen mahdollistaa funktion kutsun käyttämisen sijoitusoperaattorin vasemmalla puolella. Oletetaan, että määrittelemme suurempi()-funktiomme seuraavasti: int& suurempi(int& m, int& n) return m > n? m : n; // Palautetaan suurempi arvo Paluuarvon tyyppi on viittaus int-tyyppiseen muuttujaan ja parametrit ovat ei-const-tyyppisiä viittauksia. Koska haluamme palauttaa jomman kumman viittausparametreistä, emme voi esitellä parametrejä const-tyyppisiksi. Viittaustyyppinen paluuarvo voidaan sijoittaa sijoitusoperaattorin vasemmalle puolelle, joten sitä voidaan muokata. Voimme nyt käyttää funktiota muuttamaan kahdesta parametristä suuremman arvoa lauseella: suurempi(arvo1, arvo2) = 50; //Muutetaan suuremman arvoksi 50 Koska funktio on määritelty tällä tavalla, emme voi mitenkään käyttää vakioparametrejä. Koska parametrit ovat muita kuin const-tyyppisiä viittauksia, kääntäjä ei yksinkertaisesti salli niin tehtävän. Viittausparametri selkeästi sallii arvon muuttamisen, eikä kääntäjä ainakaan tietoisesti salli vakion muuttamista. Emme käsittele viittauksen palauttamista tässä tämän enempää, mutta voit olla varma, että tapaamme ne uudelleen myöhemmin. Kuten tulet huomaamaan, viittaustyyppiset paluuarvot ovat tärkeässä osassa, kun aloitamme omien tietotyyppiemme tekemisen luokkien avulla. Uuden muuttujan palauttaminen funktiosta Voit luoda uuden muuttujan vapaasta muistista funktiossa ja palauttaa sen kutsuvalle funktiolle osoitintyyppisen paluuarvon avulla. Käytät vain new-operaattoria muistin varaamiseen ja palautat osoitteen. Tämän tekniikan varjopuolena on kuitenkin se, että muistivuodon riski kasvaa. Joka kerta kun tällaista funktiota kutsutaan, se varaa lisää muistia vapaasta muistista ja sen vapauttaminen delete-operaattorin avulla on kutsuvan funktion vastuulla. 311

36 C++ Ohjelmoijan käsikirja Avoimet funktiot Hyvin lyhyiden funktioiden (kuten edellä ollut suurempi()-funktio) kohdalla kääntäjän muodostama koodi, joka huolehtii parametrien ja paluuarvojen välittämisestä, muodostaa huomattavan lisätyön suhteessa varsinaiseen tehtävään. Äärimmäisessä tapauksessa funktiota kutsuva koodi kuluttaa enemmän muistia kuin itse funktion koodi. Tällaisissa tapauksissa voit ehdottaa kääntäjälle, että se korvaisi kaikki kyseisen funktion kutsut funktion rungon koodilla. Näin ohjelmasta tulee lyhyempi tai nopeampi tai jopa molempia. Voit ehdottaa tätä kääntäjälle inline-avainsanalla funktion määrittelyssä: inline int suurempi(int m, int n) return m > n? m : n; Tällä määrittelyllä ehdotat kääntäjälle, että se korvaisi kutsun funktion rungon koodilla. Se on kuitenkin vain ehdotus ja kääntäjästä riippuu, otetaanko ehdotus huomioon. Kun määrittelet funktion avoimeksi, määrittelyn tulee olla saatavilla kaikissa niissä lähdetekstitiedostoissa, joista funktiota kutsutaan. Tästä syystä avoimen funktion määrittely on yleensä otsikkotiedostossa eikä lähdetekstitiedostossa ja otsikkotiedosto sisällytetään kaikkiin funktiota käyttäviin lähdetekstitiedostoihin. Eri kääntäjät käyttävät erilaisia sääntöjä siihen, korvataanko avointen funktioiden kutsu funktion rungon koodilla. Perusvaatimuksena on, että funktion tulee olla lyhyt ja yksinkertainen. Luonnollisestikin pitkän funktion määritteleminen avoimeksi funktioksi ei ole järkevää, varsinkin, jos funktiota kutsutaan useasta kohtaa ohjelmasta. Tällöin ohjelman koko kasvaisi suuresti, eikä suoritusaika nopeutuisi välttämättä lainkaan. Jos kääntäjä ei tee avointa funktiota, vaikka olet sitä pyytänyt, on sillä haittansakin. Tällöin funktion kutsu käännetään aivan tavallisena funktion kutsuna, mutta kääntäjä yleensä kohtelee funktiota paikallisena lähdetekstitiedostossa, joten jokaisella lähdetekstitiedostolla, joka käyttää tätä funktiota, on oma käännetty kopio funktiosta. Tuloksena on tarpeetonta koodin päällekkäisyyttä, jos funktiota kutsutaan useasta eri lähdetekstitiedostosta. 312 Staattiset muuttujat Kaikissa tähän saakka kirjoittamissamme funktiossa ei funktion rungossa säily mitään funktion suorituskerrasta toiseen. Oletetaan, että haluaisit laskea, kuinka monta kertaa kyseistä funktiota on kutsuttu - miten tällainen tehtäisiin? Yhtenä ratkaisuna olisi määritellä muuttuja tiedostotasolla ja kasvattaa sen arvoa funktiossa. Tämän ratkaisun huonona puolena on, että muuttujaa voidaan muuttaa kaikista lähdetekstitiedoston funktioista, etkä näin voi olla varma, että sitä muutetaan vain siellä missä pitäisi.

37 Ohjelmointi funktioiden avulla Parempi ratkaisu on esitellä muuttuja funktiossa staattiseksi käyttämällä static-avainsanaa. Tätä käsittelimme jo lyhyesti luvussa 3. Staattinen muuttuja luodaan, kun sen määrittelevä lause suoritetaan ensimmäisen kerran. Tämän jälkeen se on olemassa ohjelman suorituksen loppuun saakka. Tämä tarkoittaa sitä, että voit säilyttää muuttujan arvon funktion kutsusta toiseen. Määritelläksesi muuttujan staattiseksi, kirjoitat yksinkertaisesti tyypin eteen static-avainsanan: static int lkm = 1; Kun tämä lause suoritetaan, staattinen muuttuja lkm luodaan ja alustetaan arvolla 1. Lauseen seuraavilla suorituksilla ei ole enää vaikutusta. Muuttuja lkm on olemassa ohjelman suorituksen loppuun saakka. Jos jätät alkuarvon asettamatta, staattinen muuttuja alustetaan automaattisesti arvolla 0. Katsotaan yksinkertaista esimerkkiä funktiosta, jossa esitellään staattinen muuttuja: void seuraavaint() static int lkm = 1; cout << endl << lkm++; Tämä funktio kasvattaa staattisen muuttujan lkm arvoa tulostettuaan nykyisen arvon. Kun funktiota kutsutaan ensimmäisen kerran, se tulostaa arvon 1. Seuraavalla kutsukerralla se tulostaa 2. Joka kerta, kun funktiota kutsutaan, se tulostaa luvun, joka on yhden suurempi kuin edellinen arvo. Staattinen muuttuja lkm luodaan ja alustetaan vain kerran. Funktion peräkkäiset kutsut käyttävät muuttujan lkm nykyistä arvoa, joka on olemassa ohjelman suorituksen loppuun saakka. Voit esitellä minkä tyyppisen muuttujan tahansa staattiseksi, ja voit käyttää staattista muuttujaa kaikkeen sellaiseen, joka sinun tulee muistaa funktion kutsusta toiseen. Voit esimerkiksi haluta pitää kirjaa tiedostosta viimeksi luetun tietueen numeron tai suurimman arvon, joka on koskaan välitetty tietyssä parametrissä. Voimme havainnollistaa staattisten muuttujien käyttöä esimerkillä. Kokeile itse - Staattisen muuttujan käyttö funktiossa Fibonacin sarja on kokonaislukujen sarja, jossa jokainen numero on kahden sitä edeltävän numeron summa. Kirjoitamme funktion, joka palauttaa sarjan numeron käyttämällä staattisia muuttujia säilyttämään funktion kahden edellisen numeron. Luonnollisestikaan käytössä ei ole edellisiä numeroita ohjelman suorituksen alkaessa, joten alustamme muuttujat arvoilla 0 ja 1. Emme väitä, että tämä esimerkki olisi hyvää ohjelmointityyliä, mutta se havainnollistaa staattisten muuttujien käyttöä hieman monimutkaisemmassa tilanteessa. // Esimerkki Staattisen muuttujan käyttö #include <iostream> #include <iomanip> using namespace std; 313

38 C++ Ohjelmoijan käsikirja long seuraavafib(); int main() cout << endl << "Fibonacin sarja" << endl; for(int i = 0 ; i < 30 ; i++) if(i % 5 == 0) // Joka viides numero... cout << endl; //...aloittaa uuden rivin cout << setw(12) << seuraavafib(); cout << endl; return 0; // Funktio, joka laskee fibonacin sarjan seuraavan numeron long seuraavafib() static long viimeisin = 0; // Viimeisin numero static long sitaedellinen = 1; // Viimeistä edellinen numero long seuraava = viimeisin + sitaedellinen; sitaedellinen = viimeisin; viimeisin = seuraava; return viimeisin; // Lasketaan seuraava numero // Päivitetään viimeistä edellinen // Viimeisin on seuraava // Palautetaan seuraava Ohjelman tulostu on seuraava: Fibonacin sarja Kuinka se toimii Funktio main() kutsuu funktiota seuraavafib() 30 kertaa silmukassa: for(int i = 0 ; i < 30 ; i++) if(i % 5 == 0) cout << endl; cout << setw(12) << seuraavafib(); // Joka viides numero... //...aloittaa uuden rivin Mitään parametrejä ei välitetä, joten palautettava arvo lasketaan sisäisesti funktiossa seuraavafib(). Kaksi staattista muuttujaa säilyttävät kaksi viimeisintä sarjan numeroa: static long viimeisin = 0; static long sitaedellinen = 1; // Viimeisin numero // Viimeistä edellinen numero 314

39 Ohjelmointi funktioiden avulla Alustamalla muuttujan viimeisin arvoksi 0 ja muuttujan sitaedellinen arvoksi 1, käynnistämme sarjan laskennan kahdella ykkösellä. Jokaisella seuraavafib()-funktion kutsulla lasketaan sarjan seuraava numero laskemalla yhteen sarjan kaksi edellistä numeroa. Tulos sijoitetaan automaattiseen muuttujaan seuraava: long seuraava = viimeisin + sitaedellinen; // Lasketaan seuraava numero Voimme tehdä näin, koska viimeisin ja sitaedellinen ovat staattisia muuttujia, joiden sisältö on säilössä funktion edelliseltä kutsukerralta. Ennen kuin palautamme arvon seuraava, siirrämme muuttujan viimeisin arvon muuttujaan sitaedellinen ja uuden arvon muuttujaan viimeisin: sitaedellinen = viimeisin; viimeisin = seuraava; // Päivitetään viimeistä edellinen // Viimeisin on seuraava! Vaikka staattiset muuttujat ovatkin olemassa koko ohjelman suorituksen ajan, ne ovat käytettävissä vain siinä lohkossa, jossa ne on esitelty. Eli muuttujat viimeisin ja sitaedellinen ovat käytettävissä vain funktion seuraavafib() rungossa. Fibonacin sarja saattaa näyttää vain joukolta numeroita, mutta ne esiintyvät varsin monissa tilanteissa. Esimerkiksi kasvit kasvattavat peräkkäiset lehdet siten, että niiden välinen kulma on 2π kertaa joka toinen luku sarjassa. Eli kasvit kasvattavat lehdet kulmissa 2π kertaa 1/2 (ensimmäinen ja kolmas numero), 2π kertaa 1/3 (toinen ja neljäs numero), 2π kertaa 2/5 (kolmas ja viides numero) ja niin edelleen. Ällistyttävää vai mitä? Mahtavatkohan kasvit tuntea staattiset muuttujat! Yhteenveto Tämä luku toimi johdantona funktioiden kirjoittamiseen ja käyttämiseen. Käsittelemme funktioita lisää seuraavassa luvussa, sekä kun käsittelemme käyttäjän määrittelemiä tietotyyppejä alkaen luvusta 11. Tämän luvun tärkeän annin muodostivat: Funktiot ovat tietyn tehtävän suorittavia itsenäisiä koodinosia. Yleensä ohjelmat koostuvat suuresta joukosta pieniä funktioita, eikä pienestä joukosta suuria funktioita. Funktion määrittely koostuu funktion otsikosta, joka määrittelee parametrit ja paluuarvon tyypin sekä funktion rungosta, joka sisältää funktion suoritettavan koodin. Funktion prototyyppi mahdollistaa sen, että kääntäjä pystyy käsittelemään kaikki funktion kutsut, vaikka funktion määrittelyä ei ole vielä käsitelty. Parametrin välittäminen funktiolle arvoparametreinä välittää funktiolle kopion alkuperäisistä arvoista, joten alkuperäiset arvot eivät ole muokattavissa funktiossa. Osoittimen välittäminen funktiolle mahdollistaa osoitetun arvon muuttamisen, vaikka itse osoitin välitetäänkin arvoparametrinä. 315

40 C++ Ohjelmoijan käsikirja Osoitinparametrin esittely const-tyyppiseksi estää alkuperäisen arvon muuttamisen. Taulukon osoite voidaan välittää funktiolle osoittimessa. Jos parametrit välitetään funktiolle viittausparametreinä, vältetään arvoparametrien yhteydessä tapahtuva kopiointi. Kaikki parametrit, joita ei muuteta funktiossa, tulisi esitellä const-tyyppisiksi. Oletusarvojen määrittely funktion parametreille mahdollistaa sen, että parametreille ei tarvitse aina välittää arvoa. Jos funktio palauttaa viittauksen, funktiota voidaan käyttää sijoitusoperaattorin vasemmalla puolella. Paluuarvon määrittely const-tyyppiseksi estää tämän. Harjoituksia 8.1 Kirjoita funktio, jolle välitetään kaksi kokonaislukuparametriä ja joka pyytää käyttäjältä lukuja, kunnes syötetty luku on parametrien välissä. Käytä funktiota ohjelmassa, joka lukee käyttäjän syntymäajan ja varmistaa, että päivä, kuukausi ja vuosi ovat sallittuja. Lopuksi tulosta päivämäärä. 8.2 Kirjoita funktio, jolle välitetään merkkitaulukko ja joka kääntää sen alkiot. Mikä on paras parametrin tyyppi? Tee myös main()-funktio, jolla voit testata funktiosi toiminnan. main()- funktio pyytää käyttäjältä merkkijonon, kääntää sen ja lopuksi tulostaa käännetyn merkkijonon. 8.3 Tee ohjelma, jolle voidaan antaa komentoriviltä parametrejä ja jota voidaan kutsua kahdesta neljään parametrillä. Jos sitä kutsutaan väärällä parametrimäärällä, tulee sen tulostaa käyttäjälle, miten ohjelmaa kutsutaan. Tämän jälkeen ohjelman suoritus päättyy. 8.4 Muokkaa harjoituksessa 8.3 tekemääsi ohjelmaa siten, että sille voidaan välittää vain kaksi parametriä ja välitä toinen harjoituksen 8.2 funktiolle, joka kääntää merkkijonon. Tulosta käännetty merkkijono. 8.5 Kirjoita funktio, joka palauttaa viittauksen pienempään kahdesta kokonaisluvusta. Kirjoita myös toinen funktio, joka palauttaa viittauksen suurempaan kokonaislukuun. Käytä näitä muodostamaan Fibonacin sarja - eli numerot 1, 1, 2, 3, 5, 8, 13,..., missä jokainen numero on kahden edellisen numeron summa. (Vihje: käytä kahta kokonaislukumuuttujaa arvo1 ja arvo2 sekä silmukkaa, jossa niistä pienempään sijoitetaan niiden summa ja joka tulostetaan.) 316

41 Ohjelmointi funktioiden avulla 317

42 318 C++ Ohjelmoijan käsikirja

Osoitin ja viittaus C++:ssa

Osoitin ja viittaus C++:ssa Osoitin ja viittaus C++:ssa Osoitin yksinkertaiseen tietotyyppiin Osoitin on muuttuja, joka sisältää jonkin toisen samantyyppisen muuttujan osoitteen. Ohessa on esimerkkiohjelma, jossa määritellään kokonaislukumuuttuja

Lisätiedot

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa. Taulukot C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa. Taulukon muuttujilla (muistipaikoilla) on yhteinen nimi. Jokaiseen yksittäiseen

Lisätiedot

815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 5 Vastaukset

815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 5 Vastaukset 815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 5 Vastaukset Harjoituksen aiheena ovat aliohjelmat ja abstraktit tietotyypit sekä olio-ohjelmointi. Tehtävät tehdään C-, C++- ja Java-kielillä.

Lisätiedot

Osoittimet. Mikä on osoitin?

Osoittimet. Mikä on osoitin? Osoittimet 7 Osoittimet On aika siirtyä käsittelemään osoittimia, C++:lle elintärkeätä ominaisuutta. Osoittimet ovat tärkeitä, koska ne luovat perustan muistin dynaamiselle varaukselle ja käytölle. Ne

Lisätiedot

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta C++ - perusteet Java-osaajille luento 5/7: operaattoreiden ylikuormitus, oliotaulukko, parametrien oletusarvot, komentoriviparametrit, constant, inline, Operaattoreiden ylikuormitus Operaattoreiden kuormitus

Lisätiedot

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset 815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 3 vastaukset Harjoituksen aiheena ovat imperatiivisten kielten muuttujiin liittyvät kysymykset. Tehtävä 1. Määritä muuttujien max_num, lista,

Lisätiedot

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista Sisällys 1. Omat operaatiot Yleistä operaatioista. Mihin operaatioita tarvitaan? Oman operaation määrittely. Yleisesti, nimeäminen ja hyvä ohjelmointitapa, määreet, parametrit ja näkyvyys. HelloWorld-ohjelma

Lisätiedot

Ohjelman virheet ja poikkeusten käsittely

Ohjelman virheet ja poikkeusten käsittely Ohjelman virheet ja poikkeusten käsittely 17 Ohjelman virheet ja poikkeusten käsittely Poikkeukset ovat tapa ilmoittaa virheistä ja odottamattomista tilanteista C++-ohjelmassasi. Poikkeusten käyttö virheiden

Lisätiedot

1. Omat operaatiot 1.1

1. Omat operaatiot 1.1 1. Omat operaatiot 1.1 Sisällys Yleistä operaatioista. Mihin operaatioita tarvitaan? Oman operaation määrittely. Yleisesti, nimeäminen ja hyvä ohjelmointitapa, määreet, parametrit ja näkyvyys. HelloWorld-ohjelma

Lisätiedot

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan.

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan. Osoittimet Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan. Muistilohkon koko riippuu muuttujan tyypistä, eli kuinka suuria arvoja muuttujan

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op) ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 7. huhtikuuta 2017 Vastaa kaikkiin tehtäviin. Tee jokainen tehtävä erilliselle konseptiarkille. Kirjoittamasi luokat, funktiot ja aliohjelmat

Lisätiedot

11. oppitunti III. Viittaukset. Osa. Mikä on viittaus?

11. oppitunti III. Viittaukset. Osa. Mikä on viittaus? Osa III 11. oppitunti Viittaukset Kahdessa viime luvussa opit käyttämään osoittimia kohteiden käsittelyyn vapaalla muistialueella sekä viittaamaan noihin kohteisiin epäsuorasti. Tässä luvussa käsiteltävät

Lisätiedot

Tietueet. Tietueiden määrittely

Tietueet. Tietueiden määrittely 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

Lisätiedot

Java-kielen perusteet

Java-kielen perusteet Java-kielen perusteet Tunnus, varattu sana, kommentti Muuttuja, alkeistietotyyppi, merkkijono, literaalivakio, nimetty vakio Tiedon merkkipohjainen tulostaminen 1 Tunnus Java tunnus Java-kirjain Java-numero

Lisätiedot

Kääntäjän virheilmoituksia

Kääntäjän virheilmoituksia OHJ-1101 Ohjelmointi 1e 2008-09 1 Kääntäjän virheilmoituksia Kun progvh2 ohjelma käännetään antaa tutg++ seuraavat virheilmoitukset ja varoitukset: proffa> tutg++ progvh2.cc progvh2.cc:29:13: warning:

Lisätiedot

Virtuaalifunktiot ja polymorfismi

Virtuaalifunktiot ja polymorfismi Virtuaalifunktiot ja polymorfismi 16 Virtuaalifunktiot ja polymorfismi Polymorfismi on niin tehokas olio-ohjelmoinnin ominaisuus, että tulet varmastikin käyttämään sitä lähes kaikissa C++-ohjelmissasi.

Lisätiedot

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset 815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 2 vastaukset Harjoituksen aiheena on BNF-merkinnän käyttö ja yhteys rekursiivisesti etenevään jäsentäjään. Tehtävä 1. Mitkä ilmaukset seuraava

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 2.3.2009 T-106.1208 Ohjelmoinnin perusteet Y 2.3.2009 1 / 28 Puhelinluettelo, koodi def lue_puhelinnumerot(): print "Anna lisattavat nimet ja numerot." print

Lisätiedot

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4) 2. Lisää Java-ohjelmoinnin alkeita Muuttuja ja viittausmuuttuja Vakio ja literaalivakio Sijoituslause Syötteen lukeminen ja Scanner-luokka 1 Muuttuja ja viittausmuuttuja (1/4) Edellä mainittiin, että String-tietotyyppi

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 2.3.2011 T-106.1208 Ohjelmoinnin perusteet Y 2.3.2011 1 / 39 Kertausta: tiedoston avaaminen Kun ohjelma haluaa lukea tai kirjoittaa tekstitiedostoon, on ohjelmalle

Lisätiedot

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisältö. 2. Taulukot. Yleistä. Yleistä Sisältö 2. Taulukot Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko operaation parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 2.1 2.2 Yleistä

Lisätiedot

Ohjelmointi 1 Taulukot ja merkkijonot

Ohjelmointi 1 Taulukot ja merkkijonot Ohjelmointi 1 Taulukot ja merkkijonot Jussi Pohjolainen TAMK Tieto- ja viestintäteknologia Johdanto taulukkoon Jos ohjelmassa käytössä ainoastaan perinteisiä (yksinkertaisia) muuttujia, ohjelmien teko

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 9.2.2009 T-106.1208 Ohjelmoinnin perusteet Y 9.2.2009 1 / 35 Listat Esimerkki: halutaan kirjoittaa ohjelma, joka lukee käyttäjältä 30 lämpötilaa. Kun lämpötilat

Lisätiedot

12 Mallit (Templates)

12 Mallit (Templates) 12 Mallit (Templates) Malli on määrittely, jota käyttämällä voidaan luoda samankaltaisten aliohjelmien ja luokkien perheitä. Malli on ohje kääntäjälle luoda geneerisestä tyyppiriippumattomasta ohjelmakoodista

Lisätiedot

Java-kielen perusteet

Java-kielen perusteet Java-kielen perusteet Tunnus, varattu sana, kommentti Muuttuja, alkeistietotyyppi, merkkijono, Vakio Tiedon merkkipohjainen tulostaminen Ohjelmointi (ict1tx006) Tunnus (5.3) Javan tunnus Java-kirjain Java-numero

Lisätiedot

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit IDL - proseduurit 25. huhtikuuta 2017 Viimeksi käsiteltiin IDL:n interaktiivista käyttöä, mutta tämä on hyvin kömpelöä monimutkaisempia asioita tehtäessä. IDL:llä on mahdollista tehdä ns. proseduuri-tiedostoja,

Lisätiedot

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma. 2. Taulukot 2.1 Sisältö Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko operaation parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 2.2 Yleistä

Lisätiedot

Osoittimet ja taulukot

Osoittimet ja taulukot C! ja taulukot 1.2.2018 Tiedotteita Tämän jälkeen taas pari väliviikkoa (tenttiviikko) Seuraava luento 22.2. Laskareita ei tenttiviikolla 12.2. 16.2. 2 ja muisti Muisti Keskusyksikkö Suorittaa muistissa

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op) ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 22. huhtikuuta 2016 Vastaa kaikkiin tehtäviin. Tee jokainen tehtävä erilliselle konseptiarkille! Kirjoittamasi luokat, funktiot ja aliohjelmat

Lisätiedot

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014 18. syyskuuta 2014 IDL - proseduurit Viimeksi käsiteltiin IDL:n interaktiivista käyttöä, mutta tämä on hyvin kömpelöä monimutkaisempia asioita tehtäessä. IDL:llä on mahdollista tehdä ns. proseduuri-tiedostoja,

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op) ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 12. huhtikuuta 2019 Tee kukin tehtävä omalle konseptiarkille. Noudata ohjelmointitehtävissä kurssin koodauskäytänteitä. Yksi A4-kokoinen lunttilappu

Lisätiedot

Harjoitustyö: virtuaalikone

Harjoitustyö: virtuaalikone Harjoitustyö: virtuaalikone Toteuta alla kuvattu virtuaalikone yksinkertaiselle olio-orientoituneelle skriptauskielelle. Paketissa on testaamista varten mukana kaksi lyhyttä ohjelmaa. Ohjeita Noudata ohjelman

Lisätiedot

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen Metodit Metodien määrittely Metodin parametrit ja paluuarvo Metodien suorittaminen eli kutsuminen Metodien kuormittaminen 1 Mikä on metodi? Metodi on luokan sisällä oleva yhteenkuuluvien toimintojen kokonaisuus

Lisätiedot

Muuttujien roolit Kiintoarvo cin >> r;

Muuttujien roolit Kiintoarvo cin >> r; Muuttujien roolit Muuttujilla on ohjelmissa eräitä tyypillisiä käyttötapoja, joita kutsutaan muuttujien rooleiksi. Esimerkiksi muuttuja, jonka arvoa ei muuteta enää kertaakaan muuttujan alustamisen jälkeen,

Lisätiedot

Ohjelmointitaito (ict1td002, 12 op) Kevät 2008. 1. Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen raine.kauppinen@haaga-helia.

Ohjelmointitaito (ict1td002, 12 op) Kevät 2008. 1. Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen raine.kauppinen@haaga-helia. Ohjelmointitaito (ict1td002, 12 op) Kevät 2008 Raine Kauppinen [email protected] 1. Java-ohjelmoinnin alkeita Tietokoneohjelma Java-kieli ja Eclipse-ympäristö Java-ohjelma ja ohjelmaluokka

Lisätiedot

11. Javan toistorakenteet 11.1

11. Javan toistorakenteet 11.1 11. Javan toistorakenteet 11.1 Sisällys Laskuri- ja lippumuuttujat. Sisäkkäiset silmukat. Tyypillisiä ohjelmointivirheitä: Silmukan rajat asetettu kierroksen verran väärin. Ikuinen silmukka. Silmukoinnin

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 7.2.2011 T-106.1208 Ohjelmoinnin perusteet Y 7.2.2011 1 / 39 Kännykkäpalautetteen antajia kaivataan edelleen! Ilmoittaudu mukaan lähettämällä ilmainen tekstiviesti

Lisätiedot

Taulukot. Jukka Harju, Jukka Juslin 2006 1

Taulukot. Jukka Harju, Jukka Juslin 2006 1 Taulukot Jukka Harju, Jukka Juslin 2006 1 Taulukot Taulukot ovat olioita, jotka auttavat organisoimaan suuria määriä tietoa. Käsittelylistalla on: Taulukon tekeminen ja käyttö Rajojen tarkastus ja kapasiteetti

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op) ITKP102 Ohjelmointi 1 (6 op) Tentaattori: Antti-Jussi Lakanen 20. huhtikuuta 2018 Vastaa kaikkiin tehtäviin. Tee kukin tehtävä omalle konseptiarkille. Noudata ohjelmointitehtävissä kurssin koodauskäytänteitä.

Lisätiedot

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5) Alkuarvot ja tyyppimuunnokset (1/5) Aiemmin olemme jo antaneet muuttujille alkuarvoja, esimerkiksi: int luku = 123; Alkuarvon on oltava muuttujan tietotyypin mukainen, esimerkiksi int-muuttujilla kokonaisluku,

Lisätiedot

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU Ohjelmointi 2 Jussi Pohjolainen TAMK» Tieto- ja viestintäteknologia Tietotyypeistä C++ - kielessä useita tietotyyppejä Kirjaimet: char, wchar_t Kokonaisluvut: short, int, long Liukuluvut: float, double

Lisätiedot

811120P Diskreetit rakenteet

811120P Diskreetit rakenteet 811120P Diskreetit rakenteet 2018-2019 1. Algoritmeista 1.1 Algoritmin käsite Algoritmi keskeinen laskennassa Määrittelee prosessin, joka suorittaa annetun tehtävän Esimerkiksi Nimien järjestäminen aakkosjärjestykseen

Lisätiedot

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä Sisälls 7. Oliot ja viitteet Olio Java-kielessä. Olion luominen, elinikä ja tuhoutuminen.. Viitteiden vertailu. Varautuminen null-arvoon. Viite metodin paluuarvona.. Muuttumattomat ja muuttuvat merkkijonot.

Lisätiedot

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille: Merkkijonot C-kielessä merkkijono on taulukko, jonka alkiot ovat char -tyyppiä. Taulukon viimeiseksi merkiksi tulee merkki '\0', joka ilmaisee merkkijonon loppumisen. Merkkijono määritellään kuten muutkin

Lisätiedot

Tutoriaaliläsnäoloista

Tutoriaaliläsnäoloista Tutoriaaliläsnäoloista Tutoriaaliläsnäolokierroksella voi nyt täyttää anomuksen läsnäolon merkitsemisestä Esim. tagi ei toiminut, korvavaltimon leikkaus, yms. Hyväksyn näitä omaa harkintaa käyttäen Tarkoitus

Lisätiedot

C++11 lambdat: [](){} Matti Rintala

C++11 lambdat: [](){} Matti Rintala C++11 lambdat: [](){} Matti Rintala bool(*)(int) Tarve Tarve välittää kirjastolle/funktiolle toiminnallisuutta Callback-funktiot Virhekäsittely Käyttöliittymät Geneeristen kirjastojen räätälöinti STL:n

Lisätiedot

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat:

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat: 1. Luokan jäsenet Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat: class luokan_nimi tyypit: enum, struct, class, typedef

Lisätiedot

Sisältö. 22. Taulukot. Yleistä. Yleistä

Sisältö. 22. Taulukot. Yleistä. Yleistä Sisältö 22. Taulukot Yleistä. Esittely ja luominen. Alkioiden käsittely. Kaksiulotteinen taulukko. Taulukko metodin parametrina. Taulukko ja HelloWorld-ohjelma. Taulukko paluuarvona. 22.1 22.2 Yleistä

Lisätiedot

7. Oliot ja viitteet 7.1

7. Oliot ja viitteet 7.1 7. Oliot ja viitteet 7.1 Sisällys Olio Java-kielessä. Olion luominen, elinikä ja tuhoutuminen. Viitteiden sijoitus. Viitteiden vertailu. Varautuminen null-arvoon. Viite metodin paluuarvona. Viite metodin

Lisätiedot

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1 Ohjelmoinnin peruskurssi Y1 CSE-A1111 21.9.2015 CSE-A1111 Ohjelmoinnin peruskurssi Y1 21.9.2015 1 / 25 Mahdollisuus antaa luentopalautetta Goblinissa vasemmassa reunassa olevassa valikossa on valinta Luentopalaute.

Lisätiedot

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit. 3. Muuttujat ja operaatiot Sisällys Imperatiivinen laskenta. Muuttujat. Nimi ja arvo. Muuttujan nimeäminen. Muuttujan tyyppi.. Operandit. Arvon sijoitus muuttujaan. Aritmeettiset operaattorit. Arvojen

Lisätiedot

Luokan operaatiot. Osoittimet ja viittaukset luokan olioihin

Luokan operaatiot. Osoittimet ja viittaukset luokan olioihin Luokan operaatiot 13 Luokan operaatiot Luokkien olioiden luomiseen ja tuhoamiseen liittyy monia hienouksia, joista sinun tulee olla selvillä, jotta luokkiesi olioiden operaatiot toimivat turvallisesti

Lisätiedot

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa(); Sisällys 7. Oliot ja viitteet Olio Java-kielessä. Olion luominen, elinikä ja tuhoutuminen. Viitteiden käsittelyä: sijoitus, vertailu ja varautuminen null-arvoon. Viite metodin paluuarvona.. 7.1 7.2 Olio

Lisätiedot

3. Muuttujat ja operaatiot 3.1

3. Muuttujat ja operaatiot 3.1 3. Muuttujat ja operaatiot 3.1 Sisällys Imperatiivinen laskenta. Muuttujat. Nimi ja arvo. Muuttujan nimeäminen. Muuttujan tyyppi. Operaattorit. Operandit. Arvon sijoitus muuttujaan. Aritmeettiset operaattorit.

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 16.3.2009 T-106.1208 Ohjelmoinnin perusteet Y 16.3.2009 1 / 40 Kertausta: tiedostosta lukeminen Aluksi käsiteltävä tiedosto pitää avata: tiedostomuuttuja = open("teksti.txt","r")

Lisätiedot

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset 815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 4 vastaukset Harjoituksen aiheena ovat imperatiivisten kielten lauseisiin, lausekkeisiin ja aliohjelmiin liittyvät kysymykset. Tehtävä 1. Mitä

Lisätiedot

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1 Ohjelmoinnin peruskurssi Y1 CSE-A1111 30.9.2015 CSE-A1111 Ohjelmoinnin peruskurssi Y1 30.9.2015 1 / 27 Mahdollisuus antaa luentopalautetta Goblinissa vasemmassa reunassa olevassa valikossa on valinta Luentopalaute.

Lisätiedot

Luokat. Luokat ja olio-ohjelmointi

Luokat. Luokat ja olio-ohjelmointi Luokat 12 Luokat Tässä luvussa laajennamme edellisessä luvussa käsittelemäämme struktuurityyppiä ja siirrymme yhteen C++-ohjelmoijan kaikkein tärkeimmistä välineistä: luokkiin. Käsittelemme myöskin joitakin

Lisätiedot

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset 815338A Ohjelmointikielten periaatteet 2015-2016. Harjoitus 6 Vastaukset Harjoituksen aiheena on funktionaalinen ohjelmointi Scheme- ja Haskell-kielillä. Voit suorittaa ohjelmat osoitteessa https://ideone.com/

Lisätiedot

Metropolia ammattikorkeakoulu 05.02.2015 TI00AA43-3004: Ohjelmointi Kotitehtävät 3

Metropolia ammattikorkeakoulu 05.02.2015 TI00AA43-3004: Ohjelmointi Kotitehtävät 3 : http://users.metropolia.fi/~pasitr/2014-2015/ti00aa43-3004/kt/03/ratkaisut/ Tehtävä 1. (1 piste) Tee ohjelma K03T01.cpp, jossa ohjelmalle syötetään kokonaisluku. Jos kokonaisluku on positiivinen, niin

Lisätiedot

Osa. Listaus 2.1. HELLO.CPP esittelee C++ -ohjelman osat. 14: #include <iostream.h> 15: 16: int main() 17: {

Osa. Listaus 2.1. HELLO.CPP esittelee C++ -ohjelman osat. 14: #include <iostream.h> 15: 16: int main() 17: { Osa I 2. oppitunti C++-ohjelman osat Ennen kuin menemme yksityiskohtaisemmin sisälle C++-luokkiin, -muuttujiin jne, katsokaamme ensin, millaisista osista C++-ohjelma koostuu. Tämän tunnin aikana opit seuraavat

Lisätiedot

Apuja ohjelmointiin» Yleisiä virheitä

Apuja ohjelmointiin» Yleisiä virheitä Apuja ohjelmointiin» Yleisiä virheitä Ohjelmaa kirjoittaessasi saattaa Visual Studio ilmoittaa monenlaisista virheistä "punakynällä". Usein tämä johtuu vain siitä, että virheitä näytetään vaikket olisi

Lisätiedot

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus Jakso 4 Aliohjelmien toteutus Tyypit Parametrit Aktivointitietue (AT) AT-pino Rekursio Aliohjelmatyypit (2) Korkean tason ohjelmointikielen käsitteet: aliohjelma, proseduuri parametrit funktio parametrit,

Lisätiedot

5.6. C-kielen perusteet, osa 6/8, Taulukko 6.1.2008, pva, kuvat jma

5.6. C-kielen perusteet, osa 6/8, Taulukko 6.1.2008, pva, kuvat jma 5.6. C-kielen perusteet, osa 6/8, Taulukko 6.1.2008, pva, kuvat jma Every cloud has a silver line. - englantilainen sananlasku Tässä osiossa tärkeää: yksi- ja moniulotteinen taulukko Sisältö Yleistä Yksiulotteinen

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 16.2.2010 T-106.1208 Ohjelmoinnin perusteet Y 16.2.2010 1 / 41 Kännykkäpalautetteen antajia kaivataan edelleen! Ilmoittaudu mukaan lähettämällä ilmainen tekstiviesti

Lisätiedot

12. Javan toistorakenteet 12.1

12. Javan toistorakenteet 12.1 12. Javan toistorakenteet 12.1 Sisällys Yleistä toistorakenteista. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirheitä. Silmukan rajat asetettu

Lisätiedot

T Olio-ohjelmointi Osa 3: Luokka, muodostin ja hajotin, this-osoitin Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

T Olio-ohjelmointi Osa 3: Luokka, muodostin ja hajotin, this-osoitin Jukka Jauhiainen OAMK Tekniikan yksikkö 2010 11. Luokka Opetellaan seuraavaksi, miten omia luokkia kirjoitetaan. Aikaisemmin olikin jo esillä, että luokka on tietorakenne, joka sisältää sekä tiedot (attribuutit) että niitä käsittelevät aliohjelmat

Lisätiedot

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä Sisällys 11. Javan toistorakenteet Laskuri- ja lippumuuttujat.. Tyypillisiä ohjelmointivirheitä: Silmukan rajat asetettu kierroksen verran väärin. Ikuinen silmukka. Silmukoinnin lopettaminen break-lauseella.

Lisätiedot

Periytyminen. Luokat ja olio-ohjelmointi

Periytyminen. Luokat ja olio-ohjelmointi Periytyminen 15 Periytyminen Tässä luvussa käsittelemme aihetta, joka on olio-ohjelmoinnin kaikkein tärkein osa - periytyvyys. Periytyvyyden avulla voimme luoda uusia luokkia uudelleenkäyttämällä ja laajentamalla

Lisätiedot

Pong-peli, vaihe Aliohjelman tekeminen. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 3/7. Tämän vaiheen aikana

Pong-peli, vaihe Aliohjelman tekeminen. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 3/7. Tämän vaiheen aikana Muilla kielillä: English Suomi Pong-peli, vaihe 3 Tämä on Pong-pelin tutoriaalin osa 3/7. Tämän vaiheen aikana Jaetaan ohjelma pienempiin palasiin (aliohjelmiin) Lisätään peliin maila (jota ei voi vielä

Lisätiedot

811120P Diskreetit rakenteet

811120P Diskreetit rakenteet 811120P Diskreetit rakenteet 2016-2017 1. Algoritmeista 1.1 Algoritmin käsite Algoritmi keskeinen laskennassa Määrittelee prosessin, joka suorittaa annetun tehtävän Esimerkiksi Nimien järjestäminen aakkosjärjestykseen

Lisätiedot

Rakenteiset tietotyypit Moniulotteiset taulukot

Rakenteiset tietotyypit Moniulotteiset taulukot 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

Lisätiedot

15. Ohjelmoinnin tekniikkaa 15.1

15. Ohjelmoinnin tekniikkaa 15.1 15. Ohjelmoinnin tekniikkaa 15.1 Sisällys For-each-rakenne. Lueteltu tyyppi enum. Override-annotaatio. Geneerinen ohjelmointi. 15.2 For-each-rakenne For-rakenteen variaatio taulukoiden ja muiden kokoelmien

Lisätiedot

ITKP102 Ohjelmointi 1 (6 op), arvosteluraportti

ITKP102 Ohjelmointi 1 (6 op), arvosteluraportti ITKP2 Ohjelmointi 1 (6 op), arvosteluraportti Tentaattori: Antti-Jussi Lakanen 17. toukokuuta 219 Yleistä Tentti 1 oli pistekeskiarvon (14,6) perusteella hieman tavanomaista helpompi. Omasta tehtäväpaperista

Lisätiedot

Luento 4 Aliohjelmien toteutus

Luento 4 Aliohjelmien toteutus Luento 4 Aliohjelmien toteutus Tyypit Parametrit Aktivointitietue (AT) AT-pino Rekursio 1 Aliohjelmatyypit (2) Korkean tason ohjelmointikielen käsitteet: aliohjelma, proseduuri parametrit funktio parametrit,

Lisätiedot

Mallit standardi mallikirjasto parametroitu tyyppi

Mallit standardi mallikirjasto parametroitu tyyppi Mallit 18 Mallit Malli on tehokas mekanismi uusien luokkien generoimiseksi automaattisesti. Standardikirjaston suuri osa, standardi mallikirjasto, rakentuu kokonaan mallien määrittelymahdollisuuden ympärille,

Lisätiedot

19. Olio-ohjelmointia Javalla 19.1

19. Olio-ohjelmointia Javalla 19.1 19. Olio-ohjelmointia Javalla 19.1 Sisällys Olioiden esittely ja alustus. Metodit Yleistä metodeista. Mihin metodeja tarvitaan? Metodien määrittely. Omat metodit: nimeäminen, paikka, kutsuminen, parametrit

Lisätiedot

AS-0.1103 C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

AS-0.1103 C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin AS-0.1103 C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin Raimo Nikkilä Aalto-yliopiston sähkötekniikan korkeakoulu - Automaation tietotekniikan tutkimusryhmä 17. tammikuuta 2013

Lisätiedot

Rekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä

Rekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä Rekursiolause Laskennan teorian opintopiiri Sebastian Björkqvist 23. helmikuuta 2014 Tiivistelmä Työssä käydään läpi itsereplikoituvien ohjelmien toimintaa sekä esitetään ja todistetaan rekursiolause,

Lisätiedot

Jakso 4 Aliohjelmien toteutus

Jakso 4 Aliohjelmien toteutus Jakso 4 Aliohjelmien toteutus Tyypit Parametrit Aktivointitietue (AT) AT-pino Rekursio 1 Aliohjelmatyypit (2) Korkean tason ohjelmointikielen käsitteet: aliohjelma, proseduuri parametrit funktio parametrit,

Lisätiedot

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen Ohjelmointitaito (ict1td002, 12 op) Kevät 2009 Raine Kauppinen [email protected] 1. Java-ohjelmoinnin alkeita Tietokoneohjelma Java-kieli ja Eclipse-kehitysympäristö Java-ohjelma ja luokka

Lisätiedot

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti C! Perusteet 19.1.2017 Palautteesta (1. kierros toistaiseksi) (Erittäin) helppoa Miksi vain puolet pisteistä? Vaikeinta oli ohjelmointiympäristön asennus ja käyttö Ei selvää että main funktion pitikin

Lisätiedot

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu Taulukot Taulukon määrittely ja käyttö Taulukko metodin parametrina Taulukon sisällön kopiointi toiseen taulukkoon Taulukon lajittelu esimerkki 2-ulottoisesta taulukosta 1 Mikä on taulukko? Taulukko on

Lisätiedot

Loppukurssin järjestelyt

Loppukurssin järjestelyt C! Loppukurssin järjestelyt 29.3.2018 Ohjelmassa Yhteenvetoa palautteesta Ohjelmontitehtävän järjestelyt Tietokonetentin järjestelyt Kysyttävää / kerrattavaa 10-kierroksen asioista? Aikatauluista 10. kierroksen

Lisätiedot

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin. 2. Ohjausrakenteet Ohjausrakenteiden avulla ohjataan ohjelman suoritusta. peräkkäisyys valinta toisto Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet

Lisätiedot

Harjoitus 4 (viikko 47)

Harjoitus 4 (viikko 47) Kaikki tämän harjoituksen tehtävät liittyvät joko suoraan tai epäsuorasti kurssin toiseen harjoitustyöhön. Saa hyvän alun harjoitustyön tekoon, kun ratkaiset mahdollisimman monta tehtävää. Mikäli tehtävissä

Lisätiedot

Sisällys. 15. Lohkot. Lohkot. Lohkot

Sisällys. 15. Lohkot. Lohkot. Lohkot Sisällys 15. Lohkot Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 15.1 15.2 Lohkot Aaltosulkeet

Lisätiedot

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti C! Perusteet 19.1.2017 Palautteesta (1. kierros toistaiseksi) Toistaiseksi helppoa Miksi vain puolet pisteistä? Vaikeinta oli ohjelmointiympäristön asennus ja käyttö Vaikeaa eroavuudet Pythonin ja C:n

Lisätiedot

12. Javan toistorakenteet 12.1

12. Javan toistorakenteet 12.1 12. Javan toistorakenteet 12.1 Sisällys Yleistä toistorakenteista. Laskurimuuttujat. While-, do-while- ja for-lauseet. Laskuri- ja lippumuuttujat. Tyypillisiä ohjelmointivirheitä. Silmukan rajat asetettu

Lisätiedot

Jakso 4 Aliohjelmien toteutus

Jakso 4 Aliohjelmien toteutus Jakso 4 Aliohjelmien toteutus Tyypit Parametrit Aktivointitietue (AT) AT-pino Rekursio 1 Aliohjelmatyypit (2) Korkean tason ohjelmointikielen käsitteet: aliohjelma, proseduuri parametrit funktio parametrit,

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 1.4.2009 T-106.1208 Ohjelmoinnin perusteet Y 1.4.2009 1 / 56 Tentti Ensimmäinen tenttimahdollisuus on pe 8.5. klo 13:00 17:00 päärakennuksessa. Tämän jälkeen

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 3.3.2010 T-106.1208 Ohjelmoinnin perusteet Y 3.3.2010 1 / 44 Kertausta: tiedoston avaaminen Kun ohjelma haluaa lukea tai kirjoittaa tekstitiedostoon, on ohjelmalle

Lisätiedot

Zeon PDF Driver Trial

Zeon PDF Driver Trial Matlab-harjoitus 2: Kuvaajien piirto, skriptit ja funktiot. Matlabohjelmoinnin perusteita Numeerinen integrointi trapezoidaalimenetelmällä voidaan tehdä komennolla trapz. Esimerkki: Vaimenevan eksponentiaalin

Lisätiedot

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3 15. Lohkot 15.1 Sisällys Tutustutaan lohkoihin. Muuttujien ja vakioiden näkyvyys sekä elinikä erityisesti operaation lohkossa. Nimikonfliktit. Muuttujat operaation alussa vai myöhemmin? 15.2 Lohkot Aaltosulkeet

Lisätiedot

Lyhyt kertaus osoittimista

Lyhyt kertaus osoittimista , syksy 2007 Kertausta Luento 10 12.10.2007 Syksy 2007 1 Lyhyt kertaus osoittimista char *p; /* char, int, jne ilmoittavat, minkä tyyppisiä */ Keskusmuisti int *q; /* olioita sisältäviin muistilohkoihin

Lisätiedot

Python-ohjelmointi Harjoitus 5

Python-ohjelmointi Harjoitus 5 Python-ohjelmointi Harjoitus 5 TAVOITTEET Kerrataan silmukkarakenteen käyttäminen. Kerrataan jos-ehtorakenteen käyttäminen. Opitaan if else- ja if elif else-ehtorakenteet. Matematiikan sisällöt Tehtävät

Lisätiedot

Loppukurssin järjestelyt C:n edistyneet piirteet

Loppukurssin järjestelyt C:n edistyneet piirteet C! Loppukurssin järjestelyt C:n edistyneet piirteet 30.3.2017 Ohjelmassa Ohjelmontitehtävän järjestelyt Tietokonetentin järjestelyt Esikääntäjä Parametrilistat Funktio-osoittimet Kunniamainintoja Kuura

Lisätiedot

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python Ohjelmoinnin perusteet Y Python T-106.1208 28.2.2011 T-106.1208 Ohjelmoinnin perusteet Y 28.2.2011 1 / 46 Ohjelmointiprojektin vaiheet 1. Määrittely 2. Ohjelman suunnittelu (ohjelman rakenne ja ohjelman

Lisätiedot