Tietorakenteet ja algoritmit Muuttujat eri muisteissa Ohjelman muistialueen layout Paikallisen ja globaalin muuttujan ominaisuudet Dynaamisen muistinkäytön edut Paikallisten muuttujien dynaamisuus ADT ja dynaaminen muisti Käyttäjän valinnan mukaan ADT:n toteuttajan valinnan mukaan Handle-käsite Viitesemantiikka / arvosemantiikka 1
Missä muuttuja voi sijaita? Muuttuja voi sijaita RAM-muistissa Pinossa Data-segmentissä Dynaamisessa muistissa ( heap) Rekisterissä 2
Ohjelman muistialueen layout Command line arguments High address and environment variables Stack Heap Data segment Code segment Low address 3
Esimerkki-ohjelma Data segment int g; void main(void) { Register (probably) register int i; float l1; Stack segment double *d; d = (double*)malloc(sizeof(double)); g = 10; l1 = 1.0; f(); Heap printf("%d", g); // l2 = 'B'; this is not possible void f(void) { Stack segment char l2; l2 = 'A'; printf("%d", g); g = 20; //l1 = 1.1; this is not possible 4
Paikallisen ja globaalin muuttujan ominaisuudet Ominaisuus / Property Näkyvyys / Visibility Elinaika / Lifetime Muistialue / Storage Local Näkyy vain lohkossa, jossa määritelty. Varataan lohkoon tultaessa ja vapautetaan lohkosta poistuttaessa. Stack Global Kaikkialla, jopa ulos tiedostosta. Sama kuin ohjelman elinaika eli niin kauan kuin ohjelma on muistissa Data segment 5
Dynaamisen muistin edut 1) On tarkasti päätettävissä, milloin varaus ja vapautus tapahtuvat ts. Muisti on varattuna tarkalleen vain sen ajan kun sitä tarvitaan. 2) Varattavan muistialueen koko voidaan päättää ajon aikana. 3) Dynaamisen muistialueen koolla ei ole periaatteessa ylärajaa, koska memory management system voi kasvattaa heapin kokoa ajonaikana. (Pino sen sijaan on hyvin rajattu alue ja silla on kiinteä koko). 4) Dynaamista muistia käytettäessä voidaan testata onnistuiko musin varaus. Jos sen sijaan pinossa ei ole enää tilaa tapahtuu stack overflow, joka kaataa koko ohjelman. 6
Paikallisten muuttujien dynaamisuus 1 Näimme, että dynaamisen muuttujan varaushetki ja varattavan alueen koko voidaan määrätä vapaasti ajonaiakana. Paikallisillakin muuttujilla on tiettyä dynaamisuutta: Muuttujan varaushetkeä voidaan säätää lohkotuksilla Muuttujan tulkintatapaa voidaan muuttaa ajonaikana Esimerkki varaushetken ja vapautushetken säädöstä case 1 void main (void) { float arr1[100]; int arr2[100]; //do some calculations for arr1 //do some calculations for arr2 What is the basic difference? case 2 void main (void) { { float arr1[100]; //do some calculations for arr1 { int arr2[100]; //do some calculations for arr2 7
Paikallisten muuttujien dynaamisuus 2 Esimerkki muuttujan tulkintatavan muuttamisesta ajonaikana. void main (void) { char t[2]; t[0] = 1; t[1] = 2: printf( %d, *(int*)t); // tulostuu 513 8
ADT ja dynaaminen muisti Seuraavaksi tarkastelemme erilaisia tapoja hyödyntää dynaamista muistia abstraktien tietotyyppien yhteydessä. Esimerkkinä käytämme jo tuttua pinoa. Lähdetään aluksi siitä, että on olemassa pino, joka esiteltiin osan 6 sivulla 10. Tällöin tietotyypin määrittely oli tällainen: #define N 8 typedef... Titem; typedef struct { Titem array[n]; int top; Tstack; 9
Pinon käyttäjän valinta Pinon käyttäjä, voi valita käyttääkö hän pinoa staattisessa muistissa vai dynaamisessa muistissa. Pino staattisessa muistissa voi main(void) { Tstack stack; initialize_stack(&stack); push(&stack, a ); // jne.. Pino dynaamisessa muistissa voi main(void) { Tstack* stack; stack = (Tstack*) malloc(sizeof(tstack)); initialize_stack(stack); push(stack, a ); // jne free(stack); Näin sovellusohjelmoija saa etuna sen, että pino vie tilaa vain sen ajan kuin sitä tarvitaan ja että pino ei varaa prosessorin pinoa. Alkioiden ylärajarajoitus edelleen voimassa. 10
Pinon toteuttajan valinta 1 Pinon toteuttaja voi päättää, että pinoa käytetään vain dynaamisessa muistissa. Pinon toteuttaja tekee mahdottomaksi käyttää pinoa staattisessa muistissa. Pinon toteuttaja tekee dynaamisen muistin käytön helpoksi ja itse asiassa näkymättömäksi pinon käyttäjälle. Tietotyyppi määritellään silloin näin: #define N 8 typedef... Titem; typedef struct { Titem array[n]; int top; * Tstack; Lisäksi operaatiofunktioiden valikoimaan lisätään funktiot Tstack create_stack(void); void destroy_stack(tstack stack); 11
Pinon toteuttajan valinta 2 Pinon käyttö näyttää silloin seuraavalta. voi main(void) { Tstack stack; // stack is now a handle stack = create_stack(); push(stack, a ); // jne destroy_stack(stack); Huomautus. Nyt siis funktiossa create_stack varataan tila tietueelle, jossa on kentät array ja top. Samassa funktiossa tehdään myös ne toimenpiteet, jotka aikaisemmin tehtiin funktiossa initialize_stack. Huomautus. Funktio create_stack olisi tietysti voitu edelleen pitää initialize_stack nimisenä. Nyt muuttuja stack ei siis ole itse tietue, jossa on pinon tiedot. Se on osoitin itse tietoon. Käytetään myös termiä kahva (handle), viite tai viittaus (reference). Kyseessä on ns. viitesemantiikka, jota käytetään Javassa. Huomaa, että sovelluksessa ei ole pinoon viitattaessa missään osoiteoperaattoreita (&) eikä funktioiden prototyypeissä osoittimia (*). Javassa ei ole osoittimia tarkoittaa siis, että muuttujat ovat aina osoittimia. 12
Lisää esimerkkejä handle-käsitteestä Tiedostojen käsittely C:ssä handlen avulla: FILE *f ; // muuttuja f on handle f = fopen( A:\\text.txt, wt ); //get the handle //Tämän jälkeen tiedostoa käytetään kahvan f avulla fprintf(f, Title ); fprinff( f, %4d%-10s%5.2f, 12, abcdefg, 12.34);... fclose(f); Ikkunat Windows käyttöjärjestelmässä: HANDLE whnd; //whnd on ikkunan handle whnd = CreateWindow(windowClassName, windowtitle, windowstyle, locx, locy, width, height, handleofparent, handleofmenu, handleofapplication, lpwindowcreationdata); MoveWindow(whnd, newlocx, newlocy, newwidth, newheight, repaint);... CloseWindow(whnd); DestroyWindow(whnd); 13
Lisää handle-periaatteesta Java-kielessä on siis käytössä viitesemantiikka. C-kielessä on käytössä arvosemantiikka. Kuten opimme, C-kielessä voidaan toteuttaa abstrakteja tietotyyppejä, jotka toimivat viitesemantiikan mukaisesti. Viitesemantiikan eli handle ajattelun haittana on mm. se, että sijoitus ei toimi : Esimerkkinä tapaus jossa sijoitetaan "kopio" ja tuhotaan alkuperäinen, jolloin myös kopio häviää! Tarvitaan oma funktio Copy korvaamaan sijoitus. void main ( void ) { Tstack stack, kopio; // stack is now a handle stack = create_stack(); kopio = create_stack(); push(stack, a ); push(stack, b ); kopio = stack; // Muistivuoto!! destroy_stack(stack); // Myös kopio häviää!! destroy_stack(kopio); // Myös tämä on ongelma. Miksi? 14
Muita tapoja hyödyntää dynaamista muistia Olemme nähneet kaksi tapaa hyödyntää dynaamista muistia pinon tapauksessa, kun pinon tietoalkiot ovat edelleen vakiokokoisessa taulukossa. Osassa 7 sivulla 3 on esitetty pino, jossa taulukko varataan dynaamiselta alueelta. Tietomäärittely oli silloin #define INCR_SIZE 4 stack typedef... Titem; Dynaaminen muisti typedef struct { Titem* array; array int size; size int top; Tstack; top Myös tällaista pinoa voidaan käyttää dynaamisessa muistissa, joko pinon käyttäjän valinnan perusteella tai pinon toteuttajan päätöksellä handlena, kuten alla voi main(void) { Tstack stack; // stack in now a handle stack = create_stack(); Tällöin tilanne muistissa näyttää kuten kuvassa. 15