Del 3 Pekere 1 RR 2016
Peker/pointer En peker er en variabel som kan holde adressen (peke til) til en annen variabel. Pekere vil gi oss muligheten for å gå direkte til en adresse i minne, og lese/skrive til cella som ligger der. En uheldig side ved pekere er at programmene fort blir uoversiktlig, og vanskelig å feilsøke. 2
Hvordan lage en peker? int j = 25, int *peker_j = &j; peker_j holder adressen til int variabelen j. Vi kan nå få tak i verdien som ligger i variabelen j på 2 måter. Lese av j direkte. (Benytte j som en vanlig variabel.) Benytte peker_j, og så lese av innholdet i den cella som peker_j peker til. (Indirekte adressering). 3
Hvordan deklarere pekere? Sett * tegnet foran variabelnavnet. type *peker_navn; NB!! Må opprette pekere av den datatype den skal peke til. En int peker kan kun peke til int data. Tilsvarende kan en float peker kun peke til float data. Selve pekeren holder adressen i minne hvor "riktig" data ligger. 4
Hvordan lage en peker? int *pkr_i; float *pkr_f; // peker til integer // peker til float pkr_i kan kun peke til sin basetype som er int. Tilsvarende kan pkr_f kun peke til sin basetype som er float. 5
Hvordan få tak i adressen til en variabel? Må benytte & operatoren som gir adressen til e variabel. int i, j, *pkr_i = &i; // pkr_i peker nå til i. pkr_i = &j; // pkr_i peker nå til j. NB!! Pekere skal initieres (gis en verdi/adresse) før de benyttes. Hvis en benytter en tilfeldig peker kan en havne vilkårlig i minne og lage KAOS. F.eks stoppe operativsystemet, skrive i BIOS-oppsettet m.m 6
Eksempel med pekere. float sum=0.0, r=3.5, *pkr_r = &r; // pkr_r peker nå til variabelen r. sum = *pkr_r + 2.0; // sum blir nå 5.5 *pkr_r = 55.2; // r blir nå 55.2 pkr_r = 4.0; // FY!!! pkr_r er en adresse. 7
Hvordan endre inneholdet i cella som pekeren peker til. int *pkr_i, verdi; pkr_i = &verdi; *pkr_i = 100; // verdi = 100 *pkr_i = *pkr_i +10; // verdi = 110 (*pkr_i)++; // verdi = 111 (*pkr_i)--; // verdi = 110 Må benytte parenteser (*pkr_i)++ når vi ønsker at innholdet i cella som pekeren peker til skal inkrementeres. Hadde vi skrevet *pkr_i++ så vil først pekeren inkrementeres (peke til neste element) og deretter vil innholdet hentes. 8
Pekere og tabeller er nært koblet i C. Navnet på en tabell er egentlig en peker (adresse) til det første elementet i tabellen. float tab[100], *pkr; pkr = tab; Både tab og pkr er pekere som peker til første element i tabellen -> tab[0]. tab samme som &tab[0] tab[4] tilsvarer *(pkr +4) tilsvarer pkr[4] *(pkr + n) tilsvarer pkr[n] pkr++; // peker nå til neste element i tabellen. 9
Nullpeker. Enkelte ganger trenger vi en peker som ikke peker til en fornuftig adresse. Lager en null-peker. Kan benyttes til å avsluttet peker-operasjoner. int *pkr; pkr = 0; // Har nå laget en null peker. pkr = NULL; // Alternativ metode... if ( pkr ) { } // utføres hvis pkr ikke er en nullpeker. 10
Pekere og strenger. For å holde/peke til strenger kan vi benytte ; 1. char tabeller 2. char pekere char navn[80]= "Fredrikstad"; tabellen navn kan nå holde strenger på opp til 79 tegn + nulltegnet. char *bynavn="oslo"; Pekeren bynavn peker til første element i strengen «Oslo».. Ettersom vi benytter en peker kan pekeren bynavn peke til strenger fra 0 til uendelig tegn. 11
Pekere og strenger. Bruk av char pekere; char *navn="anton Berg"; // Pekeren navn peker nå til bokstaven A, og vi kan skrive ut hele strengen på følgende vis; puts(navn); navn="olav Digre"; puts(navn); // skriver ut strengen «Anton Berg» // Pekeren navn peker nå til bokstaven O i strengen. // skriver ut strengen «Olav Digre» 12
Hvordan lagre mange strenger i minne? Har to muligheter. 1. Benytter 2-dimensjonal char tabell. char navn[4][30]; // 4 strenger av 30 tegn. char navn[4][30] = { "Bjarne Nilsen", "Maria Berge", "Sølvi Olsen", "Arne Nilsen" }; 13
Hvordan lagre mange strenger i minne? 2. Benytter en tabell med char pekere. char *navn[4]; // 4 pekere til 4 strenger char *navn[4] = { "Bjarne Nilsen", "Maria Berge", "Sølvi Olsen", "Arne Nilsen" }; Peker navn[0] peker til strengen "Bjare Nilsen". Peker navn[3] peker til strengen "Arne Nilsen". Ønsker vi å skrive ut de fire strengene kan vi gjøre følgende; for ( k=0; k < 4 ; k++ ) puts(navn[k]); 14
Funksjonskall med vanlige parametre. void byttom(int x, int y); int main(void) { int a=2, b=3; byttom(a,b); // aktuelle argumenter er a og b printf("\nutskrift fra main(): a = %d b = %d\n", a,b); return 0; } 15
void byttom(int a, int b) { } int temp; temp = a; // Bytter om på verdiene i a og b. a = b; b = temp; printf("\nutskrift fra byttom(): a = %d b = %d\n",a,b); return; Resultat: Utskrift fra byttom(): a = 3 b = 2 Utskrift fra main(): a = 2 b = 3 Variablene a og b i main() kan ikke endres fra funksjonen byttom(). Må benytte pekere. 16
Funksjonskall med pekere som parametere. void swap(int *p_a, int *p_b); int main(void) { } int a=2, b=3; swap(&a, &b); // aktuelle argumenter er &a og &b printf("\na = %d b = %d\n", a, b); return 0; 17
Funksjonskall med pekere som parametere. void swap(int *p_a, int *p_b) { } int temp; temp = *p_a; // Bytter om på verdiene i a og b. *p_a = *p_b; *p_b = temp; return; Resultat: a = 3 b = 2 Ved å overføre pekere blir den kalte funksjon i stand til å endre de aktuelle argumentene. 18
Når vi overfører tabeller kan den kalte funksjon endre tabell-elementene, fordi en tabell overføres som en adresse (peker). Eksempel med overføring av tabell som argument. int main(void) { float kar_liste[100]; int antall=100;.. beregn( kar_liste, antall );.. } // kar_liste er samme som &kar_liste[0] 19
Når vi overfører tabeller kan den kalte funksjon endre tabell-elementene, fordi en tabell overføres som en adresse (peker). Deklarasjonen av funksjonen beregn() kan være en av følgende: 1. void beregn( float tab[], int k) 2. void beregn( float tab[100], int k) 3. void beregn( float *tab, int k) Funksjonen beregn() kan nå benytte tabellen kar_liste[100] som om det var en lokal tabell, fordi den har adressen til starten av tabellen. Og dermed er blir elementene tilgjengelig. 20
Når er pekere nyttige?? Når vi vil overføre adressen til en variabel til en annen funksjon. Når vi skal sortere strenger, fordi vi da kun behøver å bytte om adresser og ikke hele strengen. Når vi skal benytte ledig minne (dynamisk) til å lagre store datamengder (f.eks tabeller). Når vi skal benytte I/O porter, skjermbuffer, utføre lavnivå programmering. Når vi skal lage lenker mellom sammenhørende data-elementer (lenkede lister m.m) Pekere vil i en del operasjoner være raskere (mer effektivt). Vi kan løse enkelte problemstillinger mer effektivt/enklere med pekere enn uten. 21