if- valintarakenne Yksittäisen vaihtoehdon valinta if-lauseen yksinkertaisin muoto on sellainen, missä tietyt lauseet joko suoritetaan tai jätetään suorittamatta. Syntaksi: if (ehto) lauseita; Aaltosulkeiden välissä olevat lauseet valitaan suoritettaviksi vain ehdon ollessa voimassa. Lauseita voi olla yksi tai useampia. Jos lauseita on vain yksi, ei ole välttämätöntä käyttää aaltosulkeita. Yksittäisen vaihtoehdon if-rakenne voidaan esittää seuraavanlaisena vuokaaviona: salmiakkikuvio kuvaa ehtoa; suorakaide kuvaa lauseita, jotka suoritetaan ehdon ollessa tosi; nuolet kuvaavat ohjelman suorituksen etenemistä 15
#include <stdio.h> void main(void) int luku; printf ("Anna kokonaisluku :"); scanf ("%d",&luku); if (luku == 100) /*== on yhtäsuuruusvertailu */ printf ("Luku on sata"); Harj 12 Tee ohjelma, joka kysyy käyttäjältä kaksi lukua ja tulosta luvuista suuremman. Anna kaksi lukua : 2 5 Suurempi luvuista oli 5. Harj 13 Tee ohjelma, joka kysyy käyttäjältä 2 lukua. Ohjelma vähentää pienemmän luvun suuremmasta luvusta ja tulostaa laskutoimituksen ja erotuksen. Anna kaksi lukua : 2 5 5 2 = 3 16
Valinta kahdesta vaihtoehdosta Usein valintatilanne on sellainen, jossa joudutaan valitsemaan toinen kahdesta mahdollisesta vaihtoehdota. Kahden vaihtoehdon if-valintalauseessa vaihtoehto-osa alkaa avainsanalla else. if-rakenne else-osan kanssa (valinta kahdesta) syntaksi: if (ehto) else lauseita; lauseita; If-else -rakenteen vuokaavioesitys: ehdon ollessa tosi haaraudutaan oikealle epätodessa tilanteessa haaraudutaan vasemmalle kumpaakin reittiä päästään jatkamaan ohjelman suoritusta if-else -rakennetta seuraavasta lauseesta 17
#include <stdio.h> void main(void) int luku; printf ("Anna kokonaisluku :"); scanf ("%d",&luku); if (luku == 100) /*== on yhtäsuuruusvertailu */ printf ("Luku on sata"); else printf( Luku ei ole 100 ); Harj 14 Tee ohjelma, joka kysyy käyttäjältä luvun. Jos luku on jaollinen kolmella, ohjelma tulostaa jana BINGO, muussa tapauksessa ohjelma tulostaa sanan BONGO. (Tämän tehtävän ratkaisuun tarvitset modulo-operaattoria sivulta 9) Harj 15 Tee laskinohjelma, jossa käyttäjä antaa kaksi lukua ja valitsee haluamansa laskutoimituksen. Ohjelma tulostaa laskutoimituksen ja tuloksen. Anna kaksi lukua: 2 5 Valitse haluamasi laskutoimitus 1. yhteenlasku 2. vähennyslasku 3. jakolasku 4. kertolasku Valinta: 3 2 / 5 = 0.40 18
Binäärivalinnan periaate Tarkastellaan tilannetta, jossa pitäisi toimia eri tavoilla sen perusteella onko luku nolla, positiivinen vai negatiivinen. Tällainen ongelma voidaan ratkaista sisäkkäisillä if-else - rakenteella käyttäen ns. binäärivalinnan periaatetta, joka on esitetty kuvassa: salmiakkikuvio kuvaa valintaa nuolet kuvaavat ohjelman etenemistä jokaisessa valintatilanteessa on kaksi vaihtoehtoa Valinta useasta vaihtoehdosta If-rakennetta voidaan käyttää monivalintatilanteissa, joissa pitää valita yksi useasta mahdollisesta vaihtoehdosta. Esimerkki tällaisesta tapauksesta mm. kolmen vaihtoehdon valintatilanne: #include <stdio.h> #include <stdlib.h> //Sisältää srand- ja rand-funktiot #include <time.h> //Sisältää time-funktion int arvaus, arvottu; void main (void) srand(time(null)); /*Alustetaan satunnaislukugeneraattori. srand()-funktio 19
saa parametrinaan tietokoneen kellonajan, jonka perusteella se asettaa arvon satunnaislukugeneraattorin siemenluvulle, jota käyttäen satunnaisluku lasketaan*/ arvottu = rand() % 10; /*Arvotaan satunnaisluku. Lukualuetta supistetaan modulo-operaattorilla. Modulomerkin perään luku, joka on yhtä suurempi kuin haluttu suurin luku*/ printf("olen arponut luvun väliltä 1-9, arvaa luku: \n"); scanf("%d", &arvaus); if (arvottu==arvaus) printf("arvasit oikein!"); else if (arvottu>arvaus) printf("arvaus oli liian pieni"); else printf("arvaus oli liian suuri"); Harj16 Tee ohjelma, joka kysyy opiskelijan etu- ja sukunimen ja arvosanan numerona. Ohjelma tulostaa opiskelijan kokonimen ja arvosanan tekstimuotoisena (5=kiitettävä, 4=hyvä, 3=hyvä, 2=tyydyttävä, 1=tyydyttävä,0=hylätty). Jos käyttäjä syöttää jonkin muun arvon ilmoita siitä käyttäjälle! Switch...case valintarakenne On olemassa tilanteita, joissa joudutaan valitsemaan yksi vaihtoehto monien joukosta. 20
Otetaan käyttöön rakenne, jossa on käytössä yksi valintamuuttuja ja monta vaihtoehtoista toimintamallia. Monivalintaisen if-rakenteen sijaan voidaan joissakin tilanteissa käyttää switch...case rakennetta. Esimerkki tällaisesta on tilanne, jossa käyttäjä valitsee yhden vaihtoehdon usean vaihtoehdon valikosta (nk. menusta).switch...case -rakenteessa on testattavana yksi lauseke, jonka arvon perusteella haaraudutaan johonkin case-haaraan. case-haara voi sisältää useita lauseita, jotka suoritetaan peräkkäin, kunnes törmätään break-lauseeseen, joka ohjaa kontrollin pois switch-rakenteesta. Todettakoon, että tämä on ainoa paikka, jossa tässä materiaalissa käytetään breaklausetta. Muissa tilanteissa break-lauseen käyttöä ei suositella. Switch...case -rakenteen kieliopillinen muoto on seuraava: switch (lauseke) case tapausvakio1: lauseita; break; case tapausvakio2: lauseita; break; case tapausvakio3:... default: lauseita; #include <stdio.h> void main(void) int valinta; printf("arvioi tämän hetkinen C-kielen osaamisesi asteikolla 0-5: "); scanf("%d", &valinta); 21
switch (valinta) case 5: printf("\nosaamisesi on kiitettävää"); break; case 4: case 3: printf("\nhyvä-osaaminen, jatka samaan malliin"); break; case 2: printf("\ntyydyttävä-osaaminen, "); break; case 1: printf("\ntyydyttävä-osaaminen, mutta tehosta harjoittelua!"); break; case 0: printf("\ntarvinnet lisää harjoitusta kurssin läpäisemiseksi"); break; Harj17 Tee uudelleen Harj15 käyttäen switch case rakennetta. Merkkijono Merkkijonoa käsiteltiin jo edellisessä osiossa sivulla 12-14. Arvon lukeminen merkkijonomuuttujalle Olkoon määritelty merkkijono: char nimi[11]; Merkkijonolle voidaan lukea arvo mm. seuraavilla tavoilla: 22
(1) scanf ("%s", nimi); (2) gets(nimi); Syöttöfunktio sijoittaa lukemansa merkkijonon loppuun nollamerkin. Jos esimerkiksi käyttäjä syöttää nimen "Tiivitaavi", merkkitaulukon nimi sisällöksi tulee: T i i v i t a a v i \0 Taulukon loppuosa nollamerkin jälkeen on sisällöltään yhdentekevää. Järjestelmä tietää nollamerkin sijainnin perusteella, mihin todellinen merkkijono päättyy. Olkoon määritelty merkkijono: char nimi[11]; Merkkijono voidaan tulostaa printf()-funktiolla kahdella eri tavalla: (1) printf (nimi); (2) printf ("%s",nimi); Merkkijonojen vertailu Se, että merkkijono ei ole C-kielen perustietotyyppi näkyy siinä, että merkkijonoille ei voi tehdä yhtäsuuruusvertailuja ==-operaattorilla eikä merkkijonoa voi sijoittaa toisen arvoksi =-operaattorilla. Merkkijonojen vertailu on tehtävä string.h-tiedostossa määritellyllä kirjastofunktiolla strcmp(). Funktion prototyyppi on seuraava: 23
int strcmp (const char *eka, const char *toka); Funktiolle välitetään parametreina kaksi merkkijonotaulukkoa. const-määreet (constant = vakio) muuttujamäärittelyjen edessä ilmaisevat, ettei funktiolla ole lupa muuttaa parametreja. (Muuttujaparametrejahan voitaisiin periaatteessa muuttaa.) Funktion paluuarvona on luku, joka < 0 mikäli eka < toka = 0 mikäli merkkijonot ovat samoja > 0 mikäli eka > toka. Esimerkkiohjelma strcmp-funktion ja binäärivalinnan käytöstä: #include <stdio.h> #include <string.h> void main(void) char nimi[30]; int vertailu; printf("anna nimi:"); scanf("%s",nimi); vertailu=strcmp(nimi,"pekka"); if(vertailu==0) printf("kirjoitit Pekka"); else if(vertailu<0) printf("antamasi nimi on aakkosissa ennen Pekkaa\n"); 24
else printf("antamasi nimi on aakkosissa Pekan jalkeen\n"); Harj18 Ohjelma, joka tarkistaa käyttäjän syöttämän salasanan oikeellisuuden. Ohjelma antaa virheellisestä salasanasta virheilmoituksen. Anna salasana: Tiivitaavi Aivan oikein! Harj19 Tee sanakirja ohjelma, johon sana syötetään englanniksi ja ohjelma tulostaa sanan suomeksi. Jos sanaa ei löydy ohjelma tulostaa virheilmoituksen. Ohjelma tuntee sanat horse, cow, mouse ja bear sekä vastaavat suomenkieliset sanat hevonen, lehmä, hiiri ja karhu Merkkijonon kopionti Merkkijonomuuttujaa ei voi sijoittaa toisen merkkijonomuuttujan arvoksi sijoituslauseessa. Sijoitus on tehtävä string.h-tiedostossa määritellyllä kirjastofunktiolla strcpy(): char *strcpy (char *mihin, const char *mista); 25
Funktiolle välitetään parametreina kaksi merkkijonotaulukkoa. Ensimmäinen on sijoituksen kohde ja toinen lähde. Funktion paluuarvona on kohdemerkkijono (eli sen osoite). Esimerkkiohjelma kopioi käyttäjän antaman merkkijonon toisen merkkijonon arvoksi: #include <stdio.h> #include <string.h> void main (void) char nimi1[11]; char nimi2[11]; printf ("Anna nimi (max. 10 merkkiä):"); gets(nimi1); strcpy(nimi2, nimi1); printf ("Tässä on kaksi samaa nimeä: %s ja %s", nimi1,nimi2); Harj20 Tee ohjelma 19 käyttäen ainoastaan kahta printf-lausetta. Harj21 Tee Harj16 tallentaen sanallisen arvosanan merkkijonomuuttujaan. Toistorakenteet Toistorakenteen avulla voidaan suorittaa samoja lauseita toistuvasti peräkkäin. Toistojen määrä on joko etukäteen määrätty tai se riippuu ohjelmassa esitetyistä ehdoista. 26
C-kielessä on 3 toistorakennetta: for, while ja do-while. Tässä vaiheessa kurssia käsittelemme for-rakenteen ja loput toistorakenteet tulevat kurssin seuraavassa osassa. For-toistorakenne Ohjelmassa tarvittavien toistojen määrä tunnetaan usein etukäteen ts. toistoja halutaan tehdä tietty ennalta annettu määrä. Otetaan käyttöön rakenne, jossa toistojen määrä voidaan helposti ilmaista etukäteen. for-rakennetta kannattaa käyttää, jos toistojen määrä tiedetään ohjelmassa, ennen kuin toistot aloitetaan. Jos esimerkiksi halutaan sademäärätiedot viikon jokaiselle päivälle, tiedetään, että sademääriä on seitsemän kappaletta ja näin ollen sademäärää pyydetään vuorovaikutteisessa ohjelmassa seitsemän kertaa. For-lauseessa toistojen lukumäärää hallitaan ohjelmassa olevan toistolaskurin avulla. Se määritellään yleensä kokonaislukumuuttujana, jolle annetaan nimi "i": int i; Laskurin arvo asetetaan toistojen alussa ykköseksi ja sitä kasvatetaan jokaisella toistokerralla yhdellä. Toistot päätetään, kun laskuri on ylittänyt sovitun loppuarvon. Jos for-silmukoita on sisäkkäin, tarvitaan useita laskureita. Sisempien silmukoiden laskureille voidaan käyttää nimiä j ja k. Laskuriin kohdistuu kolme oleellista operaatiota. Nämä ovat 1. laskurin alkuarvon asettaminen (alkutoimet) 2. laskurin arvon vertaaminen loppuarvoon (toistoehto) 3. laskurin arvon kasvattaminen yhdellä (lopputoimet) 27
Nämä kaikki asiat tehdään for-lauseen alussa sulkujen sisällä: for (i=1; i<=n; i++) For-rakenteen kieliopillisesta muodosta on oleellista huomata että kaikki kolme toistoa ohjaavaa operaatiota kirjoitetaan for-riville sulkujen sisään: for (alkutoimet; toistoehto; lopputoimet) lauseita; for-rakenteen sisään kirjoitetut lauseet suoritetaan niin monta kertaa kuin halutaan for-rivin päätteeksi ei kirjoiteta puolipistettä. Puolipiste päättäisi for-rakenteen, minkä seurauksena toistettavat lauseet suoritettaisiin ainoastaan kerran. Ohjelmassa tulostetaan lukujen 1-10 toiset potenssit: #include <stdio.h> #define KOKO 10 void main(void) int i; for (i=1; i<=koko; i++) printf ("%d potenssiin 2 on %d\n",i, i*i); Harj22 Ohjelma, joka tulostaa näytölle nimesi 100 kertaa. Harj23 28
Ohjelma, joka tulostaa näytölle luvut 1-10000, 20 lukua yhdellä rivillä. Harj 24 Ohjelma, joka kysyy sata lukua ja laskee lukujen summan. Harj25 Tee ohjelma, joka kysyy sata lukua ja laskee sekä positiivisten lukujen summan ja keskiarvon. (siis käyttäjä voi syöttää myös negatiivisia lukuja tai nollan) 29
Taulukot Jos on käsiteltävä suuri määrä yhteenkuuluvia samanlaisia tietoja, ei kannata nimetä jokaista tietoa erikseen, vaan kannattaa järjestää tiedot peräkkäin ja antaa tiedoille yksi yhteinen nimi. Yksittäiseen tietoon viitattaessa käytetään yhteistä nimeä ja järjestysnumeroa. Tähän asti käytetyt tietorakenteet (muuttujat) ovat olleet yksinkertaista tyyppiä, mikä tarkoittaa, että muuttujiin liittyy kerrallaan täsmälleen yksi arvo. Reaalimaailman tieto on kuitenkin usein luonteeltaan sellaista, että sitä kuvaa tietojen tai arvojen joukko: "Henkilötiedot"-tietojoukko voi koostua nimestä, syntymäajasta, syntymäpaikasta jne. "Kuukauden päivälämpötilat" koostuu useasta lämpötila-arvosta. Tämäntapaisia tietojoukkoja ei voi kuvata yhdellä muuttujan arvolla. Tarkemmin tutkittaessa esitettyjä tapauksia, huomataan, että nekin ovat luonteeltaan erilaisia. Ensimmäinen tietojoukko (henkilötiedot) koostuu yhtä asiaa (henkilöä) kuvaavista erityyppisistä tiedoista. Jälkimmäinen (päivälämpötilat) koostuu useasta samantyyppisestä tiedosta. Näitä kumpaakin erilaista rakennetta varten on oma tietotyyppinsä. Tässä vaiheessa kurssia tutustumme taulukkoon, joka sisältää ainoastaan samaa tyyppiä olevaa tietoa. Tietotyyppiä, jonka avulla voidaan kuvata useasta samantyyppisestä tiedosta muodostuvaa tietojoukkoa, kutsutaan taulukkotyypiksi. Vastaava tietorakenne on nimeltään taulukko. Taulukko on aina käyttäjän määrittelemä siinä mielessä, että käyttäjä määrää taulukon koon ja taulukon alkion tyypin. Taulukon määrittelyn muoto on: 30
alkion_tyyppi taulukon_nimi [taulukon_koko]; 10 kokonaislukua sisältävä kokonaislukutaulukko määritellään seuraavasti: int taulukko[10]; Taulukon määrittelyssä on huomattava, että hakasulkeisiin kirjoitettavan arvon tulee olla vakio. Taulukon kokoa ei voi määrittää muuttujan avulla eikä sitä voi ohjelman aikana muuttaa. Taulukon yksittäisiin lokeroihin eli taulukon alkioihin viitataan taulukon nimen ja indeksin avulla. Taulukon alkion nimi on muotoa taulukon_nimi[indeksi]. Taulukon looginen rakenne: taulukko koostuu useasta samantyyppisestä tiedosta lokerot nimetään käyttäen taulukon nimeä ja indeksiä indeksi kirjoitetaan nimen perään hakasulkeisiin Taulukon alkiota voidaan käyttää kuten mitä tahansa yksinkertaista muuttujaa. Arvon sijoitus taulukon kolmanteen alkioon käy esimerkiksi seuraavasti: taulukko[2] = 5; 31
Taulukko voidaan alustaa ohjelman alussa määrittelynsä yhteydessä. Yleisin alustustapa on nollata taulukko. Tässä taulukon ensimmäiselle alkiolle asetetaan arvo nolla. Loput alkiot nollautuvat, koska niille ei anneta alkuarvoja. int taulukko[20]=0; Tässä taulukon ensimmäiselle ja toiselle alkiolle asetetaan arvot. Loput alkiot nollautuvat, koska niille ei anneta alkuarvoja. int taulukko[20]=1,2; Taulukon alkioita käsitellään usein järjestyksessä alkaen indeksistä nolla ja päätyen taulukon viimeiseen indeksiin. Tällaisessa käsittelyssä on kätevää käyttää for-rakennetta siten, että for-rakenteen toistomuuttujaa käytetään taulukon indeksinä. Ohjelma lukee taulukkoon lukuja ja tulostaa taulukon sisällön. Taulukko täytetään ja tulostetaan järjestyksessä alkaen indeksistä 0. #include <stdio.h> #define KOKO 20 void main (void) int taulukko [KOKO], i; /* Taulukon täyttö: */ for (i = 0; i < KOKO; i++) printf ("Anna luku"); scanf ("%d",&taulukko[i]); /* Alkioiden tulostaminen yksitellen: */ for (i = 0; i < KOKO; i++) printf ("%d\n",taulukko[i]); 32
Harj26 Tee ohjelma, joka tallentaa taulukkoon täydet kymmenet väliltä 1-10000 (10 20 30 40 9970 9980 9990). Ohjelma tulostaa taulukon sisällön kymmenen lukua rivillään. Harj27 Ohjelma kysyy käyttäjältä 7 päivän lämpötilat ja laskee viikon keskilämpötilan. Ohjelma tulostaa käyttäjän syöttämän lämpötilat ja keskilämpötilan seuraavasti: ******************************************************** MA TI KE TO PE LA SU Keskilämpötila 23 22 17 15 15 14 27 19 ******************************************************** Harj26 Luokalle on pidetty ohjelmoinnin tentti, tee ohjelma joka kysyy arvosanat ja tallentaa ne sellaiseen taulukkoon, että tämän perusteella ohjelma voi tulostaa arvosanojen keskiarvon ja jakauman seuraavasti: Tentin keskiarvo 2,96 0: *** 1: ***** 2: ****** 3: ******* 4: *** 5: ****** 33