Taulukot

Kurssilla on totuttu käyttämään STL:n vector-rakennetta tilanteissa, joissa keskenään saman tyyppisiä arvoja halutaan tallentaa siten, että alkioihin päästään käsiksi järjestysnumerolla (indeksillä). C++:ssa on kuitenkin myös alkeellisempi tyyppi, joka käyttäytyy osittain samoin vektorin kanssa. Tätä rakennetta kutsutaan taulukoksi (array).

Taulukko on hyvin yksinkertainen, kooltaan staattinen tietorakenne (alkioiden lukumäärä on kiinteä), jonka yksitäisiin alkioihin päästään käsiksi [] -operaattorilla. Rakenne on suoraa perintöä C-kielestä, joka on C++:n esi-isä.

Tällä kurssilla taulukoille ei itsessään ole käyttöä, koska kaikki tarvittavat asiat voidaan tehdä helpommin vector:in avulla, mutta yleissivistävistä syistä on hyvä tietää jotain taulukon luonteesta. Tätä tietoa tarvitaan, jos ikinä tulee eteen tarve kirjoittaa ohjelmia C-kielellä.

Taulukkotyyppinen muuttuja määritellään seuraavasti:

int luvut[3];  // Taulukossa tilaa kolmelle int:ille.

Edellisen määrittelyn seurauksena kääntäjä varaa niin pitkän yhtenäisen pätkän muistia, että sinne voidaan sijoittaa peräkkäin kolme int-tyyppistä arvoa:

../../_images/muisti_06.png

Taulukon alkioita voidaan nyt käsitellä []-operaattorilla:

luvut[0] = 6;
luvut[1] = 12;
luvut[2] = 24;
cout << luvut[0] + luvut[2] << endl;  // 30

minkä jälkeen tilanne muistissa näyttäisi seuraavalta:

../../_images/muisti_07.png

Taulukkotyyppi on toteutettu tehokkuussyistä siten, että taulukko esitetään toteutustasolla vakio-osoittimena sen ensimmäiseen alkioon. Eli siis koodi

cout << luvut << endl;

tulostaisi esimerkkitaulukosta näytölle osoitteen 0x000004.

Seurauksena siitä, että taulukko samaistetaan aina osoitteeksi sen ensimmäiseen alkioon, on taulukko melko alkeellinen tietotyyppi:

  • Taulukkomuuttujaa ei voi sijoittaa toiseen taulukkomuuttujan operaattorin = avulla. Taulukkoa ei myöskään voi alustaa toisella taulukolla.
  • Jos taulukko annetaan funktiolle parametrina, käyttäytyy se aina viiteparametrin tavoin, koska funktio saa muodollisessa parametrissa tiedon taulukon ensimmäisen alkion muistiosoitteesta. Kun tätä osoitetta sitten käsitellään []-operaattorilla, päädytään luonnollisesti operoimaan alkuperäisen taulukon alkioilla.
  • Taulukko ei voi olla suoraan STL-säiliöiden alkiona.

Taulukko voidaan kuitenkin käydä läpi osoittimen avulla:

int* taulukko_osoitin = nullptr;
taulukko_osoitin = luvut;
while ( taulukko_osoitin < luvut + 3 ) {
    cout << *taulukko_osoitin << endl;
    ++taulukko_osoitin;
}

Edellä on hyödynnetty nk. osoitinaritmetiikkaa: Taulukon alkuosoitteeseen voidaan lisätä kokonaisluku, jolloin tuloksena saadaan osoitin alkioon, jonka indeksi vastaa lisättyä kokonaislukua.

Jos lisätty luku on taulukon alkioiden lukumäärä, saadaan osoitin siihen kohtaan muistissa, joka on heti viimeisen alkion perässä:

cout << luvut + 3 << endl;  // Tulostuu muistiosoite 16

Tätä osoitetta voidaan käyttää näppärästi taulukkoa osoittimen avulla läpi käyvän silmukan ehdossa, kuten edellä oli tehty.

Käytännössä osoitinaritmetiikasta seuraa myös se, että esimerkiksi koodi

cout << luvut[2] << endl;
luvut[1] = 99;

tarkoittaa samaa kuin koodi

cout << *(luvut + 2) << endl;
*(luvut + 1) = 99;

Kuten todettua, tällä kurssilla taulukot ja niiden käyttäytyminen ei aivan olennaisinta sisältöä. Asia kannattaa kuitenkin tiedostaa yleissivistävistä syistä. Ei ole aivan odottamatonta, että esimerkiksi myöhemmillä kursseilla (Mikroprosessorit, Laitteistonläheinen ohjelmointi, tms.) saattaa päätyä kirjoittamaan matalamman tason koodia C-kielellä, jossa asia tulee vastaan.