L 5,R 65,J,T 5 20 @Ots(Parametrin v„lityksest„ ja osoittimista, kertaus) Aliohjelmia k„ytettiin mm. seuraavista syist„: BEGIN HYPHENS kyseess„ on luonteeltaan oma looginen kokonaisuutensa joku toinen (tai itse aikaisemmin) on kirjoittanut halutunteht„v„n suorittavan aliohjelman END HYPHENS Mik„li aliohjelma kirjoitetaan itse, tulee ongelmaksi tietysti se, mit„ parametreja aliohjelmaan esitell„„n. Mik„li aliohjelman on kirjoittanut joku toinen, on ongelmanase miten aliohjelmaa kutsutaan. SECTION Aliohjelman kutsuminen Valmiin aliohjelman kutsuminen on helppoa: etsit„„n aliohjelman esittely ja kirjoitetaan kutsu, jossa on vastaavantyyppiset parametrit vastaavissa paikoissa. SUBSECTION Yksinkertainen funktio PARAGRAPH sin Esimerkiksi funktion sin esittely saattaa olla muotoa: BEGIN GESIMERKKI double sin(double x); For real sin, x is in radians. sin of a real argument returns a value in the range -1 to 1. END GESIMERKKI Funktion tyyppi on double ja sille vied„„n double tyyppinenparametri. Funktio ei muuta mit„„n parametrilistassa esitelty„ parametriaan (mist„ tiet„„?). Siis funktiota ei olemit„„n mielt„ kutsua muuten kuin sijoittamalla sen palauttama arvo johonkin muuttujaan tai k„ytt„m„ll„ funktiota osanajotakin lauseketta. x:„„ vastaava parametri voi olla mik„tahansa double tyyppisen arvon palauttava lauseke (tietystimielell„„n sellainen joka tarkoittaa kulmaa radiaaneissa): BEGIN GESIMERKKI double kulman_sini,a,b,x,y; ... kulman_sini = sin(x); ... y = sin(x/2) + cos(a/3); ... END GESIMERKKI Funktiota voitaisiin tietysti kutsua my”s muodossa: BEGIN GESIMERKKI double x = 3.1; sin(x); END GESIMERKKI mutta kutsussa olisi yht„ v„h„n j„rke„ kuin kutsussa BEGIN GESIMERKKI double x=3.1; x + 3.0; END GESIMERKKI tai jopa BEGIN GESIMERKKI 3.0; END GESIMERKKI Mihin lausekkeiden arvot menisiv„t? Eiv„t minnek„„n! Siislausekkeissa ei ole mielt„! PARAGRAPH strcmp Esimerkki moniparametrisesta tavallisesta funktiosta olisivaikkapa strcmp, joka on esitelty seuraavasti: BEGIN GESIMERKKI Compares one string to another int strcmp(const char *s1, const char *s2); Prototype in string.h Returns a value that is < 0 if s1 is less than s2 == 0 if s1 is the same as s2 > 0 if s1 is greater than s2 Performs a signed comparison. END GESIMERKKI Funktio siis vertaa kahta merkkijonoa toisiinsa. Funktionesittelyn mukaan funktiolle vied„„n parametrina kaksi osoitinta. const sanalla korostetaan sit„, ettei funktio muutaniiden muistipaikkojen sis„lt”„, joihin osoittimet osoittavat. Siis t„m„k„„n funktio ei muuta parametrejaan, jotensen ainoa j„rkev„ k„ytt” on sijoittaa tulos johonkin muuttujaan tai k„ytt„„ funktiota muuten osana lauseketta: BEGINGESIMERKKI char j1[20]="Kissa", j2[20]="Koira"; int samat; ... samat = strcmp(j1,j2); if ( samat == 0 ) printf("Jonot ovat samat!\n"); ...tai.. if ( strcmp(j1,j2) < 0 ) printf("Jono 1 on ensin!\n"); END GESIMERKKI PARAGRAPH Varo toistoa Vaikka yksinkertaista funktiota voidaankin k„ytt„„ mukavastilausekkeen osana, tulee seuraavankaltaista rakennetta v„ltt„„: BEGIN GESIMERKKI int i,a_lkm=0; char st[50]="Saippuakauppias"; ... /* VŽŽRIN */ for (i=0; i<strlen(st); i++) if ( jono[i]=='a' ) a_lkm++; ... END GESIMERKKI Miksik”? Koska strlen-funktio suoritetaan jokaisella silmukan kierroksella ja strlen suorittaminenhan vaati koko jononl„pik„ymisen! Rakenne tulee korvata seuraavasti: BEGIN GESIMERKKI int i,a_lkm=0,st_pit; char st[50]="Saippuakauppias"; ... st_pit=strlen(st); for (i=0; i= 0; i--) if ( jono[i]=='a' ) a_lkm++; END GESIMERKKI SUBSECTION Funktio, joka muuttaa my”s parametrejaan Aina kun palautettavia arvoja on enemm„n kuin yksi, on vainkaksi mahdollisuutta: tulos palautetaan funktion nimess„tietueessa tai parametreissa osoitteen avulla. EsimerkiksiC-kielen valmiissa kirjastoissa ei juurikaan k„ytet„ tietueita funktioiden arvoina. PARAGRAPH strcpy BEGIN GESIMERKKI Copies string src to dest char *strcpy(char *dest, const char *src); Prototype in string.h Returns dest. END GESIMERKKI T„m„ funktio laittaa tuloksensa (eli src-jonon kopion)dest-merkkijonoon. Toisaalta tulos-jonon osoite palautetaanmy”s funktion nimess„. Koska C-kieless„ lausekkeen arvoa eiole pakko sijoittaa mihink„„n (muuten esim. i++ ei olisimielek„s), voidaan t„llaista funktiota kutsua joko itsen„isen„ proseduurimaisesti tai sitten lausekkeen osana: BEGIN GESIMERKKI char jono[30], *st; ... strcpy(jono,"Kissa"); ... st = strcpy(jono,"Susikoira") + 4; /* st osoittaa jonoon"koira" */ END GESIMERKKI On tietysti olemassa my”s void-funktioita, jotka eiv„t palauta mit„„n arvoa nimess„„n. SUBSECTION Vaihteleva m„„r„ parametreja C-kieless„ on my”s mahdollista tehd„ funktiota, joiden parametrilistan pituus (ja jopa parametrien tyyppi) on vaihteleva. PARAGRAPH printf ja scanf BEGIN GESIMERKKI Formatted output to stdout int printf(const char *format [, argument, ...]); Prototype in stdio.h printf formats a variable number of arguments according tothe format, sending the output to stdout. Returns the number of bytesoutput. In the event of error, it returns EOF. END GESIMERKKI Kaikki t„llaiset muuttuvaparametriset ohjelmat tarvitsevatainakin yhden parametrin (1., miksi) joka ilmoittaa muidenparametrien m„„r„n ja luonteen (==tyypin). Funktiossa printf t„m„ parametri on 1. merkkijono, format-jono, jonka % -merkkien m„„r„ suurinpiirtein ilmoittaa muiden parametrien m„„r„n ja % -merkkien j„lkeinen kirjain ilmoittaa niiden tyypin. K„„nt„j„ ei tietenk„„n voi tiet„„merkkijonon sis„ll”st„, joten parametrien tyypin tarkastusj„„ tekem„tt„ ja v„„rist„ kutsuista saattaa seurata jopa koneen kaatuminen! Tyypillisin v„„r„ esimerkki on kutsu: BEGIN GESIMERKKI int lkm; printf("Anna kissojen lkm>"); scanf("%d",lkm); /* VŽŽRIN VŽŽRIN VŽŽRIN */ END GESIMERKKI SECTION Aliohjelman esitteleminen K„yt„nn”ss„ usein tulee tilanne, jossa tarvittaisiin aliohjelmaa, jota ei valmiina mist„„n l”ydy. T„ll”in aliohjelmatietenkin kirjoitetaan itse. Ongelmaksi saattaa muodostuaaliohjelman parametrilistan keksiminen. SUBSECTION Sy”tt”- ja tulosparametrit Er„s tilanne voisi olla vaikka seuraava. Jostakin on saatu(esim. p„„tteelt„ luettu) merkkijono, jossa on p„iv„m„„r„. Toisaalta ohjelmassa on esitelty p„iv„m„„r„tyyppi BEGIN GESIMERKKI typedef struct { int pv,kk,vv; } Pvm_tyyppi; END GESIMERKKI Merkkijonossa oleva p„iv„m„„r„ pit„isi saada muutetuksiPvm_tyyppiseksi. Millainen aliohjelma kirjoitetaan? Helpointa on ehk„ mietti„ kutsun kautta: Aliohjelmalle t„ytyy v„litt„„ parametrina muutettava merkkijono. Aliohjelman tulee palauttaa muutettu p„iv„m„„r„. Siis kutsu voisi olla muotoa: BEGIN GESIMERKKI char jono[40]="13.3.1992"; Pvm_tyyppi pvm; ... muuta_jono_pvmksi(jono,pvm); /* ??? */ END GESIMERKKI T„m„ ei tietenk„„n voi toimia, koska aliohjelma ei pystyisimuuttamaan pvm-muuttujaa! Siis kutsu t„ytyy olla muotoa: BEGIN GESIMERKKI muuta_jono_pvmksi(jono,&pvm); END GESIMERKKI T„st„ seuraa ett„ aliohjelma t„ytyy vastaavasti esitell„muodossa: BEGIN GESIMERKKI void muuta_jono_pvmksi(char *jono, Pvm_tyyppi *pvm) { ... } END GESIMERKKI Aliohjelman tyyppi voisi olla my”skin int, jolloin aliohjelman nimess„ palautetaan tieto siit„, onnistuiko muutos vaiei (miksi ei onnistuisi?). Joskus t„llainen aliohjelma voidaan esitell„ jopa char*-tyyppiseksi, jotta nimess„ voidaan palauttaa osoitin virhett„ kuvaavaan viestiin: BEGIN GESIMERKKI char *viesti,... ... viesti = muuta_jono_pvmksi(jono,&pvm); if ( viesti ) { printf("%s\n",viesti); return 1; } END GESIMERKKI Koko ohjelma testiohjelmineen voisi olla esimerkiksi seuraava: L 5,R 83,J,T 5 20 BEGIN GESIMERKKI /* FNAME pvmjono.c */ #include #include typedef struct { int pv,kk,vv; } Pvm_tyyppi; static char *VAARA_MUOTO="V„„r„ muoto!"; char *muuta_jono_pvmksi(char *jono, Pvm_tyyppi *pvm) { if ( sscanf(jono,"%d.%d.%d",&pvm->pv,&pvm->kk,&pvm->vv) != 3 ) return VAARA_MUOTO; return NULL; } int tulosta_pvm_jono(char *jono) { char *viesti; Pvm_tyyppi pvm; viesti = muuta_jono_pvmksi(jono,&pvm); if ( viesti ) { printf("%s\n",viesti); return 1; } printf("%02d.%02d.%d",pvm.pv,pvm.kk,pvm.vv); return 0; } int main(void) { tulosta_pvm_jono("18.5.1992"); printf("\n"); tulosta_pvm_jono("12.3"); printf("\n"); return 0; } END GESIMERKKI L 5,R 65,J,T 5 20 SUBSECTION Sy”tt”- ja tulosparametri samassa Joissakin tilanteissa ei v„ltt„m„tt„ tarvita erillisi„ parametreja sy”t”lle ja tulokselle. Esimerkiksi p„iv„m„„r„n tapauksessa voisi tulla vastaan tarve lis„t„ p„iv„m„„r„„ yhdell„ (tai useammalla p„iv„ll„). T„ll”in kutsu olisi muotoa: BEGIN GESIMERKKI Pvm_tyyppi pvm; ... seuraava_pvm(&pvm); END GESIMERKKI Aliohjelma esitelt„isiin vastaavasti: BEGIN GESIMERKKI void seuraava_pvm(Pvm_tyyppi *pvm) { ... } END GESIMERKKI Toisaalta funktion nimess„ voitaisiin palauttaa vaikkapatieto siit„, muuttuiko kuukausi BEGIN GESIMERKKI int seuraava_pvm(Pvm_tyyppi *pvm) { ... } ... int kuukausi_muuttui; kuukausi_muuttui = seuraava_pvm(&pvm); if (kuukausi_muuttui)... END GESIMERKKI Aikojen kuluessa aliohjelman tarve saattaisi muuttua muotoon BEGIN GESIMERKKI lisaa_pvm(&pvm,3); END GESIMERKKI Esittely muuttuisi tietysti: BEGIN GESIMERKKI int lisaa_pvm(Pvm_tyyppi *pvm, int lkm) { ... } END GESIMERKKI BEGIN TEHTAVA TehtOts Ajan lis„ys Kirjoita tyyppim„„rittely kellonajalle (tunnit,min) ja kirjoita aliohjelma, jolla kellonaikaa voidaan lis„t„ halutullaminuuttim„„r„ll„. Muista, ett„ minuuttim„„r„ voi olla my”snegatiivinen! END TEHTAVA SUBSECTION Tietueet osoitteiden avulla IXREF tietue osoitteen avulla Usein tietueet v„litet„„n osoitteiden avulla, vaikkei niit„olisikaan tarkoitus muuttaa aliohjelmassa. T„h„n on syyn„se, ett„ parametrin v„lityksess„h„n kopioidaan kutsumuuttujien arvot aliohjelman lokaaleihin muuttujiin. Tietueidentapauksessa kopiointi saattaa olla isokin, ja t„m„n vuoksiosoitteen v„lityksess„ menee pelkk„ osoite, ei koko tietueensis„lt”. BEGIN GESIMERKKI int vertaa_pvm(Pvm_tyyppi pv1, Pvm_tyyppi pv2) { ... } ... int ero; Pvm_tyyppi pvm1,pvm2; ero = vertaa_pvm(pvm1,pvm2); /* Oikein, mutta iso kopiointi */ if ( ero < 0 ) printf("Pvm1 on ensin!\n"); END GESIMERKKI Parametrin v„litys osoitteiden avulla kopioinnin v„ltt„miseksi: BEGIN GESIMERKKI int vertaa_pvm(Pvm_tyyppi *pv1, Pvm_tyyppi *pv2) { ... } ... int ero; Pvm_tyyppi pvm1,pvm2; ero = vertaa_pvm(&pvm1,&pvm2); /* Oikein */ if ( ero < 0 ) printf("Pvm1 on ensin!\n"); END GESIMERKKI T„ll”in tietysti aliohjelman kirjoittajan on oltava huolellinen, ettei muuta pv1 ja pv2 osoitteiden osoittamia p„iv„m„„ri„! Usein t„t„ kommentoidaan const esittelyll„: IXMASTER const BEGIN GESIMERKKI int vertaa_pvm(const Pvm_tyyppi *pv1, const Pvm_tyyppi *pv2) { ... } ... END GESIMERKKI BEGIN TEHTAVA TehtOts vertaa_pvm Kirjoita aliohjelma vertaa_pvm, joka palauttaa -1, mik„lipv1 on ennen pv2:sta, 0 jos p„iv„m„„r„t ovat samoja ja 1muuten. END TEHTAVA SUBSECTION Useita parametreja Olkoon vaikkapa seuraava tilanne: Laskettava henkil”n bruttotulosta verottajalle ja henkil”lle itselleen j„„v„t osuudet. Miten t„t„ aliohjelmaa kutsuttaisiin? Aliohjelma tarvitsee tietysti parametrikseen henkil”n bruttotulon, veroprosentin sek„ tiedon siit„ mihin tulokset laitetaan. Tulosmuuttujia pit„„ voida muuttaa, joten niiden kohdalle kutsuuntulee tietysti osoittimet. Siis kutsu voisi olla esimerkiksi: BEGIN GESIMERKKI double tulo,pid_pros,verottaja,netto; ... laske_verot(tulo,pid_pros,&verottaja,&netto); END GESIMERKKI Aliohjelman esittely kutsun perusteella t„ytyisi olla siis BEGIN GESIMERKKI void laske_verot(double brutto, double pros, double *pros_osuus, double *netto) { ... } END GESIMERKKI SUBSECTION Parametrien lis„„minen Usein itse tehty„ aliohjelmaa voidaan j„lkeenp„in tehd„yleisk„ytt”isemm„ksi tai paremmaksi lis„„m„ll„ siihen parametreja. Olkoon alkuper„inen ongelma sellainen, ett„ on esitettymerkkijono muodossa "a-f". T„m„ pit„isi saada muutettuamuotoon "abcdef". Tarkoitusta varten kirjoitetaan aliohjelma tayta_valit. Mitk„ ovat aliohjelman parametrit? Tietysti alkuper„inenjono. Mihin tulos? T„h„n tarvitaan ehk„ toinen parametri. Nimess„„n aliohjelma voi viel„ palauttaa osoitteen tulosjonoon, kuten merkkijonofunktioilla yleens„kin on tapana: BEGIN GESIMERKKI char merkit[4]="a-f", tulos[20], sallitut[20]; ... tayta_valit(tulos,merkit); ...tai... strcat(sallitut,tayta_valit(tulos,merkit)); END GESIMERKKI Funktion esittely on siis muotoa: BEGIN GESIMERKKI char *tayta_valit(char *tulos, char *jono); ... END GESIMERKKI My”hemmin huomataan, ett„ tulos-jonon maksimipituus voidaanylitt„„! Siksi funktion kutsuun lis„t„„nkin yksi parametri: BEGIN GESIMERKKI tayta_valit(tulos,20,merkit); END GESIMERKKI ja vastaavasti esittelyyn: BEGIN GESIMERKKI char *tayta_valit(char *tulos, int max_pit, char *jono); ...tai: char *tayta_valit(char *tulos, int max_pit, const char*jono); END GESIMERKKI Toinen vastaava esimerkki oli seuraava_pvm aliohjelman muuttaminen aliohjelmaksi lisaa_pvm. SUBSECTION Ei printf eik„ scanf On aina muistettava, ett„ aliohjelman teht„v„ on ty”skennell„ sy”tt”- ja tulosparametriensa avulla. Siis jollei ongelmaan ole erikseen m„„ritelty p„„tesy”tt”„ tai tulostusta, eialiohjelmassa saa olla printf ja scanf eik„ muita vastaavialauseita! Olisihan todella yll„tys, mik„li kutsussa BEGIN GESIMERKKI y = sin(x); END GESIMERKKI rupeaisi tulostumaan n„yt”lle jotakin tai jopa odotettaisiinsy”tt”„ p„„tteelt„! SECTION Osoitteista ja osoittimista Mik„li ohjelmassa esitell„„n osoitintyyppisi„ muuttujia, pit„„ aina muistaa perustella mihin osoittimet osoittavat. Esimerkiksi seuraava ohjelma olisi todella v„„rin: BEGIN GESIMERKKI char *jono; /* VŽŽRŽ ESIMERKKI!!!! */ strcpy(jono,"Kissa"); END GESIMERKKI Mihin muuttuja jono osoittaisi? Satunnaiseen paikkaan? Jat„nne satunnaiseen paikkaan kopioidaan teksti "Kissa"! Vastaavasti seuraava ohjelma olisi jo oikeampi: BEGIN GESIMERKKI char *jono, st[30]; strcpy(st,"Kissa"); jono = st+5; strcpy(jono,"tarha"); /* -> st = "Kissatarha" */ END GESIMERKKI Vikana olisi tietysti viel„ se, ettei merkkijonojen maksimipituuksien ylitt„mist„ valvota! SUBSECTION Muista aina sijoitus tai malloc Aina kun ohjelmassa esiintyy osoitintyyppinen muuttuja, pit„„ muistaa, ett„ ennen sen k„ytt”„ se on alustettu joko sijoituksella toiseen osoitteeseen tai osoittimelle on annettuarvo malloc-funktiolla (johon palataan my”hemmin!). Siisaina: BEGIN GESIMERKKI int *osoitin; /* Aina joko */ /* 1 */ osoitin = &muuttuja; /* tai */ /* 2 */ osoitin = malloc(...); END GESIMERKKI Noudattaako aliohjelman parametrit t„t„ s„„nt”„? BEGIN GESIMERKKI void laske_verot(double brutto, double pros, double *pros_osuus, double *netto) END GESIMERKKI Kyll„, koska kutsu BEGIN GESIMERKKI laske_verot(10000.0,40.2,&verottajalle,&itselle) END GESIMERKKI tarkoittaa sijoitusta aliohjelman parametreihin: BEGIN GESIMERKKI brutto = 10000.0 pros = 40.2 pros_osuus = &verottajalle netto = &itselle END GESIMERKKI SUBSECTION Milloin osoitin? Yleens„ ohjelman muuttajat esitell„„n tavallisiksi muuttujiksi (paitsi taulukot, jotka ovat aina osoitteita). Osoitinmuuttujia esitell„„n p„„s„„nt”isesti BEGIN NUMBERED parametrilistoissa kun k„ytet„„n dynaamisia muuttujia kun edet„„n taulukkoa osoittimen avulla (indeksien sijasta) virheviestien (tai sen tyylisten) vakio-osoitteiden saamisessa END NUMBERED SUBSECTION Varoitus Aloittelevalla ohjelmoijalla seuraavann„k”inen aliohjelmanalku on p„„s„„nt”isesti v„„rin: BEGIN GESIMERKKI int oma_ali(...) { int *a; ... END GESIMERKKI Siis tarkista aina huolellisesti kaikki lohkon alkumerkinj„lkeen m„„ritellyt osoitintyyppiset muuttujat! SUBSECTION Kertausteht„vi„ Kirjoita seuraavat funktiot tai aliohjelmat sek„ kirjoitakullekin oma testip„„ohjelma. BEGIN TEHTAVA TehtOts suurin_kirjeen_paino Kirjoita funktio intsuurin_kirjeen_paino(doublerahaa) jokapalauttaa mik„ on suurin kirjeen paino, joka voidaan l„hett„„ raham„„r„ll„ rahaa. BEGIN GESIMERKKI Olkoon postimaksut: korkeintaan 50 g 2.10 mk korkeintaan 100 g 3.40 mk korkeintaan 200 g 5.20 mk korkeintaan 500 g 9.30 mk END GESIMERKKI END TEHTAVA BEGIN TEHTAVA TehtOts kysy_ika Kirjoita aliohjelma kysy_ika, joka kysyy henkil”n i„n ja palauttaa sen parametrinaan. Ik„„ kysyt„„n kunnes i„ksi onvastattu luku v„lilt„ 18-65 vuotta. Aliohjelman nimess„ palautetaan tieto siit„, monestiko ik„„ piti kysy„ ennenkuinhyv„ksytty tulos saatiin. END TEHTAVA BEGIN TEHTAVA TehtOts palindromi Kirjoita funktio palindromi, joka palauttaa nimess„„n tiedonsiit„ (1=kyll„, 0=ei) onko parametrina v„litetty sanapalindromi vai ei. END TEHTAVA BEGIN TEHTAVA TehtOts laske_merkin_maarat Kirjoita funktio laske_merkin_maarat, joka palauttaa nimess„„n tiedon siit„, monestiko parametrina v„litetty merkkiesiintyy parametrina v„litetyss„ jonossa (HUOM! ei t„ysinsama kuin laske_merkit, mik„ ero?). BEGIN GESIMERKKI merkki = 's' jono = "Kissa" -> palauttaa 2 END GESIMERKKI END TEHTAVA BEGIN TEHTAVA TehtOts vero Kirjoita aliohjelma vero, joka toimii seuraavasti: BEGIN GESIMERKKI esimerkki aliohjelma vero (nimess„ ei mit„„n) parametrit: brutto 20000.0 (sy”tt”, input) vero 20.0 (sy”tt”, input) -> netto 16000.0 (palautus, output) -> verottajalle 4000.0 (palautus, output) END GESIMERKKI END TEHTAVA BEGIN TEHTAVA TehtOts maara_alennus Kirjoita funktio maara_alennus, joka laskee tavaroiden hinnan ja my”nt„„ siihen m„„r„alennuksen, mik„li tavaraa ostetaan enemm„n kuin tietty„ rajaa (toimii esim. seuraavasti): BEGIN GESIMERKKI esimerkki 1 esimerkki 2 funktio maara_alennus parametrit: kpl_hinta 10.0 10.0 ostos_maara 3.0 10.0 alennus_raja 5.0 5.0 alennus % 20.0 20.0 nimess„ -> 30.0 80.0 END GESIMERKKI END TEHTAVA BEGIN TEHTAVA TehtOts mjono_ajaksi Kirjoita muuta_jono_pvmksi matkien aliohjelma mjono_ajaksi. Muuta mjono_ajaksi aliohjelmaa siten, ett„ se toimiiseuraavasti: BEGIN GESIMERKKI jono tun min sek sad "14" -> 14 0 0 0 "14:30" -> 14 30 0 0 "14:30:25" -> 14 30 25 0 "14:30:25.20" -> 14 30 25 20 END GESIMERKKI END TEHTAVA BEGIN TEHTAVA TehtOts aika_mjonoksi Kirjoita edelliselle k„„nteinen aliohjelma aika_mjonoksi. Muuta aika_mjonoksi -aliohjelmaa siten, ett„ v„litet„„n aika-tyypin lis„ksi muotoa kuvaava parametri seuraavasti: BEGIN GESIMERKKI 0 -> muutetaan muotoon "14:30" 1 -> muutetaan muotoon "14:30:25" 2 -> muutetaan muotoon "14:30:25.20" END GESIMERKKI Kirjoita testip„„ohjelma, jossa kysyt„„n kellonaikaa merkkijonoon kunnes ajaksi vastataan q (kannattaa kutsua lue_jono_oletus -aliohjelmaa) ja muutetaan t„m„ sitten mjono_ajaksi -aliohjelmalla ja tulostetaan aikatyyppi k„ytt„en apunaaika_mjonoksi -aliohjelmaa. Muuta aika_mjonoksi -aliohjelmaa siten, ett„ muoto-parametrin arvoilla 0 ja 1 suoritetaan katkaisun sijasta py”ristys. BEGIN GESIMERKKI 14 30 35 45 -> "14:31" 14 30 29 30 -> "14:30" 14 30 30 00 -> "14:31" 14 30 35 45 -> "14:30:36" END GESIMERKKI END TEHTAVA