Taulukot ja merkkijonot

Samankaltaiset tiedostot
Ohjelmointi 1 Taulukot ja merkkijonot

Osoittimet. Mikä on osoitin?

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan.

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Osoitin ja viittaus C++:ssa

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Taulukot. Jukka Harju, Jukka Juslin

Java-kielen perusteet

5.6. C-kielen perusteet, osa 6/8, Taulukko , pva, kuvat jma

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

3. Muuttujat ja operaatiot 3.1

Java-kielen perusteet

12 Mallit (Templates)

Tietueet. Tietueiden määrittely

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Sisältö. 22. Taulukot. Yleistä. Yleistä

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

ITKP102 Ohjelmointi 1 (6 op)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU

Ohjelmoinnin perusteet Y Python

Valinnat ja päätökset

ITKP102 Ohjelmointi 1 (6 op)

Mallit standardi mallikirjasto parametroitu tyyppi

13 Operaattoreiden ylimäärittelyjä

Ohjelmoinnin perusteet Y Python

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat ja operaatiot

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

Kirjoita oma versio funktioista strcpy ja strcat, jotka saavat parametrinaan kaksi merkkiosoitinta.

Kääntäjän virheilmoituksia

Ohjelmointi funktioiden avulla

Tietotyypit ja operaattorit

Perustietotyypit ja laskutoimitukset

Omat tietotyypit. Mikä on olio?

Muuttujien roolit Kiintoarvo cin >> r;

811120P Diskreetit rakenteet

Operaattoreiden uudelleenmäärittely

Johdatus Ohjelmointiin

11. Javan toistorakenteet 11.1

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Osa. Toimintojen toteuttaminen ohjelmissa vaatii usein haarautumisia ja silmukoita. Tässä luvussa tutustummekin seuraaviin asioihin:

Ohjelmoinnin perusteet Y Python

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu

ITKP102 Ohjelmointi 1 (6 op)

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Ohjelman virheet ja poikkeusten käsittely

Ohjelmoinnin perusteet Y Python

ITKP102 Ohjelmointi 1 (6 op)

7. Näytölle tulostaminen 7.1

7/20: Paketti kasassa ensimmäistä kertaa

Osoittimet ja taulukot

Ohjelmoinnin perusteet Y Python

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat:

Ohjelmointiharjoituksia Arduino-ympäristössä

Ohjelmoinnin perusteet Y Python

// Tulostetaan double-tyyppiseen muuttujaan "hinta" tallennettu // kertalipun hinta ja vaihdetaan riviä. System.out.printf("%.1f euros.

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Java-kielen perusteita

Kirjoita, tallenna, käännä ja suorita alla esitelty ohjelma, joka tervehtii käyttäjäänsä.

Kerta 2. Kerta 2 Kerta 3 Kerta 4 Kerta Toteuta Pythonilla seuraava ohjelma:

Lausekielinen ohjelmointi II Ensimmäinen harjoitustyö

Ohjelmoinnin perusteet Y Python

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

Periytyminen. Luokat ja olio-ohjelmointi

Metropolia ammattikorkeakoulu TI00AA : Ohjelmointi Kotitehtävät 3

C++ rautaisannos. Kolme tapaa sanoa, että tulostukseen käytetään standardikirjaston iostreamosassa määriteltyä, nimiavaruuden std oliota cout:

Ohjelmassa on käytettävä funktiota laskeparkkimaksu laskemaan kunkin asiakkaan maksu. Funktio floor pyöristää luvun lähimmäksi kokonaisluvuksi.

Javan perusteet. Ohjelman tehtävät: tietojen syöttö, lukeminen prosessointi, halutun informaation tulostaminen tulostus tiedon varastointi

Harjoitustyö: virtuaalikone

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

Luokat. Luokat ja olio-ohjelmointi

Java-kielen perusteita

Datatähti 2019 loppu

Ohjausrakenteet. Valinta:

811120P Diskreetit rakenteet

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Sisällys. 6. Muuttujat ja Java. Muuttujien nimeäminen. Muuttujien nimeäminen. Muuttujien nimeäminen. Muuttujan tyypin määritys. Javan tietotyypit:

16. Ohjelmoinnin tekniikkaa 16.1

1. Omat operaatiot 1.1

Sisällys. 17. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. for-lause lyhemmin

Sisällys. 16. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. Aritmetiikkaa toisin merkiten

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

PERUSLASKUJA. Kirjoita muuten sama, mutta ota KAKSI välilyöntiä (SEURAA ALUEMERKINTÄÄ) 4:n jälkeen 3/4 +5^2

Ohjelmoinnin perusteet Y Python

Toinen harjoitustyö. ASCII-grafiikkaa 2017

PERUSLASKUJA. Kirjoita muuten sama, mutta ota välilyönti 4:n jälkeen 3/4 +5^2

Esimerkkiprojekti. Mallivastauksen löydät Wroxin www-sivuilta. Kenttä Tyyppi Max.pituus Rajoitukset/Kommentit

Transkriptio:

Taulukot ja merkkijonot 6 Taulukot ja merkkijonot Olemme käsitelleet kaikki perustietotyypit ja olemme käsitelleet, miten ohjelmissa suoritetaan laskentaa sekä tehdään päätöksiä. Tämän luvun tarkoituksena on yhä laajentaa tähän astikin käsittelemiämme ohjelmoinnin perustekniikoita yksittäisistä tietoalkioista tietoalkioiden kokoelmiin. Käsittelemme myöskin merkkijonojen käsittelyä. Luvun aiheita ovat: Mikä on taulukko ja mihin sitä voi käyttää Miten erityyppisiä taulukoita määritellään ja alustetaan Mikä on null-merkkiin päättyvä merkkijono Miten char-tyyppiseen taulukkoon talletetaan merkkijono Miten moniulotteinen taulukko määritellään ja miten sitä käytetään Miten char-tyyppistä taulukkoa käytetään null-merkkiin päättyvänä merkkijonona Miten luodaan string-tyyppinen muuttuja Millaisia operaatioita voidaan suorittaa string-tyyppisille olioille ja miten niitä käytetään Taulukot Tiedämme jo, miten luodaan muuttujia perustietotyypeistä. Kukin muuttuja voi tallettaa yhden tietyntyyppisen tietoalkion - voimme käyttää muuttujaa, joka tallettaa kokonaisluvun tai muuttujaa, joka tallettaa merkin ja niin edelleen. Taulukkoon voidaan tallettaa useita tietyntyyppisiä tietoalkioita. Sinulla voi olla kokonaislukujen taulukko tai merkkien taulukko - itse asiassa minkä tahansa tyyppinen taulukko. Katsotaan esimerkkiä tilanteesta, jossa saattaisit tarvita taulukkoa. Olemme jo kirjoittaneet ohjelman, joka laskee lämpötilojen keskiarvon. Mutta oletetaan, että olisit halunnut myöskin tietää, kuinka monta lämpötilalukemaa oli keskiarvon yläpuolella ja kuinka monta alapuolella. Alkuperäiset lämpötilalukemat tulee tallettaa, jotta voimme tehdä tämän, mutta jokaisen lämpötilalukeman tallettaminen omaan muuttujaan olisi turhan monimutkaista koodata ja käyttökelvotonta, jos lukemia on hiukankaan enemmän. Taulukon avulla tästä selvitään helposti. 179

