previous next Title Contents Index

14. Parametrin välityksestä ja osoittimista, kertaus


Mitä tässä luvussa käsitellään?

* kerrataan aliohjelmien kutsumista

* kerrataan aliohjelmien suunnittelua

* kerrataan osoittimia parametreina

* kerrataan referenssejä parametreina

14.1 Miksi aliohjelmia käytetään

Aliohjelmia käytettiin mm. seuraavista syistä:

* kyseessä on luonteeltaan oma looginen kokonaisuutensa

* joku toinen (tai itse aikaisemmin) on kirjoittanut halutun tehtävän suorittavan aliohjelman

Mikäli aliohjelma kirjoitetaan itse, tulee ongelmaksi tietysti se, mitä parametreja aliohjelmaan esitellään.

Mikäli aliohjelman on kirjoittanut joku toinen, on ongelmana se miten aliohjelmaa kutsutaan.

14.2 Yksinkertaisen aliohjelman kutsuminen

Valmiin aliohjelman kutsuminen on helppoa: etsitään aliohjelman esittely ja kirjoitetaan kutsu, jossa on vastaavan tyyppiset parametrit vastaavissa paikoissa.

14.2.1 sin

Esimerkiksi funktion sin esittely saattaa olla muotoa:
	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. 
Funktion tyyppi on double ja sille viedään double tyyppinen parametri. Funktio ei muuta mitään parametrilistassa esiteltyä parametriaan (mistä tietää?). Siis funktiota ei ole mitään mieltä kutsua muuten kuin sijoittamalla sen palauttama arvo johonkin muuttujaan tai käyttämällä funktiota osana jotakin lauseketta. x:ää vastaava parametri voi olla mikä tahansa double tyyppisen arvon palauttava lauseke (tietysti mielellään sellainen joka tarkoittaa kulmaa radiaaneissa):
	double kulman_sini,a,b,x,y;
	...
	kulman_sini = sin(x);
	...
	y = sin(x/2) + cos(a/3);
	... 
Funktiota voitaisiin tietysti kutsua myös muodossa:
	double x = 3.1;
	sin(x); 	
mutta kutsussa olisi yhtä vähän järkeä kuin kutsussa
	double x=3.1;
	x + 3.0;  	
tai jopa
	3.0; 	
Mihin lausekkeiden arvot menisivät? Eivät minnekään! Siis lausekkeissa ei ole mieltä!

14.2.2 strcmp

Esimerkki moniparametrisesta tavallisesta funktiosta olisi vaikkapa strcmp, joka on esitelty seuraavasti:
	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. 
Funktio siis vertaa kahta merkkijonoa toisiinsa. Funktion esittelyn mukaan funktiolle viedään parametrina kaksi osoitinta. const sanalla korostetaan sitä, ettei funktio muuta niiden muistipaikkojen sisältöä, joihin osoittimet osoittavat. Siis tämäkään funktio ei muuta parametrejaan, joten sen ainoa järkevä käyttö on sijoittaa tulos johonkin muuttujaan tai käyttää funktiota muuten osana lauseketta:
	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");

14.2.3 Varo toistoa

Vaikka yksinkertaista funktiota voidaankin käyttää mukavasti lausekkeen osana, tulee seuraavankaltaista rakennetta välttää:
	int i,a_lkm=0;
	char st[50]="Saippuakauppias";
	for (i=0; i<strlen (st); i++) 	
	  if ( jono[i]=='a' ) a_lkm++;
	... 
Miksikö? Koska strlen- funktio suoritetaan jokaisella silmukan kierroksella ja strlen suorittaminenhan vaati koko jonon läpikäymisen! Rakenne tulee korvata seuraavasti:
	int i,a_lkm=0,st_pit;
	char st[50]="Saippuakauppias";
	...
	st_pit=strlen(st);
	for (i=0; i<st_pit; i++)
	  if ( jono[i]=='a' ) a_lkm++;
	... 
Edellä kävisi myös seuraava, miksi?
	for (i=strlen(st)- 1; i >= 0; i- - )
	  if ( jono[i]=='a' ) a_lkm++;

14.3 Funktio, joka muuttaa myös parametrejaan

Aina kun palautettavia arvoja on enemmän kuin yksi, on vain kaksi mahdollisuutta: tulos palautetaan funktion nimessä tietueessa tai parametreissa osoitteen avulla. Esimerkiksi C- kielen valmiissa kirjastoissa ei juurikaan käytetä tietueita funktioiden arvoina.

14.3.1 strcpy

	Copies string src to dest
	
	  char *strcpy (char *dest, const char *src);
	
	Prototype in  string.h
	
	Returns dest. 
