C! Dynaaminen muisti 9.2.2017
Agenda Kertausta merkkijonoista Dynaaminen muisti Valgrind-perusteet ja esimerkkejä Seuraava luento to 2.3. Ei harjoituksia arviointiviikolla 13.2. 17.2. 2
Palautetta merkkijonoihin liittyen Mistä ne risuaidat tulevat? Loppunollat Strcat haluaa alustetun merkkijonon pohjalle stringien käsittelyyn tarkoitetut funktiot ovat täysin noituutta Merkkijonojen käsittely haastavampaa kuin Pythonilla C:ssä täytyy löytää ihan oma tapa ajatella vrt. esim pythoniin. mikä sinänsä ihan loogista, kun miettii miten "Tyhmä" kieli C on. 3
Yleisiä virheitä merkkijonoihin liittyen 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 4
Virtuaalimuistin rakenne mm. paikalliset muuttujat 0xFFFF FFFF FFFF FFFF Dynaamisesti varattu muisti Koodi ja vakiomerkkijonot 0x0000 0000 0000 0000 5
Pino ja pinokehys int multiply(int para, int parb) { int result = para * parb; return result; } void calc(int value) { int num = 10; int res = multiply(num, value); }.(aikaisemmat funktiot). 0x0004DA23 paluuosoite 20 value 10 num? res 0x0004D95A paluuosoite 10 para 20 parb 200 result Pinokehys (yksi jokaista funktiokutsua kohden) calc() multiply() 6
Miksi dynaaminen muisti? Pitkäikäinen data Funktion paikalliset muuttujat häviävät funktiosta poistuttaessa Tarvitaan N:lle alkiolle tilaa mutta koodausaikana ei tiedetä N:ää N voi vaihdella ohjelman aikana, voi kasvaa suureksikin Ei tulisi käyttää enempää muistia kuin tarvitaan Muistinkäsittelyvirheet joskus helpompi havaita Valgrind Pinossa virheelliset muistiviittaukset aiheuttavat hämmentäviä ongelmia 7
Dynaaminen muisti ja keko (heap) Ohjelma voi varata muistia dynaamisesti tarvitsemansa määrän void *malloc(koko) Vaihtuvanmittaiset tietorakenteet Ajon aikana luotavat tietorakenteet Dynaamisesti varattu muisti pitää vapauttaa käytön jälkeen Muistivuoto: vapauttamaton muisti vie resursseja järjestelmän muulta käytöltä Dynaamisesti varattu muisti säilyy vapauttamiseen asti Osoitinta voidaan välittää funktiolta toiselle 8
Malloc esimerkki: dynaaminen taulukko (n kokonaislukua) int funktio(int n) { int *table; // uninitialized at this point table = malloc(n * sizeof(int)); if (table == NULL) return -1; // memory allocation failed for (int i = 0; i < n; i++) { table[i] = n - i; } free(table); return 0; } table: 0 1 2 3 n 9
Malloc - esimerkki ja muistin alueet char *alloc_buffer(size_t n) { char *buffer; buffer = malloc(n); strncpy(buffer, "some text", n-1); // buffer = "jotain tekstiä"; -- olisi ongelma // tarvitaanko jotain muuta? } n Pino 100 Keko 0x82de Koodi (read-only) alloc_buffer{ } buffer 0x82de some text\0 malloc strncpy 10
void* -- tyypitön osoitinmuuttuja mallocin palauttamalla osoittimella ei ole tyyppiä Osoittimeen ei sellaisenaan voi viitata void* - osoitin voidaan sijoittaa tyypitettyyn osoittimeen Tämän jälkeen viittaukset, osoitinaritmetiikka, jne. toimivat (miksi ei muuten toimisi?) Osoittimen sijoitus tyypistä toiseen vaatii eksplisiittisen tyyppimuutoksen ( explicit type cast ) Yleensä kannattaa välttää (mutta joskus tarvitaan) char *buffer = malloc(n); int *buf_int = (int *) buffer; 11
Muistin vapauttaminen Funktiokutsu: free(void* osoitin) Viittaa dynaamisesti varatun muistilohkon ensimmäiseen osoitteeseen (eli siihen joka saatiin malloc:lla) Voidaan käyttää vain dynaamisesti varattuun muistiin Ja vain kerran / varattu muistilohko Turhaan varattu muisti on pois järjestelmän muulta käytöltä Ohjelman lopussa järjestelmä vapauttaa ohjelman käyttämät resurssit 12
Valgrind Kokoelma työkaluja ohjelman debugaukseen ja profilointiin Valgrind - analyysi tehdään ohjelman suorituksen aikana Tällä kurssilla keskitytään memcheck työkaluun, joka havaitsee muunmuassa seuraavia asioita: Virheelliset muistiviitteet Alustamattomien muuttujien epäilyttävä käyttö Muistivuodot Virheelliset muistin vapautukset memcpy, jne. funktioiden käyttö päällekkäisillä muistilohkoilla 13
Valgrind käyttö (pääsääntöisesti Linux) Ohjelma kannattaa kääntää g optiolla (mukaan tietoja ohjelmakoodin symboleista) Tehtäväprojektien makefilet sisältävät jo tämän Suoritus komentorivillä: valgrind ohjelma ohjelma : käännöksen tuloksena syntynyt tiedosto --leak-check=full optiolla saa enemmän tietoa muistivuodoista Tuottaa tulostetta ongelmakohdista ja niiden paikasta koodissa Usein toistuvia samankaltaisia ilmoituksia: aloita tarkastelu ja korjailu alkupään virheistä 14
Valgrind ja TIM Kierroksesta 5 lähtien tehtävät tarkastetaan myös Valgrindilla Jos Valgrind-virheitä, -50 % pisteistä Jos lisäksi varoituksia, -50 % pisteistä è molemmat: neljännes alkuperäisistä pisteistä Koodi saattaa tehdä näennäisesti oikeita asioita, mutta olla silti virheellinen Muistivuodot, virheelliset viittaukset 15
Valgrind: erilaisia muistivuotoja Valgrind luokittelee muistivuodot sen mukaan kuinka peruuttamattomasti muistilohko on kadonnut Definitely lost: varattuun lohkoon ei ole enää osoitinta ja sitä ei voi mitenkään vapauttaa Aiheuttaa virheen TMC-testeissä Indirectly lost / possibly lost: osoittimia vielä on, mutta ne eivät ole suoraan saatavillla Still reachable: muistia ei vapautettu, mutta osoitin siihen on vielä tallella, joten se voitaisiin vapauttaa Valgrind ja TMC ovat armollisia näiden suhteen 16