C++ Ohjelmoijan käsikirja Taulukon käyttö Taulukko on yksinkertaisesti joukko muistipaikkoja, joista jokainen voi tallettaa saman tyyppisen tietoalkion, ja joihin kaikkiin pääsee käsiksi samalla muuttujan nimellä. Voit esimerkiksi tallettaa 366 lämpötilalukemaa taulukkoon, joka määritellään seuraavasti: double lampotilat[366]; //Lämpötilojen taulukko Tämä määrittelee double-tyyppisen taulukon, jonka nimi on lampotilat ja jossa on 366 alkiota. Tämä tarkoittaa, että tällä taulukolla on 366 muistipaikkaa, joista jokaiseen voidaan tallettaa double-tyyppinen arvo. Hakasulkeiden sisällä olevaa alkioiden lukumäärää kutsutaan termillä taulukon koko. Taulukon yksittäiseen alkioon viitataan käyttämällä kokonaislukua, jota kutsutaan indeksiksi. Tietyn alkion indeksi on yksinkertaisesti siirtymä ensimmäisestä alkiosta lukien. Taulukon ensimmäisen alkion siirtymä on 0, joten sen indeksi on 0. Indeksin arvo 3 viittaa neljänteen alkioon - kolme alkiota ensimmäisestä lukien. Viittaat tiettyyn alkioon sijoittamalla tämän indeksin hakasulkeisiin taulukon nimen perään. Esimerkiksi sijoittaaksesi arvon 99 neljänteen alkioon taulukossa lampotilat, voit kirjoittaa: lampotilat[3] = 99.0; //Asetetaan neljänteen alkioon arvo 99 Katsotaan toista taulukkoa. Taulukon korkeus rakenne on esitetty seuraavassa: Korkeus[0] Korkeus[1] Korkeus[2] Korkeus[3] Korkeus[4] Korkeus[5] 26 37 47 55 62 75 Taulukko on int-tyyppinen ja siinä on kuusi alkiota. Kukin laatikko kuvaa muistipaikkaa, joka sisältää taulukon alkion arvon. Kuhunkin taulukon alkioon voidaan viitata edellä mainitulla tavalla. Tämä taulukko on esitelty lauseella: int korkeus[6]; //Esitellään taulukko kuudelle korkeudelle Kääntäjä varaa tällä esittelyllä kuusi peräkkäistä muistipaikkaa int-tyyppisille arvoille (joten se on myös määrittely). Jos int-tyypin koko on tietokoneessasi 4 tavua, on tämän taulukon koko 24 tavua. Taulukon tyyppi määrittelee kunkin alkion vaatiman muistimäärän. Taulukon kaikki alkiot talletetaan yhteen peräkkäisistä muistipaikoista koostuvaan. muistilohkoon. Kuten kaaviosta näkyy, jokaisessa taulukon korkeus alkiossa on eri arvot. Ne saattavat olla esimerkiksi pihan pensaiden korkeudet pyöristettyinä lähimpään tuumaan. Koska taulukossa on kuusi alkiota, taulukon indeksit ovat 1-5. Voit halutessasi laskea yhteen taulukon korkeus kolmen ensimmäisen alkion arvot lauseella: int summa3 = korkeus[0] + korkeus[1] + korkeus[2]; 180

Taulukot ja merkkijonot Tässä taulukon yksittäiset alkiot käyttäytyvät aivan samaan tapaan kuin tavallisetkin kokonaisluvut. Et voi kuitenkaan sijoittaa koko taulukkoa toiseen taulukkoon yhdessä sijoituksessa - voit käsitellä vain taulukon yksittäisiä alkioita. Kokeile itse - Taulukon käyttö Katsotaan heti taulukon käyttöä esimerkin avulla. Voimme käyttää kokonaislukutaulukkoa ihmisryhmän pituuksien keskiarvon laskennassa, kun haluamme myöskin tietää, montako henkilöä on alle keskiarvon. // Esimerkki 6.1 - Taulukon käyttö #include <iostream> #include <cctype> using namespace std; int main() int pituus[10]; int laskuri = 0; char vastaus = 0; // Pituuksien taulukko // Pituuksien lukumäärä // Vastaus kysymykseen // Luetaan pituudet silmukassa do cout << endl << "Syötä pituus kokonaisina tuumina: "; cin >> pituus[laskuri++]; // Tarkistetaan halutaanko syöttää lisää pituuksia cout << "Syötätkö lisää (k tai e)? "; cin >> vastaus; } while(laskuri < 10 && tolower(vastaus) == 'k'); // Ilmoitetaan, jos taulukko on täynnä if(laskuri == 10) cout << endl << "Taulukko on täynnä." // Lasketaan keskiarvo ja tulostetaan se double keskiarvo = 0.0; // Tallettaa keskiarvopituuden for(int i = 0; i < laskuri ; i++) keskiarvo += pituus[i]; // Lisätään pituus keskiarvo /= laskuri; // Jaetaan pituuksien lukumäärällä cout << endl << "Keskiarvopituus on " << keskiarvo << " tuumaa." // Lasketaan montako on alle keskiarvopituuden int yli_keskiarvon = 0; // Yli keskiarvon olevien lukumäärä for(int i = 0 ; i < laskuri ; i++) if(pituus[i] > keskiarvo) // Yli keskiarvon? yli_keskiarvon++; // Jos on, kasvatetaan laskuria 181

C++ Ohjelmoijan käsikirja } cout << "Oli " << yli_keskiarvon << (yli_keskiarvon == 1? " pituus " : " pituutta ") << "yli keskiarvon." return 0; Ohjelman tyypillinen tulostus näyttää seuraavalta: Syötä pituus kokonaisina tuumina: 75 Syötätkö lisää (k tai e)? k Syötä pituus kokonaisina tuumina: 56 Syötätkö lisää (k tai e)? k Syötä pituus kokonaisina tuumina: 63 Syötätkö lisää (k tai e)? k Syötä pituus kokonaisina tuumina: 42 Syötätkö lisää (k tai e)? k Syötä pituus kokonaisina tuumina: 70 Syötätkö lisää (k tai e)? e Keskiarvopituus on 61.2 tuumaa. Oli 3 pituutta yli keskiarvon. Kuinka se toimii Määrittelemme ensin taulukon ja kaksi muuta muuttujaa: int pituus[10]; // Pituuksien taulukko int laskuri = 0; // Pituuksien lukumäärä char vastaus = 0; // Vastaus kysymykseen Taulukon pituus koko on 10, joten voimme tallettaa siihen maksimissaan 10 kokonaislukua. Käytämme muuttujaa laskuri viittaamaan seuraavaan vapaaseen taulukon alkioon ja koska taulukon ensimmäisen alkion indeksi on 0, sen arvo on myös sama kuin taulukkoon talletettujen kokonaislukujen lukumäärä. Aluksi ensimmäinen alkio on tyhjä eikä yhtään arvoa ole talletettu, eli laskuri on 0. Taulukon pituus arvot luetaan do-while -silmukalla: do cout << endl << "Syötä pituus kokonaisina tuumina: "; cin >> pituus[laskuri++]; // Tarkistetaan halutaanko syöttää lisää pituuksia cout << "Syötätkö lisää (k tai e)? "; cin >> vastaus; } while(laskuri < 10 && tolower(vastaus) == 'k'); 182

Taulukot ja merkkijonot Syötteen pyytämisen jälkeen luemme pituuden näppäimistöltä ja talletamme sen muuttujan laskuri viittaamaan alkioon. Tämän jälkeen muuttujan laskuri arvoa kasvatetaan viittaamaan seuraavaan vapaaseen alkioon. Seuraavaksi tulostamme kysymyksen, halutaanko syöttää uusia arvoja. Vastaus talletetaan muuttujaan vastaus. do-while -silmukka tarkistaa sekä syötettyjen arvojen lukumäärän taulukon kokoon nähden sekä muuttujan vastaus arvon. Jos muuttuja laskuri on saavuttanut arvon 10, joka on taulukon alkioiden lukumäärä, tai muuttujan vastaus arvo ei ole k tai K, silmukan suoritus päättyy. Huomaa, että C++ ei tarkista indeksien laillisuutta. Sinun tulee itse huolehtia, ettet viittaa taulukon alkioihin, jotka ovat taulukon rajojen ulkopuolella. Jos talletat tietoa indeksiin, joka on taulukon rajojen ulkopuolella, kirjoitat jonkin muun yli muistissa tai saat muistin suojausvirheen. Kummassakin tapauksessa on lähes varmaa, että ohjelmasi suoritus päättyy ennen aikojaan. Kun silmukka on suoritettu, tarkistamme, onko muuttujan laskuri arvo sellainen, että olemme täyttäneet koko taulukon. Jos näin on, näytämme viestin. Tämän jälkeen laskemme keskiarvopituuden: double keskiarvo = 0.0; // Tallettaa keskiarvopituuden for(int i = 0; i < laskuri ; i++) keskiarvo += pituus[i]; // Lisätään pituus keskiarvo /= laskuri; // Jaetaan pituuksien lukumäärällä cout << endl << "Keskiarvopituus on " << keskiarvo << " tuumaa." Summaamme for-silmukassa pituudet muuttujaan keskiarvo. Silmukka kiertää i:n arvot 0 - laskuri-1, mikä on tarkalleen taulukon alkioiden lukumäärä. Kun i:n arvo muuttuu samaksi kuin muuttujan laskuri arvo, silmukka päättyy. Laskemme keskiarvon yksinkertaisesti jakamalla muuttujassa keskiarvo olevan summan pituuksien lukumäärällä (muuttujan laskuri mukaan). Viimeiseksi laskemme montako pituutta on yli keskiarvon: int yli_keskiarvon = 0; for(int i = 0 ; i < laskuri ; i++) if(pituus[i] > keskiarvo) yli_keskiarvon++; // Yli keskiarvon olevien lukumäärä // Yli keskiarvon? // Jos on, kasvatetaan laskuria Tässä vertaillaan for-silmukassa kaikkia pituuksia muuttujassa keskiarvo olevaan keskiarvoon. Jos pituus on suurempi kuin muuttujan keskiarvo arvo, muuttujan yli_keskiarvon arvoa kasvatetaan yhdellä. Lopuksi tulostamme yli keskiarvon olevat pituudet lauseella: cout << "Oli " << yli_keskiarvon << (yli_keskiarvon == 1? " pituus " : " pituutta ") << "yli keskiarvon." Ehto-operaattorin avulla muutamme tulostuksen yksikkö- tai monikkomuotoon. 183

