* kerrataan aliohjelmien suunnittelua
* kerrataan osoittimia parametreina
* kerrataan referenssejä parametreina
* 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.
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ä!
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");
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++;
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.
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 */
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:
#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; }
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) { ... }
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) { ... } ...
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) { ... }
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.
Olisihan todella yllätys, mikäli kutsussa
y = sin(x);rupeaisi tulostumaan näytölle jotakin tai jopa odotettaisiin syöttöä päätteeltä!
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!
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
int oma_ali(...) { int *a; // aloittelijalla ...Siis tarkista aina huolellisesti kaikki lohkon alkumerkin jälkeen määritellyt osoitintyyppiset muuttujat!
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
merkki = 's' jono = "Kissa" - > palauttaa 2
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)
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
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
0 - > muutetaan muotoon "14:30" 1 - > muutetaan muotoon "14:30:25" 2 - > muutetaan muotoon "14:30:25.20"
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"