Tämä funktio laittaa tuloksensa (eli src- jonon kopion) dest- merkkijonoon. Toisaalta tulos- jonon osoite palautetaan myös funktion nimessä. Koska C- kielessä lausekkeen arvoa ei ole pakko sijoittaa mihinkään (muuten esim. i++ ei olisi mielekäs), voidaan tällaista funktiota kutsua joko itsenäisenä proseduurimaisesti tai sitten lausekkeen osana:
	char jono[30], *p;
	...
	strcpy(jono,"Kissa");
	...
	p = strcpy(jono,"Susikoira") + 4;  /* p osoittaa jonoon "koira" */
On tietysti olemassa myös void- funktioita, jotka eivät palauta mitään arvoa nimessään.

14.4 Vaihteleva määrä parametreja

C- kielessä on myös mahdollista tehdä funktiota, joiden parametrilistan pituus (ja jopa parametrien tyyppi) on vaihteleva.

14.4.1 printf ja scanf

	Formatted output to stdout
	
	  int printf (const char *format
	             [, argument, ...]);
	
	Prototype in  stdio.h
	
	printf formats a variable number of arguments according to the format,
	sending the output to stdout. Returns the number of bytes output. In the
	event of error, it returns EOF. 
Kaikki tällaiset muuttuvaparametriset ohjelmat tarvitsevat ainakin yhden parametrin (1., miksi) joka ilmoittaa muiden parametrien 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 tarkastus jää tekemättä ja vääristä kutsuista saattaa seurata jopa koneen kaatuminen! Tyypillisin väärä esimerkki on kutsu:

	int lkm;
	printf("Anna kissojen lkm>");
	scanf("%d",lkm);  /* VÄÄRIN VÄÄRIN VÄÄRIN */	

14.5 Aliohjelman esitteleminen

Käytännössä usein tulee tilanne, jossa tarvittaisiin aliohjelmaa, jota ei valmiina mistään löydy. Tällöin aliohjelma tietenkin kirjoitetaan itse. Ongelmaksi saattaa muodostua aliohjelman parametrilistan keksiminen.

14.5.1 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
	typedef struct {
	  int pv,kk,vv;
	} Pvm_tyyppi; 
Merkkijonossa oleva päivämäärä pitäisi saada muutetuksi Pvm_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:

	char jono[40]="13.3.1992";
	Pvm_tyyppi pvm;
	...
	muuta_jono_pvmksi(jono,pvm);   /* ??? */
Tämä ei tietenkään voi toimia C- kielessä, koska aliohjelma ei pystyisi muuttamaan pvm- muuttujaa! Siis kutsu täytyy olla muotoa:
	muuta_jono_pvmksi(jono,&pvm); 
Tästä seuraa että aliohjelma täytyy vastaavasti esitellä muodossa:
	void muuta_jono_pvmksi(const char *jono, Pvm_tyyppi *pvm)
	{
	...
	}
Toisaalta C++:ssa myös muoto
	muuta_jono_pvmksi(jono,pvm);   /* ??? */
voisi toimia, mikäli aliohjelma esiteltäisiin viitemuuttujaa käyttäväksi
	void muuta_jono_pvmksi(const char *jono, Pvm_tyyppi &pvm)
Aliohjelman tyyppi voisi olla myöskin int, jolloin aliohjelman nimessä palautetaan tieto siitä, onnistuiko muutos vai ei (miksi ei onnistuisi?).

Joskus tällainen aliohjelma voidaan esitellä jopa char * - tyyppiseksi, jotta nimessä voidaan palauttaa osoitin virhettä kuvaavaan viestiin:

	const char *viesti,...
	...
	viesti = muuta_jono_pvmksi(jono,&pvm);
	if ( viesti ) {
	  printf("%s\n",viesti);
	  return 1;
	}
Koko ohjelma testiohjelmineen voisi olla esimerkiksi seuraava:

param\pvmjono.c - esimerkki tietueesta parametrina

	#include <stdio.h>
	#include <string.h>
	
	typedef struct {
	  int pv,kk,vv;
	} Pvm_tyyppi;
	
	static char *VAARA_MUOTO="Väärä muoto!";
	
	const char *muuta_jono_pvmksi(const 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(const char *jono)
	{
	  const 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;
	}

Tehtävä 14.134 pvmjono.cpp

Kirjoita pvmjono.c:stä C++ - versio. Ensimmäinen versio pelkästään muuttamalla osoitteet referensseiksi ja toinen muuttamalla koko Pvm_tyyppi luokaksi cPvm ja kaikki mahdolliset funktiot tietysti luokan metodeiksi.

14.5.2 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:
	Pvm_tyyppi pvm;
	...
	seuraava_pvm(&pvm); 
Aliohjelma esiteltäisiin vastaavasti:
	void seuraava_pvm(Pvm_tyyppi *pvm)
	{
	...
	}
Toisaalta funktion nimessä voitaisiin palauttaa vaikkapa tieto siitä, muuttuiko kuukausi
	int seuraava_pvm(Pvm_tyyppi *pvm)
	{
	...
	}
	...
	  int kuukausi_muuttui;
	  kuukausi_muuttui = seuraava_pvm(&pvm);
	  if ( kuukausi_muuttui )... 
Aikojen kuluessa aliohjelman tarve saattaisi muuttua muotoon
	  lisaa_pvm(&pvm,3); 
Esittely muuttuisi tietysti:
	int lisaa_pvm(Pvm_tyyppi *pvm, int lkm)
	{
	...
	}

Tehtävä 14.135 Päivämäärän lisäys

Täydennä pvmjono.cpp:ssä luokkaan cPvm myös metodi lisaa.

14.5.3 Tietueet osoitteiden avulla tai viitteiden avulla

Usein tietueet ja oliot välitetään osoitteiden tai viitteiden 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. Tietueiden tapauksessa kopiointi saattaa olla isokin, ja tämän vuoksi osoitteen välityksessä menee pelkkä osoite, ei koko tietueen sisältö.
	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");
Parametrin välitys osoitteiden avulla kopioinnin välttämiseksi:
	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");
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ä:
	int vertaa_pvm(const Pvm_tyyppi *pv1, const Pvm_tyyppi *pv2)
	{
	...
	}
	... 

Tehtävä 14.136 vertaa_pvm

Kirjoita aliohjelma vertaa_pvm, joka palauttaa - 1, mikäli pv1 on ennen pv2:sta, 0 jos päivämäärät ovat samoja ja 1 muuten.

Tehtävä 14.137 vertaa- metodi

Täydennä pvmjono.cpp:ssä luokkaan cPvm myös metodi vertaa(const cPvm &pv2), joka toimii kuten edellinen vertaa_pvm, mutta verrattavina ovat *this ja pv2.

14.5.4 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. Tulos muuttujia pitää voida muuttaa, joten niiden kohdalle kutsuun tulee tietysti osoittimet. Siis kutsu voisi olla esimerkiksi:
	double tulo,pid_pros,verottaja,netto;
	...
	laske_verot(tulo,pid_pros,&verottaja,&netto); 
Aliohjelman esittely kutsun perusteella täytyisi olla siis
	void laske_verot(double brutto, double pros,
	                 double *pros_osuus, double *netto)
	{
	...
	}

Tehtävä 14.138 Sama viitteiden avulla

Toista edellinen päättely parametrien tyypeistä, jos voit käyttää viiteparametreja (referenssi).

14.5.5 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 esitetty merkkijono muodossa "a- f". Tämä pitäisi saada muutettua muotoon "abcdef". Tarkoitusta varten kirjoitetaan aliohjelma tayta_valit.

Mitkä ovat aliohjelman parametrit? Tietysti alkuperäinen jono. Mihin tulos? Tähän tarvitaan ehkä toinen parametri. Nimessään aliohjelma voi vielä palauttaa osoitteen tulosjonoon, kuten merkkijonofunktioilla yleensäkin on tapana:

	char merkit[4]="a- f", tulos[20], sallitut[20];
	...
	tayta_valit(tulos,merkit);
	...tai...
	strcat(sallitut,tayta_valit(tulos,merkit)); 
Funktion esittely on siis muotoa:
	char *tayta_valit(char *tulos, const char *jono);
	... 
Myöhemmin huomataan, että tulos- jonon maksimipituus voidaan ylittää! Siksi funktion kutsuun lisätäänkin yksi parametri:
	tayta_valit(tulos,20,merkit); 
ja vastaavasti esittelyyn:
	char *tayta_valit(char *tulos, int max_pit, const char *jono); 
Toinen vastaava esimerkki oli seuraava_pvm aliohjelman muuttaminen aliohjelmaksi lisaa_pvm.

14.5.6 Ei printf eikä scanf tai tietovirtoja

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, ei aliohjelmassa saa olla printf ja scanf eikä muita vastaavia lauseita (mm. cin >>, cout <<) !

Olisihan todella yllätys, mikäli kutsussa

	y = sin(x); 
rupeaisi tulostumaan näytölle jotakin tai jopa odotettaisiin syöttöä päätteeltä!

14.6 Osoitteista ja osoittimista

Mikäli ohjelmassa esitellään osoitintyyppisiä muuttujia, pitää aina muistaa perustella mihin osoittimet osoittavat. Esimerkiksi seuraava ohjelma olisi todella väärin:
	char *jono;              /*  VÄÄRÄ ESIMERKKI!!!! */ 
	strcpy(jono,"Kissa"); 	
Mihin muuttuja jono osoittaisi? Satunnaiseen paikkaan? Ja tänne satunnaiseen paikkaan kopioidaan teksti "Kissa"!

Vastaavasti seuraava ohjelma olisi jo oikeampi:

	char *jono, st[30];
	strcpy(st,"Kissa");
	jono = st+5;
	strcpy(jono,"tarha");  /*  - > st = "Kissatarha" */
Vikana olisi tietysti vielä se, ettei merkkijonojen maksimipituuksien ylittämistä valvota!

14.6.1 Muista aina sijoitus tai malloc tai new

Aina kun ohjelmassa esiintyy osoitintyyppinen muuttuja, pitää muistaa, että ennen sen käyttöä se on alustettu joko sijoituksella toiseen osoitteeseen tai osoittimelle on annettu arvo malloc- funktiolla tai new- operaattorilla (joihin palataan myöhemmin!). Siis aina:
	                  int *osoitin;
	/* Aina joko */
	/* 1 */           osoitin = &muuttuja;
	/* tai */
	/* 2 */           osoitin = malloc(...);
	// tai
	/* 3 */           osoitin = new...
Noudattaako aliohjelman parametrit tätä sääntöä?
	void laske_verot(double brutto, double pros,
	                 double *pros_osuus, double *netto) 
Kyllä, koska kutsu
	laske_verot(10000.0,40.2,&verottajalle,&itselle) 
tarkoittaa sijoitusta aliohjelman parametreihin:
	brutto     = 10000.0
	pros       = 40.2
	pros_osuus = &verottajalle
	netto      = &itselle

14.6.2 Milloin osoitin?

Yleensä ohjelman muuttajat esitellään tavallisiksi muuttujiksi (paitsi taulukot, jotka ovat aina osoitteita). Osoitinmuuttujia esitellään pääsääntöisesti

14.6.3 Varoitus

Aloittelevalla ohjelmoijalla seuraavannäköinen aliohjelman alku on pääsääntöisesti väärin:
	int oma_ali(...)
	{
	  int *a; 	// aloittelijalla 
	... 
Siis tarkista aina huolellisesti kaikki lohkon alkumerkin jälkeen määritellyt osoitintyyppiset muuttujat!

14.6.4 Kertaustehtäviä

Kirjoita seuraavat funktiot tai aliohjelmat sekä kirjoita kullekin oma testipääohjelma.

Tehtävä 14.139 suurin_kirjeen_paino

Kirjoita funktio int suurin_kirjeen_paino(double rahaa) joka palauttaa mikä on suurin kirjeen paino, joka voidaan lähettää rahamäärällä rahaa.
	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

Tehtävä 14.140 kysy_ika

Kirjoita aliohjelma kysy_ika, joka kysyy henkilön iän ja palauttaa sen parametrinaan. Ikää kysytään kunnes iäksi on vastattu luku väliltä 18- 65 vuotta. Aliohjelman nimessä palautetaan tieto siitä, monestiko ikää piti kysyä ennenkuin hyväksytty tulos saatiin.

Tehtävä 14.141 palindromi

Kirjoita funktio palindromi, joka palauttaa nimessään tiedon siitä (1=kyllä, 0=ei) onko parametrina välitetty sana palindromi vai ei.

Tehtävä 14.142 laske_merkin_maarat

Kirjoita funktio laske_merkin_maarat, joka palauttaa nimessään tiedon siitä, monestiko parametrina välitetty merkki esiintyy parametrina välitetyssä jonossa (HUOM! ei täysin sama kuin laske_merkit, mikä ero?).
	merkki = 's'
	jono   = "Kissa"  - > palauttaa 2

Tehtävä 14.143 vero

Kirjoita aliohjelma vero, joka toimii seuraavasti:
	                          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) 

Tehtävä 14.144 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):
	                           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

Tehtävä 14.145 mjono_ajaksi

Kirjoita muuta_jono_pvmksi matkien aliohjelma mjono_ajaksi.
Muuta mjono_ajaksi aliohjelmaa siten, että se toimii seuraavasti:
	  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 

Tehtävä 14.146 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:
	   0  - >  muutetaan muotoon  "14:30"
	   1  - >  muutetaan muotoon  "14:30:25"
	   2  - >  muutetaan muotoon  "14:30:25.20" 
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 apuna aika_mjonoksi - aliohjelmaa.
Muuta aika_mjonoksi - aliohjelmaa siten, että muoto- parametrin arvoilla 0 ja 1 suoritetaan katkaisun sijasta pyöristys.
	  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"

Tehtävä 14.147 cAika

Lisää aiemmin esitettyyn luokkaan cAika metodit jonoksi ja sijoita, jotka mukailevat aliohjelmia mjono_ajaksi ja aika_mjonoksi.


previous next Title Contents Index