C++ Ohjelmoijan käsikirja Taikanumeroiden välttäminen Käsittelimme jo luvussa 2, että taikanumerot eivät ole toivottuja ohjelmissasi. Käytimme kuitenkin juuri sellaista edellisessä esimerkissä: taulukon pituus koko. Voisimme välttää tämän esittelemällä ja alustamalla vakion, joka sisältää haluamamme taulukon koon: const int max_pituuksia = 10; //Taulukon koko Nyt voisimme määritellä taulukon tämän vakion avulla: int pituus[max_pituuksia]; //Pituuksien taulukko Ohjelmassa on kaksi muutakin kohtaa, joissa pitää tehdä muutoksia: do-while -silmukan ehtolauseke ja if-lause: do cout << endl << "Syötä pituus kokonaisina tuumina: "; cin >> pituus[laskuri++]; // Tarkistetaan halutaanko syöttää lisää pituuksia cout << "Syötätkö lisää (k tai e)? "; cin >> vastaus; } while(laskuri < max_pituuksia && tolower(vastaus) == 'k'); // Ilmoitetaan, jos taulukko on täynnä if(laskuri == max_pituuksia) cout << endl << "Taulukko on täynnä." Nyt ohjelmassa ei ole taikanumeroita ja jos haluaisimme muuttaa taulukon kokoa, meidän tarvitsisi muuttaa vain vakion max_pituuksia alkuarvoa. Huomaa, että tässä sinun täytyy esitellä max_pituuksia const-tyyppiseksi. Jos et näin tee, kääntäjä ei huoli sitä taulukon kooksi. Taulukon koko voi olla vakiotyyppinen kokonaislukulauseke, mutta kääntäjän tulee pystyä laskemaan sen arvo kokonaisluvuksi, jotta se voi varata riittävän määrän muistia taulukon alkioille. Tämä merkitsee sitä, että lausekkeessa voi olla vain literaalivakioita, const-vakioita ja lueteltujen tyyppien arvoja. Taulukoiden alustaminen Jos haluamme alustaa taulukon, alkioiden arvot sijoitetaan aaltosulkeiden sisään ja sulkulauseke sijoitetaan taulukon nimen esittelyä seuraavan yhtäsuuruusmerkin perään. Seuraavassa on esimerkki taulukon esittelystä ja alustuksesta: int naytteet[5] = 2, 3, 5, 7, 11}; 184 Luettelon arvot vastaavat taulukon indeksejä, eli tässä esimerkissä naytteet[0] arvo on 2, naytteet[1] arvo on 3, naytteet[2] arvo on 5 ja niin edelleen. Alkuarvojen luetteloa kutsutaan alkuarvoluetteloksi.

Taulukot ja merkkijonot Alkuarvoluettelossa ei saa olla enemmän alkuarvoja kuin taulukossa on alkioita, mutta niitä voi olla vähemmän. Jos alkuarvoja on vähemmän, alkuarvot sijoitetaan peräkkäisiin indekseihin alkaen ensimmäisestä alkiota, jonka indeksi on 0. Taulukon alkioille, joille ei luettelossa ole alkuarvoja, asetetaan alkuarvoksi 0. Tämä ei ole kuitenkaan sama kuin alkuarvoluettelon jättäminen kokonaan pois. Ilman alkuarvolistaa taulukon alkiot sisältävät roskaa. C++:n syntaksi sallii tyhjän alkuarvoluettelon, jolloin kaikki alkiot alustetaan nollaksi. Suosittelen kuitenkin, että sijoitat vähintään yhden alkuarvon luetteloon. Kokeile itse - Taulukon alustus Voimme havainnollistaa edellä ollutta varsin yksinkertaisella esimerkillä, joka tulostaa kahden taulukon alkioiden arvot: // Esimerkki 6.2 - Taulukon alustus #include <iostream> #include <iomanip> using namespace std; int main() const int koko = 5; int arvot[koko] = 1, 2, 3}; double roskaa[koko]; cout for(int i = 0 ; i < koko ; i++) cout << " " << setw(12) << arvot[i]; cout for(int i = 0 ; i < koko ; i++) cout << " " << setw(12) << roskaa[i]; cout } return 0; Tässä esimerkissä esittelemme kaksi taulukkoa, joista ensimmäinen, arvot, alustetaan osittain ja toista taulukkoa, roskaa, ei alusteta ollenkaan. Ohjelma tuottaa esimerkiksi seuraavanlaisen tuloksen: 1 2 3 0 0 4.24399e-314 2.2069e-312 1.11216e-306 1.81969e-307 1.99808e-307 Tulostuksen toinen rivi (arvot roskaa[0] - roskaa[4]) voi hyvinkin olla aivan eri sinun tietokoneessasi. 185

C++ Ohjelmoijan käsikirja Kuinka se toimii Kuten tulostuksesta huomaat, taulukon arvot kolme ensimmäistä alkiota sisältävät alustetut arvot ja kaksi viimeistä alkiota sisältävät arvon 0. Taulukon roskaa kohdalla kaikki alkioiden arvot ovat roskaa, koska emme antaneet sille alkuarvoja lainkaan. Taulukon alkioiden sisältö on arvoja, joita viimeksi näissä muistipaikoissa säilytettiin. Taulukon alkioiden alustaminen nollaksi Taulukon alkioiden alustaminen nollaksi on parempi ratkaisu kuin antaa niissä olla roskaa. Tämän voimme tehdä jo edellä mainitulla tavalla. Esimerkiksi edellisessä esimerkissä olisimme voineet alustaa taulukon roskaa nollilla seuraavalla lauseella: double roskaa[koko] = 0}; Tai tällä lauseella: double roskaa[koko] = }; //Alustetaan alkiot nolliksi //Alustetaan alkiot nolliksi Alkuarvo 0 muunnetaan aina taulukon tyyppiä vastaavaksi. Ensimmäisessä näistä kahdesta lauseesta asetetaan ensimmäisen alkion alkuarvoksi 0. Myöskin seuraavat alkiot alustetaan nolliksi, koska niille ei ole annettu erikseen alkuarvoa. Käytä mieluummin ensimmäistä muotoa, koska mielestäni se selventää aikomuksesi paremmin. Taulukon koon määrittely alkuarvoluettelolla Taulukon esittelyssä voit jättää myös taulukon koon ilmoittamatta, jos alustat taulukon alkuarvoluettelolla. Taulukon alkioiden lukumääräksi tulee tällöin sama kuin on alkuarvoluettelon arvojen lukumäärä. Tarkastellaan esimerkiksi seuraavaa esittelyä: int arvot[] = 2, 3, 4}; Tämä määrittelee taulukon, jossa on kolme alkiota ja joiden alkuarvo ovat 2, 3 ja 4. Se on sama kuin kirjoittaisit: int arvot[3] = 2, 3, 4}; 186 Ensimmäisen muodon etuna on se, että et voi saada väärää taulukon kokoa, koska kääntäjä määrittelee sen puolestasi! On kuitenkin tärkeää huomata, että C++ ei salli taulukoita, joiden koko on nolla, joten alkuarvoluettelossa tulee olla vähintään yksi alkuarvo, jos jätät taulukon koon pois. Taulukon alkioiden lukumäärän laskeminen Näimme aikaisemmin, kuinka voimme välttää taikanumerot taulukon alkioiden lukumäärän kohdalla määrittelemällä vakion, jonka alkuarvo on taulukon koko. Emme kuitenkaan voi käyttää tätä, jos annamme kääntäjän päättää taulukon koon alkuarvoluettelon arvojen perusteella. Näimme luvussa 3, että sizeof()-operaattori palauttaa muuttujan viemän muistimäärän. sizeof()-operaattoria voidaan käyttää myös taulukon alkioiden lukumäärän selville saamiseksi. Oletetaan, että olemme esitelleet taulukon seuraavasti:

Taulukot ja merkkijonot int arvot[] = 2, 3, 5, 6, 11, 13, 17, 19}; Lausekkeen sizeof arvot arvona on koko taulukon viemä muistimäärä. Lausekkeen sizeof arvot[0] arvo on puolestaan yhden alkion viemä muistimäärä - tässä tapauksessa ensimmäisen alkion muistimäärän, mutta muukin alkio käy, sillä nehän ovat kaikki saman kokoisia. Eli lausekkeen sizeof arvot / sizeof arvot[0] arvo on taulukon alkioiden lukumäärä. Kokeillaanpa tätä esimerkin avulla. Kokeile itse - Taulukon alkioiden lukumäärän laskeminen Seuraavassa on hyvin yksinkertainen esimerkki, jossa käytetään edellä esitettyä tekniikkaa: // Esimerkki 6.3 - Taulukon alkioiden lukumäärän laskeminen #include <iostream> using namespace std; int main() int arvot[] = 2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; cout << endl << "Taulukossa " << sizeof arvot / sizeof arvot[0] << " alkiota." int summa = 0; for(int i = 0 ; i < sizeof arvot / sizeof arvot[0] ; summa += arvot[i++]) ; cout << "Taulukon alkioiden summa on " << summa return 0; } Ohjelman tulostus näyttää seuraavalta: Taulukossa on 10 alkiota. Taulukoiden alkioiden summa on 129 Kuinka se toimii Kääntäjä määrittelee taulukon alkioiden lukumäärän alkuarvoluettelon arvojen perusteella: int arvot[] = 2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; Taulukon esittelyn jälkeen tulostamme taulukon alkioiden lukumäärän lauseella: cout << endl << "Taulukossa " << sizeof arvot / sizeof arvot[0] << " alkiota." 187

C++ Ohjelmoijan käsikirja Kuten aiemmin käsittelimme, käytämme sizeof()-operaattoria alkioiden lukumäärän laskennassa. Taulukko on tässä int-tyyppinen (joten olisimme voineet käyttää sizeof(int) sizeof arvot[0] sijaan), mutta tämä lauseke tuottaa oikean alkioiden lukumäärän riippumatta taulukon tyypistä. Todistaaksemme, että tämä toimii, käytämme samaa lauseketta uudelleen for-silmukassa: int summa = 0; for(int i = 0 ; i < sizeof arvot / sizeof arvot[0] ; summa += arvot[i++]) ; Tämä silmukka summaa toimintalausekkeessaan summa += arvot[i++] taulukon alkiot yhteen. Se myöskin kasvattaa laskurimuuttujan i arvoa, kun nykyinen alkio on lisätty muuttujaan summa. Itse silmukan lause on tyhjä, mikä näkyy seuraavalla rivillä yksinään olevasta puolipisteestä. Lopuksi tulostamme muuttujan summa arvon lauseella: Merkkitaulukot cout << "Taulukon alkioiden summa on " << summa char-tyyppisellä taulukolla voi olla kaksi persoonallisuutta. Se voi yksinkertaisesti olla taulukollinen merkkejä, jossa jokainen alkio sisältää yhden merkin, tai se voi esittää merkkijonoa. Jälkimmäisessä tapauksessa jokainen merkkijonon merkki sijoitetaan yhteen taulukon alkioon ja merkkijonon päättyminen ilmoitetaan erillisellä päättymismerkillä \0, jota kutsutaan nullmerkiksi. Tätä merkkijonojen esitystapaa kutsutaan C-tyyliseksi merkkijonoksi; vastakohtana on C++:n standardikirjastossa määritelty string-tyyppi. string-tyyppiset merkkijonot eivät tarvitse nullmerkkiä. Nyt käsittelemme C-tyylisiä merkkijonoja pelkästään taulukoiden yhtenä muotona. Palaamme string-tyyppiin myöhemmin tässä luvussa. Tulet huomaamaan, että tyypin string käyttö on tehokkaampaa ja miellyttävämpää kuin char-tyyppisten taulukoiden käyttö. Voimme alustaa merkkitaulukon seuraavanlaisella lauseella: char vokaalit[5] = a, e, i, o, u }; Jokainen taulukon alkio alustetaan alkuarvoluettelon mukaan. Kuten numeeristenkin taulukoiden kohdalla, jos alkuarvoluettelossa on vähemmän arvoja kuin taulukossa on alkioita, loput alkiot alustetaan nollalla - eli tässä null-merkeillä, joiden bitit ovat kaikki nollia, ei merkillä 0. Voit myöskin jättää taulukon koon määrittelyn kääntäjän huoleksi: char vokaalit[5] = a, e, i, o, u }; //Viisi alkiota Voimme esitellä taulukon char-tyyppiseksi ja alustaa sen merkkijonoliteraalilla: 188 char nimi[10] = Mae West ;

Taulukot ja merkkijonot Koska alustimme taulukon merkkijonoliteraalilla, null-merkki lisätään automaattisesti, joten taulukon sisältö on: M A E W E S T \0 \0 Myöskin viimeiseen alkioon sijoitetaan null-merkki, koska sille ei ole alkuarvoluettelossa alkuarvoa. Kuten jo mainittua, voit jättää taulukon koon määrittelemisen kääntäjän huoleksi: char nimi[] = Mae West ; Tällöin taulukossa on yhdeksän alkiota: kahdeksan alkiota sisältää merkkijonon merkit ja lisämerkkinä on merkkijonon päättävä null-merkki. Olisimme voineet käyttää tätä muotoa myös esitellessämme taulukon vokaalit: char vokaalit[] = aeiouäö ; //Kahdeksan alkiota Tämän esittelyn ja aikaisemman esittelyn välinen ero on siinä, että. nyt alustamme sen merkkijonoliteraalilla. Siihen on lisätty \0 -merkki kuvaamaan merkkijonon loppua, joten taulukkoon vokaalit tulee kuusi alkiota. Voit tulostaa taulukkoon sijoitetun merkkijonon käyttämällä pelkästään taulukon nimeä. Taulukossamme nimi oleva merkkijono voitaisiin siis tulostaa lauseella: cout << nimi Tämä tulostaa kaikki merkkijonon merkit \0 -merkkiin saakka. Merkkijono lopussa täytyy olla \0. Jos sitä ei ole, tulostat peräkkäisten muistipaikkojen merkkejä kunnes null-merkki löytyy tai tapahtuu laiton muistiviittaus. Et voi tulostaa numeerista taulukkoa pelkästään taulukon nimen avulla. Tämä tulostustapa toimii pelkästään char-tyyppisillä taulukoilla. Kokeile itse - Merkkijonon analysointi Katsotaan seuraavaksi, kuinka voimme käyttää char-tyyppistä taulukkoa. Tämä ohjelma lukee rivin tekstiä ja tulostaa siinä olevien vokaalien ja konsonanttien lukumäärän. // Esimerkki 6.4 - Merkkijonon merkkien analysointi #include <iostream> #include <cctype> using namespace std; int main() const int maxpituus = 100; char teksti[maxpituus] = 0}; // Taulukon koko // Syötetty merkkijono 189

C++ Ohjelmoijan käsikirja cout << endl << "Syötä tekstirivi:" // Lue rivillinen merkkejä välilyönnit mukaan lukien cin.getline(teksti, maxpituus); cout << "Syötteesi oli:" << endl << teksti int vokaaleja = 0; // Vokaalien lukumäärä int konsonantteja = 0; // Konsonanttien lukumäärä for(int i = 0 ; teksti[i]!= '\0' ; i++) if(isalpha(teksti[i])) // Onko merkki? switch(tolower(teksti[i])) // Testataan pienenä merkkinä case 'a': case 'e': case 'i': case 'o': case 'u': vokaaleja++; break; default: konsonantteja++; } cout << "Syötteessäsi oli " << vokaaleja << " vokaalia ja " << konsonantteja << " konsonanttia." return 0; } Seuraavassa on ohjelman esimerkkitulostus: Syötä tekstirivi: Rikas mies ei ole muuta kuin köyhä mies, jolla on rahaa. Syötteesi oli: Rikas mies ei ole muuta kuin köyhä mies, jolla on rahaa. Syötteessäsi oli 21 vokaalia ja 22 konsonanttia. // Se oli vokaali // Se oli konsonantti Kuinka se toimii Esittelemme char-tyyppisen taulukon, jossa on const-tyyppisessä muuttujassa määritelty määrä alkioita: const int maxpituus = 100; char teksti[maxpituus] = 0}; // Taulukon koko // Syötetty merkkijono Talletamme syötteen taulukkoon teksti. Emme kuitenkaan voi käyttää normaalia syöttötapaamme (>>-operaattoria), koska se ei tee sitä mitä haluamme tässä tapauksessa tehdä. Tutkitaan seuraavaa lausetta: cin >> teksti; 190 Tämä kyllä lukee merkit teksti-taulukkoon, mutta vain ensimmäiseen välilyöntiin saakka. >>operaattori tulkitsee välilyönnin arvojen erotinmerkkinä, joten se ei lue koko merkkijonoa, jossa on välilyöntejä. Emme voi käyttää >>-operaattoria edes lukemaan syötettä merkki kerrallaan,

