Tietorakenteet¶
Osaamistavoitteet: Tietorakenteiden käyttö C-kielessä ja käyttötapauksia sulautettujen järjestelmien ohjelmoinnissa.
Tietorakenne (engl. struct, suomeksi tietue) on useasta muuttujasta laadittu looginen rakenne/yhdistelmä/kooste, johon toisiinsa liittyvää tietoa voidaan ryhmitellä yhteistä käsittelyä varten. Tietorakenteet ovat hyvin yleinen ohjelmoinnin konsepti ja niillä on käyttötapauksensa myös sulautettujen ohjelmoinnissa, joten katsotaanpas miten niitä C-kielessä käytetään.
C-kielessä tietorakenteen esittelylle on varattu sana
struct
ja tietorakenne esitellään lohkona seuraavasti. Tietorakenteen jäsenet voivat C-kielessä olla mitä tahansa muuttujatyyppejä. struct tietorakenteen_tyyppinimi { jäsenten esittelyt; }
Esimerkiksi. Esitellään tietorakenne tyyppiä
point
, jossa on jäseninä x- ja y-koordinaatit ja koordinaattipisteen nimi merkkijonona. Esitellään heti perään toinen tietorakenne rect
, joka koostuu {{{point}}-tyypin tietorakenteista. struct point {
uint16_t x;
uint16_t y;
char nimi[16];
};
struct rect {
struct point max;
struct point min;
struct point all_points[10];
};
Itse tietorakenteen esittelyssä ei alusteta rakenteen muuttujia, koska esittelyssä ikäänkuin luodaan uusi muuttujatyyppi. Ohjelmassa voimme sitten esitellä ja alustaa tietorakennetta vastaavia muuttujia ja rakenteen alustus tehdään sitten sille varsinaiselle muuttujalle.
Tietorakenne C-ohjelmassa¶
Tietorakenteen jäsenten käyttöön ohjelmassa tarvitaan kaksi uutta operaattoria:
- Jäseniin viitataan
.
-operaattorilla kuin mihin tahansa muuttujiin. - Kun tietorakenne on osoitin tai sisältää osoittimia, niihin viitataan operaattorilla
->
.
Esimerkki.
// Tietorakenteiden esittely
struct point {
uint16_t x;
uint16_t y;
}
struct rect {
struct point max;
struct point min;
struct point all_points[10];
}
// Muuttujat ja alustus
struct point min_pt;
min_pt.x = 100;
min_pt.y = 100;
struct rect laatikko;
laatikko.max.x = 320;
laatikko.max.y = 200;
printf("%d,%d", laatikko.max.x, laatikko.max.y);
Esittely ja alustus¶
Syntaktisesti tietorakenteen esittely ja alustus voidaan tehdä kuten mille tahansa muuttujalle. Tässä C-kielen syntaksi on joustava ja alustukseen on useita tapoja.
// Muuttujan esittely tietorakenteen esittelyn perässä
// tässä piste1
struct point {
uint16_t x;
uint16_t y;
char nimi[20];
} piste1;
piste1.x = 320;
piste1.y = 200
char keskipiste[] = "Keskipiste";
// Käytetään string-kirjaston funktioita!
strncpy(piste1.nimi, keskipiste, strlen(keskipiste));
// Erikseen muuttujana
struct point piste2;
piste2.x = 320;
piste2.y = 200;
strncpy(piste2.nimi, keskipiste, strlen(keskipiste));
struct point piste3 = { 320, 200, "Keskipiste" };
Muitakin tapoja tietorakenteen alustukseen ja esittelyyn on, mutta yllä selkeimmät.
Tietorakenteita voi myös sijoittaa toisiinsa, jolloin jokainen jäsen kopioidaan.
min_pt = max_pt;
Osoittimet ja tietorakenteet¶
Funktion parametreinä tietorakenteita käytetään tyypillisesti osoittimien avulla, johtuen ihan samasta syystä mitä osoittimien käyttöön funktioiden kanssa aiemmin esitettiin, eli koska argumenteista tehdään kopiot ja sitten halutaan säästää muistia.
Osoittimien kautta tietorakenteen jäseneen viittaamiseen tarvitaan operaattori
->
. Huomataan ettei tähtioperaattoria käytetä, koska nuolioperaattori korvaa sen! Alla koodiesimerkki sen käytöstä. #include <stdio.h>
#include <inttypes.h>
struct point {
uint16_t x;
uint16_t y;
} piste = { 160, 100 };
int main() {
struct point *osoitin_piste = &piste;
printf("x=%d y=%d\n", piste.x, piste.y);
printf("x=%d y=%d\n", osoitin_piste->x, osoitin_piste->y);
// sijoitus jäseneen a.y osoittimen kautta
osoitin_piste->y=103;
printf("x=%d y=%d\n", piste.x, piste.y);
return 0;
}
Sisäkkäisten tietorakenteiden jäseniin viittaamiseksi osoittimella täytyy hoksata, että mihin jäseneen osoitin tarkalleen ottaen viittaa..
#include <stdio.h>
#include <inttypes.h>
struct rect {
struct point {
uint16_t x;
uint16_t y;
} piste;
} r = { .piste.x = 101, .piste.y = 102};
int main() {
struct rect *osoitin_r = &r;
// osoitin_r:n jäseniin nuolioperaattorilla
// p:n jäseniin pisteoperaattorilla
printf("x=%d y=%d)\n", osoitin_r->piste.x, osoitin_r->piste.y);
return 0;
}
Hox! Katso myös string-kirjaston funktio
memcpy
, jolla voidaan kopioida kokonaisia muistilohkoja. Toisinsanoen, tietorakenteen sisältö voidaan kopioida toiseen niin että kopioidaankin osoittimien avulla tietorakenteelle varattu muistialue sellaisenaan. Laiteläheisyys mahdollistaa meille tämänkin keinon. Tietorakenteiden vertailu¶
Tietorakenteiden vertailu on ehkä selkeintä tehdä omalla funktiollaan, jossa jokaisen jäsenen arvoja verrataan osoittimien avulla.
Esimerkki. Funktio siis palauttaa osoittimen siihen tietorakenteeseen, jossa on suurempi arvo jäsenessä
x
.#include <stdio.h>
#include <inttypes.h>
struct point {
uint16_t x;
uint16_t y;
};
// Funktio joka palauttaa tietorakenteen osoittimen
struct point *kumpi_isompi(struct point *a, struct point *b);
int main() {
struct point eka = { 320, 200}, toka = { 321, 201 };
struct point *isompi = kumpi_isompi(&eka, &toka);
printf("x=%d y=%d\n", isompi->x, isompi->y);
return 0;
}
struct point *kumpi_isompi(struct point *a, struct point *b) {
if (a->x > b->x) {
return a;
} else {
return b;
}
}
Alkaa olla tämä osoitintouhu jo suht korkealentoista tavaraa..
Sulautetut tietorakenteet¶
Kuten aiemmin todettiin, tietorakenne on kätevä tapa sitoa toisiinsa liittyvää informaatiota loogisesti yhteenkuuluvaksi paketiksi. Sulautetuissa järjestelmissä tällainen paketti voisi olla samalla mittaushetkellä kerätty anturidata tai vaikkapa oheislaitteen asetusparametrit tai langattoman tiedonsiirron viestinvälityksen asetukset. Koska anturidataa useimmiten analysoidaan aikasarjana, on oleellista tietää tarkka mittaushetki. Monet sovellukset myös hyödyntävät esimerkiksi älykkäämpien palveluiden toteutuksessa usealta eri anturilta samanaikaisesti kerättyä dataa, jolloin mittaushetki sitoo erityyppiset data yhteen.
Ja, kun data on nätisti paketissa tietorakenteessa, siitä voidaan helposti saada CSV-muotoinen merkkijono, joka sitten langattomasti lähetetään talteen IoT-taustajärjestelmään.
Kunhan kurssilla päästään SensorTagin ohjelmointiin asti, huomataan että monet laitteen käyttöjärjestelmän valmiit ohjelmistokomponentit itseasiassa haluavat asetusparametrinsä tietorakenteena.
Omat muuttujatyypit¶
C-kieli tarjoaa tietorakenteiden lisäksi mahdollisuuden esitellä ikäänkuin omia muuttujatyyppejä, siten että uudelleennimetään tai kootaan standardoituja/johdettuja muuttujatyyppejä. Näissä muuttujissa ei ole mitään erityistä, ne toimivat täsmälleen samoin kuin esi-isänsä muuttujatyypit. Tähän tarkoitukseen C-kielessä on varattu sana
typedef
. Esimerkiksi.
// Korvataan tyyppi uint16_t nimellä Length
typedef uint16_t Length;
Length len = 100;
printf("%d\n", len);
Sulautetuissa järjestelmissä (ja joskus myös C-ohjelmoissa työasemalla) usein
typedef
:iä käytetään laitealustalta toiselle portattavan (siirrettävän) koodin kirjoittamiseen. Nyt kun ohjelmn rakennetaan omien muuttujatyyppien varaan, säilyy ohjelman toiminta yhdenmukaisena vaikka koodi siirrettäisiin laitealustalta toiselle. Alustoilla täytyy vain sitten typedef
:n avulla esitellä sopivat omia muuttujatyyppejä vastaavat muuttujatyypit. Tämä ominaisuus on erittäinen hyödyllinen juurikin monimutkaisempien tietorakenteiden käytössä, mutta on standardoitujen muuttujatyyppienkin uudelleennimeämisestä joskus hyötyä.Näin juuri tehdään kurssillakin SensorTagia ohjelmoinnissa. Tulemme käyttämään sekä valmiita tietorakenteita että uudelleennimettyjä muuttujatyyppejä, koska suuri osa valmistajan ohjelmointiypäristön kirjastoista ja muista valmiista määrittelyistä on toteutettu niihin perustuen. Perusteluna on tietysti yhteensopivuus ja helppo koodin siirtäminen valmistajan omilta laitteilta toiselle, niin kaikki koodi toimii laitteesta riippumatta halutusti valmistajan omassa sulautettujen käyttöjärjestelmässä.
Lopuksi¶
Materiaalissa esitettyjen useiden tietorakenteiden esittely- ja alustustapojen kanssa ei tarvitse hikoilla. Riittää kuin opettelee yhden tavan ja käyttää sitä, kaikki ovat yhtä oikein.
Anna palautetta
Kommentteja materiaalista?