Ohjausrakenteet Luento antaa yleiskuvan siitä kuinka ohjelmassa suorittaan vaihtoehtoisia tehtäviä valintarakenteiden avulla ja kuinka samanlaisia ohjelma-askeleita toistetaan toistorakenteiden avulla Valinta: 1. Loogiset lausekkeet if -valintarakenne a. Esimerkki if -valintarakenteen käytöstä b. if -valintarakenne ja peräkkäisyys c. Sisäkkäiset if -valintarakenteet 2. switch -valintarakenne useille vaihtoehdoille 124
Toisto: 1. while -toistorakenne 2. do-while -toistorakenne 3. for -toistorakenne 4. sisäkkäiset rakenteet 125
Loogiset lauseet ja if -valintarakenne if -valintarakenteen ehto ilmaistaan yleensä loogisen lausekkeen avulla, tulokseksi saadaan totuusarvo totuusarvo. epätosi, jota kuvataan luvulla 0 tosi, jota kuvataan luvulla 1 (kaikki nollasta poikkeavat arvot tulkitaan arvoksi tosi) 126
C-kielen vertailuoperaattorit: Operaattori merkitys == yhtäsuuri!= erisuuri < aidosti pienempi <= pienempi tai yhtäsuuri > aidosti suurempi >= suurempi tai yhtäsuuri 127
Seuraava esimerkki selvittää ehtolausekkeiden käyttöä Oletetaan että muuttujilla on seuraavanlaiset arvot: x = -5, MIN = -999.0, NUM = 999, exp = 1024, y = 7, MAX = 1024, kirjain ='J', alkio = 1.5, num = 999 Operaattori Ehto tulkinta Arvo <= x <= 0 x pien, tai yhtäs. kuin 0 1 < eksp < MAX eksp pienempi kuin MAX 0 >= x >= y x suur. tai yhtäs. kuin y 0 > alkio > MIN alkio suurempi kuin MIN 1 == kirjain == 'J' kirjain yhtä kuin 'J' 1!= num!= NUM num erisuuri kuin NUM 0 128
if valintarakenteen perusmuoto C-kielessä if (ehto) lause1; else lause2; Joskus else osan voi jättää pois if (ehto) lause1; 129
Esimerkki if- valintarakenteen käytöstä Tehtävä: Valitse kolmesta annetusta numerosta suurin ja tulosta se. 130
Suunnittelu: Karkean tason algoritmi: 1. Lue kolme lukua num1, num2, num3 2. Talleta suurin luku muuttujaan suurin 3. Tulosta suurin luku Askel 2. tarkennettuna 2.1. Talleta suurempi luvuista num1, num2, muuttujaan suurin 2.2. Talleta suurempi luvuista suurin, num3 muuttujaan suurin 131
Tarkennetaan askel 2.1 Jos num1 on suurempi kuin num2 niin suurin on num1 muutoin suurin on num2 Tarkennetaan askel 2.2 Jos num3 on suurempi kuin suurin niin suurin on num3 Sama suunnittelu karkean tason rakennekaaviona, jossa on esitetty myös tietovirta. 132
Lue kolme lukua ja ilmoita mikä niistä on suurin num1 num2 num3 num1 num2 num3 suurin suurin Lue kolme lukua Etsi suurin luvuista Tulosta suurin Rakennekaavio suurimman luvun etsimiselle 133
Toteutus #include <stdio.h> int main(void) { int num1, num2, num3; /* kolme lukua */ int suurin; /* suurin kolmesta */ /* Käyttäjältä luvut */ printf("anna ensimmäinen luku > "); scanf("%d", &num1); printf("anna toinen luku > "); scanf("%d", &num2); printf("anna kolmas luku > "); scanf("%d", &num3); 134
/* etsitään suurin luku */ if(num1 > num2) suurin = num1; else suurin = num2; if(num3 > suurin) suurin = num3; /* tulostetaan suurin luku */ printf("suurin luku on %d\n", suurin); } return(0); 135
136
if -valintarakenne ja peräkkäisyys Yleinen muoto oli if (ehto) lause1; else lause2; Käyttäen sisäkkäisyyttä eo. lauseet voidaan korvata muilla rakenteilla esim. peräkkäisyys lohkorakenteen avulla. 137
Esimerkkinä kahden muuttujan x ja y arvon vaihto siten, että pienempi arvo talletetaan muuttujaan x. if (x > y) { temp = x; x = y; y = temp; } Peräkkäisyys if-valintarakenteen sisällä saavutetaan kirjoittamalla suoritettavat lauseet aaltosulkeiden { ja } sisään, tyyli on vapaa, mutta sisennys on tärkeää. 138
Sisäkkäiset if-valintarakenteet Esimerkki. Oletetaan kolme kokonaislukumuuttujaa nimeltään numpos, numneg ja numnolla, joiden arvoa kasvatetaan yhdellä riippuen siitä onko muuttujan x arvo suurempi, pienempi tai yhtä suuri kuin nolla (versio1). if(x > 0) numpos = numpos + 1; else if(x < 0) numneg = numneg + 1; else numnolla = numnolla + 1; 139
Sama tehtävä voidaan vaihtoehtoisesti suorittaa peräkkäisillä ifrakenteilla seuraavasti (versio2): if(x > 0) numpos = numpos + 1; if(x < 0) numneg = numneg + 1; if(x == 0) numnolla = numnolla + 1; Kumpi on parempi vaihtoehto? 140
x > 0 tosi numpos = numpos +1 epätosi x < 0 tosi numneg = numneg +1 epätosi numnolla = numnolla +1 141
Switch -valintarakenne Valintarakenne, jonka avulla ehtolausekkeen arvon perusteella voidaan valita yksi useammasta vaihtoehtoisesta toiminnasta. Yleinen muoto: 142
switch (lause) { case tunnus1: lauseet; break; } case tunnus2: lauseet; break;... case tunnusn: lauseet; break; default: lauseet; 143
1. Lauseen arvoa (tyyppiä char tai int) verrataan ylhäältä lähtien case-rivien tunnuksiin kunnes arvot täsmäävät, tunnukset ovat vakioita, kaksoispiste ei kuulu tunnuksen nimeen. 2. Täsmäävän vaihtoehdon case-rivin jälkeiset lauseet suoritetaan järjestyksessä ensimmäiseen break-riviin saakka, jonka kohdalla poistutaan rakenteesta. 3. Jos täsmäävää vaihtoehtoa ei löydy suoritetaan defaultvaihtoehdon lauseet mikäli sellainen on ja poistutaan rakenteesta. 144
Esimerkki: switch-rakenne, joka määrittää lampun odotetun eliniän lampun tehonkulutuksen (wattimäärän) perusteella. #include <stdio.h> int main(void){ int lampunteho; int elinika; printf("syötä lampun teho > "); scanf("%d", &lampunteho); 145
switch (lampunteho) { case 40: elinika = 2400; break; case 60: case 80: elinika = 2000; break; case 100: elinika = 1800; break; } default: elinika =1500; break; } printf("lampun elinika on %d tuntia\n", elinika); 146
147
148
Esimerkki: switch-valintarakenne, joka tulostaa muuttujan vari arvoa vastaavan värin: arvo 'P' -> punainen, 'S' -> sininen, 'K' - > keltainen. #include <stdio.h> int main(void){ char vari; printf("syötä väriä kuvava kirjain "); scanf("%c", &vari); 149
} switch (vari){ case 'P': printf("punainen\n"); break; case 'S': printf("sininen\n"); break; case 'K': printf("keltainen\n"); break; } return 0; 150
Jos käyttäjä syöttää jonkun muun kirjaimen tai vaikka oikean kirjaimen pienaakkosilla, niin ohjelma ei tulosta mitään! 151
Muista tunnus voi olla vain tyyppiä char tai int, ei double eikä merkkijono jokaisen vaihtoehdon loppuun yleensä break, paitsi poikkeustilanteissa default -tunnus loppuun aina kun mahdollista 152
while -toistorakenne Toiston käytön algoritmissa määrää tehtävän luonne. Ratkaise tehtävä jossain erityistapauksessa ja selvitä: 1. Onko ongelmanratkaisussa askeleita joita toistetaan? 2. Tiedetäänkö etukäteen kuinka monta kertaa askel suoritetaan? 3. Mistä tiedetään kuinka monta kertaa askel toistetaan? 153
Yleinen muoto: ohjausmuuttujan alustus while(toistoehto) lause; 154
Toiminta: 1.Ohjausmuuttujalle annetaan alkuarvo 2.Toistoehdossa tutkitaan onko ohjausmuuttujan arvoa. Jos arvo on haluttu, arvoksi tulee tosi, jolloin runko-osan lause suoritetaan. lauseen suorittamisen täytyy muuttaa ohjausmuuttujan arvoa. 3.Runko-osan lause suoritetaan uudelleen niin kauan kuin while - toistoehto on tosi. 4.Kun toistoehto on epätosi poistutaan silmukasta ja ohjelman suoritus jatkuu while -rakennetta seuraavasta lauseesta. 155
#include <stdio.h> int main(void) { int n; /* haluttu merkkien lukumäärä */ int laskuri; /* laskee montako a-kirjainta on tulostettu */ /* pyydetään käyttäjältä a-kirjainten lukumäärä */ printf("anna tulostettavien a-kirjainten lukumäärä >"); scanf("%d", &n); 156
laskuri = 0; /* laskuri on ohjausmuuttuja */ /* käynnistetöön toistorakenne, joka tulostaa a-kirjaimet toistetaan kunnes ehto laskuri < n muuttuu epätodeksi */ while( laskuri < n){ printf("a"); laskuri = laskuri + 1; /* kirjataan yksi a-kirjain tulostetuksi */ } } printf("\n\n"); /* rivinvaihtoja loppuun */ return(0); 157
158
do-while toistorakenne ohjausmuuttujan alustus do lause; while (toistoehto); 159
Toiminta: 1. Ohjausmuuttujan alustaminen 2.Runko-osan lause suoritetaan (ainakin yhden kerran). 3.Testataan toistoehto. Jos se on tosi, niin runko-osan lause suoritetaan uudelleen. Lauseen suorittaminen muuttaa ohjausmuuttujan arvoa. 4.Runko-osan lauseen suoritusta jatketaan, kunnes toistoehto saa arvon epätosi. 5.Kun toistoehto on epätosi poistutaan toistorakenteesta 160
Esimerkki: Jälleen ohjelma, joka tulostaa käyttäjän haluaman määrän a-kirjaimia peräkkäin. #include <stdio.h> int main(void) { int n; /* haluttu merkkien lukumäärä */ int laskuri; /* laskee montako a-kirjainta on tulostettu */ /* pyydetään käyttäjältä a-kirjainten lukumäärä */ printf("anna tulostettavien a-kirjainten lukumäärä >"); scanf("%d", &n); 161
laskuri = 0; /* nollataan ohjausmuuttuja, joka laskee tulostettujen kirjainten lukumäärää */ /* käynnistetöön toistorakenne, joka tulostaa a-kirjaimet toistetaan kunnes ehto laskuri < n muuttuu epätodeksi */ do{ printf("a"); laskuri = laskuri + 1; /* kirjataan yksi a-kirjain tulostetuksi */ }while( laskuri < n) printf("\n\n"); /* rivinvaihtoja loppuun */ return(0); 162
163
Huomaa olennainen ero if-rakenteeseen, jossa lohko suoritetaan korkeintaan kerran. #include <stdio.h> int main(void) { int n; /* haluttu merkkien lukumäärä */ int laskuri; /* laskee montako a-kirjainta on tulostettu */ /* pyydetään käyttäjältä a-kirjainten lukumäärä */ printf("anna tulostettavien a-kirjainten lukumäärä >"); scanf("%d", &n); 164
laskuri = 0; /* nollataan toistolaskuri */ if (laskuri < n){ printf("a"); laskuri = laskuri + 1; } } printf("\n\n"); /* rivinvaihtoja loppuun */ return(0); 165
166
Toinen esimerkki: while-silmukka, joka laskee käyttäjän antamien kokonaislukujen tuloa niin kauan kuin lukujen tulo on pienempi kuin 1000. Muuttuja tulo kahdessa roolissa, kertolaskun lopputuloksen säilyttäjänä ja ohjausmuuttujana (tulo < 1000) Ohjelmassa luku-muuttujan vanha arvo tuhoutuu jokaisella toistokerralla. Ohjelma jää "ikuiseen silmukaan" jos käyttäjä syöttää luvun 0! 167
#include <stdio.h> int main(void) { int tulo = 1; /* alustus tulo 1, kertolaskun takia */ int luku = 0; /* alustetaan luku */ /* toistetaan silmukkaan niin kauan kuin tulo on alle 1000 */ while(tulo < 1000) { /* luetaan käyttäjän syöttämä kokonaisluku */ printf("anna kokonaisluku > "); scanf("%d", &luku); } /* lasketaan uusi tulon arvo */ tulo = tulo * luku; /* tulostetaan tulon arvo lopuksi */ printf("tulo on: %d\n\n", tulo); return(0); } 168
169
for -toistorakenne Toistorakenteen toiminnassa olennaista on ohjausmuuttujan rooli. Esimerkeissä ohjausmuuttujia laskuri ja tulo. Toistorakenteen ohjausmuuttujan alustus, toistoehto ja ohjausmuuttujan päivitys (arvon muuttaminen) 170
for -toistorakenne sisältää ennaltamäärätyt paikat näille komponenteille: Yleinen muoto: for(alustus; toistoehto; päivitys) lause; 171
1. Aluksi suoritetaan alustuksen lause 2. Testataan toistoehto 2.1 Jos toistoehto on tosi niin suoritetaan runko-osan lause ja päivityksen lause 3. Tämän jälkeen jatketaan testaamalla toistoehtoa uudestaan ja suorittamalla runko-osan lause ja päivitys uudestaan niin kauan kuin toistoehto on tosi. 4. Kun toistoehto tulee epätodeksi poistutaan silmukasta ja toiminta jatkuu for -rakennetta seuraavasta lauseesta. 172
Esimerkki: Versio a-kirjaimia tulostavasta ohjelmasta toteutettuna for -toistorakenteella #include <stdio.h> int main(void) { int n; /* haluttu merkkien lukumäärä */ int laskuri; /* laskee montako a-kirjainta on tulostettu */ /* pyydetään käyttäjältä a-kirjainten lukumäärä */ printf("anna tulostettavien a-kirjainten lukumäärä >"); scanf("%d", &n); 173
/* käynnistetään toistorakenne, joka tulostaa a-kirjaimia toistetaan kunnes ehto laskuri < n muuttuu epätodeksi */ for( laskuri = 0; laskuri < n; laskuri = laskuri + 1){ printf("a"); } printf("\n\n"); /* rivinvaihtoja loppuun */ return(0); } 174
175
Sisäkkäiset rakenteet Useissa ohjelmointitehtävissä ohjausrakenteita täytyy sijoittaa sisäkkäin, silmukka silmukassa jne. Esimerkki: Ohjelma, joka laskee nollasta poikkeavien käyttäjän antamien reaalilukujen tulon. Tulon laskennasta eli silmukasta poistutaan, kun käyttäjä antaa lopetusmerkin (-999.9). #include <stdio.h> #define LOPETUS -999.9 int main(void) { double tulo = 1; /* alustetaan tulo */ double luku; 176
printf("anna reaaliluku (%lf lopettaa) > ", LOPETUS); for(scanf("%lf", &luku); luku!= LOPETUS; scanf("%lf", &luku)){ } /* lasketaan uusi tulon arvo, nollalla ei kertolaskua */ if( 0!= luku ) tulo = tulo * luku; /* luetaan käyttäjän syöttämä kokonaisluku */ printf("anna reaaliluku (%lf lopettaa) > ", LOPETUS); /* tulostetaan tulon arvo lopuksi */ printf("tulo on: %d\n\n", tulo); return(0); } 177
178
Edellä olevassa ohjelmassa ei tiedetty etukäteen montako kertaa silmukka suoritetaan -> pyydetään käyttäjältä lopetusmerkki. Silmukan suoritus päättyy kun käyttäjä syöttää näppäimistöltä lopetusmerkin (edellisessä tehtävässä luvun merkin -999.9). Yleinen muoto: Lue sisään rivi Niin kauan kuin ei lueta lopetusmerkkiä Käsittele rivi Lue sisään seuraava rivi Seuraava ohjelma laskee positiivisten kokonaislukujen summaa. 179
#include <stdio.h> #define LOPETUS 0 int main(void) { int summa = 0; int luku; printf("anna kokonaisluku (%d lopettaa) > ", LOPETUS); scanf("%d", &luku); while(luku!= LOPETUS){ summa = summa + luku; } printf("anna kokonaisluku (%d lopettaa) > ", LOPETUS); scanf("%d", &luku); } /* tulostetaan summan arvo lopuksi */ printf("summa on: %d\n\n", summa); return(0); 180
Seuraavilla sivuilla esimerkkiohjelmat tulostavat kertotaulu-matriisin käyttäen eri toistorakenteita. 181
#include <stdio.h> int main(void) { int i,j; for(i=1; i <= 10; i=i+1){ for(j=1; j <= 10; j=j+1){ printf("%4d", (i * j)); } printf("\n"); } printf("\n\n"); } return(0); 182
183
#include <stdio.h> int main(void) { int i,j; i = 1; while( i <= 10 ){ j=1; while( j <= 10 ){ printf("%4d", (i * j)); j = j+1; } } i= i+1; printf("\n"); } printf("\n\n"); return(0); 184
185
#include <stdio.h> int main(void) { int i,j; i = 1; do { j=1; do{ printf("%4d", (i * j)); j = j+1; }while( j <= 10 ); i= i+1; printf("\n"); } }while( i <= 10 ); printf("\n\n"); return(0); 186
187
#include <stdio.h> int main(void) { int i,j; for(i = 0; i < 4; i = i+1){ printf("i: %d\n", i); } for(j=0; j < i; j = j+1){ printf(" j: %d\n", j); } } return(0); 188
189