Osa 2 - Ohjausrakenteita
Normaalisti C++:ssa kuten muissakin ohjelmointikielissä, ohjelma suoritetaan alusta loppuun lausekkeiden mukaisessa järjestyksessä. Tätä kutsutaan niin sanotuksi peräkkäissuorittamiseksi (Sequential). Lausekkeita suoritetaan niin kauan kuin niitä riittää, tämän jälkeen ohjelman suorittaminen loppuu.
Harvat ohjelmat ovat hyödyllisiä jos ne suorittavat samat lausekkeet aina jokaisella kerralla. Tai vähintäänkin hyödyllisemmät ohjelmat mukautuvat suoritusympräistön ja käyttäjän toimien mukaisesti.
Tämän toteuttamiseen vaaditaan ohjausrakenteita, jotta voidaan esimerkiksi mukauttaa toimintaa riippuen siitä mitä nappia käyttäjä painaa tai sisältääkö avattu tiedosto tiettyjä sanoja.
Ohjelmoinnissa voidaan käyttää kahdenlaisia ohjausrakenteita:
- Ehtorakenteet (Conditionals)
- Silmukkarakenteet (Loop)
Kuva 1. C++ ohjausrakenteita (Wikitechy), lähde: https://www.wikitechy.com/tutorials/c++/c++-flow-control
Ehtorakenteet
Jotta ohjelma voi tarpeen mukaan haarautua ja näin ollen suorittaa erilaisia peräkkäisiä lauseita, tarvitaan ehtorakenne. Ehtorakenne perustuu syötteeseen, jonka arvoa validoitaan ja validoinnin perusteella edetään eri haaroihin (tai jätetään haarautuminen suorittamatta). C++:ssa haarautumiseen on käytössä kaksi ehtorakennetta:
- If -rakenne
- switch-case -rakenne
Ehtojen testaaminen
Ehtojen totuusarvojen testaamisessa käytetään kahden luokan operaattoreita, loogiset sekä relationaaliset. Näiden operaattoreiden tuottama tulos on aina joko true tai false
Relaatio-operaatiot kuvaavat kahden arvon suhdetta toisiinsa, ne ovat esitettynä seuraavassa taulukossa.
Operaattori | Tarkoitus | Esimerkki (tuottaa true jos x = 5) |
---|---|---|
> | Suurempi kuin | x > 5 |
>= | Suurempi tai yhtäsuuri | x => 5 |
< | Pienempi kuin | x < 6 |
<= | Pienempi tai yhtäsuuri | x <=5 |
== | Yhtäsuuri | x == 5 |
!= | Erisuuri | x != 6 |
Edellä kuvatut operaattorit toimivat kuten aritmeettiset operaatiot, mutta tuottavat siis true/false, näitä lauseita kutsutaankin totuuslauseiksi (Boolean expression).
Loogisia operaatioita voidaan yhdistää relaatio-operaatioiden kautta. Näin voidaan muodostaa entistä monimutkaisempia totuuslauseita. Reelaatiooperaatioita voidaan ryhmitellä sulkeiden avulla, jotta saadaan haluttu suoritusjärjestys. HUOM! Molempien relaatio ja loogisten operaattoreiden suoritusjärjestys on vasemmalta oikealle.
Operaattori | Tarkoitus | Esimerkki |
---|---|---|
&& | JA / AND | x > 5 && y > 5 |
|| | TAI / OR | x > 5 || y > 5 |
! | EI / NOT | !x |
Seuraavassa taulukossa on havainnollistettu loogiste operaatioiden tuottamia arvoja
a | b | a && b |
---|---|---|
true | true | true |
false | true | false |
true | false | false |
false | false | false |
a | b | a || b |
---|---|---|
true | true | true |
false | true | true |
true | false | true |
false | false | false |
a | !a |
---|---|
true | false |
false | true |
Muutama esimerkki loogisten operaatioiden ja relaatio-operaatioiden käytöstä
int x = 10;
int y = 5;
!(x > 5) → false
(x > y) && (y > 0) → true
(x < y) && (y > 0) → false
(x < y) || (y > 0) → true
Edellisissä lauseissa voitaisiin käyttää myös suoreen Boolean (bool) tyyppisiä muuttujia, koska ne sisältävät true tai false arvon. Toisaalta C++:ssa mikä tahansa muuttuja toimii totuusarvoja, sillä C++:ssa arvo 0 = false ja nollasta eroava arvo on true. Tätä on havainnollistettu seuraavassa
bool totuus_arvo = false;
(0) → false
(2) → true
(-2) → true
(1000) → true
if, if-else and else if lauseet
if rakenne
if -lause rakentuu seuraavasti.
if(ehto)
{
lause;
lause;
}
If-rakenne voidaan kuvata seuraavasti:
Kuva 1. If-rakenne (https://www.javascripttutorial.net)
Ehtona toimii lause, joka tuottaa arvon true tai false. Jos ehto on true if -lohkon sisällä olevat lauseet suoritetaan. Muuten if -lohkon osuus ohitetaan. Jos if -lohkossa on vain yksi lause se voidaan kirjoittaa myös muotoon, jossa { } -merkit jätetään pois.
if(ehto)
lause;
If-else lause taas kirjoitetaan muotoon.
if(ehto)
{
lauseA1;
lauseA2;
}
else
{
lauseB1;
lauseB2;
}
if-else rakenne
If-else -rakenne voidaan kuvata seuraavasti:
Kuva 2. If-else rakenne (https://www.javascripttutorial.net)
Jos ehto on tosi suoritetaan if-lohkon sisällä olevat lauseet A1 ja A2, jne… , jos taas ehto on epätosi suoriteaan lauseet B1 ja B2, jne… Jälleen jos jommassakummassa lohkossa on vain yksi lause voidaan if-else kirjoittaa muotoon.
if(ehto)
lauseA;
else
lauseB;
Esimerkki if-else rakenteesta
int main() {
int x = 6;
int y = 2;
if(x == y)
cout << “x ja y ovat yhtäsuuret\n”;
else
cout << “x ja y ovat eri suuret\n”;
}
if-else if-else rakenne
If-else ehtorakenteessa valitaan siis suoritettava lohko yhden ehdon mukaisesti. Laajennuksen if-else lohkoon tuo if-else if-else rakenne, jolloin voidaan testata useita ehtoja.
if(ehtoA)
{
lauseA1;
lauseA2;
}
else if(ehtoB)
{
lauseB1;
lauseB2;
}
else if(ehtoC)
{
lauseC1;
lauseC2;
}
else
{
lauseD1;
lauseD2;
}
Tässä rakenteessa suoritetaan se ehto joka saa ensimmäisenä (ylhäältä-alas) arvon tosi. Jos mikään ehdoista ei täyty suoritetaan else-lohko. Rankenteessa ei ole pakollista olla else-lohkoa.
Esimerkki if-else if-else rakenteesta
int main() {
int x = 6;
int y = 2;
if(x > y)
cout << “x on suurempi kuin y\n”;
else if(y > x)
cout << “y suurempi kuin x\n”;
else
cout << “x ja y ovat yhtäsuuret\n”;
}
switch-case lause
switch-case rakenne on toinen C++:ssan ehtorakenne, myös tässä ehtorakenteessa on mahdollista, että lauseita suoritetaan tai rakenne ohittaa lauseiden suorittamisen kokonaan.
switch(lause)
{
case vakio_arvo_1 :
lauseA1;
lauseA2;
...
break;
case vvakio_arvo_2 :
lauseB1;
lauseB2;
...
break;
...
default:
lauseC1;
lauseC2;
...
}
switch-case voidaan havainnollistaa seuraavasti.
Kuva 3. switch-case rakenne (https://www.geeksforgeeks.org/)
Switch lauseke laskee arvon lauseelle, joka on sulkeiden sisällä ja suorittaa lauseen arvoa vastaavan lohkon, valiten sen case-lohkoista. Jos lauseen arvo on vakio_arvo_1 suoritetaan ensimmäinen lohko, lauseet A1,A2,jne… Suorittaminen jatkuu break-lausekkeesen asti, jos break-lauseketta ei ole jatkuu arvojen testaaminen. Jos mikään lohko ei vastaa lauseen arvoa suoritetaan default-lohko. Case-lohkoja ei tarvitse laittaa kaarisulkeiden sisään.
Switch-case rakenteessa on mahdollista myös yhdistellä case-lauseita, niin että yksi tai useampi lause jakaa samat suoritettavat lauseet.
Seuraavassa esimerkki switch-case rakenteesta:
int main()
{
int x = 6;
switch(x)
{
case 1:
cout << “x is 1\n”;
break;
case 2:
case 3:
cout << "x on 2 tai 3";
break;
default:
cout << "x ei ole 1, 2, eikä 3";
}
return 0;
}
HUOM! Hyvä käytäntö on käyttää #define määrittely kun määritellään switch case lauseenehtoja, näin päästään eroon ns. taikanumeroista ja koodista tulee luettavampi
#define AUDI 1
#define BMW 2
#define CITROEN 3
#define EMPTY 0
int main()
{
int automalli = EMPTY;
switch(automalli)
{
case AUDI:
cout << "Valitsit AUDI"
break;
default:
cout << "Et valinnut autoa";
}
return 0;
}
Silmukkarakenteet
Ehtorakenteet suorittavat lausekkeita, jos jonkin ehto täyttyy. Silmukkarakenteet taas suorittavat lausekkeita kunnes jokin ehto ei täyty. C++:ssa on kolme eri silmukkarakennetta: while, do-while, ja for.
while ja do-while
while-loopin syntaksi on lähellä if-lauseen syntaksia.
while(ehto)
{
lause1
lause2
…
}
while-silmukka voidaan havainnollistaa seuraavasti.
Kuva 4. while -rakenne (http://www.brentwoodhigh.com/)
while-silmukassa olevia lausekkeita suoritetaan siis niin kauan kuin ehto täyttyy. while-silmukan kohdalla on mahdollista, että lohkon sisällä olevia lauseita ei suoriteta kertaakaan. HUOM! jos while silmukan sisällä on vain yksi lause, sitä ei tarvitse laittaa kaarisulkeiden sisään.
Esimerkki while-silmukasta
int x = 0;
int y = 1;
while(x < 0 && y < 10)
{
x++;
y *= x;
}
cout << "Lopullinen x:" << x " ja y:" << y << endl;
do-while -silmukalla voidaan varmistaa, että silmukassa olevat lausekkeet suoritetaan ainakin kerran. Sen syntaksi on seuraava:
do
{
statement1
statement2
…
}
while(ehto);
do while -silmukka voidaan havainnollistaa seuraavasti.
Kuva 5. do while -rakenne (http://www.brentwoodhigh.com/)
Esimerkki do while-silmukasta
int x = 0;
do
{
cout << x << “\n”;
x++;
}while(x < 10)
cout << "Lopullinen x:" << x << endl;
!HUOM, muuttujan korotus sijoitetaan hyvien koodauskäytänteiden mukaisesti viimeiselle silmukan riville.
for
for -silmukan syntaksi on lähellä while -loopin syntaksia.
for(alustukset; ehto; arvojen muunto)
{
statement1
statement2
…
}
for -silmukalle annetaan kolme lauseketta (näistä jokainen voi olla myös tyhjä). Ensimmäiseen osioon, alustukset, voidaan kirjoittaa lausekkeet jotka alustavat silmukassa ja sen ehdoissa käytettävät muuttujat. Toiseen osioon, ehto, tulee vertailuehto kuten while -silmukassa. Kolmanteen osioon, arvojen muunto, tulee korotus- tai supistamislauseke. HUOM! jos for silmukan sisällä on vain yksi lause, sitä ei tarvitse laittaa kaarisulkeiden sisään.
for -silmukka voidaan havainnollistaa seuraavasti.
Kuva 6. for -rakenne (http://www.brentwoodhigh.com/)
Yksinkertainen for silmukka voisi olla seuraava.
for(int x = 0; x < 10; x = x + 1)
{
cout << x << “\n”;
}
Jos muuttujan x korotus halutaan tehdä for -silmukan sisällä voidaan kirjoittaa seuraavasti
for(int x = 0; x < 10;)
{
cout << x << “\n”;
x = x + 1
}
Taas jos muuttuja onkin alustettu jo ennen for silmukkaan voidaan kirjoittaa seuraavasti
int x = 0;
for(;x < 10; x = x + 1)
{
cout << x << “\n”;
}
Lisämateriaalia
Kontrollirakenteiden sisäkkäisyys
Kontrollirakenteita voidaan laittaa C++:ssa myös sisäkkäin esimerkiksi seuraavasti.
int main()
{
for(int x = 0; x < 10; x = x + 1)
{
for(int y = 0; y < 10; y = y + 1)
cout << "X:" << x << " Y:" << y << “\n”;
}
return 0;
}
Ternary operator (?:)
?: on erikoisoperaattori joka ottaa kolme parameteria. Sen syntaksi on seuraava:
ehto ? arvo jos tosi : arvo jos epätosi
?: operaattorilla voidaan if-else lause kirjoittaa yhdelle riville.
Seuraava if-else lause:
if(a > b)
tulos = x;
else
tulos = y;
voidan kirjoittaa
tulos = a > b ? x : y;
break;
break -lausekeella voidaan poistua mistä tahansa ehto- tai silmukkarakenteesta ulos.
Tätä voidaan käyttää yksinkertaistamaan koodia, jos esimerkiksi toteutamme ikuisen silmukan while -silmukalle. Mutta haluammekin poistua silmukasta tietyllä arvolla.
int i = 1;
while ( true )
{
if(i > 10)
break;
cout << i << "\n";
i++;
}
break -lauseketta kannattaa käyttää silloin, jos sillä voidaan yksinkertaistaa silmukkaa tai tehdä koodista luettavampaa. Muunmuassa for -silmukoita kirjoitettaessa break -lausekkeella saadaan luotua silmukan sisälle useita poistumiskohtia.
continue;
continue -lauseke on sukua break -lausekkeelle, continue -lausekkeen kohdalla siirrytään suoraan silmukkarakenteen seuraavalle kierrokselle ja ohitetaan jäljellä olevat lausekkeet.
Esimerkiksi jos haluamme tulostaa parilliset luvut.
for(int i = 0; i <= 10; ++i)
{
if(i % 2 != 0)
continue ;
cout << i << "\n"
}