Helsingin yliopisto Tietojenkäsittelytieteen laitos
 

Tietojenkäsittelytieteen laitos

Tietoa laitoksesta:

 
Helsingin yliopisto / Tietojenkäsittelytieteen laitos / Copyright © 2000 Jan Lindström. Tämän oppimateriaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.

58127-1 C-ohjelmointi : 6 Funktiot ja ohjelman rakenne


  • 6 Funktiot ja ohjelman rakenne

    6 Funktiot ja ohjelman rakenne

    Kuten useimmissa muissakin ohjelmointikielissä myös C-kielinen ohjelma koostuu yhdestä tai useammasta ohjelmayksiköstä, joita ovat pääohjelma ja aliohjelmat. C-kielessä kaikki ohjelmayksiköt ovat funktioita. Ohjelman ei kuitenkaan tarvitse käyttää hyväkseen funktion palauttamaa arvoa, jolloin funktiota siis käytetään proseduurina.

    C-ohjelma voidaan jakaa useisiin tiedostoihin, kunhan ohjelmayksikkö ei jakaudu eri tiedostoihin. Suositeltavaa on jakaa ohjelma tiedostoihin loogisen moduulijaon mukaan. Vaikka C-kielessä ei ole varsinaista moduulikäsitettä, siinä on kuitenkin piirteitä, jotka tukevat modulaarisuutta eli asiallisesti yhteenkuuluvien aliohjelmien ja määrittelyjen muodostamista erillisiksi, suhteellisen itsenäisiksi kokonaisuuksiksi.

    C-ohjelmassa voi olla määrittelyjä, jotka eivät ole minkään funktion sisällä vaan ovat globaaleja. C-ohjelman yleisrakenne on yksinkertainen: se koostuu funktioista ja globaaleista määrittelyistä. Funktioita on oltava vähintään yksi (pääohjelma eli main) määrittelyt voivat puuttua. Funktioiden ja määrittelyjen järjestys on muuten vapaa, mutta tunnusta voi käyttää vasta määrittelyn jälkeen.

    Pääohjelman nimi tulee olla main (no tosiasiassa tämänkin voi vaihtaa, mutta se vaatii kurssin ohi menevää tietoutta). Ohjelman suoritus alkaa main funktiosta. Ohjelman suorituksen voi ajatella koostuvan siitä, että ohjelmaa käynnistäessään käyttöjärjestelmä kutsuu main funktiota, joka voi kutsua muita funktioita. Pääohjelman suorituksen päättyessä päättyy koko ohjelman suoritus. main ei voi palauttaa arvoa samassa mielessä kuin funktiot yleensä, mutta se voi palauttaa käyttöjärjestelmälle paluukoodin, joka kertoo ohjelman toiminnan onnistumisesta.

    Täten main on koonaislukutyyppinen funktio ja voi palauttaa arvon lauseella return n;, jolloin luku n välittyy käyttöjärjestelmälle. Paluukoodi 0 merkitsee normaalia paluuta ja nollasta poikkeavat arvot ovat järjestelmäkohtaisia virhekoodeja.

    Ennen yleistä kuvausta C-funktioiden rakenteesta otetaanpa esimerkki. Ohjelma sisältää funktion power, joka korottaa luvun potenssiin, kun kantaluku on kokonaisluku ja eksponentti luonnollinen luku:

    #include<stdio.h>
    
    long power(int x, int n)
    {
        if ( n == 0 )
            return 1;
        else
            return x * power(x,n-1);
    }
    
    int main(void)
    {
        int kanta,ekspon;
        long potenssi;
    
        scanf("%d %d",&kanta,&ekspon);
        potenssi = power(kanta,ekspon);
        printf("%d potenssiin %d on %ld\n",kanta,ekspon,potenssi);
        return 0;
    }
    

    6.1 Funktion rakenne

    Aluksi on syytä selventää käsitteitä:
    • funktio on C-ohjelman osa, joka kuvaa jonkin toiminnon ja jolla on oma nimi eli tunnus.

    • funktion kutsu on lauseke, jossa esiintyy funktion tunnus ja jonka laskeminen aiheuttaa vastaavan funktion suorittamisen.

    • funktion määrittely on määrätynlainen kuvaus funktiosta ja kertoo funktion tyypin, nimen ja argumenttilistan rakenteen.

    Hiukan yksinkertaistettuna funktion yleinen muoto on: funktionotsikko funktionrunko, missä funktionotsikko on muotoa: muistimäärite tyyppimäärite deklarattori (argumenttilista). Funktion eri osien tarkempi rakenne ja merkitys on seuraava:

    • Muistimäärite voi olla extern tai static. Extern tapauksessa kääntäjä välittää tiedon funktion nimestä latausohjelmalle, mikä mahdollistaa funktion kutsumisen niistä ohjelman osista, jotka on kirjoitettu eri tiedostoihin kuin funktio itse. Määrite static aiheuttaa, että funktiota voi kutsua vain siitä ohjelman osasta, joka on kirjoitettu samaan tiedostoon kuin funktio.

    • Tyyppimäärite ilmoittaa funktion tyypin eli funktion palauttaman arvon tyypin. Tyyppi voi olla mikä hyvänsä C-kielen tyyppi paitsi taulukko tai funktiotyyppi. Funktio kyllä voi palauttaa osoittimen taulukkoon tai funktioon.

    • Deklaraattori on tunnus, eli funktiolle annettava nimi.

    • Argumenttilista koostuu argumenttien määrittelyistä, jotka erotetaan toisistaan pilkuilla. Kukin argumentin määrittely koostuu tyypistä ja tunnuksesta.

    • Funktionrunko on rakenteeltaan lohko eli koostuu aaltosuluissa olevasta määrittelyjen ja lauseiden jonosta.
    double cube(double x);
    
    double cube(double x)
    {
        return x * x * x;
    }
    
    Eli yhteenvetona funktion määrittelee:
    • funktion nimi. Samat säännöt kuin muuttujien nimeämisessä
    • palautusarvo, joka voi olla void jos funktio ei palauta mitään
    • nolla tai enemmän parametrejä
    • nolla tai enemmän lauseita lohkossa jotka määrittelevät mitä funktio tekee
    • yleinen muoto:
           palautustyyppi funktionimi (parametrityyppi1 parametri1, ... )  
           {
               lause1
               ...
               lauseN
           }
      
    • jos parametrejä ei ole sulkeet jätetään tyhjiksi
    • kaarisulkeita {} on pakko käyttää

    6.2 Funktion kutsu

    Funktion kutsu koostuu funktion tunnuksesta ja suluissa olevasta argumenttilistasta eli todellisten argumenttien listasta. Jos funktiolla ei ole ollenkaan argumentteja (void funktio), sulut tarvitaan kuitenkin funktion tunnuksen perään. Todellisten argumenttien määrän tulee olla sama kuin muodollisten.

    Kunkin todellisen argumentin tulee olla muunnettavissa vastaavaan muodolliseen argumenttiin. Kääntäjä huolehtii automaattisesti tyypinmuunnoksista samojen sääntöjen mukaan kuin sijoituslausekkeissa.

    Funktion kutsu on lauseke ja voi esiintyä laajemman lausekkeen osana. Esimerkiksi:

    
    scanf("%d",&i);
    if ( scanf("%d",&j) != 1)
        printf("Virhe syöttötiedoissa\n");
    

    6.3 Funktion argumentit

    Funktion muodollinen argumentti vastaa paikallista muuttujaa, joka saa alkuarvokseen vastaavan todellisen argumentin arvon, jonka kutsuva funktio laskee ennen varsinaista kutsua. Funktio saa tämän takia muuttaa muodollisen argumenttinsa arvoa. Esimerkiksi:
    long kertoma(int);
    
    long kertoma(int n)
    {
        long tulos = 1;
    
        while( n > 1 )
        {
            tulos *= n;
            n--;
        }
    
        return tulos;
    }
    

    6.4 Näkyvyyssäännöt

    Iso ohjelma kannattaa jakaa useampaan tiedostoon. Funktioita (tiedostoja) voidaan kääntää erikseen ja sitoa sitten osat yhteen.

    Paikalliset (auto) muuttujat esitellään lohkon alussa ja ne ovat käytössä vain esittelylohkossaan ja sisemmissä lohkoissa. Saman nimiset tunnukset rinnakkaisissa lohkoissa eivät liity mitenkään toisiinsa. Funktion parametrit rinnastetetaan paikallisiin muuttujiin.

    Ulkoiset (globaalit, extern) muuttujat ovat käytettävissä esittelyn jälkeen sen tiedoston loppuun, jossa esittely sijaitsee. Määrittelemällä muuttuja extern-määrittelyssä, voidaan sitä käyttää ennen esittelyä.

    Paikallinen muuttuja voidaan myös määritellä staattiseksi. Tällöin muuttujan arvo säilyy funktion kutsukerrasta toiseen. Paikallinen staattinen muuttuja on siis paikallinen pysyvä talletustila.

    Funktiokin voidaan määritellä staattiseksi. Tällöin se on käytössä vain esittelytiedostossaan, eli staattisen funktion nimi ei näy tiedoston ulkopuolelle.

    Jatkuvasti käytettäviä paikallisia muuttujia voidaan varustaa määreellä register. Tämä on ehdotus, että muuttuja saattaa kannattaa säilyttää rekisterissä koko ajan. Kääntäjä voi jättää register-määreen huomioimatta.

    Tavoitteena on, että kukin määrittely kirjoitetaan vain kerran. Tällöin esim. muutosten tekeminen on luotettavampaan. Kirjoitetaan määrittelyt tiedostoon, joka liitetään #include-rivillä kunkin tiedoston alkuun tarpeen mukaan.


    6.5 Funktioprototyypit

    Funktioden ja muuttujien on oltava määriteltyjä ennen käyttöä. Jos funktiota käytetään ohjelmassa ennen määritystä kääntäjä ei "tunne" funktiota. Ohjelmaa katsotaan ylhäältä ja alaspäin eli esim. main():ssä käytetyt funktiot on määriteltävä ennen main()-funktiota. Funktioprototyypit ratkaisevat ongelman esittelemällä kääntäjälle miltä funktio "näyttää" ja kääntäjä osaa sen jälkeen käyttää funktiota. Yleisesti:

    palautustyyppi funktionimi (parametrityyppi1 parametri1, ... );
    
    Prototyyppimäärittely ei sisällä itse funktion koodia vaan koodi on muualla ehkä jopa toisessa tiedostossa. Funktioprototyypit kirjoitetaan yleensä tiedoston alkuun tai otsikkotiedostoihin.



    Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)