Taulukot ja merkkijonot koska kaikki tyhjät merkit, \n mukaan lukien tulkitaan erotinmerkiksi. Tämä tarkoittaa sitä, että emme voi tallettaa rivinvaihtomerkkiä, joten emme voi käyttää sitä ilmaisemaan merkkijonon päättymistä. Kun haluamme lukea kokonaisen tekstirivin, välilyönnit mukaan lukien, tarvitsemme muunlaisen toteutustavan kuin standardi syöttövirta tarjoaa. Tästä syystä syötteen pyytämisen jälkeen luemme rivin standardista syöttövirrasta lauseella: cin.getline(teksti, maxpituus); cin-virran getline()-funktio lukee ja tallettaa kokonaisen rivin merkkejä, välilyönnit mukaan lukien. Syöttö päättyy, kun \n -merkki luetaan, eli kun käyttäjä painaa RETURN-näppäintä. Poiketen muista näkemistämme funktioista, getline()-funktiolle välitetään kaksi parametriä. Merkkijono sijoitetaan niistä ensimmäisen osoittamaan muistipaikkaan; tässä tapauksessa taulukkoon teksti. Toinen parametri on talletettavien merkkien maksimimäärä. Maksimimäärään kuuluu mukaan myös null-merkki, joka lisätään automaattisesti syötetyn merkkijonon perään. Vaikka emme niin tässä tehneetkään, olisimme voineet välittää getline()-funktiolle kolmannenkin parametrin. Sen avulla voitaisiin määritellä jokin muu syötteen päättävä merkki kuin \n. Jos esimerkiksi haluaisit, että huutomerkki päättäisi syötön, voisit käyttää seuraavaa lausetta: cin.getline(teksti, maxpituus,! ); Miksi haluaisit tehdä tämän? Yhtenä syynä voisi olla, että haluat syöttää useamman tekstirivin. Jos! -merkki ilmaisisi syötteen päättymisen, voisit syöttää niin monta riviä tekstiä kuin haluaisit, \n -merkki mukaan lukien. Syötät vain! -merkit, kun olet valmis. Luonnollisesti syötettävien merkkien lukumäärää rajoittaa maxpituus. Palataan takaisin esimerkkiin. Näytämme vielä, että todella voimme tulostaa juuri syötetyn merkkijonon: cout << "Syötteesi oli:" << endl << teksti Nyt, kun olemme lukeneet ja tulostaneet syötetyn rivin, analysoimme merkkijonon varsin suoraviivaisesti: int vokaaleja = 0; int konsonantteja = 0; for(int i = 0 ; teksti[i]!= '\0' ; i++) if(isalpha(teksti[i])) switch(tolower(teksti[i])) case 'a': case 'e': case 'i': case 'o': case 'u': vokaaleja++; break; default: konsonantteja++; } // Vokaalien lukumäärä // Konsonanttien lukumäärä // Onko merkki? // Testataan pienenä merkkinä // Se oli vokaali // Se oli konsonantti 191

C++ Ohjelmoijan käsikirja Laskemme vokaalien ja konsonanttien lukumäärän kahteen tässä esittelemäämme muuttujaan. for-silmukan ehtolausekkeessa testataan, oliko merkki merkkijonon päättävä null-merkki. Silmukassa käytämme isalpha()-funktiota tarkastamaan, että merkki on kirjain. Jos näin on, käytämme kirjaimen pientä versiota switch-lauseen ehtolausekkeessa. Koska suoritamme switchlauseen vain, jos teksti[i] on kirjain, ja koska kirjain on konsonantti jos se ei ole vokaali, voimme kasvattaa muuttujan konsonantteja arvoa default-osassa. Lopuksi tulostamme lasketut lukumäärät: cout << "Syötteessäsi oli " << vokaaleja << " vokaalia ja " << konsonantteja << " konsonanttia." Moniulotteiset taulukot Tähän saakka esittelemissämme taulukoissa on tarvittu vain yhtä indeksiä alkion valinnassa. Tällaista taulukkoa kutsutaan yksiulotteiseksi taulukoksi, koska yhden indeksin avulla voidaan viitata kaikkiin taulukon alkioihin. Voit kuitenkin määritellä taulukoita, joissa tarvitaan kahta tai useampaa indeksiä alkioon viittaamiseksi. Sellaisia kutsutaan yleisesti moniulotteisiksi taulukoiksi. Taulukkoa, jonka alkioihin viittaamiseksi tarvitaan kahta indeksiä, kutsutaan kaksiulotteiseksi taulukoksi. Taulukko, jossa on kolme indeksiä, on kolmiulotteinen taulukko ja niin edelleen aina niin moneen ulottuvuuteen saakka kuin saatatkin tarvita. Kuvitellaan, että olet ahne puutarhanhoitaja, ja haluat pitää kirjaa pienessä puutarhassasi olevien yksittäisten porkkanoiden painoista. Koska porkkanasi kasvavat kolmessa rivissä ja jokaisessa rivissä on neljä porkkanaa, voit esitellä kaksiulotteisen taulukon tähän tarkoitukseen: double porkkanat[3][4]; Viitataksesi taulukon porkkanat yhteen alkioon, tarvitset kaksi indeksin arvoa: ensimmäinen indeksi määrittelee rivin (0-2) ja toinen indeksi määrittelee porkkanan tässä rivissä (0-3). Tallettaaksesi toisen rivin kolmannen porkkanan painon, voit kirjoittaa: porkkanat[1][2] = 1.5; 192

Taulukot ja merkkijonot Tämän taulukon rakenne muistissa esitetään seuraavassa kaaviossa: double porkkanat[3][4]; Tämä rivi on porkkanat[0] Porkkanat[0][0] Porkkanat[0][1] Porkkanat[0][2] Porkkanat[0][3] Tämä rivi on porkkanat[1] Porkkanat[1][0] Porkkanat[1][1] Porkkanat[1][2] Porkkanat[1][3] Tämä rivi on porkkanat[2] Porkkanat[2][0] Porkkanat[2][1] Porkkanat[2][2] Porkkanat[2][3] Porkkanat viittaa koko taulukkoon. Rivit talletetaan muistissa peräkkäin. Kuten kaaviosta huomaat, kaksiulotteinen taulukko on itse asiassa yksiulotteinen taulukko, jossa on kolme alkiota, joista jokainen on nelialkioinen yksiulotteinen taulukko. Eli se on taulukko, jossa on kolme neljänmittaista taulukkoa. Kun viittaamme yhteen alkioon, käytämme kahta indeksiä. Oikeanpuoleinen indeksi valitsee rivillä olevan alkion. Tämän indeksin arvo muuttuu useammin. Jos luet taulukon oikealta vasemmalle, oikeanpuoleinen indeksi vastaa sarakkeen numeroa. Taulukoissa, joissa on enemmän kuin kaksi ulottuvuutta, oikeanpuoleisin indeksi on se, joka muuttuu kaikkein useimmin ja vasemmanpuoleisin kaikkein vähiten. Kuten mainittua, voit käyttää taulukon nimeä ja yhtä hakasulkeiden sisällä olevaa indeksiä viitataksesi taulukon kokonaiseen riviin. Tähän palaamme luvussa 8, kun käsittelemme funktioita. Taulukon nimi puolestaan viittaa koko taulukkoon. Huomaa, että tämän taulukon kohdalla et voi tulostaa riviä etkä koko taulukkoa tällä tekniikalla. Esimerkiksi rivi cout << porkkanat; //Ei tee sitä, mitä odotat! tulostaa yhden heksadesimaalisen arvon, joka on itse asiassa taulukon ensimmäisen alkion muistiosoite. Näemme seuraavassa luvussa, kun käsittelemme osoittimia, miksi näin on. chartyyppiset taulukot ovat hieman erilaisia, kuten näimme aikaisemmin. Tulostaaksesi koko taulukon, rivi riviltä, sinun tulee kirjoittaa jotain tällaista: for(int i = 0 ; i < 3 ; i++) for(int j = 0 ; j < 4 ; j++) cout << setw(12) << porkkanat[i][j]; cout } 193

