: Osoittimet ja taulukot 26.1.2016
Moduli 1 yhteenvetoa laskuharjoituksista (PS: palautteen saa jättää myös suomeksi jos haluaa) Ongelmia ympäristön asennuksessa Hoitakaa kuntoon ajoissa, niin loppukurssilla on mukavampaa Oikean tulostusformaatin arvaaminen hankalaa Floatin ja Doublen formaatissa on eroa %f vai %lf Puolipisteen unohtaminen Ohjelmatiedoston löytäminen oli haastavaa Koko ohjelma ei ole yhdessä tiedostossa. Tehtävä osuus löytyy usein source.c tiedostosta 2
Tiedotteita Ensimmäisen kierroksen sulkeutuu perjantaina! Pistetilanne löytyy verkosta Myrkystä tietoa pisteistä ja arvostelusta Kakkoskierros on auennut (deadline 12.2.) Laskareihin voi tulla kysymään jo kakkoskierroksen tehtävistä, mutta ykköskierros on etusijalla 3
Osoittimet Osoitin (pointteri): viittaus muistiosoitteeseen Osoitinmuuttujan arvo on muistiosoite C-kielen voi ymmärtää erivärisinä numeroina Erittäin alhaisen abstraktiotason ominaisuus Keskeinen osa a Käsitteenä usein hankala 4
Osoittimien käyttötarkoitukset Laiteläheinen ohjelmointi Mahdollistaa tiedon tehokkaan käsittelyn Taulukot Dynaamisen muistin käsittely Monimutkaiset tietorakenteet Referenssitietotyypin toiminnallisuus Simuloitu pass-by-reference (Ohjelman rikkominen lähes äärettömän monella eri tavalla) 5
Osoittimen määrittely char a = 10; char *d = &a; Syntaksi: tietotyyppi *muuttuja Ylläolevat määritelmät kaksi eri tietotyyppiä Osoitinmuuttujan arvot ovat muistiosoitteita Osoittimen viitetietotyyppi oltava oikein, jotta tiedon käsittely toimii oikealla tavalla char *d voisi olla myös char* d & - operaattorilla saadaan annetun objektin osoite &a: palauta muuttujan a osoite &:n unohtaminen edellä tuottaisi kääntäjän varoituksen 6
Yksinkertainen esimerkki int main(void) { char a = 10; char b = 12; int c = 123456; char *d = &a; char *e; } Osoittimeen e viittaaminen johtaisi virheeseen (ja tod. näk. ohjelman kaatumiseen) 7
Yksinkertainen esimerkki int main(void) { char a = 10; char b = 12; int c = 123456; char *d = &a; char *e; e = d; } Annetaan e:lle arvo 8
Yksinkertainen esimerkki int main(void) { char a = 10; char b = 12; int c = 123456; char *d = &a; char *e; e = d; printf("*e: %d e: %p\n", *e, e); } *d = 13; printf("*d: %d d: %p *e: %d a: %d\n", *d, d, *e, a); *e: 10 e: 0x1000 *d: 13 d: 0x1000 *e: 13 a: 13 9
Viittausoperaattori *muuttuja: viite osoittamaan osoittamaan muistipaikkaan Voi käyttää lausekkeissa kuten muitakin muuttujia Sijoitus aiheuttaa kohteen arvon muuttumisen Eräänlainen vastakohta & - osoiteoperaattorille muuttuja voi olla myös lauseke: *(a + 2) on ok Eri operaatio kuin osoitinmuuttujan määrittäminen Jos osoitin viittaa virheelliseen osoitteeseen, ja sitä yritetään käyttää, ohjelma todennäköisesti kaatuu Useimmiten Segmentation fault Joskus vain outo toiminta 10
Yksinkertainen esimerkki void main() { char a = 10; char b = 12; int c = 123456; char *d = &a; char *e; e = d; *d = 13; d = 14; } Osoitin d viittaa virheelliseen muistiosoitteeseen d:hen viittaus todennäköisesti kaataa ohjelman 11
Binky pointer fun 12
NULL NULL on osoitinmuuttujan nolla-arvo Käytännössä määritelty: #define NULL ((void*)0) NULL ei osoita mihinkään kelvolliseen muistipaikkaan Käytetään virhearvona tai erikoisarvona Esimerkiksi listan loppu voidaan määritellä käyttämällä NULLia 13
Korkeamman asteen osoitinmuuttujat Osoitinmuuttujia voidaan myös ketjuttaa Esimerkiksi int **p; Tällöin p osoittaa int* -tyyppiseen muuttujaan Tarvitaan moniulotteisissa taulukoissa ja monimutkaisemmissa tietotyypeissä Lisää aiheesta 3. kierroksella! 14
Constin käyttö const TYPE *ptr tai TYPE const *ptr Osoitinmuuttujan kohde, eli *ptr on const Osoittinmuuttujan arvo, eli ptr ei ole const Yleisin constin käyttö TYPE * const ptr Osoitinmuuttujan arvo, eli ptr on const Osoittinmuuttujan kohde, eli *ptr ei ole const TYPE voisi esimerkiksi olla: int, char, char*, jne. Constin käyttö on tärkeää, koska on muistialueita joihin kirjoittaminen on virhe! 15
Osoittimien arvojen muuttaminen Osoittimia voidaan liikuttaa eteen ja taaksepäin: Myös --, ++ ja muut operaatiot toimivat! Laskujärjestys on tärkeä! *p++; on eri asia kuin (*p)++; Osoittimien välistä etäisyyttä voidaan laskea Kohteen tyyppiä ei tarvitse huomioida etäisyydessä Osoittimia voidaan verrata keskenään Verrataan ainoastaan muistiosoitteita Tämä on yleinen virhe merkkijonojen tarkastelussa! 16
Välitehtävä Mitä tämä tulostaa? #include <stdio.h> int main(void) { int a = 5, d = 0; int *b = &a; int *c = &d; *c = *b; *b = 1; } printf(" a: %d\t b: %d\t c: %d\t d: %d\n", a, *b, *c, d); 17
Osoitinmuuttujat funktiossa void count(int *nro) { // nro on pointterin arvo // *nro on pointterin osoittaman paikan arvo if (nro!= NULL) { printf("%d\n", *nro); *nro += 1; } } int main(void) { int a = 1; count(&a); count(&a); } 18
Taulukko Taulukko on saman tyylisiä muuttujia peräkkäisissä muistipaikoissa C-kielessä ei mekanismia taulukon koon selvittämiseen Tiedettävä/sovittava muilla tavoin Mikään ei estä taulukon käyttöä yli kokorajojen Aiheuttaa virheen joka voi olla vaikea havaita Taulukolla läheinen suhde osoitinmuuttujaan Taulukkoon voidaan viitata osoitinmuuttujalla sen ensimmäiseen alkioon 19
Osoitinaritmetiikka esimerkki short arr[4]; short *ref = arr; arr 0x1000 0x1002 0x1004 0x1006 0x1000 ref 20
Osoitinaritmetiikka esimerkki *ref = 1; ref++; (samalla johdatusta taulukoihin) arr 1 0x1000 0x1002 0x1004 0x1006 0x1002 ref 21
Osoitinaritmetiikka esimerkki *ref = 10; ref = ref + 2; (samalla johdatusta taulukoihin) arr 1 10 0x1000 0x1002 0x1004 0x1006 0x1006 ref -- Tämän jälkeen ref++ viittaisi taulukon yli (paikkaan 0x1008) -- Kääntäjä ei huomaa virhettä -- Ohjelma ei välttämättä kaadu -- Viittaus silti väärään muistipaikkaan -- Joskus vaikea havaita, monet tietoturva-aukot perustuvat tämäntyyppisiin virheisiin 22
Taulukkoesimerkki short apples = 10; short slots[4]; short oranges = 20; int i; for (i = 0; i < 4; i++) { /* Here we initialize the array */ slots[i] = i + 1; } for (i = 0; i < 4; i++) { /* Output the values in array */ printf("array element %d is %d\n", i, *(slots + i)); } 23
Taulukon käyttäminen [ ] operaattori Muoto: muuttuja[indeksi] Toimii kuten yksittäinen muuttuja Indeksi voi olla vakioarvo tai toinen muuttuja tai lauseke Indeksointi alkaa 0:sta Mikään ei estä viittaamasta taulukon yli tai alle (taulu[-1]) Vaihtoehtoisesti myös *(muttuja + indeksi) toimii Indeksioperaattoria voi käyttää myös kun taulukkoon viitataan osoittimella 24
Taulukon käyttö funktiossa Taulukko voidaan välittää osoittimena sen ensimmäiseen arvoon Taulukon pituutta ei voi nähdä osoittimen perusteella Mutta voidaan esim. kertoa funktion parametrina Staattisen taulun koon voi määrittää, mutta se ei ole täysin virhevarmaa Taulukko ei voi olla funktion paluuarvona Mutta funktio voi muokata annettua taulukkoa osoittimen kautta 25
Taulukko ja funktio -- esimerkki void show_table(short *a, size_t n) { int i; for (i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } int main(void) { short table[] = { 1, 4, 6, 8}; printf("size: %lu\n", sizeof(table)); /* print array size for fun */ } /* below is one way to get the number of elements */ show_table(table, sizeof(table)/sizeof(short)); // in this case the above would be equivalent to: show_table(table, 4); 26
Taulukon pituuden välittäminen Taulukon pituutta ei voi nähdä pelkästä osoittimesta Vaihtoehtoja Pituus tunnettu / sovittu (sovelluksesta riippuen) Pituus kerrotaan muuttujalla tai funktion parametrissa Ed. esimerkki Tietty arvo lopettaa taulukon Kuten merkkijonoissa Annetaan osoitin taulukon loppuun Pituus kerrotaan ensimmäisessä alkiossa Em. Vaihtoehtojen soveltuvuus riippuu käyttötarpeesta
Muuta huomioitavaa taulukoista Taulukkoa ei voi kopioida suoralla sijoitusoperaatiolla Kopioitava alkio kerrallaan Taulukkoja ei voi vertailla suoraan Vaan esim. alkio kerrallaan 28
Taulukko esimerkki double avg(int *arr, size_t size) { if (arr == NULL) return 0; int sum = 0; for (int i=0; i < size; i++) sum += arr[i]; } return sum / (double)size; int main(void) { int array1[10] = {0}; int array2[] = {1, 1, 1, 1, 2, 2, 2, 2}; int array3 = 13; } double result1 = avg(array1, 10); double result2 = avg(array2 + 4, 4); double result3 = avg(&array2[2], 4); double result4 = avg(&array3, 1); Result1: 0 Result2: 2 Result3: 1,5 Result4: 13 29
Taulukko tehtävä Mitä eri tapoja on selvittää taulukon pituus? 30
Merkkijonot 31
Merkkijono Merkkijono C-kielessä on char tyyppinen taulukko Merkkijonon loppu merkitään arvolla \0 Nolla-arvolle varattava tila taulukossa C:ssä merkkijono-taulukoilla on erityisasema Merkkijono voidaan määrittellä lainausmerkeissä normaalin taulukkosyntaksin ohella Useita kirjastofunktioita merkkijonojen käsittelyyn printf ja scanf - formatointi käyttäen %s muotoilumäärettä Merkkijono toimii kuten mikä tahansa taulukko Indeksointi, osoitinaritmetiikka, jne 32
Merkkijonon määrittely Merkkijono voidaan määritellä kuten mikä tahansa taulukko, tai käyttäen merkkijono notaatiota Jälkimmäisessa \0 on näkymätön ja lisätään automaattisesti char *string_a = "This is first string"; char string_b[] = "Another string"; char string_c[] = { 'O','n','e',' ','m','o','r','e','\0' }; string_a on vakiomuotoinen merkkijono Ei voi muuttaa Kuten kaikissa taulukoissa, alustava merkkijono määrää taulukon pituuden kun sitä ei ole erikseen annettu \0 merkki annettava jos käytetään taulukko-notaatiota 33
Merkkijonon määrittely char *string_a = "This is first string"; char string_b[] = "Another string"; char string_c[] = { 'O','n','e',' ','m','o','r','e','\0' }; 34
Yleisiä virheitä sizeof ja strlen sekoittaminen strlen: merkkijonon pituus merkkeinä olettaa char-taulukon joka päättyy nollamerkkiin (ajonaikainen) sizeof: muuttujan tarvitsema tila, osoittimille aina samaa kohdetyypistä riippumatta (käännösaikainen) 0-merkin unohtaminen merkkijonon lopusta Merkkijonon (tai muun taulukon) sijoittaminen = - operaattorilla Tarvitaan esim. strcpy() - funktio Merkkijonon vertailu loogisilla operaattoreilla Tarvitaan esim. strcmp() - funktio 35
Yleisiä virheitä Lainausmerkkien sekoittaminen: x on merkki (char) x, jonka arvo on 120 (ASCII) x on merkkijono, joka on tyyppiä (char *) Muistin varaaminen Merkkijono tarvitsee myös lopetusmerkille tilaa Esim. strlen(string) + 1 Harhaanjohtava väritys aiheuttanut kysymyksiä C++:n keywordit värjäytyvät editorissa (string, new, jne.) 36
Merkkijonojen käsittelyyn funktioita Funktio strcpy(s1, s2) strcat(s1, s2) strlen(s1) Toiminnallisuus Kopioi merkkijonon s2 paikkaan s1 Lisää merkkijonon s2 merkkijonon s1 perään Palauttaa merkkijonon pituuden strcmp(s1, s2) Palauttaa 0, jos s1 on sama kuin s2. strchr(s1, ch) Palauttaa pointterin ensimmäisen merkin ch esiintymiseen strstr(s1, s2) Palauttaa pointterin ensimmäiseen s2- merkkijonon esiintymiseen merkkijonossa s1 37
Esimerkki merkkijonoista int countslash(const char *str) { int count = 0; while ((str = strchr(str, '/'))!= NULL) { count++; str++; } return count; } int countslash(const char *str) { int count = 0; for (; *str!= 0; ++str) { if (*str == '/') count++; } return count; } 38
Ensi viikolla Debuggerin käyttö Auttaa virheiden etsinnässä! Jatkoa osoittimista Moniulotteiset taulukot 39