Tie koodariksi

C++-ohjelmointi

Luku 7: Taulukot

Taulukon määrittely

Taulukko muodostuu peräkkäisistä alkioista, joilla on sama tyyppi. Esimerkiksi seuraava koodi luo int-tyyppisen taulukon t, jossa on 5 alkiota:
int t[5];
Jos taulukko määritellään funktion sisällä, sen sisältö on alustamaton. Jos taas taulukko määritellään päätasolla, sen jokainen alkio on aluksi 0.

Taulukon määrittelyn yhteydessä voi myös antaa listan alkioita sen sisällöksi. Jos listassa ei ole arvoa kaikille alkioille, muiden arvoksi tulee 0.

int a[5] = {3,2,4,8,2};
int b[5] = {0}; // {0,0,0,0,0}
int c[5] = {1}; // {1,0,0,0,0}

Taulukon indeksointi

Taulukon alkioita indeksoidaan kokonaisluvuin 0:sta alkaen. Kun taulukossa on n alkiota, ensimmäisen indeksi on 0 ja viimeisen indeksi on n-1. Taulukkoa voi käsitellä vaikkapa näin:
t[0] = 5;
t[1] = 2;
t[2] = t[0]+t[1];
Seuraava koodi puolestaan tulostaa taulukon sisällön for-silmukalla:
for (int i = 0; i < n; i++) {
    cout << t[i] << " ";
}

Moniulotteinen taulukko

Moniulotteisessa taulukossa jokaista ulottuvuutta vastaa hakasulkupari. Esimerkiksi seuraava koodi luo kaksiulotteisen taulukon, joka vastaa 4x7-ruudukkoa.
int t[4][7];
Tällaista taulukkoa voi käsitellä näin:
t[0][0] = 2;
t[2][1] = 5;

Taulukon rajoitukset

Taulukko on staattinen tietorakenne, eli sen kokoa ei pysty muuttamaan määrittelyn jälkeen.

Taulukossa ei ole valmiita toimintoja, joilla sen sisältöä voisi käsitellä kokonaisuutena. Niinpä taulukon sisältöä ei voi kopioida, vertailla eikä tulostaa suoraan, eikä mikään seuraavista koodeista toimi taulukoille:

a = b;
if (a == b)
cout << t << "\n";
Jos näille toiminnoille on tarvetta, ne täytyy toteuttaa itse esimerkiksi for-silmukalla.

Luvussa 14 tutustumme vektoriin, joka on taulukon tapainen tietorakenne, mutta poistaa monia sen rajoituksia.

Taulukko parametrina

Taulukko käyttäytyy funktion parametrina eri tavalla kuin tavallinen muuttuja. Taulukosta välitetään funktiolle osoitin, minkä vuoksi muutokset vaikuttavat kutsukohtaan.
void testi(int t[]) {
    t[2] = 3;
}

int main() {
    int t[5] = {0};
    testi(t);
    cout << t[2] << "\n"; // 3
}
Funktiolle ei tule tietoa, montako alkiota taulukossa on. Jos tätä tietoa tarvitaan, se täytyy välittää erikseen esimerkiksi toisena parametrina:
void testi(int t[], int n) {
    for (int i = 0; i < n; i++) {
        cout << t[i] << "\n";
    }
}

Yli viittaaminen

C++ ei huolehdi siitä, että taulukkoa käytetään oikein. Esimerkiksi seuraavan koodin voi kääntää ja suorittaa:
int t[5];
t[100] = 2;
Tämä koodi luo taulukon, jossa on 5 alkiota, mutta viittaa sen jälkeen kohdassa 100 olevaan alkioon eli taulukon ulkopuolelle. Tämän seurauksena ohjelma muuttaa jotain taulukon ulkopuolista kohtaa muistissa, mikä voi johtaa siihen, että ohjelma toimii oudosti tai keskeytyy virheeseen.

Miksei C++ varmista, että taulukkoa käytetään oikein? Syynä on tehokkuus: veisi aikaa tarkastaa, että indeksi on taulukon sisällä. C++ luottaa, että ohjelmoija tietää, mitä on tekemässä.