C++ Ohjelmoijan käsikirja Tässä käytetään taikanumeroita 3 ja 4. Voimme välttää tämän käyttämällä sizeof()- operaattoria: for(int i = 0 ; i < sizeof porkkanat / sizeof porkkanat[0] ; i++) for(int j = 0 ; j < sizeof porkkanat[0] / sizeof(double) ; j++) cout << setw(12) << porkkanat[i][j]; cout } Olisi kuitenkin ollut vielä parempaa olla käyttämättä taikanumeroita alkujaankaan. Meidän tulisi koodata tämä esimerkki vielä siistimmin: const int nrivit = 3; const int nsarakkeet = 4; double porkkanat[nrivit, nsarakkeet]; // Taulukon alkioiden arvojen asetus... for(int i = 0 ; i < nrivit ; i++) for(int j = 0 ; j < nsarakkeet ; j++) cout << setw(12) << porkkanat[i][j]; cout } Taulukon esitteleminen kolmiulotteiseksi vaatii vain yhdet hakasulkeet lisää. Voit esimerkiksi haluta tallettaa kolme lämpötilalukemaa jokaiselta päivältä, 7 päivää viikossa ja 52 viikkoa vuodessa. Voisit esitellä seuraavanlaisen long-tyyppisen taulukon: long lampotilat[52][7][3]; Taulukossa on kolme arvoa kullakin rivillä. Koko viikon tiedoissa on seitsemän tällaista riviä ja 52 tällaista viikkoa koko vuodessa. Tässä taulukossa on kokonaisuudessaan 1092 long-tyyppistä alkiota. Tulostaaksesi viikon 26 3. päivän keskimmäisen lämpötilalukeman, voit kirjoittaa: cout << lampotilat[25][2][1]; Muista, että kaikkien indeksien ensimmäinen arvo on 0, joten viikkojen numerot ovat tässä 0-51, päivät 0-6 ja päivän lukemat 0-2. 194 Moniulotteisten taulukoiden alustus Tapa jolla määrittelet moniulotteisen taulukon alkuarvot, juontaa juurensa siihen, että kaksiulotteinen taulukko on taulukko, jonka alkioina on yksiulotteisia taulukoita. Yksiulotteisen taulukon alkuarvot kirjoitetaan aaltosulkeiden sisään pilkuilla eroteltuina. Tästä seuraa se, että voimme esitellä ja alustaa taulukon porkkanat esimerkiksi seuraavanlaisella lauseella: double porkkanat[3][4] = }; 2.5, 3.2, 3.7, 4.1}, // 1. rivi 4.1, 3.9, 1.6, 3.5}, // 2. rivi 2.8, 2.3, 0.9, 1.1} // 3. rivi

Taulukot ja merkkijonot Kukin rivi on yksiulotteinen taulukko, joten kunkin rivin alkuarvot on kirjoitettu omien aaltosulkeiden sisään. Nämä kolme alkuarvoluetteloa on sitten yhdistetty aaltosulkeiden sisään, koska kaksiulotteinen taulukko on yksiulotteinen taulukko, joka sisältää yksiulotteisia taulukoita. Tätä periaatetta voidaan laajentaa vaikka kuinka monille ulottuvuuksille - jokainen lisäulottuvuus vaatii uuden aaltosulkeiden ja alkuarvojen tason. Kysymys, joka pitäisi nyt heti tulla mieleen on, Mitä tapahtuu, jos jätät joitakin alkuarvoja pois? Vastaus on juuri sellainen kuin voit odottaakin edellä olleen perusteella. Jokainen sisimmistä aaltosulkeiden pareista sisältää rivien alkuarvoluettelot. Ensimmäinen luettelo vastaa riviä porkkanat[0], toinen riviä porkkanat[1] ja kolmas vastaa riviä porkkanat[2]. Aaltosulkeiden sisällä olevat arvot sijoitetaan rivin vastaaviin elementteihin. Jos rivin jokaiselle alkiolle ei ole alkuarvoa alkuarvoluettelossa, loput alkiot alustetaan arvolla 0. Katsotaan esimerkkiä: double porkkanat[3][4] = 2.5, 3.2 }, // 1. rivi 4.1 }, // 2. rivi 2.8, 2.3, 0.9 } // 3. rivi }; Ensimmäisen rivin kahdella ensimmäisellä alkiolla on alkuarvot lueteltu, kun taas toisella rivillä vain ensimmäisellä alkiolla on alkuarvo. Kolmannella rivillä on kolmella ensimmäisellä alkiolla alkuarvot lueteltu. Alkiot alustetaan siis seuraavasti: porkkanat[0][1] porkkanat[0][2] porkkanat[0][3] 2.5 3.2 0.0 0.0 porkkanat[1][0] porkkanat[1][1] porkkanat[1][2] porkkanat[1][3] 4.1 0.0 0.0 0.0 porkkanat[2][0] porkkanat[2][1] porkkanat[2][2] porkkanat[2][3] 2.8 2.3 0.9 0.0 Kuten huomaat, alkioille, joilla ei ole alkuarvoa lueteltu, on asetettu alkuarvoksi 0. Jos et kirjoita alkuarvoluetteloa taulukon jokaista riviä varten, rivit, joilla ei ole alkuarvoluetteloa lainkaan, alustetaan nollalla. Tästä seuraa se, että voit alustaa taulukon kaikki alkiot nollalla seuraavanlaisella lauseella: double porkkanat[3][4] = 0}; Jos kirjoitat alkuarvoluetteloon arvoja, mutta et kirjoita sen sisälle aaltosulkeita rivejä varten, alkuarvot sijoitetaan peräkkäin alkioille sen mukaan, miten ne talletetaan muistissa - eli oikeanpuoleisimman indeksin muuttuessa nopeiten. Oletetaan, että esittelemme taulukon seuraavasti: double porkkanat[3][4] = 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7}; 195

C++ Ohjelmoijan käsikirja Taulukon alkiot alustetaan seuraavasti: porkkanat[0][1] porkkanat[0][2] porkkanat[0][3] 1.1 1.2 1.3 1.4 porkkanat[1][0] porkkanat[1][1] porkkanat[1][2] porkkanat[1][3] 1.5 1.6 1.7 0.0 porkkanat[2][0] porkkanat[2][1] porkkanat[2][2] porkkanat[2][3] 0.0 0.0 0.0 0.0 Alkuarvot asetetaan peräkkäisiin alkioihin. Loput arvot alustetaan arvolla 0. Ulottuvuuden määrittely oletusarvoisesti Voit antaa kääntäjän määritellä minkä tahansa taulukon ensimmäisen (vasemmanpuoleisimman) ulottuvuuden alkuarvoluettelon mukaan. Olisimme voineet määritellä kaksiulotteisen taulukkomme porkkanat lauseella: double porkkanat[][4] = 2.5, 3.2 }, // 1. rivi 4.1 }, // 2. rivi 2.8, 2.3, 0.9 } // 3. rivi }; Tässä muodostuu kolme riviä, kuten aikaisemminkin, koska ulommaisten aaltosulkeiden sisällä on kolme alkuarvoluetteloa. Jos niitä olisi vain kaksi, taulukkoon muodostuisi kaksi riviä. Eli lauseke double porkkanat[][4] = 2.5, 3.2 }, // 1. rivi 4.1 }, // 2. rivi }; luo samanlaisen taulukon kuin jos olisimme esitelleet sen seuraavasti: 196 double porkkanat[2][4] = 2.5, 3.2 }, // 1. rivi 4.1 }, // 2. rivi }; Kolmi- tai useampiulotteiset taulukot voidaan esitellä siten, että kääntäjä määrittelee ensimmäisen ulottuvuuden alkuarvoluetteloiden perusteella. Seuraavassa on esimerkki kolmiulotteisen taulukon esittelystä: int numerot[][3][4] = }, 2, 4, 6, 8}, 3, 5, 7, 9}, 5, 8, 11, 14}

Taulukot ja merkkijonot 12, 14, 16, 18}, 13, 15, 17, 19}, 15, 18, 21, 24} } }; Tässä taulukossa on kolme ulottuvuutta, joiden koot ovat 2, 3 ja 4. Uloimmat aaltosulkeet sisältävät kaksi aaltosuljeryhmää, joista jokainen puolestaan sisältää kolme alkuarvoluetteloa, jotka jokainen sisältävät neljä alkuarvoa. Kuten tämä esimerkkikin osoittaa, kolme- tai useampiulotteisten taulukoiden käsittely monimutkaistuu nopeasti ja sinun tulee olla erittäin huolellinen sijoittaessasi alkuarvoja aaltosulkeiden sisään. Aaltosulkeita voidaan kirjoittaa niin moneen tasoon kuin taulukossa on ulottuvuuksia. Moniulotteiset merkkitaulukot Voit esitellä kaksi- tai useampiulotteisia taulukoita, jotka sisältävät minkä tahansa tyyppistä tietoa. char-tyyppinen kaksiulotteinen taulukko on erityisen kiinnostava, koska se voi olla merkkijonojen taulukko. Kun alustat kaksiulotteisen char-tyyppisen taulukon lainausmerkkien sisällä olevilla merkkijonoilla, et tarvitse aaltosulkeita - lainausmerkit toimivat tässä tapauksessa aaltosulkeina. Esimerkiksi: char tahdet[6][80] = "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy" }; Taulukon kukin rivi sisältää merkkijonon, joka on elokuvatähden nimi. Merkkijonon päättävä null-merkki \0 lisätään automaattisesti kuhunkin merkkijonoon. Kokeile itse - Kaksiulotteinen merkkitaulukko Voimme havainnollistaa edellä ollutta taulukkoa esimerkin avulla. Tämä ohjelma valitsee tähtinäyttelijäsi syöttämäsi kokonaisluvun perusteella: // Esimerkki 6.5 - Merkkijonojen talletus taulukkoon #include <iostream> using namespace std; int main() char tahdet[][80] = "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Sharon Stone" 197

