5.6. C-kielen perusteet, osa 6/8, Taulukko 6.1.2008, pva, kuvat jma Every cloud has a silver line. - englantilainen sananlasku Tässä osiossa tärkeää: yksi- ja moniulotteinen taulukko Sisältö Yleistä Yksiulotteinen taulukko Taulukon esittely Taulukon alustus Taulukko, esimerkki 1 Funktio ja taulukko Taulukko, esimerkki 2 Moniulotteinen taulukko 2-osainen taulukko, esim. Yleistä Jos ohjelman tehtävänä on vaikka seurata jonkin nesteen lämpötilan kehitystä minuutin välein, voidaan mittaustuloksia käsitellä niin, että kullekin mittaustulokselle annetaan oma muuttuja. Tiedot ovat luonnollisesti samaa tyyppiä ja kutakin lämpötilaa kuvaa yksi lukuarvo. Suuri määrä eri muuttujanimiä on hankala käsitellä. Tällöin on järkevää koota mittaustulokset yhteen ja tehdä niistä taulukko. Taulukot ovat erityisen käteviä kun pitää käsitellä useita, samantyyppisiä, mutta jollain tavalla yhteen kuuluvia arvoja. Taulukko koostuu peräkkäisistä muistipaikoista, jotka on koottu 'saman katon alle'. Yksittäistä muistipaikkaa sanotaan taulukon alkioksi tai soluksi. Taulukolla on oltava nimi, jolla sitä käsitellään. 1
Yksiulotteinen taulukko Kuva 5.6.1. Yksiulotteinen taulukko, vektori Taulukon alkioihin viitataan indeksillä, index. Kaikki taulukon alkiot on numeroitu alkaen numerosta 0. Siis taulukon ensimmäisen alkion indeksi on [0]. Taulukon indeksit ovat aina kokonaislukutyyppiä olevia vakioita, muuttujia tai lausekkeita. Taulukon käsittely kokonaisena ei ole mahdollista, siis vaikka kopiointi sellaisenaan. Kaikki toimenpiteet tapahtuvat alkioittain. C-kielessä ei ole minkäänlaista tarkistusta sille, onko annettu taulukon indeksi sallittujen rajojen sisällä. Joten ole tarkkana. Jos sijoitat taulukkoon tai luet taulukosta arvoja rajojen ulkopuolella olevalla indeksillä, seuraa ongelmia. Taulukon esittely Taulukon esittely kertoo taulukon tyypin, nimen ja koon eli alkioiden lukumäärän. Esittely varaa muistista tilaa taulukolle. taulukon esittelyn formaatti: tietotyyppi nimi[koko]; int taulu[4]; Esittely varaa 4 kpl integer-tyyppisiä muistipaikkoja (4 * 16 bittiä) taulukolle, jonka nimi on taulu. - tietotyyppi, on taulukon alkioiden tietotyyppi (int, char, float) - nimi on taulukon tunniste - koko kertoo montako alkiota taulukko sisältää Taulukko on rakenteinen muuttuja. Kaikilla taulukon alkioilla on sama tyyppi. Taulukon koon on oltava kokonaisluku, mutta indeksi voi olla myös laskulauseke jonka lopputulos on kokonaisluku. Käännin ei tarkista onko indeksi rajojen sisällä. 2
taulukon indeksi tulisi antaa symbolisena vakiona #define N 100 int loota[n]; char s[] = abc ; - käännin luulee eli tietää että määritys tarkoittaa char s[] = a, b, c, \0 ; Taulukon nimi on ensimmäisen alkion talletusosoite. Taulukon muistista varaaman tilan koko riippuu alkioiden määrästä ja siitä, mitä tietotyyppiä niihin tallennetaan. Char-tyyppi varaa 8 bittiä (yksi tavu) ja int 16 bittiä (kaksi tavua) muistia per alkio. Taulukon koon on oltava kokonaisluku, mutta taulukon alkioita käytettäessä indeksi voi olla myös kokonaisluvun antava laskutoimitus. Näin C-käännin tietää jo ennen ohjelman suoritusta, kuinka paljon tilaa taulukolle pitää varata. Taulukon koko on vakio, sitä ei voi ohjelman aikana muuttaa. Taulukon alustus Taulukko alustetaan, eli siihen asetetaan arvot, määrittelyn yhteydessä. Se voi tapahtua myös myöhemmin, jos se esimerkiksi täytetään anturista saatavilla lämpötila-arvoilla. Jos taulukkoa ei alusteta, siinä saattaa olla mitä tahansa arvoja (SRAM on tehty kiikuista). Siksi on viisainta jo määrittelyvaiheessa suorittaa taulukon nollaus, eli täyttää kukin alkio nollalla. Alustus tapahtuu kirjoittamalla taulukon tyypin ja nimen perään sijoitusmerkki (=) ja aaltosulkuihin alkioiden arvot pilkulla erotettuna. Jos aaltosuluissa on vähemmän arvoja kuin taulukossa on alkioita, taulukon loppupään alkiot saavat arvokseen nollan. int kierrokset [5] = 2, 4, 7, 8, 56; char taulukko[] = 3, 5, 7; // taulukkoon varataan 3 alkiota int lammot[6] = 20, 38, 50; // nimissä ei saa käyttää ääkkösiä, ä, ö, å Jos taulukolle ei määritetä kokoa, mutta se alustetaan, niin C-käännin osaa antaa koolle oikean arvon. Jos taulukkoon laitetaan enemmän arvoja kuin taulukossa on soluja, käännin antaa virheilmoituksen. Jos taulukko on määritetty tietyn suuruiseksi, esim. 10 solua, mutta vain 5 ensimmäistä on alustettu, loput sisältävät mitä sattuu. Jos ohjelma lukee noista epämääräisistä soluista, ohjelman toiminta vääristyy. Taulukon rajojen tarkistusta ei tehdä. Jos indeksi viittaa taulukon ulkopuolelle, seuraa myös virheilmoitus. Älä esittele taulukkoa liian suurella indeksillä, se tuhlaa muistia. Taulukon koko on vakio, sitä ei voi ohjelman aikana muuttaa. Taulukon nollaus tapahtuu vaivattomasti for-silmukalla; int taulukko[20]; for(laskuri = 0; laskuri < 20; taulukko[laskuri++] = 0) 3
Taulukko, esimerkki 1 /********************************************************** Project : taulukko_1.c, Hardware: PV-M32 + PV-LEDIT on PORTB Software: AVSRStudio4.13 + WinAVR-20070525 Date : 11.11.2007 Author : pva Comments: tulostaa taulukon B-porttiin **********************************************************/ #include <avr/io.h> #include <util/delay.h> #define WAIT(time) for(uint16_t i=0;i<2000;i++)_delay_loop_2(time); int main(void) uint8_t i = 0; uint8_t loota[] = 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80; // määritetään taulukko, jossa 8 alkiota DDRB = 0xFF; while(1) PORTB = loota[i]; WAIT(200); // tulostetaan i:s alkio i++; if(i > 7) i = 0; // seuraava alkio // jos kaikki näytetty, aloita alusta Analysointi Main-funktiossa on esitelty muuttuja i, jota käytetään silmukkalaskurina. Samalla se toimii taulukon indeksinä eli solun osoittajana. Taulukkomuuttujan tunnistaa hakasuluista []. Taulukon kokoa ei ole määritetty, se selviää siitä, kuinka monta muuttujaa sinne on tallennettu. Tässä tapauksessa 8 kpl. Indeksin numerointi alkaa nollasta. while-silmukassa tulostetaan B-porttiin taulukon jokainen solu. Kun kaikki alkiot on läpikäyty, aloitetaan alusta. Funktio ja taulukko Taulukon yksittäisen elementin välittäminen funktiolle tapahtuu välittämällä kopio kyseisen elementin arvosta kutsuvalle funktiolle. Huomaa, että C-kielessä funktion palautusarvon tyyppinä ei voi olla taulukko, vaan taulukko välitetään funktiolle osoittimen avulla. Funktiolle välitetään kopio taulukon alkuosoitteesta, taulukon elementtien sisältöä ei kopioida. Lisämääre const estää funktiota muuttamasta parametrinaan saamaansa taulukkoa, joten taulukon sisältöä voidaan lukea mutta ei muuttaa. Taulukko, esimerkki 2 /********************************************************** Project : taulukko_2.c, Hardware: PV-M32 + PV-LEDIT on PORTB Software: AVRStudio413 + WinAVR-20070525 Date : 11.11.2007 Author : pva Comments: tulostaa taulukon B-porttiin, käyttää funktiota **********************************************************/ 4
#include <avr/io.h> #include <util/delay.h> #define WAIT(time) for(uint16_t i=0;i<2000;i++)_delay_loop_2(time); uint8_t Ali(uint8_t i) uint8_t heksat[] = 0x18, 0x3C, 0x7E, 0xFF, 0x7E, 0x3C, 0x18, 0x00; // tulostettavat hex-kuviot return heksat[i]; // parametri i kertoo monesko alkio palautetaan int main(void) uint8_t k; // silmukkamuuttuja DDRB = 0xFF; while(1) for(k = 0; k < 8; k++) PORTB = Ali(k); // kutsutaan ali-funktiota, argumenttina k // return palauttama arvo sijoitetaan B-porttiin WAIT(1000); Analysointi Tässä esimerkissä taulukko on määritetty omassa Ali-funktiossa. Main-funktiosta kutsutaan Alifunktiota käyttäen argumenttina for-silmukkalaskurin arvoa. Huomaa, että argumentti voi olla eri niminen kuin kutsuttavan funktion parametri, kunhan tyypit ovat samat. Argumenttia käytetään osoittamaan taulukon indeksiä, joten sen on oltava kokonaisluku. Return palauttaa kutsujalle indeksin mukaisen solun sisällön. Harjoituksia 1. Muuta esimerkin koodia siten, että tulostus tapahtuu main-funktiossa, eli kutsutaan argumentilla tietty solun sisältö ja return-lauseella palautetaan se main-funktioon, jossa se tulostetaan. 2. Kirjoita ohjelma, joka nollaa 4-soluisen taulukon. 3. Kirjoita ohjelma, jossa on kaksi 4 soluista taulukkoa, toinen muuttujille ja toinen viiveelle. Anna taulukoille alkuarvot. Tulosta muuttujataulukko ja vastaavat viiveet for-silmukassa samalla indeksillä. Kun kaikki solut on tulostettu, indeksi nollaantuu ja sama uudelleen. Lisätietoa taulukoista Indeksin perusteella taulukosta voidaan hakea tietoa, tai tallettaa siihen tietoa. Operaation suoritusaika on (lähes) riippumaton tiedon sijainnista. Indeksiksi käy luonnollinen luku, tai vastaava. Indeksi kertoo tiedon sijainnin: kolmas, kuudes, kolmastoista, tietoalkio. Tiedon lisääminen alkioiden väliin on mahdotonta (tai ainakin hyvin hidasta). Tämä johtuu siitä, että taulukko sijoitetaan peräkkäisiin muistipaikkoihin. Ohjelma on (todennäköisesti) ottanut taulukon jälkeisen muistialueen käyttöönsä. 5
Moniulotteinen taulukko Taulukon alkion tyyppi voi olla mikä tahansa, siis jopa taulukko. 2-ulotteinen taulukko on taulukko, jonka rivit ovat yksiulotteisia taulukoita. C-kielessä voidaan määritellä myös kolme- ja useampiulotteisia taulukoita. uint8_t taulukko[3][3] = 1, 2, 3, 4, 5, 6, 7, 8, 9 Kuva 5.6.2. Kaksiulotteinen taulukko. Moniulotteisiin taulukoihin ei pienissä mikro-ohjaimissa ei ole riittävästi muistitilaa. Niiden opiskelu jääköön toiseen yhteyteen. Tutkitaan malliksi nyt kuitenkin 2-osainen taulukko. 2-osainen taulukko, esim. /********************************************************** Project : 2-os_taulukko.c HW : PV-M32 + PV-LEDIT SW : WinAVR-20070525 Date : 05.12.2007 Author : pva Comments: tulostaa 2-osaisen taulukon B-porttiin **********************************************************/ #include <avr/io.h> #include <util/delay.h> // *** Primitive wait() *** void wait(uint16_t time) volatile uint16_t i; for(i=0;i<2000;i++) _delay_loop_2(time); int main(void) uint8_t i; uint8_t loota[2][3] = 0x01, 0x02, 0x03, 0x10, 0x20, 0x30 ; // määritetään taulukko, jossa 2 riviä, kummassakin 3 alkiota DDRB = 0xFF; 6
while(1) for(i=0; i<3; i++) PORTB = loota[0][i]; wait(1000); PORTB = loota[1][i]; wait(1000); // tulostetaan 0-rivin i:s alkio // tulostetaan 1-rivin i:s alkio Analysointi Kommentit kertokoon olennaisen. 7