Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Copyright  Hannu Laine Osoittimet ja taulukot Hannu Laine.

Samankaltaiset esitykset


Esitys aiheesta: "Copyright  Hannu Laine Osoittimet ja taulukot Hannu Laine."— Esityksen transkriptio:

1 Copyright  Hannu Laine Osoittimet ja taulukot Hannu Laine

2 HL1 int main (void) { int a; int *p;//p on osoitinmuuttuja p = &a; *p = 10; printf(”%d”, *p); //tulostuu muuttujan a sisältö scanf(”%d”, p); //muuttujaan a luetaan uusi arvo printf(”%d”, a); //tulostetaan se } * muuttujan määrittelyssä (määrää muuttujan tyypin) * muuttujaa käytettäessä (tarkoittaa epäsuoraa osoitusta) & ei voi esiintyä muuttujan määrittelyssä (C:ssä) & muuttujaa käytettäessä on osoiteoperaattori int * vai int* ? *(&a)ok ja on sama kuin a *a on kielioppivirhe &(*p)ok ja on sama kuin &a Entä &p ? Left value / Right value Osoittimet

3 HL2 Taulukkomuuttujan määrittely Yleinen muoto: alkiotyyppi nimi[koko]; Esim: float taulukko[5]; //tila varataan pinosta 5 float- alkiolle. Koon pitää olla vakio (käännösaikana selvitettävissä oleva luku). Alkiotyyppi voi olla mikä tyyppi tahansa. Taulukon nimi on muistissa vakio-osoitin, jonka kohdetyyppi on alkion tyyppi. Yllä olevassa tapauksessa siis vakion taulukko tyyppi on float *. Sisäinen esitys muistissa: Taulukot (käännösaikana varattavat) 1 taulukko

4 HL3 Taulukot (käännösaikana varattavat) 2 Taulukon alustus float t1[5] = {1.0, 2.0, 3.0, 4.0, 5.0}; float t2[5] = {0}; // kaikki 5 alkiota alustetaan nollaksi float t3[ ] = {1.0, 2.0, 3.0, 4.0, 5.0}; //varataan tila 5:lle float t4[ ] ; //ei ole järkevä merkintä // muuttujan määrittelyssä Taulukon sijoitus: Taulukon sijoitus ei onnistu: float t5[5]; t5 = t1; Edellä oleva lause aiheuttaa käännösvirheen: Syntax error: ”Left value required” tai ”t5 is constant” Tarvitaan silmukka, jossa kopioidaan alkio alkiolta Yksittäistä alkiota voidaan osoittaa indeksoimalla: t1[3] tarkoittaa neljättä alkiota eli neljättä float lukua taulukossa.

5 HL4 float t [5]; //Varataan tila taulukolle float *p; //Varataan tila osoittimelle float x; p = t; //tämä onnistuu ( t = p ei onnistuisi ) Tämän jälkeen ilmaisut t [2] ja p [2] viittaavat samaan alkioon taulukossa t. Miksi näin ? Koska indeksointi merkitsee vain osoitinaritmetiikkaa: *t = 1.0;  t [0]= 1.0; *(t+1) = 2.0;  t [1]= 2.0; *(t+2) = 3.0;  t [2]= 3.0; Osoitinaritmetiikka: lisätään int ( + )  osoitin vähennetään int ( - )  osoitin vähennetään osoitin ( - )  int int edustaa näissä kohdetyypin kokoisia yksiköitä vertailuoperaatiot mahdollisia Osoitinmuuttuja edustaa taulukkoa t p

6 HL5 int main(void) { float t [5]; float *p; //Tietojen luku for (p = t ; p < t + 5 ; p++) scanf(”%f ”, p); //Tietojen tulostus for (p = t ; p < t + 5 ; p++) printf(” %f ”, *p); } //Vastaa allaolevaa ohjelmaa int main(void) { float t [5] ; int i; //Tietojen luku for (i = 0; i < 5 ; i++) scanf(”%f ”, &t [i]); //Tietojen tulostus for (i = 0; i < 5 ; i++) printf(” %f ”, t [i]); } Osoitinmuuttujan ”juoksutus”

7 HL6 int lue_taulukko(float *t, int koko); void tulosta_taulukko(const float *t, int n); int main(void) { float taul[5] ; int n; n = lue_taulukko(taul, 5); tulosta_taulukko(taul, n); return 0; } //Funktioiden toteutukset #include int lue_taulukko(float *t, int koko) { int i = 0; float x ; printf(”\nAnna taulukon %d :s alkio”, i); scanf( ”%f”, &x ); while ( x > 0 && i < koko) { t [ i++ ] = x; if ( i < koko) { printf(”\nAnna taulukon %d :s alkio”, i); scanf( ”%f”, &x ); } return i; } Taulukko parametrina 1 5 t koko taul

8 HL7 //Funktioiden toteutukset void tulosta_taulukko(const float *t, int n) { int i ; printf(”\nTaulukon sisältö on”); for (i = 0 ; i < n ; i++) printf(”\n %7.2f”, t [i]); } Taulukko parametrina 2 Lisämääre const parametrilistassa Taulukko välitetään siis aina osoittimena tehokkuussyistä. Halutaan säilyttää periaate, että protosta näkee, mitä parametreja funktio pääsee muuttamaan. Kääntäjä huolehtii, että osoitinta ei käytetä kohteen muuttamiseen. Yllä olevan funktion toteutuksessa t++; olisi ok, koska muuttaa osoitinta t *t++ = x; ei ole ok, koska muuttaa kohdetta

9 HL8 //Vastaa muuten aikaisemmin esillä ollutta (sivun 5 // alaosa) ohjelmaa mutta nyt taulukko varataan // ajonaikana dynaamisesta muistista #include int main(void) { float *t;// tässä on ero int koko, i; //Tilan varaus alkioille koko = 5; // koko voi nyt olla muuttujassa t = (float*)malloc(koko*sizeof(float)); // tässä on ero //Tietojen luku for (i = 0; i < koko ; i++) scanf(”%f ”, &t [i]); //Tietojen tulostus for (i = 0; i < koko ; i++) printf(” %f ”, t [i]); free(t); //taulukolle varatun tilan vapautus } Huom. Tässä varattu taulukko t voitaisiin välittää aivan samalla tavalla funktioille lue_taulukko ja tulosta_taulukko kuin aikaisempi staattisesti varattu taulukko t. Taulukko dynaamisessa muistissa (esim)

10 HL9 Esimerkki #include float *f(int *size); int main(void) { float *t; int n, i; t = f(&n); //Tietojen tulostus for (i = 0; i < n ; i++) printf(” %f ”, t [i]); return 0; } //Tällä ei toimi. Miksi? float *f(int *n) { float taul [4] = {1.0, 2.0, 3.0, 4.0}; *n = 4; return taul; } Taulukon palautus funktiosta //Tällä toimii. Miksi? float *f(int *n) { float *taul = (float*)malloc(4*sizeof(float)); taul[0] =1.0; taul[1] =2.0; taul[2] =3.0; taul[3] =4.0; *n = 4; return taul; }

11 HL10 2-ulotteiset taulukot (staattiset) Määrittely: Vaihtoehto 1. TElementType arr_2dim[N][M]; Vaihtoehto 2. (Määritellään tyyppinimi riville (typedef)) typedef TElementType Trow[M]; Trow arr_2dim[N]; Molemmissa tapauksissa tilaa varataan NxM alkiolle. Taulukon nimi arr_2dim on edelleen vakio-osoitin. Tämän osoittimen kohdetyyppi on yksiulotteinen taulukko (Trow). Kuinka määritellään vastaava osoitinmuuttuja? Vaihtoehto 1. TElementType (*pointer_for_arr_2dim)[M]; Vaihtoehto 2 typedef TElementType Trow[M]; Trow *pointer_for_arr_2dim; Nyt siis tilaa varataan osoittimelle, ei taulukon alkioille. arr_2dim

12 HL11 Edellisellä sivulla määritelty osoitinmuuttuja pointer_for_arr_2dim voi edustaa kaksiulotteista taulukkoa, jossa on M saraketta. Tämän muuttujan tyyppi on siis samalla sellaisen funktion parametrin tyyppi, joka ottaa kyseisenlaisen kaksiulotteisen taulukon parametrina. Tällaisen funktion prototyyppi on silloin Vaihtoehto A. void f(Trow *array, int rows); Vaihtoehto B. void f(Titem (*array)[M], int rows); Kuinka tällaista funktiota kutsutaan: typedef Titem Trow[M]; void main (void) { //Trow arr[N];//Vaihtoehto A Titem arr[N][M]; //Vaihtoehto B f(arr, N);// Tässä kutsutaan funktiota f } 2-ulotteinen taulukko (staattinen) parametrina Kuinka funktio toteutetaan Funktion toteutuksessa taulukon alkioihin päästään käsiksi normaalilla kaksoisindeksoinnilla tai osoitinaritmetiikalla: array[i][j]  *(*(array+i)+j) Rivien määrä voi siis funktiossa olla vaihteleva, mutta sarakkeiden määrä on nyt lyöty lukkoon käännösaikana.

13 HL12 Vain rivien lukumäärä voidaan tässä määrätä ajonaikana. Tiedämme jo millainen osoitinmuuttuja voi edustaa tällaista kaksiulotteista taulukkoa (pointer_for_arr_2dim). Nyt vain pitää varata tila dynaamisesta muistista: Vaihtoehto A. pointer_for_arr_2dim = (Trow *) malloc(n*sizeof(Trow)); Tämä versio on täysin analoginen yksiulotteisen taulukon kanssa. Nyt vain alkiotyyppi on Trow, joka sattuu olemaan taulukko. Vaihtoehto B. pointer_for_arr_2dim = (float (*)[M]) malloc(m*n* sizeof(float)); tai pointer_for_arr_2dim = (float (*)[M])malloc(n*sizeof(float [M])); Vain rivimäärä voidaan valita ajonaikana. Sarakkeiden määrän on oltava käännösaikainen vakio. Seuraavaksi tehdään taulukko, jossa sarakkeiden määrä on valittavissa ajonaikana ja rivien määrä on käännösaikainen vakio Lopullisena vaiheena on taulukko, jossa molemmat dimensiot määrätään ajonaikana. Rivien määrän suhteen dynaaminen 2- ulotteinen taulukko

14 HL13 Sarakkeiden määrän suhteen dynaaminen 2-ulotteinen taulukko 1 Nyt oletetaan tilanne, että tarvittavien rivien maksimimäärä tiedetään käännösaikana, mutta riville tarvittavien sarakkeiden määrä saadaan selville vasta ajonaikana. Vaaditaan edelleen, että yksittäiseen alkioon päästään merkinnällä arr [ i ] [ j ]; Nyt jokainen rivi on varattava dynaamisesta muistista, koska sen koko määräytyy vasta ajonaikana. Jokaista riviä edustava osoitin tallennetaan osoittimien taulukkoon. Tämä taulukko voi tässä tapauksessa olla staattinen taulukko. 2-ulotteista taulukkoa edustavan staattisen yksiulotteisen osoitintaulukon määrittely on silloin: Telement *arr[ROWS]; Rakenne näyttää silloin seuraavalta: arr

15 HL14 Sarakkeiden määrän suhteen dynaaminen 2-ulotteinen taulukko 2 Tilanvarausfunktio: void allocateMatrix(Telement **arr, int rows, int cols) { int i; for(i = 0; i < rows ; i++) arr[i] = (Telement*)malloc(cols*sizeof(Telement)); }

16 HL15 Täydellisesti dynaaminen 2-ulotteinen taulukko 1 Halutaan, että rivien määrä ja sarakkeiden määrä on valittavissa ajonaikana. Vaaditaan silti edelleen, että yksittäiseen alkioon päästään merkinnällä arr [ i ] [ j ]; Tilanne on nyt muuten sama kuin edellä, mutta nyt myös rivejä edustavien osoittimien taulukko on varattava dynaamisesta muistista, koska senkin koko määräytyy vasta ajonaikana. Tätä osoittimien taulukkoa edustaa silloin osoitin, jonka kohdetyyppi on osoitin. 2-ulotteista taulukkoa edustavan osoittimen tyyppi on nyt Telement **arr; Rakenne näyttää silloin seuraavalta: Tilanvarausfunktio: Telement ** allocateMatrix(int rows, int cols) { Telement **p; int i; p =(Telement**)malloc(rows*sizeof(Telement*)); for(i = 0; i < rows ; i++) p[i] = (Telement*)malloc(cols*sizeof(Telement)); return p; } arr

17 HL16 Täydellisesti dynaaminen 2-ulotteinen taulukko 2 Huomautus 1. Nyt jokainen rivi voisi olla myös pituudeltaan erilainen (ragged array). Huomautus 2. Varausfunktion looppi voitaisiin korvata yhdellä varauksella ja sen jälkeen käydä loopissa sijoittamassa oikeat osoitteet osoitintaulukkoon. Pohdittavaa. Entä kun varausfunktion halutaan tuottavan varatun alueen osoitin parametrissaan. Tällöin siis varausfunktio allocateMatrix olisi prototyypiltään muotoa void allocateMatrix(????);

18 HL17 Esimerkkinä 2-ulotteinen float-taulukko //Funktioiden prototyypit float ** allocateMatrix(int rows, int cols); void readMatrix(float **matrix, int rows, int cols); void displayMatrix(float **matrix, int rows, int cols); int main (void) { float **matrix; int n = 3, m = 4; matrix = allocateMatrix(n,m); //koko voidaan määrätä ajonaikana readMatrix(matrix, n, m); printf(”%f”, matrix[ 1 ][ 2 ]); //kaksoisindeksointi toimii displayMatrix(matrix, n, m); return 0; } Täydellinen 2-ulotteinen taulukko parametrina //Tässä funktion readMatrix toteutus: void readMatrix(float **matrix, int rows, int cols) { int i, j; for (i = 0 ; i < rows ; i++ ) for (j = 0 ; j < cols ; j++ ) scanf(”%f”, &matrix[ i ] [ j ]); //kaksoisindeksointi toimii }

19 HL18 Merkkijono on C-kielessä merkkien taulukko, jossa merkki, jonka koodi on 0, päättää merkkijonon. Tähän erityisominaisuuteen perustuu kaikki merkkijonofunktiot. Merkkijonon tyyppi on siis osoitin merkkiin (char *). Merkkijonot (luku, tulostus, kopiointi) Esimerkki. int main(void) { char name[20];//tilaa 19 merkille + loppunollalle char kopio[20]; scanf(”%s”, name); //voidaan lukea ilman looppia strcpy(kopio, name); //kopioi merkki merkiltä //kopio = name; on edelleen käännösvirhe printf(”%s”, kopio); return 0; } Huomautus 1. scanf ja strcpy hoitavat myös päätösnollan kohteeseensa. Merkkien tulostuksen lopetus ajoissa perustuu päätösnollan löytymiseen. Huomautus 2. Jos kyseessä olisi mikä tahansa muu kuin merkkitaulukko, sen lukemiseen, kopioimiseen ja tulostamiseen pitää itse kirjoittaa loopin sisältävä koodi.

20 HL19 A) Kaikki mitä on sanottu taulukoista yleensä on edelleen voimassa: A1) staattinen taulukko char string1[10]; //tilan varaus char string2[10] = {’a’, ’b’, ’c’, ’d’, ’e’, ’\0’}; //tilanvaraus ja alustus (tilaa 10) char string3[ ] = {’a’, ’b’, ’c’, ’d’, ’e’, ’\0’}; //tilanvaraus ja alustus (tilaa 6) A2) dynaamisesti varattu n = 6; char *string4 = (char*)malloc(n*sizeof(char)); string4[0] = ’a’ ; string4[1] = ’b’ ; string4[2] = ’c’ ; string4[3] = ’d’ ; string4[4] = ’e’ ; string4[5] = ’\0’ ; string4 = ”abcde” ; //ei kopioi merkkejä mutta mitä? Merkkijonot (määrittely ja alustus) B) Voimassa erityisesti merkkijonoille char string5[10] = ”abcde”; //helppo alustus char string6[ ] = ”abcde”; // (tilaa 6) char *string7 = ”abcde”; //mikä on ero edellisiin! string6 = ”abcde”; // ei toimi (left value required!) string7 = ”abcde”; // toimii

21 HL20 Merkkijonojen taulukko on 2-ulotteinen taulukko, koska yksi merkkijono on jo taulukko. Kaikki mitä on sanottu kaksiulotteisista taulukoista yleensä pätee edelleen. Nyt vain yhtä merkkijonoa (yhtä 2-ulotteisen taulukon riviä) voidaan käsitellä kokonaisuutena kirjastofunktioilla. Kaikki versiot siis staattisesta, merkkijonojen määrän (rivien määrän) suhteen dynaamisesta, merkkien määrän (sarakkeiden määrän) suhteen dynaamisesta ja täysin dynaamisesta versiosta ovat mahdollisia. Lisäksi vielä on muistettava, että itse merkkijonot voivat sijaita vakio-merkkijonoalueella. Merkkijonotaulukot

22 HL21 char names1[4][30]={”Pekka”, ”Petra”, Matti”,”Maija”}; char names1[ ][30] ={”Pekka”, ”Petra”, Matti”,”Maija”}; Mitä antaa sizeof (names1) ? Seuraava ei toimi: char names1[ ][ ] = {”Pekka”, ”Petra”, Matti”,”Maija”}; Mikä on erona seuraavissa? char *names2[4] = {”Pekka”, ”Petra”, ”Matti”,”Maija”}; char *names2[ ] = {”Pekka”, ”Petra”, ”Matti”,”Maija”}; Mitä antaa sizeof (names2) ? Entä mitä eroa seuraavassa? char **names3; Mitä antaa sizeof (names3) ? Sijoitukset names3 = names1; // ei ok names3 = names2; //on ok Kaikissa tapauksissa toimii: int i; for (i = 0 ; i < 4 ; i++) printf("%s", namesX[i]); //X voi olla 1,2 tai 3 Merkkijonotaulukot (esimerkkejä)

23 HL22 Ohjelmalle voidaan antaa käynnistyksen yhteydessä komentoriviparametreja. Käytännössä nämä ovat käytettävissä main-funktion parametreina. startup koodi vie parametrit pinoon ennen main- funktion kutsua normaalin funktion kutsumenettelyn mukaan. Komentoriviparametrit (Command line parameters) int main (int argc, char **argv) { for (int i = 0 ; i < argc ; i++) printf(”%s ”, argv[i]); //or printf(”%s ”, *argv++); return 0; } Oletus: Komentorivi on muotoa C:\>oh.exe aaa bb 123 Pino argv argc 4 ohexe.’\0’ aaa bb 123


Lataa ppt "Copyright  Hannu Laine Osoittimet ja taulukot Hannu Laine."

Samankaltaiset esitykset


Iklan oleh Google