C++ Ohjelmoijan käsikirja int valinta = 0; }; cout << endl << "Valitse tähtesi!" << " Syötä numero väliltä 1 ja " << sizeof tahdet / sizeof tahdet[0] << ": "; cin >> valinta; if(valinta >= 1 && valinta <= sizeof tahdet / sizeof tahdet[0]) cout << endl << "Tähtinäyttelijäsi on " << tahdet[valinta - 1]; else cout << endl // Syöte ei kelpaa << "Valitan, tähtinäyttelijääsi ei löydy."; cout return 0; } Ohjelman tulostus voisi näyttää seuraavalta: Valitse tähtesi! Syötä numero väliltä 1 ja 8: 6 Tähtinäyttelijäsi on Mae West Kuinka se toimii Jos jätetään ohjelman viihteellinen vaikutus huomioimatta, kiinnostavin kohta ohjelmassa on taulukko tahdet. Se on kaksiulotteinen char-tyyppinen taulukko, jossa voi olla useita merkkijonoja, joissa voi null-merkki mukaan lukien olla maksimissaan 80 merkkiä. Merkkijonojen alkuarvot on sijoitettu aaltosulkeiden sisään ja eroteltu toisistaan pilkulla: char tahdet[][80] = }; "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Sharon Stone" Koska olemme jättäneet kertomatta taulukon ensimmäisen ulottuvuuden koon, kääntäjä luo taulukon, johon mahtuu kaikki alustusluettelossa olevat merkkijonot. Kuten näimme aikaisemmin, voimme jättää vain ensimmäisen ulottuvuuden kertomatta. Kaikkien muiden ulottuvuuksien koko tulee kertoa. Pyydämme kokonaisluvun käyttäjältä lauseella: cout << endl << "Valitse tähtesi!" << " Syötä numero väliltä 1 ja " << sizeof tahdet / sizeof tahdet[0] << ": "; 198

Taulukot ja merkkijonot Syötettävän kokonaisluvun maksimiarvo määritellään lausekkeella sizeof tahdet / sizeof tahdet[0]. Tämän lausekkeen tulos on taulukon rivien lukumäärä, joten lause mukautuu automaattisesti mahdollisesti taulukon nimien lukumäärään tekemääsi muutoksiin. Käytämme samaa tekniikkaa if-lauseessa, joka valitsee tulostettavan viestin: if(valinta >= 1 && valinta <= sizeof tahdet / sizeof tahdet[0]) cout << endl << "Tähtinäyttelijäsi on " << tahdet[valinta - 1]; else cout << endl // Syöte ei kelpaa << "Valitan, tähtinäyttelijääsi ei löydy."; if-lauseen ehtolauseke testaa, onko syötetty kokonaisluku jo aikaisemmin tulostetulla arvovälillä. Kun haluamme tulostaa merkkijonon, meidän tulee viitata siihen pelkällä ensimmäisellä indeksillä. Yhdellä indeksillä valitsemme halutun 80-elementtisen ali-taulukon ja koska se sisältää merkkijonon, tulostusoperaattori tulostaa sen sisällön null-merkkiin saakka. Indeksi annetaan lausekkeella valinta - 1, koska muuttujan valinta arvot alkavat arvosta 1, kun taas indeksit alkavat arvosta 0. Tämä on varsin yleinen toimintatapa, kun ohjelmoimme taulukoiden kanssa.! Taulukoiden käyttämisellä edellä kuvatulla tavalla on eräs huono puoli. Tällöin nimittäin jää lähes aina muistia käyttämättä. Kaikki edellä olleet merkkijonot ovat alle 80 merkkisiä, eli varsin suuri määrä alkioita jää käyttämättä. Seuraavassa luvussa käsittelemme parempaa tapaa käsitellä null-merkkiin päättyviä merkkijonoja. Paremmat merkkijonot Olemme nähneet, kuinka char-tyyppistä taulukkoa voidaan käyttää null-merkkiin päättyvien (C-tyylisten) merkkijonojen tallettamiseen. Tämä ei kuitenkaan ole C++:lle ominaista, joten sille on parempikin vaihtoehto. Otsikkotiedosto string määrittelee string-tyypin, jolla on ominaisuuksia, jotka tekevät sen käsittelystä helpomman kuin null-merkkiin päättyvien merkkijonojen käsittely on. string-tyyppi on määritelty luokkana (tai tarkemmin sanottuna mallina). Vaikka emme olekaan vielä käsitelleet luokkia, sillä ei ole vaikutusta tässä, koska luokka itse asiassa yksinkertaisesti lisää kieleen uuden tyypin. Käytännössä luokka-perustaisen tyypin käyttäminen ei eroa mitenkään perustietotyypin käytöstä - sinun tulee vain tietää, mihin luokka-perustaiset tyypit pystyvät. Luokkatyypin ilmentymää kutsutaan yleensä olioksi (eikä muuttujaksi), joten käytämme tätä terminologiaa string-tyypin yhteydessä. Tässä osassa on muutama asia, joita emme vielä voi selittää tarkkaan, mutta voit silti verrata string-tyyppiä null-merkkiin päättyviin merkkijonoihin ja huomata, että on yleensä aina parempi käyttää string-tyyppiä. Vaikka string-tyyppi ei olekaan yksi perustietotyypeistä, se on osa ANSI-standardia, joten voit aivan hyvin ajatella sen olevan yksi aina käytettävissä olevista tyypeistä. Voit käsitellä stringtyyppisiä olioita lähes samaan tapaan kuin muitakin perustietotyyppien muuttujia. Aloitetaan ensin string-tyyppisen olion luonnilla. 199

C++ Ohjelmoijan käsikirja string-tyyppisten olioiden esittely string-tyyppinen olio sisältää char-tyyppisiä merkkejä. Se voi olla myös tyhjä merkkijono. Voit esitellä string-tyyppisen olion lauseella: string omamerkkijono; //Esittelee tyhjän merkkijonon Tämä lause esittelee string-tyyppisen olion omamerkkijono. Tässä tapauksessa omamerkkijono on tyhjä string-tyyppinen olio - eli se esittää merkkijonoa, jossa ei ole yhtään merkkiä ja jonka pituus on nolla. Voit esitellä ja alustaa string-tyyppisen olion merkkijonoliteraalilla: string sananlasku = Siellä, tuolla ja jokapuolella ; Tässä sananlasku on string-tyyppinen olio, joka sisältää merkkijonoliteraalissa olleen merkkijonon. Voisit käyttää myös funktiomuotoista merkintätapaa olion alustuksessa kirjoittamalla edellisen esimerkin seuraavasti: string sananlasku( Siellä, tuolla ja jokapuolella ); Talletettava merkkijono ei tarvitse null-merkkiä. string-tyyppinen olio pitää kirjaa sen sisältämän merkkijonon sisällöstä. Saat selville string-tyyppisen olion pituuden sen length()-funktiolla, jolle ei välitetä mitään parametrejä. Esimerkiksi: cout << sananlasku.length(); Tämä lause kutsuu sananlasku-olion length()-funktiota ja tulostaa palautetun arvon cout-virran avulla. Se tulostaa sananlasku-olioon talletetun merkkijonon pituuden, joka on tässä tapauksessa 30. Lausekkeessa sananlasku.length() olevaa pistettä kutsutaan pisteoperaattoriksi. Se identifioi length()-funktion sananlasku-olion jäsenfunktioksi. Tästä aiheesta saat lisää tietoa, kun käsittelemme omien tietotyyppien luomista luvussa 11. string-tyyppisen olion alustamiseen on muitakin tapoja. Et voi alustaa string-oliota yhdellä merkillä, mutta voit alustaa sen useammalla samalla merkillä (mukaan lukien yksi kappale!). Mitä ihmettä tämä sitten tarkoittaa? Voit siis esitellä ja alustaa nukkumatin string-olion lauseella: string nukkuu(6, z ); Tässä string-olio nukkuu sisältää merkkijonon zzzzzz. Jos haluat, voit alustaa merkkijonon nukkuu vain yhdellä merkillä: string nukkuu(1, z ); Tämä alustaa olion nukkuu merkkijonoliteraalilla z. Et voi kuitenkaan kirjoittaa: string nukkuu = z ; //Väärin! Ei käänny! Voit myös alustaa string-olion jo olemassa olevalla string-oliolla. Jos sananlasku on esitelty kuten edellä, voit kirjoittaa seuraavan lauseen, joka esittelee uuden olion: 200 string lause = sananlasku;

