Pikkuaskelin C-Ohjelmoijaksi
Tässä kurssissa käydään C-kielen perusasioita läpi ja opetellaan ihan sitä perusohjelmointia. Kurssi on jo aika vanha ja löytyy myös Jarkko Laineen ohjelmointikoulusta, joten se voi olla joillekin tuttu. Jos C-kieltä et vielä tunne, kannattaa tutustua tähän kurssiin.
11.3.1999 julkaistun artikkelin on kirjoittanut jalaine.
Olet nyt siis päättänyt alkaa ohjelmoimaan. Sinua kiinnostavat pelit ja se miten ne tehdään. Haluat päästä esiriipun taakse tai ainakin raottaa sitä nähdäksesi mitä nuo suuret ohjelmoijat oikeastaan tekevät (onko se jotain yliluonnollista? ;-). Siispä toivotan sinut tervetulleeksi tähän suureen seikkailuun, jolle rajat asettaa vain mielikuvituksesi (...ja koneiden tehot, taidot, taiteellisuus... :-). Älä pelkää, jos ongelmia tulee vastaan, ovat ihmiset sitä varten, että he auttavat toisiaan. Silloin vain rohkeasti kirjoitat vaikka minulle osoitteeseen: jarkko.laine@suomipelit.com tai jonnekin muualle kuten DJGPP:n newsgrouppiin tai Suomipelit.com:in keskustelualueelle.
Nyt kuitenkin asiaan (siis siihen mitä otsikossa lukee)... Miksi ohjelmoisit C:llä? Se on sellainen kysymys, johon ei aivan selvää vastausta löydy, mutta melko hyvin voin sen luultavasti selittää. Ensinnäkin, kaikki nykyaikaiset pelit tehdään C:llä (tai C++:lla, joka on C:n sukuinen ja sen kanssa yhteensopiva kieli). Toiseksi, C-kieli on sellainen, että se tuntuu oikealta ohjelmointikieleltä (toisin kuin Pascal tai Basic), ainakin minusta C:n ohjelmointityyli tuntuu hyvältä. Joo. C on yksinkertaisesti paras, ainakin jos aiot päästä pitkälle. Pascalilla pääsee alkuun, mutta sitten on kuitenkin vaihdettava C:hen, kun alkaa tosissaan ohjelmoida. Suosittelen aloittamaan suoraan C:stä, se on hauskaa eikä ollenkaan niin vaikeaa kuin yleensä annetaan ymmärtää!
DJGPP on DJ Delorien ja kumppaneiden ilmainen C-kääntäjä. Se siis tarkoittaa, että sinun ei tarvitse maksaa satoja tai tuhansia markkoja saadaksesi hyvän kääntäjän. Sellainen on saatavilla aivan ilmaiseksi. Jopa Quake on tehty DJGPP:llä. Kaiken lisäksi DJGPP on erittäin tehokas ja hyvä kääntäjä ja sillä on vankka ja yhtenäinen käyttäjäkunta ympäri maailmaa, joten tietoa ja apua ei ole vaikea löytää. Ainut puute on se, että DJGPP ei VIELÄ osaa tehdä Windows-ohjelmia, mutta sehän ei meitä haittaa. Ne kun muutenkin ovat niin hankalia. DJGPP-linkkejä ja muuta tietoa löytyy täältä ja muualta netistä. Pysy mukana!
Ensimmäinen ohjelma #
Olemme nyt jo niin pitkällä, että pääsemme kopeloimaan konetta (nopeasti - eikö?), teemme ensimmäisen C-ohjelman ja käännämme sen.
Kautta aikojen ovat C-ohjelmoijat (myös ne jotka nykyään tekevät Quakeja sun muita huippupelejä) aloittaneet uransa seuraavan ohjelman eri variaatioilla, joista luultavasti tämä perusversio on tavallisin. Eli, tässä on koodi, selitän sen myöhemmin.
/* Ensimmäinen C-ohjelmani */
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
}
Nyt kirjoita tuo teksti juuri niin kuin se tuossa on (/* ... */- rivin voi jättää pois, se on vain huomautus, eikä kääntäjä välitä siitä). Kirjoittaminen onnistuu aivan tavallisella teksturillakin (esim. DOS:in EDIT), mutta suosittelen Rhideä, joka tekee ohjelmoinnin paljon mukavammaksi. Rhide on Robert Höhnen tekemä (ilmainen) kehitysympäristö DJGPP:lle. Se näyttää käskyt ja numerot yms. eri väreillä ja hoitaa kääntäjän käsittelyn puolestasi. Tallenna sitten ohjelma nimellä hello.c ja käännä se joko komentoriviltä: gcc hello.c -o hello.exe tai Rhidessä: RUN/RUN (valikkoriviltä).
Ja nyt muutama sana tuosta ohjelmasta. Siitä näkyvät C:n perusrakenteet aika hyvin, ei ihme, että siitä lähdetään useimmiten liikkeelle. Äsken mainitsinkin jo ensimmäisellä rivillä olevista /* ... */-merkeistä. Ne ovat siis huomautusmerkkejä, jotka kertovat koneelle, että niiden välissä olevaa tekstiä ei pidä kääntää. Niiden väliin voi siis kirjoittaa tietoa muille ohjelmoijille tai itselle muistettavaksi. Se on muuten ihmeellistä kuinka sitä itsekin viikon päästä on aika ulkona, jos ei ole kunnolla selittänyt mitä missäkin tapahtuu. Ei yksinkertaisesti vain muista mitä päässä on sillä hetkellä liikkunut.
Seuraavana onkin include-rivi. Niitä voi ohjelmassa olla enemmänkin, mutta ne ovat aina juuri tuossa ohjelman alussa. C-kieli itsessään ei sisällä paljonkaan erilaisia käskyjä, minkä takia on olemassa standardikirjastot, joissa on eri tyyppisiä käskyjä. Ja kirjastoja käytettäessä täytyy niiden headerit eli otsikkotiedostot liittää ohjelmakoodiin include-lauseilla. Yksi tällainen headeri on stdio.h, joka sisältää tiedon ja tiedostojen käsittelyyn liittyviä funktioiden prototyyppejä (tässä vaiheessa riittää kun ajattelet niitä käskyinä). Elikä, sinun täytyy siis aina ohjelman alussa valita mitä headereita otat mukaan (mitä kirjastoja käytät, vaikkakaan kaikki headerit eivät liity mihinkään kirjastoon) sen mukaan mitä käskyjä haluat käyttää. Voit tietenkin lisätä aina uudentyyppisen käskyn tullessa mukaan sen vaatiman headerin. Headerit liitetään ohjelmaan siis aina #include-lauseella vertailumerkkien <;> välissä (jos se on include-hakemistossa, esim. C:\DJGPP\INCLUDE) tai lainausmerkkejen välissä (" "), jos se on samassa hakemistossa kuin muu lähdekoodi.
int main(void) aloittaa pääohjelman, jollainen täytyy aina C-ohjelmassa olla. Se on toiminnan kannalta korkeimman tason ohjailija, päämies. Se kertoo missä järjestyksessä ohjelma toimii. Myöhemmin kerron myös muista funktioista (aliohjelmista), mutta nyt riittää, että ymmärrät pääohjelman merkityksen (pakollinen, ohjaa toimintaa) ja osaat sellaisen kirjoittaa. Siis... intin tilalla voisi olla myös void tai char (tai unsigned char jne..), mutta ANSI:n standardin mukaan main-funktion täytyy olla muotoa int main(...). Funktion esittelylauseen (int main(void)) jälkeen tulee itse funktio, joka alkaa AINA {-merkillä ja loppuu AINA }-merkkiin. Ja sisennyksiä kannattaa käyttää samalla tavalla kuin mallissa, koska ne selkeyttävät ohjelmaa paljon. Myös tyhjää väliä voi ihan vapaasti laittaa koodia selkeyttämään kunhan muuttujat tai käskyt (yms.) pysyvät kokonaisina.
Ja vielä on yksi rivi. printf("Hello World!\n"). Siitä nyt vähän selitystä: printf() itsessään on standardifunktio, jonka käyttämiseen tarvitset otsikkotiedoston stdio.h (siis erittäin tavallinen käsky), joka tulostaa tekstiä ruudulle. "Hello World!\n" on tämä teksti. Mutta \n ei ohjelmaa ajettaessa näy ruudulla, eihän? Se on nimittäin rivinvaihtomerkki. Sen voit lisätä mihin tahansa väliin, mutta muista laittaa se lainausmerkkien väliin.
Erittäin tärkeä on myös lauseen lopussa oleva puolipiste. Jokainen C-kielinen lause lukuunottamatta niitä, jotka esimerkissä ovat puolipisteettömiä, päättyy puolipisteeseen. No niin, nyt olet jo ohjelmoija, osaat tehdä ohjelman, joka kirjoittaa tekstiä ruudulle. Jos et aivan kaikkea tajunnut, kokeilepa muuttaa ohjelmaa ja katso mitä tapahtuu. Kokeile vaikka muuttaa se sellaiseksi, että se kehuu sinua (muistaakseni itse kirjoitutin koneella: "Jarkko Laine, maailman paras ohjelmoija" ;-). Hauskaa, eikö vain.
Muuttujat #
Muuttujat ovat ikään kuin pieniä (tai suuria) lokeroita, joihin voi säilöä tietoa ja myöhemmin sen sitten sieltä katsoa. (Näin minulle ainakin joskus asiaa selitettiin...). Käytännössä erittäin suuri osa ohjelmoinnista perustuu muuttujiin. On tärkeää pystyä muuttamaan lukuja ja merkkijonoja kesken ohjelman suorituksen. Eihän esimerkiksi ohjelmasta, joka laskee aina vain yhden ja saman laskun ole paljon hyötyä. Tehdäänpä muuten sellainen. Jos haluat kokeilla
taitojasi, tee se itse ensin katsomatta tästä.
#include <stdio.h>
int main(void)
{
printf("1 + 1 = %d", 1 + 1);
}
Tällaisesta ohjelmasta ei kuitenkaan ole paljon hyötyä. Tosin esimerkkinä se kylläkin on vallan mainio, tuossakin tuli esille semmoinen tärkeä asia, jota en vielä ole selittänyt. Selitän sen nyt. Printf-lauseeseen voi sijoittaa (muuttuvaa) tietoa myös %d -merkillä. Näitä "prosenttimerkkejä" on muitakin, mutta niistä lisää myöhemmin. Tässä vaiheessa riittää, kun tiedät, että %d tarkoittaa, että sen paikalle laitetaan jokin kokonaisluku (1,2,5,-1...). Lainausmerkkien jälkeen pilkulla erottaen kirjoitetaan tieto, joka %d:n tilalle halutaan. Tässä tapauksessa se on 1+1, mutta useimmiten joku muuttuja. Naitä %d:itä voi olla myös enemmän lauseen sisällä (printf("luku: %d, toinen luku %d", 1, 45);).Tämä tulee vielä monta kertaa esille, joten älä huoli jos et tätä täysin vielä käsittänyt.
Muuttujilla siis tarkoitetaan lokeroa, jolla on kuvaava nimi, esim. pisteet. Tällainen muuttuja voisi pelissä pitää sisällään tietoa pelaajan pisteistä. Jos pelaajia on kaksi, tarvitaan taulukkoa, mutta unohda se vielä. Muuttujalla siis täytyy olla nimi, mutta se ei vielä riitä; sillä on myös oltava tyyppi sen mukaan millaista tietoa säilömään sitä käytetään:
[DEFTABLE]
[D]int[/D]
[E]kokonaislukumuuttuja, ei murto- eikä desimaalilukuja[/E]
[D]char[/D]
[E]merkkimuuttuja, voi sisältää yhden merkin (esim 'a')[/E]
[D]float[/D]
[E]liukuluku, jos tarvitset desimaalilukuja, tähän niitä saa [/E]
[D]double[/D]
[E]kaksoistarkkuuden liukuluku, floatti isommalla arvoalueella [/E]
[/DEFTABLE]
Siinä niistä oli perustyypit, joita tulet varmasti tarvitsemaan. Näistä saa sitten erilaisia muunnelmia: esim. unsigned-määritys tekee inteistä ja chareista etumerkittömiä. Luultavasti et näistä tyypeistä kovinkaan monta tarvitse. Int on varmasti yleisin, käytä vain intiä aina kun se on mahdollista. Muitakin toki saa käyttää (enhän minä sitä voi kieltääkään :-). Charia käytät tietenkin sitten merkkitiedon käsittelyyn, esimerkiksi kirjaimiin (tai merkkijonoihin, taulukkoa käyttäen).
Muuttujan tyypin valinta siis riippuu siitä mitä muuttujaan haluaa tallentaa ja ohjelman luonteesta. Hyötyohjelmassa, jossa nopeus ei ole niin tärkeää, voit huoletta käyttää vaikka long doublea jos haluttaa, mutta peleissä nopeus on valttia ja int täten paras vaihtoehto. Merkit tietysti chariin. OK, vielä pari sanaa muuttujien nimistä.
Voisi luulla, että kaikki nimet käyvät, mutta valitettavasti asia ei aivan niin ole, on olemassa muutama (pieni) sääntö. Tunnus (=nimi) ei saa alkaa numerolla ja se saa sisältää kirjaimia (a-z ja A-Z) ja alaviivoja (_) sekä numeroita. Tunnuksen pituus saa olla enintään 32 merkkiä. Nimien kannattaa olla mahdollisimman selkeitä, ei mitään yksikirjaimisia salakoodeja. Skandinaaviset merkit eivät sovi muuttujien nimiin. Ja vielä yksi tärkeä huomautus: isot ja pienet kirjaimet ovat eri asia, eli Kissa on eri muuttuja kuin kissa tai kissA. Näistä ei sitten muuta. Seuraavaksi sijoitusoperaattorit.
Jos tämä teksti on jotenkin sekavaa, se johtuu vain siitä, että yritän tätä kirjoittaessani samalla epätoivoisesti asentaa Internet Explorer nelosta, mikä tahtoo häiritä tähän keskittymistä. Älä välitä, korvaan menetyksen jossain muussa luvussa, sopiiko? ... Nyt se lataa, katotaan miten käy. Olen yrittänyt tätä hieman jälkeenpäin selventää (korjata) - toivottavasti onnistuin edes jotenkin :)
Okei, takaisin asiaan. C:ssä on sijoitusoperaattori '=', suomeksi sanottuna: kun haluat laittaa vaikka kokonaislukumuuttujaan numero (int) luvun 10, kirjoitat: numero=10; (ja muistathan puolipisteen!) No niin, nyt pieni tehtävä.
Tehtävä
Mitkä seuraavista kelpaavat muuttujan nimeksi C-kielessä, miksi?
- summa100
- valon_nopeus
- 22_Pistepirkko
- Päiväys
- _xyz
- vasta-aine
Oikea vastaus
summa100, valon_nopeus, _xyz
Eihän ollut vaikeaa? Ja vaikka tehtävä olisi mennytkin väärin niin siitä ainakin opit tuon asian. Eteenpäin!
Varmuuden vuoksi olisi varmaan hyvä katsoa vielä yksi esimerkki ennen kuin mennään eteenpäin. Alussa tekemämme laskuohjelma muuttujia käyttäen, var så god!
#include <stdio.h>
int main( void )
{
int vastaus; // esitellään muuttuja
vastaus = 1 + 1;
printf(" 1 + 1 = %d ",vastaus);
}
Jos ymmärsit tuon, olet ymmärtänyt muuttujien syvimmän olemuksen! Onneksi olkoon!
Ehtolauseilla muuttujiin eloa #
Muuttujistakaan ei yksinään ole paljon hyötyä. Jotta niistä saataisiin kaikki mahdollinen irti, tarvitaan monenlaisia juttuja, joista yksi on nämä ehtolauseet. Ehtolauseita käytetään tutkimaan, onko jokin asia jonkinlainen, eli katsomaan onko muuttujan arvo haluttu ja sitten reagoimaan sen mukaan. Se Explooreri ei ole muuten vieläkään alkanut kunnolla latautua :(.. Joo, tässä on ensin ehtolause-esimerkki paljaalla suomen kielellä:
jos ikä on suurempi kuin 18 niin saa ajaa autoa
Ja nyt C:llä:
if (ika > 18) saa_ajaa_autoa();
Huomaatko kuinka C on kaunista, tiivistä ja selkeää? Ensin sana if, joka vastaa suomen sanaa jos, sitten suluissa ehto (ikä on suurempi kuin 18). Huomaa, että C-kielessä ei voi käyttää ääkkösiä, joten ä:stä tulee a. Ja ehdon jälkeen tulee seuraus, joka jos se on vain yksi lause, voidaan kirjoittaa suoraan ehdon perään. Vielä pieni huomio tuosta saa_ajaa_autoa()-lauseesta. Se on funktiokutsu eli sinun ei vielä siitä tarvitse tietää paljon mitään. Kerrottakoon kuitenkin, koska sen kaltaisia lauseita käytetään tässä paljon, että funktion nimen tulee olla kaikkien samojen sääntöjen mukainen kuin muuttujanimien (ei ää- tai öö-kirjaimia, ei välilyöntejä yms.) ja sulut kuuluvat tuohon perään. Usein sulkujen sisään laitetaan muuttujia, nyt niitä ei kuitenkaan tarvita. Jos ehtolauseen seuraus on pitempi, täytyy se koota {}-merkkien väliin ja sisentää kauniisti:
if(a!=10){
printf("a ei ole 10\n");
}
Siinä oli siis toinen ehtolause-esimerkki, josta näemme, että vertailumerkkejä on monia erilaisia, yksi niistä on tuo !=. Tässä on sellainen luettelo, jossa näet kaikki eri vertailuoperaattorit:
[DEFTABLE]
[D]>[/D]
[E]suurempi kuin[/E]
[D]<[/D]
[E]pienempi kuin[/E]
[D]>=[/D]
[E]suurempi tai yhtäsuuri kuin[/E]
[D]<=[/D]
[E]pienempi tai yhtäsuuri kuin[/E]
[D]==[/D]
[E]yhtäsuuri kuin[/E]
[D]!=[/D]
[E]erisuuri kuin[/E]
[/DEFTABLE]
Siinä tuli näitä vertailuoperaattoreita. Tarkemmat selitykset kaikista operaattoreista löytyy sieltä liitteistä. Mutta hei! Ei se vertailu siihen vielä loppunutkaan, onhan vielä else! Elikä nyt vielä se autonajoehtolause uudestaan:
jos ikä on suurempi kuin 18 niin saa ajaa autoa mutta jos ei niin ei saa ajaa autoa
Siis uuttahan on tuo jos ei-juttu. Nyt C:llä
if ( ika > 18 ) saa_ajaa_autoa(); else ei_saa_ajaa_autoa();
Se on näin yksinkertaista! Jos if-lauseen ehto ei toteudu (jos ika EI ole suurempi kuin 18), toimitaan else-lauseen mukaan (ei saa ajaa autoa). Tässä siis oli perusehtolause if...then..else. Ja sitten case-lause, jota myöskin saattaa silloin tällöin tarvita.
Ajatellaanpa taas ensin äidinkielellämme:
Tutkitaan ruokalistaa
jos makkarakeitto: syödään
jos perunamuusi: kaadetaan maitoa sekaan
jos maito: juodaan
jos paperi: ei syödä
Aika tyhjänpäiväinen ruokalistantutkiminen, mutta toivon mukaan se selittää jotenkin tätä asiaa. Siis asiahan on niin, että case-lause (jota tuokin esittää) on vertailulause, joka sisältää eri vaihtoehdot muuttujan eri arvoille. Nyt C:llä
switch ( ruokalista ){
case makkarakeitto: syodaan();
break;
case perunamuusi: maitoa_sekaan();
break;
case maito: juodaan();
break;
case paperi: ei_syodak();
default: break;
}
Niin. Case-lause alkaa jostain syystä switchillä (kaipa sitä switch-lauseeksikin voisi sanoa :-), jonka perään laitetaan sulkuihin tutkittavan muuttujan nimi. Case-kohdissa on sitten muuttujan arvo, jolla tapahtuu jotain ja kaksoispisteen jälkeen toiminto. "Mihin tätä?", sinä kysyt. Esimerkiksi valikoissa, joissa on erilaisia vaihtoehtoja, tämä on hyödyllinen. Ettei tämä jäisi pelkäksi teoriaksi, otetaan todellinen esimerkki, ohjelma, jota voit kokeilla kääntämällä sen.
#include <stdio.h>
int main(void)
{
int luku;
luku=2; // kokeile muuttaa tätä muuttujaa
clrscr(); // ruuduntyhjennys
switch (luku)
{
case 1 : printf("ykkönen");
break;
case 2 : printf("kakkonen");
break;
case 5 : printf("viitonen");
break;
default: printf("joku muu..");
break;
}
}
Vielä muutama sana tuosta switch- (tai case-)lause-esimerkistä. Ohjelmassa on ainakin yksi sellainen "käsky", jota et varmaan aiemmin ole nähnyt: clrscr();. Se on saman näköinen kuin jonkin aikaa sitten näkemämme saa_ajaa_autoa()-funktiot mikä johtuu juuri siitä, että sekin on funktio, joka löytyy standardikirjastosta stdio. Ai niin, olin unohtaa kertoa mitä se tekee. Jos kokeilit ohjelmaa, huomasit varmaan, että ruutu tyhjeni ohjelman alussa. Tämä johtui juuri tästä clrscr();-käskystä (lyhenne sanoista clear screen).
Muuten kaikki onkin varmaan edellisten selitysten pohjalta selvää. Switch:illä aloitetaan ja case kertoo vaihtoehdot ja niiden seuraukset. Jokainen case-pätkä loppuu käskyyn break;. Tulikohan se tuosta selväksi? Toivottavasti :).
Jess! Siinä olivat ehtolauseet, kokeile ja tutki niitä niin kauan kunnes koet hallitsevasi ne ja sitten mennään eteenpäin.
Silmukat, narua ja solmuja? #
No niin, nyt ehkä alkaa tämä kirjoitus sujua; explooreri latautuu kauniisti tuolla taustalla ja kello on kymmentä vaille seitsemän aamulla. Tucowsista se lopulta löytyi. Niin, ei kannata suoraan Microsoftilta yrittääkään, se on liian hankalaa. Mutta nyt silmukoihin, ei enää selaintekstiä.
Silmukat ovat sellaisia koodinpätkiä, jotka toistuvat monta kertaa peräkkäin tehden saman asian hieman muuttujien arvoja muutellen uudelleen ja uudelleen. Silmukoita on kolmenlaisia: for-, while- ja do..while -silmukoita. Näitä kaikkia saattaa joissain tapauksissa tarvita. Aloitetaanpa for-silmukasta, joka kuitenkin varmaan on kaikista tavallisin. Ensin suomeksi:
olkoon x aluksi 0, sitten kierretään niin kauan kuin x on pienempi kuin 10 (x < 10) ja kasvatetaan joka kierroksella x:ää yhdellä. tehdään jotain (joka kierroksella)
Anteeksi, mutta minä tässä innostuin eilen tuohon suomenkieliseen ohjelmointiin, en sellaista ollut ennen kokeillutkaan. Vielä tämä loppuun, C:llä:
for(x = 0; x < 10; x++ )
{
tehdaan_jotain();
}
Siis for on silmukan tyyppi. x=0- kohdasssa sijoitetaan äksälle alkuarvo, tässä tapauksessa nolla. Eli, kun silmukka alkaa, on x nolla. x < 10 on vertailu, joka tarkistaa onko aika lopettaa silmukka. Siis, jos tämä ehto ei toteudu - siis x on 10, päättyy silmukka, muuten (niin kauan kuin x < 10) jatketaan ja toimitaan seuraavan kasvatusohjeen mukaan, tässä tapauksessa x++ (kasvatetaan x:ää yhdellä). Ja joka kierroksella tehdään ne asiat, jotka ovat aaltosulkujen {} välissä, esimerkissä se on funktio tehdaan_jotain(); .
Ennen kuin käymme for-silmukan rakennetta tarkemmin läpi haluan kertoa jotain noista C-kielen lyhyistä ja ytimekkäistä kasvatusoperaattoreista.. Jos olet aikaisemmin ohjelmoinut jollain muulla kielellä, on sinulle varmaan tuttua se, että kun haluat kasvattaa vaikka nyt tuota muuttujaa x yhdellä, kirjoitat x=x+1. Järkevää, eikö? Tuo toimii toki myös C:llä, mutta koska kielen kehittäjät ovat pyrkineet mahdollisimman lyhyeen koodiin, suositeltavaa onkin käyttää C-kielelle (ja nykyään myös Javalle) ominaista ++-operaatiota. Katso hetki alla olevaa taulukkoa.
[DEFTABLE]
[D]x++[/D]
[E]Lisätään x:ään 1[/E]
[D]x--[/D]
[E]Vähennetään x:ästä 1[/E]
[D]x+=5[/D]
[E]Lisätään x:ään 5[/E]
[D]x-=7[/D]
[E]Vähennetään x:ästä 7[/E]
[/DEFTABLE]
Taulukosta vielä. Siis x on int-tyypin muuttuja ja nuo ovat mahdolliset operaatiot mitä sille voi suoraan lyhyesti tehdä. Tästä puhutaan vielä, mm. seuraavassa, eli jatketaan for-silmukan tutkimista: for-silmukan runko selityksin, olkaa hyvä!
for(alustus; jatkuvuusehto; kasvatus)
{
toiminta;
}
Ensin alustetaan, sitten tutkitaan ja lopuksi kasvatetaan silmukan muuttujaa. Mutta tästä ei vielä ilmene se mihin silmukkaa käytetään: For-lauseen viimeisen sulun jälkeen tulee tuttu {-merkki ja sitten koodi mikä suoritetaan joka kerralla kun silmukka kierretään läpi. Tämä pätkä kannattaa sisentää ja se päättyy }-merkkiin. Tässä taas pieni kaunis esimerkki:
for(luku=1; luku < 11; luku++)
{
tulos = 2 * luku;
printf("%d*2=%d\n", luku, tulos);
}
Jos ymmärsit mitä tuossa tapahtuu, olet uskomattoman hyvä oppimaan, onnitteluni! Jos et ymmärtänyt, ei se haittaa; minä selitän. Tuo on ohjelma, joka laskee kahden kertotaulun (1*2=2, 2*2=4 jne.) ja tulostaa laskut näytölle. Tuonhan olisi toki voinut tehdä toisinkin. Olisi laskenut jokaisen laskun erikseen, mutta se olisi vaatinut paljon enemmän turhaa kirjoittamista. Silmukalla tämä oli paljon helpompi tehdä (Muuten siitä explorerista vielä.. ei se ollutkaan niin yksinkertaista. Se minkä tucowsista kopioin, olikin englanninkielinen versio. Nyt pitäisi löytää suomenkielinen...).
Ensin on for-lause, jossa määritellään silmukka 1:stä 10:een. Nyt varmaan ihmettelet, että miksi siellä sitten on 11 (ai et ihmettele vai? No se on hyvä). Syy on yksinkertainen: haluamme laskea kertolaskun kaikilla luvuilla 1-10. Jos laittaisimme vertailulauseeseen (luku < 11) luvun 10, jäisi kymppi laskuista pois. Kymmenenhän ei ole pienempi kuin kymmenen. Että silleen. Sitten tulee {-merkki ja alkaa silmukan toiminto-osio. Joka kierroksella lasketaan lasku tulos=2*luku;, jossa tulos on jossain aiemmin esitelty int-tyypin muuttuja samoin kuin luku, joka esiintyy myös for-lauseessa. Huomaammekin nyt, että luku on joka kierroksella eri numero: Ensin lasketaan tulos=2*1;, sitten tulos=2*2; jne. Eiköhän se tullut selväksi. Ja koska tiedot täytyy näyttää käyttäjälle, käytämme printf-lausetta, jossa siis %d:n kohdalle tulee muuttujan arvo ja muuttujat ovat lainausmerkkien jäljessä siinä järjestyksessä kuin ne halutaan näyttää.
Jos haluat kokeilla tuota esimerkkiä käytännössä, avaa uusi c-tiedosto, laita sen alkuun tarvittavat includet, tässä tapauksessa stdio.h riittää, ja sitten tuo silmukka main-funktion sisään. Äläkä unohda esitellä muuttujia (ne ovat tyyppiä int).
Siinä oli pieni silmukkaesimerkki. Nyt vielä muutama sana muista silmukkatyypeistä. while-silmukkaa kierretään niin kauan kuin ehto on tosi. Kuulostaa aika samanlaiselta kuin for, eikö. Ainoa ero onkin se.. no katsotaanpa esimerkki: (suomeksi :-)
niin kauan kuin ikä on 7 käydään ekaa luokkaa
Huomasitkin varmaan sen mitä äsken yritin selittää, eli ainoa ero on se, että ehtoon liittyviä argumentteja (muuttujia tms) ei alusteta missään, eikä mitään kasvateta. Nyt C:llä:
while(ika==7)
{
kaydaan_koulua();
}
Niin. Tämä tyyppi sopii hyvin esimerkiksi peliin, jonka täytyy pyöriä niin kauan kunnes painetaan esciä. Näytän tästä esimerkin (hieman Allegro-koodia käyttäen):
while(!key[KEY_ESC])
{
peli_asiat();
}
Ai että selitystä kaivataan? Joo. Tuo silmukka pyörii siis niin kauan kuin esciä ei paineta. key[KEY_ESC] on Allegron juttu, joka tarkoittaa, että esciä on painettu ja ! on C-kielen operaattori, joka tarkoittaa samaa kuin suomeksi ei.
Tässä vielä pieni esimerkki while-silmukasta:
/* Ohjelma, joka laskee kahden kertotaulun
while-silmukkaa käyttäen */
#include <stdio.h>
int main(void)
{
int luku, vastaus;
luku=1;
vastaus=0;
while(luku <= 10)
{
vastaus = 2 * luku;
printf("2*%d=%d\n",luku, vastaus);
luku++;
}
}
Siinä olivatkin kaikki C-kielen ehtolauseet. Minä lähden kouluun. Tavataan taas tehtävien jälkeen. Moi!
Tehtävä #
Tee ohjelma, joka laskee ympyrän alan ja ilmoittaa sen kahden desimaalin tarkkuudella.
Vihje
Kannattaa käyttää kaikissa muuttujissa float-tyyppiä, koska haluamme käyttää desimaalilukuja.
Ympyrän alahan lasketaan kaavalla: Ala=pii*säde², jossa pii on pyöreästi 3.145 tai jotain sellaista.
Jonkin aikaa sitten puhuimme printf-funktion %d-sijoituksista eli siitä, että printf-lauseisiin voi sijoittaa int-tyyppisiä lukuja käyttäen tuota %d-merkkiä. Jos et muista, selaa vähän taaksepäin, sieltä se löytyy..
Myös float-tyyppisiä muuttujia voi tällä tavoin kirjoittaa ruudulle. Silloin ei kuitenkaan käytetä %d:tä vaan %.1f:ää tai jotain sen tapaista.
Siis pisteen jälkeen tulee desimaalien määrä ja sitten f-kirjain. Esimerkiksi, jos haluat kirjoittaa muuttujan a, joka on tyyppiä float, kahden desimaalin tarkkuudella, tapahtuu se seuraavasti:
printf("%.2f",a);
Ratkaisuehdotus
/* Ohjelma, joka laskee ympyrän alan */
#include <stdio.h>
int main(void)
{
float pii, sade, tulos;
pii=3.145; // Annetaan piille arvo
sade=15; // Ympyrän säde
tulos=pii*sade*sade; // A=pii*r²
printf("\nYmpyrän ala = pii * %.1f ² = %.2f",sade,tulos);
}
Tehtävä #
Tee ohjelma, joka laskee nollasta 20:een ja kertoo onko luku suurempi vai pienempi kuin 10.
Vihje
Käytä muuttujatyyppiä int. Ja for-silmukka voisi myös olla aika kätevä.
Ratkaisuehdotus
/* Ohjelma joka laskee nollasta kahteen-
kymmeneen ja kertoo ovatko luvut pienempiä
vai suurempia kuin 10 */
#include <stdio.h>
int main(void)
{
int t;
clrscr(); // tyhjennetään ruutu
for(t=0; t < 20; t++){
printf("Luku: %d", t); // tulostetaan luku (t)
if(t==10) printf(" on 10"); // luku=10 (t)
else if(t < 10) printf(" on suurempi kuin 10"); // luku > 10
else printf(" on pienempi kuin 10"); // luku < 10
printf("\n"); // rivinvaihto
}
}
Taulukoita ja merkkijonoja #
Joo. Nyt olet jo niin pitkällä, että osaat melkein kaikki C-kielen PERUS-asiat. Se tarkoittaa, että kohta voidaan alkaa puhua jostain mielenkiintoisemmasta. Mutta vielä muutama tärkeä asia sitä ennen. Tässä kappaleessa aloitamme viimeisen etappimme, taulukoin ja merkkijonoin.
Olet varmaankin ihmetellyt ja miettinyt tässä muuttujia katsoessasi, että miten on mahdollisra, että tekstille ei ole kuin char-tyyppinen yhden merkin kokoinen muuttuja, mutta kuitenkin on ohjelmia, joissa säilötään pitempiä sanoja ja jopa lauseita muuttujiin. Monissa muissa kielissä se tehdään omalla string -muuttujatyypillä, mutta sellaistahan C:ssä ei ole. Nyt sinusta varmaan tuntuu, että kiertelen ja kaartelen aivan turhaan. Olet varmaan jo otsikosta arvannut mistä tässä on kyse ja alat ikävystyä, anteeksi :(. Okei, asiaan.
C-kielessä merkkijonot ovat char-tyyppisiä taulukoita. Siispä ensin täytynee perehtyä taulukoihin. Taulukko on muuttujoiden muodostama kokonaisuus, lokerikko (jos muuttuja on lokero :). Se voi olla jono, jolloin puhutaan yksiulotteisesta taulukosta tai kaksi- tai useampiulotteinen taulukko. Kaavio varmaan selventäisi asiaa:
Siinä oli siis pieni kaavio yksiylotteisesta taulukosta, jossa on n solua tai lokeroa tai mieluiten alkiota, niinkuin virallisesti sanotaan. Se alustetaan kuten tavallinen muuttuja, mutta perään laitetaan hakasulkeissa taulukon koko. Esimerkiksi, jos haluamme säilöä viiden pelaajan pistetiedot yhteen taulukkoon (viisisoluinen taulukko), teemme sen seuraavasti:
int pisteet[5];
Taulukko on tyyppiä int, koska pistetiedot ovat useimmiten kokonaislukuja ja viitonen perässä kertoo alkioiden lukumäärän eli taulukon koon. Huomaa, että ensimmäisen alkion numero ei ole 1 vaan 0. Kaksiulotteinen taulukko tehtäisiin vastaavasti:
int monet_pisteet[5][2];
Tuo taulukko voisi näyttää lokerikoksi ajateltuna tältä:
Perään siis vain lisätään toisen ulotteen (ulottuvuuden?) alkioiden määrä. Ja ulotteita (äh, mitä ne nyt on?) voi lisätä niin paljon kuin haluaa - kuitenkaan kovin montaa ei kannata käyttää.
Nyt meillä on yksiulotteinen taulukko pisteet ja haluamme varmaan jo päästä sitä täyttelemään. Teemme näin: Jos haluamme sijoittaa ykköspelaajan pisteet ensimmäiseen alkioon, sijoitamme ne sinne (yksinkertaista ;)
pisteet[0]=52000;
Sinne meni sitten ykköspelaajalle 52000 pistettä. Huomaa edelleen: 0, ei 1. Vastaavasti voisimme antaa viitospelaajalle 10 pistettä:
pisteet[4]=10;
Samaan tapaan voi sitten sijoitella arvoja muillekin pelaajille. Nyt osaat tehdä taulukoita ja kuten jo taisin mainitakin, taulukoita voi tehdä kaiken tyyppisiä (int, float, char...). Ai niin, vielä yksi tärkeä asia. Jos joskus ohjelmasi alkavat antaa ihan ihmeellisiä lukuja, sellaisia joita et varmasti ole taulukkoihisi syöttänyt, on varmaankin kyse ylivuodosta. C nimittäin ei mitenkään vahdi sijoituksiasi, se antaa sinun kaikessa rauhassa sijoittaa 5-alkioisen pistetaulukon alkioon 200, jos niin haluat, mutta laittaa tiedot sitten muualle ja sotkee kaiken. Mitä yritän sanoa on, että huolehdi itse siitä, ettei ohjelmasi sijoittele tietoja olemattomiin alkioihin.
Nyt merkkijonoihin. Selitän tämän nopeasti, koska tämä tosiaankin on erittäin yksinkertainen asia. Tuossa äsken selitin taulukot ja merkkijonot ovat vain niiden yksi muoto. Siispä, jos haluat tallentaa vaikkapa parhaan pelaajan nimen, tarvitset taulukon tyyppiä char:
char paras[10];
Nyt sinulla on kymmenen merkin mittainen merkkijono. Helppoa, eikö? Sijoittaminen siihen ei sen sijaan ole niin helppoa, se ei tapahdu suoraan kuten joissain muissa kielissä vaan stdio-funktiolla strcpy, joka toimii seuraavasti:
strcpy(taulukko,merkkijono);
Taulukko on siis meidän tapauksessamme tuo paras, jonka loimme äsken. Ja merkkijono on tekstiä (tai toinen taulukko), joka halutaan sijoittaa ensimmäiseen merkkijonoon. Esimerkki varmaankin selventää..
strcpy(paras,"Teemu S.");
Tuossa sijoitimme Teemu Selänteen nimen alun merkkijonoon (char-tyypin taulukkoon) paras. Merkkijonon käsittelyyn on myös muita funktioita, mutta selittelen niitä vasta kun tarvitsemme sellaisia. Ei ehkä ihan vielä.
Vähän aikaa sitten puhuimme printf-funktion %d-sijoituksista (int-tyypin muuttujia). Samalla tavalla voit tulostaa myös merkkijonoja käyttäen %s-merkkiä. Tässä täysin toimiva, mutta typerä esimerkki.
#include <stdio.h>
int main(void)
{
char nimi[10];
strcpy(nimi,"Jarkko");
printf("Jarkon nimi on %s",nimi);
}
Lupailin tuossa edellisen kappaleen lopussa interaktiivisuutta, siispä saamanne pitää. Nimittäin stdio-kirjastosta löytyy funktio gets(); jolla voi lukea syötettä näppäimistöltä. Gets-funktiolle annetaan parametrina merkkijono, eli char-tyyppinen taulukko, jonka se sitten täyttää. Ensin pieni esimerkki ja sitten tehtävä.
char tekstia[50]; gets(tekstia); // kysyy käyttäjältä syötettä // ja tallentaa sen taulukkoon tekstia
Tuo ei ollut vielä kokonainen ohjelma, sellaisen saat tehdä itse seuraavassa tehtävässä.
Tehtävä #
Tee ohjelma, joka kysyy käyttäjän nimen ja tervehtii tätä sillä nimellä.
Vihje
Nimelle kannattaa varata tarpeeksi iso merkkijono, esimerkiksi 50 merkkiä.
Ratkaisuehdotus
/* Ohjelma joka kysyy käyttäjän nimen
ja tervehtii häntä nimeltä */
#include <stdio.h>
int main(void)
{
char nimi[50];
printf("Mikä on nimesi? ");
gets(nimi);
printf("\nTerve, %s!",nimi);
}
Tehtävä #
Seuraavaa tehtävää varten tarvitset vielä yhden tiedon, jota en aiemmin ole pystynyt kertomaan. Nimittäin, kuten varmaan arvasitkin, ei gets-funktiolla voi kysyä kuin merkkijonoja. Niinpä jos halutaan kysyä käyttäjältä kokonaislukuja, täytyy ottaa gets:illä merkkijono ja muuttaa se sitten int:iksi. Se tapahtuu stdlib-funktiolla atoi();, joka ottaa syötteenä merkkijonon ja palauttaa sen inttinä jos mahdollista. Esimerkki ei liene pahitteeksi..
int luku; char merkkijono[50]; gets(merkkijono); luku=atoi(merkkijono); // tallentaa muuttujaan luku // merkkijonon sisältämän luvun
Ja nyt sitten se tehtävä!
Tee ohjelma, joka kysyy käyttäjän ikää ja kertoo onko hän tarpeeksi vanha saadakseen äänestää. Vihjeitä en anna tällä kertaa, edellä oleva atoi-juttu saa riittää.
Ratkaisuehdotus
/* Ohjelma, joka kysyy käyttäjän ikää ja tutkii
onko hän äänioikeutettu vai ei */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ika;
char merkkijono[50];
printf("Kuinka vanha olet? ");
gets(merkkijono); // luetaan syöte
ika=atoi(merkkijono); // muutetaan se kokonaislukumuotoon
if(ika < 18) printf("\nEt saa äänestää vielä!\n");
else printf("\nOlet äänioikeutettu.\n");
}
Funktiot #
Jess! Tämä on tosi tärkeä luku. Kaikissa (vähänkin isommissa) ohjelmissa tarvitaan funktioita. Mutta toisaalta tämä on myös hauska luku, koska funktioden oppiminen avaa sinulle aivan uudet mahdollisuudet ohjelmiesi kehittelyyn. Nyt siis tarkkana!
Funktiot eli aliohjelmat ovat ohjelmanpätkiä, jotka suorittavat jonkun tietyn usein tarvittavan pienen (tai isomman) tehtävän. Esimerkiksi luolalentelypelissä yhden funktion tehtävänä voisi olla ruudun vierittäminen, toisen tehtävä olisi törmäysten tutkiminen jne.
Ohjelma siis kannattaa pilkkoa tällaisiin pieniin osiin selkeyden ja käytännöllisyyden takia. Selkeyden siksi, että on paljon helpompi lukea ohjelmakoodia, joka on jaoteltu osiin, samoin kuin tietosanakirjasta jossa on otsikot ja aakkosjärjestys on paljon helpompi etsiäö tietoa kuin täysin järjestämättömästä. Käytännöllisyyden sen takia, että on aika hankalaa ja jopa typerää kirjoittaa sama ohjelmanpätkä joka kerta uudestaan vaikkapa 20 tai 100 kertaa. Se lisää koodin pituutta huomattavasti, mikä on huono asia, jos et yritä saada maailman pisimmän ohjelman ennätystä (mikä on melkoisen vaikeaa sekin - Windows 2000:ssa ohjelmarivejä on 30 miljoonaa, ja Microsoft käyttää funktioita).
Funktioita siis kannattaa käyttää, mutta miten? Tässä tulee vastaus kysymykseesi. Kaikkien taiteen sääntöjen mukaan tulee jokainen funktio esitellä ohjelman alussa heti includejen jälkeen. Itse en tätä vielä vähän aikaa sitten jaksanut tehdä (kääntäjä kyllä kääntää muutenkin..), mutta sitten alkoi tulla ongelmia funktiokutsujen kanssa, kun sen funktion jota kutsutaan piti olla ennen kutsujaa, eikä se tietenkään ollut ja sitten ei sitä löytynyt... Jotenka minä olen sitä mieltä, että tässä kannattaa tehdä ANSI:n mukaan, siis näin:
1.) Ohjelman alkuun #include-rivien jälkeen esittelyt kaikista funktioista
int laske_lasku(int a, int b); ...
2.) Sitten main-funktio (pääohjelma)
3.) Ja sen jälkeen funktiot, samalla tavalla kuin mainikin:
int laske_lasku(int a, int b)
{
int c; // Tehdään muuttuja laskun avuksi
c=a+b; // Lasketaan
return c; // Palautetaan c
}
Siis, funktion rakenne on samanlainen kuin mainissa, joka sekin on melkein tavallinen funktio. Funktion alussa on rivi, joka alkaa tässä tapauksessa int (voisi olla myös char, float, void...), on esittelyrivi. Alussa ennen mainia on siis aivan sama rivi vain sillä erolla, että siellä alussa se loppuu puolipisteeseen. Se alkaa siis muuttujatyypillä, joka kertoo minkä tyyppisiä palautusarvoja voi funktiolta odottaa. Nyt varmaan odotat minun selittävän mikä palautusarvo on ja senpä taidan tehdäkin. Palautusarvo on luku tms., jonka funktio antaa takaisin kutsujalle return- käskyllä. Sitten tulee funktion nimi, jolla sitä muut funktiot kutsuvat. Ja suluissa kaikki tieto, jota funktiolle annetaan sitä kutsuttaessa. (tässä tapauksessa a ja b). Näitä tietoja voidaan käyttää kuin funktiossa esiteltyjä muuttujia (c=a+b). Näin tehdään siis funktio ja nyt kerron vielä kuinka sitä kutsutaan niin päästään eteenpäin.
Funktiota kutsutaan kirjoittamalla funktion nimi ja suluissa sille annettavat tiedot. Kirjoitetaanpa äsken tehtyä laske_lasku-funktiota kutsuva main-funktio.
int main(void)
{
int luku; // Luku
luku=laske_lasku(1,2);
printf("Tulos: %d",luku);
}
Alussa esitellään muuttuja vastauksen varastoimista varten ja sitten kutsutaan funktiota. Koska se on tyyppiä int ja palauttaa arvon, voimme ottaa palautusarvon muuttujaan (luku=laske...). Jos funktion tyyppi olisi void, tämä ei onnistuisi (ja luultavasti ei olisi tarpeellistakaan). Sitten on funktion nimi ja suluissa arvot muuttujille: ykkönen menee a:han ja kakkonen b:hen. Ja lopuksi kutsutaan stdio-funktiota printf. Muista laittaa alkuun #include <stdio.h>. Nyt siinä taisi tulla tarpeeksi funktioista. Jos jotain puuttuu tästä, jos et tajunnut kaikkea mitä haluaisit, kerro minulle. Lisään sitten tähän lisää tietoa ja vastaan mielelläni kysymyksiisi suoraankin. Huh! Selvittiin tästäkin.
Tyypit, structit ja muuta hauskaa #
Niinpä, nyt päästiinkin viimeiseen peruskurssin osaan vai miten sen nyt ilmaisee. Vielä on paljon kerrottavaa, mutta tämän luvun jälkeen voitkin jo valita mihin päin haluat suuntautua, mikä kiinnostaa. Minua kiinnosti ja kiinnostaa edelleen peliohjelmointi, mutta paljon on muitakin mahdollisuuksia. Oppiminenhan jatkuu varmasti yhtä pitkään kuin ohjelmointia harrastaa (ja minä ainakin toivon vielä kahdeksankymppisenä ukkonakin sitä tekeväni), joten matka on pitkä, mutta varmasti kiinnostava. Nyt kuitenkin tämän luvun asioihin.
Tyypit ovat tyyppejä. Siis, yritän selittää tämän jotenkin järkevästi.. No joo, voit siis määrittää omia tyyppejäsi kuten muuttujatyyppejä, vaikkapa näin:
typedef kokonaisluku int;
Siinä yksinkertaisesti luotiin tyyppi nimeltä kokonaisluku, joka käyttäytyy aivan samoin kuin int. Sellaista.. En sano tästä enempää vaan siirryn structeihin, jotka ovat varmasti paljon hyödyllisempiä.
Structeihin voit koota yhteen erilaisia muuttujia helpommin hallittaviksi kokonaisuuksiksi, vähän niinkuin olio-ohjelmoinnissa. Esimerkiksi seuraavalla tavalla voidaan esitellää structi: (niin, tämä samoin kuin tyyppien esittely tehdään funktioiden ulkopuolella, vaikka niiden includejen jälkeen)
typedef struct PELAAJA
{
char nimi[20];
int pisteet;
int x;
int y;
} PELAAJA;
Siinä oli johonkin yksinkertaiseen peliin sopiva pelaajastructi. Se sisältää tilan pelaajan nimelle (20 merkkiä), pistemäärälle ja koordinaateille, riittää hyvin vaikka tämän tutoriaalin esimerkkipeliin. Eli: structin rakenne on seuraava: ensin typedef struct structin nimi ja aaltosulku {. Sitten vain luetellaan kaikki muuttujat, joita siihen halutaan ja loppuun aaltosulku toisin päin ja structin nimi uudestaan.
Kun sitten jossain vaiheessa haluat käyttää structia, esittelet sellaisen, kuten muuttujankin:
PELAAJA pl1;
Siinä tuli PELAAJA-tyyppinen structi nimeltänsä pl1. Nyt pl1:n kaikkiin muuttujiin voi viitata rakenteella: pl1.muuttuja. Selvitelläänpä asiaa täyttämällä pelaajan tiedot:
strcpy(pl1.nimi,"Saku Koivu"); // Pelaajan nimi pl1.pisteet=10000; pl1.x=10; pl1.y=10;
Siinä tuli se juttu. Structit olen todennut pelejä ohjelmoidessani tosi hyödyllisiksi, muussa ohjelmoinnissa en niitä ole vielä käyttänyt. Kannattaa käyttää!
Ai niin, lupasin tuossa luvun otsikossa jotain muutakin hauskaa, mitäköhän se voisi olla. No kerronpa vaikka miten satunnaislukuja saa arvottua helposti.
Ensinnäkin tarvitsee includoida time.h ja stdlib.h. Satunnaislukusysteemi alustetaan komennolla srand ja satunnaisluvun saat komennolla rand. Katso vaikka tästä:
srand((unsigned int)time((time_t *)NULL)); luku=(rand()%6)+1;
Jos aiot tehdä seuraavan tehtävän, tarvitset tätä satunnaislukujuttua.
Tehtävä #
Tee ohjelma, joka "ajattelee" jotain lukua ja pyytää käyttäjää arvaamaan sitä kunnes arvaa oikein tai liian monta kertaa. Se voisi myös kertoa onko haluttu luku pienempi vai suurempi kuin arvaus
Vihje
Siinä oli kysymys ja tässä sitten hieman apuja. Niin, kyseessä on aivan perusarvailuleikki, ei kovin loisteliasta, mutta varmasti opettavaista. Kirjoita ohjelmasi ja kokeile sitä, käännä se rohkeasti, pelaile ja kehittele vaikka arvauspeli deluxe. Älä ryntää heti katsomaan vastausta vaan katso se vasta lopuksi nähdäksesi mitä tein eri tavalla. Tässä vielä muutama käsky joita tulet siinä tarvitsemaan, ja sitten ei muuta kuin töihin!
[DEFTABLE]
[D]gets(merkkijono);[/D]
[E]Tällä saat luettua käyttäjältä rivin tekstiä.[/E]
[D]luku = atoi(merkkijono);[/D]
[E]Ja tällä saat niin paljon merkkijonosta kuin mahdollista muutettua luvuksi [/E]
[/DEFTABLE]
Ratkaisuehdotus
/* Esimerkki ohjelmasta, joka arvuuttelee lukua
käyttäjältä
(C) Jarkko Laine 1999 */
// INCLUDET ///////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// PROTOTYYPIT ////////////////////////////////////
void nayta_alkutekstit(void);
// PÄÄOHJELMA /////////////////////////////////////
int main(void)
{
int luku; // oikea luku
int arvattu; // arvattu luku
char arvaus[80]; // merkkijono arvausta varten
int arvaukset=1; // Kuinka monta kertaa pelaaja on arvannut
nayta_alkutekstit(); // kutsutaan omaa funktiotamme,
// joka kirjoittaa tervehdykset
// Alustetaan satunnaislukugeneraattori
srand((unsigned int)time((time_t *)NULL));
// Arvotaan luku
luku=(rand()%10)+1;
while(arvattu!=luku){ // Niin kauan kunnes pelaaja arvaa
printf("\n\nAnnapas tulla arvausta! >");
gets(arvaus); // kysytään käyttäjältä syöte
arvattu=atoi(arvaus); // muutetaan se intiksi
if(arvattu < luku) printf("\nLuku on suurempi...");
else if(arvattu > luku) printf("\nLuku on pienempi...");
else printf("\n\nOnneksi olkoon! Käytit vain %d arvausta",arvaukset);
arvaukset++; // Taas meni yksi arvaus...
}
}
// funktiomme, joka näyttää alkutekstit
void nayta_alkutekstit(void)
{
clrscr(); // tyhjennetään ruutu
printf("Tervetuloa arvauspeliin!\n");
printf("------------------------\n");
printf("Tehtäväsi on arvata ajattelemani luku väliltä 1-10\n");
printf("Onnea!");
}
Siinä se oli... Yksinkertainen, mutta toimiva ohjelma. Funktioitakin käytettiin mallin vuoksi.
Mitä nyt? #
Jess! Nyt olet C-guru! Tai et ehkä ihan, mutta tiedät joka tapauksessa niin paljon C-kielestä, että voit jatkaa eteenpäin. Jos olisin oikea opettaja, pitäisin tässä kohdassa kurssikokeen - mutta jääköön se väliin tällä kertaa.
Nyt voit jatkaa mihin suuntaan haluat. Maailma on mahdollisuuksia täynnä. Ja näiltäkin sivuilta löydät mahdollisuuksia vaikka muille jakaa. Ja niitä tulee koko ajan lisää. Tutki ja ohjelmoi! Äläkä epäröi kysyä. Onnea tutkimusmatkallesi! Kuka ties sinusta tulee uusi Bill Gates tai vaikka Jarkko Laine - hehheh :)
Huh! Sainpas sen tehtyä. Vajaa kaksi vuotta siihen meni (taukoineen), mutta nyt se on valmis... Jos löydät virheitä tai epätarkkuuksia, ota yhteyttä sähköpostilla (jarkko.laine@suomipelit.com) ja kerro minulle. Tai jos sinulla on toiveita siitä minkälaisia kursseja haluat jatkossa nähdä.