Taulukot ja merkkijonot lause-olio alustetaan samalla merkkijonoliteraalilla kuin sananlasku-oliokin, joten myös sen sisältö on Siellä, tuolla ja jokapuolella. Tässä tapauksessa voit käyttää myöskin funktiomuotoista merkintätapaa: string lause(sananlasku); string-olion merkit on indeksoitu alkaen arvosta 0 - aivan samaan tapaan kuin taulukotkin. Voimme näin esimerkiksi valita osan olemassa olevasta string-oliosta ja käyttää tätä osaa toisen string-olion alustamiseen. Esimerkiksi lause string lause(sananlasku, 0, 13); monistaa osan sananlasku-oliosta uuteen string-olioon lause. lause S I e l l ä, t u o l l sananlasku S I e l l ä, t u o l l j a j o k a p u o l e l l. a merkkijonon alku merkkien lukumäärä stringlause(sananlasku, 0, 13); Sulkeissa oleva ensimmäinen parametri on string-tyyppisen olion nimi, jota käytämme alustuksen lähteenä. Toinen parametri on tämän lähdemerkkijonon alkuindeksi. Kolmas parametri on alkuarvoksi valittavien merkkien lukumäärä. Joten tässä valitsemme sananlasku-olion 13 ensimmäistä merkkiä alkaen ensimmäisestä merkistä (indeksistä 0) lause-olion alkuarvoksi. Uuden olion sisällöksi tulee siis Siellä, tuoll. string-olioden käsittely Ehkä kaikkein yksinkertaisin operaatio, jonka voit string-tyyppisellä oliolla tehdä on sijoitus. Voit sijoittaa string-olion merkkijonoliteraalin toiseen string-olioon: string adjektiivi = "nukkuva"; string sana = "roskaa"; // Esittelee merkkijonon // Esittelee toisen merkkijonon sana = adjektiivi; adjektiivi = "kaksiaikainen"; // Muuttaa sanaa // Muuttaa adjektiiviä 201

C++ Ohjelmoijan käsikirja Kolmas lause sijoittaa adjektiivi-olion sisällön, joka on nukkuva, sana-olioon, eli roskaa korvataan. Viimeinen lause sijoittaa uuden merkkijonoliteraalin kaksiaikainen adjektiivi-olioon, joten alkuperäinen sisältö nukkuva korvataan. Eli näiden sijoitusten jälkeen sana-olion sisältö on nukkuva ja adjektiivi-olion sisältö on kaksiaikainen. Merkkijonojen yhdistäminen Merkkijonojen yhdistäminen toisiinsa tapahtuu lisäysoperaattorilla. Voimme havainnollistaa yhdistämistä juuri esittelemillämme olioilla: string kuvaus = adjektiivi + + sana + lammas ; Tämän lauseen suorituksen jälkeen kuvaus-olion sisältö on kaksiaikainen nukkuva lammas. Kuten huomaat, voimme yhdistää merkkijonoliteraaleja string-tyyppiseen olioon +- operaattorilla. Huomaa, että et voi yhdistää pelkkiä merkkijonoliteraaleja +-operaattorilla. Toinen operandeista täytyy aina olla string-tyyppinen. Esimerkiksi seuraava lause ei käänny: string kuvaus = lammas + + sana; Ongelmana on tässä se, että kääntäjä yrittää evaluoida sijoituslauseen oikealla puolella olevan muodossa (( lammas + ) + sana), eikä +-operaattori toimi kahden merkkijonoliteraalin kanssa. Järkevä sulkeiden käyttö tekee tästä lauseesta kuitenkin sallitun: string kuvaus = lammas + ( + sana); Tässä jälkimmäinen +-operaatio on sallittu ja sen tuloksena on string-tyyppi, joka voidaan yhdistää merkkijonoliteraaliin lammas. Kokeile itse - Merkkijonojen yhdistäminen Siinä oli riittävästi teoriaa hetkeksi; on käytännön aika. Tämä ohjelma lukee etu- ja sukunimesi: // Esimerkki 6.6 - Merkkijonojen yhdistäminen #include <iostream> #include <string> using namespace std; int main() string etunimi; string sukunimi; cout << endl << "Syötä etunimesi: "; cin >> etunimi; cout << endl << "Syötä sukunimi: "; cin >> sukunimi; // Tallettaa etunimen // Tallettaa sukunimen // Luetaan etunimi // Luetaan sukunimi 202

Taulukot ja merkkijonot string lause = "Koko nimesi on "; lause += etunimi + " " + sukunimi + "."; // Luodaan lause // Liitetään lauseeseen nimet cout << endl << lause // Tulostetaan lause cout << "Merkkijonossa on " // Tulostetaan sen pituus << lause.length() << " merkkiä." return 0; } Seuraavassa on ohjelman esimerkkitulostus: Syötä etunimesi: Mari Syötä sukunimi: Aalto Koko nimesi on Mari Aalto. Merkkijonossa on 26 merkkiä. Kuinka se toimii Esittelemme aluksi kaksi string-tyyppistä oliota: string etunimi; string sukunimi; // Tallettaa etunimen // Tallettaa sukunimen Koska emme ole määritelleet mitään alkuarvoja, nämä molemmat alustetaan tyhjiksi merkkijonoiksi. Seuraavaksi pyydämme syöttämään nimet ja luemme nimet näppäimistöltä: cout << endl << "Syötä etunimesi: "; cin >> etunimi; cout << endl << "Syötä sukunimi: "; cin >> sukunimi; // Luetaan etunimi // Luetaan sukunimi >>-operaattori toimii string-tyyppisten olioiden kanssa samaan tapaan kuin char-tyyppisten taulukoidenkin yhteydessä. Merkkejä luetaan, kunnes löydetään ensimmäinen tyhjä merkki, joten et voi lukea tällä tavalla merkkijonoja, jotka sisältävät välilyöntejä. Tähän ongelmaan esitämme ratkaisun aivan pian. Merkkijonojen lukemisen jälkeen luomme uuden string-tyyppisen olion, mutta tällä kertaa alustamme sen eksplisiittisesti merkkijonoliteraalilla: string lause = "Koko nimesi on "; // Luodaan lause lause-olio alustetaan annetulla merkkijonolla. Tämän jälkeen käytämme tätä oliota muodostaessamme merkkijonon, jonka haluamme tulostaa: lause += etunimi + " " + sukunimi + "."; // Liitetään lauseeseen nimet 203

C++ Ohjelmoijan käsikirja Oikealla puolella oleva etunimi-olio yhdistetään ensiksi literaaliin, tähän yhdistetään sukunimi-olio ja lopuksi literaali.. Edellä näytetyssä tilanteessa oikea puoli evaluoidaan string-tyyppiseksi oliksi, jonka sisältö on Mari Aalto. Kuten tämä lause havainnollistaa, += -operaattori toimii myös string-tyyppisten olioiden yhteydessä samaan tapaan kuin olemme jo nähneet perustietotyyppien kohdalla, joten tässä oikealla puolella olevan lausekkeen arvo lisätään vasemmalla puolella olevaan lause-olioon. Eli lauseen suorituksen jälkeen lause-olion sisältö on Koko nimesi on Mari Aalto.. Kun käytät += -operaattoria yhdistämään arvon string-tyyppiseen olioon, oikeanpuoleisen lausekkeen arvo voi olla null-merkkiin päättyvä merkkijono, yksittäinen char-tyyppinen merkki tai string-tyyppinen olio. Ohjelman lopuksi käytämme <<-operaattoria tulostamaan lause-olion sisällön ja merkkijonon pituuden: cout << endl << lause // Tulostetaan lause cout << "Merkkijonossa on " // Tulostetaan sen pituus << lause.length() << " merkkiä." Kuten tästä lauseesta huomaamme, voimme tulostaa string-tyyppisen olion sisällön aivan samaan tapaan kuin voimme tulostaa minkä tahansa muun jo käsittelemämme tyyppisen muuttujan. Merkkijonon yksittäisten merkkien käsittely Voit viitata merkkijonon yksittäiseen merkkiin hakasulkeiden sisällä olevan indeksi avulla aivan samaan tapaan kuin taulukonkin kohdalla. string-tyyppisen olion ensimmäinen merkki on indeksissä 0. Esimerkiksi lause-olion kolmanteen merkkiin voit viitata lausekkeella lause[2]. Tällaisen lausekkeen voi sijoittaa myös sijoitusoperaattorin vasemmalle puolelle, joten voit myös muuttaa merkkijonon yksittäisiä merkkejä. Seuraava lause muuttaa kaikki lause-olion merkit isoiksi: for(int i = 0 ; i < lause.length() ; i++) lause[i] = toupper(lause[i]); Voimme harjoitella tätä taulukko-tyylistä viittaustapaa muuttamalla jo aikaisemmin näkemäämme ohjelmaa, joka laskee merkkijonossa olevien vokaalien ja konsonanttien lukumäärän. Uusi versio käyttää string-tyyppistä oliota. 204 Kokeile itse - Yksittäisten merkkien käsittely Tämä ohjelma tekee paljon samaa kuin esimerkki 6.4, mutta tässä käytetään string-tyyppistä oliota char-tyyppisen taulukon sijaan. // Esimerkki 6.7 - Merkkijonon yksittäisten merkkien käsittely #include <iostream> #include <string> #include <cctype>