30
STRUKTURE PODATAKA I ALGORITMI 1 Pokazivači i nizovi

05 Pokazivaci i Nizovi (1)

Embed Size (px)

DESCRIPTION

f

Citation preview

Page 1: 05 Pokazivaci i Nizovi (1)

STRUKTURE PODATAKA I ALGORITMI 1Pokazivači i nizovi

Page 2: 05 Pokazivaci i Nizovi (1)

POKAZIVAČI I ADRESE

Dobijanje adrese nekog objekta (operator &)p = &c; /* p pokazuje na c */

Operator & se primenjuje samo na objekte u memoriji

Pristupanje objektu na koji pokazivač pokazuje (operator *, operator dereferenciranja)

int x = 1, y = 2, z[10];int *ip; /* ip je pokazivac na int */

ip = &x; /* ip sada pokazuje na x */y = *ip; /* y je sada 1 */*ip = 0; /* x je sada 0 */ip = &z[3];/* ip sada pokazuje na z[3] */

Page 3: 05 Pokazivaci i Nizovi (1)

Deklaracija pokazivača

int *ip;double *dp, atof(char *);

Ako ip pokazuje na celobrojnu promenljivu, onda se *ip može koristiti u svakom kontekstu u kome se koristi ceo broj

*ip = *ip + 10;

Unarni operatori & i * imaju viši prioritet od aritmetičkih operatora

y = *ip + 1;

*ip += 1;

++*ip;

(*ip)++;

Pokazivači su takođe promenljive, pa se mogu koristiti i bez dereferenciranja

q = p; *q=*p;

Page 4: 05 Pokazivaci i Nizovi (1)

POKAZIVAČI I ARGUMENTI FUNKCIJA

Pozvana funkcija ne može direktno da promeni promenljivu u funkciji koja je poziva

Primer: Funkcija koja menja mesta dvema promenljivama

swap(x,y);

...

void swap(int a, int b) /* POGRESNO*/{

int temp;

temp = a;a = b;b = temp;

}

Page 5: 05 Pokazivaci i Nizovi (1)

Promena se može postići slanjem pokazivača na promenljive koje treba promeniti

swap(&x, &y);...

void swap(int *pa, int *pb) /* zamena *pa i *pb */{

int temp;

temp = *pa;*pa = *pb;*pb = temp;

}

Shematski prikaz prosleđivanja pokazivača funkciji

Page 6: 05 Pokazivaci i Nizovi (1)

POKAZIVAČI I NIZOVI

U C-u postoji jaka veza između pokazivača i nizova

Deklaracija

int a[10];

definiše niz a veličine 10, odnosno niz od 10 uzastopnih celih brojeva а[0], a[1], ..., a[9].

Shematski prikaz niza a u memoriji računara.

Notacija a[i] ukazuje na i-ti element niza.

Page 7: 05 Pokazivaci i Nizovi (1)

Ukoliko je p pokazivač na ceo broj, deklarisan kao

int *p;

onda linija

p = &a[0];

postavlja pokazivač p da pokazuje na nulti element niza a (indeksi nizova u C-u počinju od nule), ili drugačije rečeno pokazivač p sadrži adresu elementa a[0].

Pokazivač p pokazuje na nulti element niza a

Page 8: 05 Pokazivaci i Nizovi (1)

Sada linija

x = *p;

dodeljuje promenljivoj x vrednost elementa a[0].

Ukoliko pokazivač p pokazuje na određeni element niza, po definiciji p+1 pokazuje na sledeći element, p+i pokazuje na i-ti element iza p, a p-i pokazuje na i-ti element ispred p. Tako, ukoliko p pokazuje na a[0], onda

*(p+1)

predstavlja sadržaj elementa a[1], a

*(p+i)

sadržaj elementa a[i].

Pristupanje elementima niza pomoću pokazivača

Page 9: 05 Pokazivaci i Nizovi (1)

Indeksiranje nizova i aritmetika pointera su veoma bliski.

Po definiciji, naziv niza je upravo adresa nultog elementa niza. Zbog toga nakon izvršenja linije

p = &a[0];

p i a imaju jednake vrednosti, tako da prethodna linija može biti napisana i kao

p = a;

a[i] se može napisati kao *(a+i)

Ukoliko primenimo operator & na oba ova oblika, dobijamo da je &a[i] isto što i a+i, tj. adresa i-tog elementa niza.

Slično važi i za pokazivač p, pa je p[i] isto što i *(p+i).

Postoji razlika između pokazivača i nizovap=a i p++ je dozvoljenoa=p i a++ nije dozvoljeno

Page 10: 05 Pokazivaci i Nizovi (1)

Kada se funkciji prosleđuje naziv niza, ono što se zapravo šalje je adresa nultog elementa.

Unutar pozvane funkcije ovaj argument je lokalna promenljiva, a parametar koji predstavlja niz je pokazivač, odnosno promenljiva koja sadrži adresu nultog elementa niza.

Posmatrajmo funkciju za određivanje dužine stringa (niza karaktera):

/* duzina: vraca duzinu stringa s */int duzina(char *s){

int n;

for(n = 0; *s != '\0'; s++) n++;

return n;}

...

duzina("Zdravo, drugari!"); /* konstantan string */duzina(niz); /* char niz[100]; */duzina(pok); /* char *pok */

Page 11: 05 Pokazivaci i Nizovi (1)

U definiciji funkcija dozvoljeno je deklarisanje formalnih parametara na oba načina; i kao niza i kao pokazivača.

Deklaracije

char s[]

i

char *s

su ekvivalentne.

Funkciji je moguće proslediti i deo nekog niza tako što bi joj se prosledio pokazivač na podniz. Tako, ukoliko je a niz, pozivi

fun(&a[5]);

i

fun(a+5);

prosleđuju funkciji fun podniz koji počinje od petog elementa niza a.

Page 12: 05 Pokazivaci i Nizovi (1)

ADRESNA ARITMETIKA

Ako je p pokazivač na neki element niza, tada p++ uvećava p tako da pokazuje na naredni element

p+=1 uvećava p tako da pokazuje na element koji se nalazi i elemenata iza onog na koji trenutno p pokazuje

ako pokazivači p i q pokazuju na članove istog niza, onda se mogu primeniti operatori ==, !=, <, >, <=, >=

p < q je tačno ukoliko p pokazuje na element koji je ispred onog na koji pokazuje q

p+n pokazuje na n-ti objekat iza onog na koji pokazuje p, bez obzira tip objekata

Page 13: 05 Pokazivaci i Nizovi (1)

PRIMER: FUNKCIJE ZA ALOKACIJU MEMORIJE

alloc(n) – obezbeđuje memoriju za n uzastopnih znakovnih pozicija i vraća pokazivač na prvi član tog niza

afree(p) – oslobađa memoriju koja je na ovaj način rezervisana i na koju pokazuje pokazivač p

funkcije afree se moraju pozivati u suprotnom redosledu od onog u kojima je pozivana funkcija alloc

Page 14: 05 Pokazivaci i Nizovi (1)

#define ALLOCSIZE 10000 /* size of available space */

static char allocbuf[ALLOCSIZE]; /* storage for alloc */static char *allocp = allocbuf; /* next free position */

char *alloc(int n) /* return pointer to n characters */{

if (allocbuf + ALLOCSIZE - allocp >= n) /* it fits */{

allocp += n;return allocp - n; /* old p */

}else /* not enough room */

return 0;}

void afree(char *p) /* free storage pointed to by p */{

if (p >= allocbuf && p < allocbuf + ALLOCSIZE)allocp = p;

}

Page 15: 05 Pokazivaci i Nizovi (1)

PRIMER: FUNKCIJA STRLEN

/* strlen: return length of string s */int strlen(char *s){

char *p = s;

while (*p != '\0')p++;

return p - s;}

Dozvoljene operacije sa pokazivačima:- dodeljivanje pokazivača istog tipa- sabiranje i oduzimanje pokazivača i celog broja- oduzimanje i poređenje dva pokazivača- dodeljivanje nule pokazivaču i njegovo poređenje sa nulom

Nije dozvoljeno:- sabiranje, množenje i deljenje dva pokazivača- pomeranje i maskiranje po bitovima- dodavanje brojeva tipa float i double pokazivačima- dodeljivanje pokazivača jednog tipa pokazivaču drugog tipa bez eksplicitne

konverzije(osim za void*)

Page 16: 05 Pokazivaci i Nizovi (1)

ZNAKOVNI POKAZIVAČI I FUNKCIJE "Ja sam string" je string konstanta

printf("hello, world\n");

char *pmessage;

pmessage = "now is the time";

char amessage[] = "now is the time"; /* niz */

char *pmessage = "now is the time"; /* pokazivač */

Page 17: 05 Pokazivaci i Nizovi (1)

PRIMER: FUNKCIJA STRCPY Funkcija strcpy(s,t) kopira string t u string s

Verzija sa nizovima

/* strcpy: copy t to s; array subscript version */void strcpy(char *s, char *t){

int i;

i = 0;while ((s[i] = t[i]) != '\0') i++;

}

Verzija sa pokazivačima

/* strcpy: copy t to s; pointer version */void strcpy(char *s, char *t){

int i;

i = 0;while ((*s = *t) != '\0') {

s++;t++;

}}

Page 18: 05 Pokazivaci i Nizovi (1)

Iskusni C programeri bi napisali

/* strcpy: copy t to s; pointer version 2 */void strcpy(char *s, char *t){

while ((*s++ = *t++) != '\0');}

Ili još kraće

/* strcpy: copy t to s; pointer version 3 */void strcpy(char *s, char *t){

while (*s++ = *t++);}

Page 19: 05 Pokazivaci i Nizovi (1)

PRIMER: FUNKCIJA STRCMP

Funkcija strcmp(s,t) poredi stringove s i t i vraća negativnu vrednost, nulu ili pozitivnu vrednost u zavisnosti od toga da li je s leksikografski ispred, jednako ili iza t

Verzija sa nizovima

/* strcmp: return <0 if s<t, 0 if s==t, >0 if s>t */int strcmp(char *s, char *t){

int i;

for (i = 0; s[i] == t[i]; i++)if (s[i] == '\0')

return 0;

return s[i] - t[i];}

Page 20: 05 Pokazivaci i Nizovi (1)

Verzija sa pokazivačima

/* strcmp: return <0 if s<t, 0 if s==t, >0 if s>t */int strcmp(char *s, char *t){

for ( ; *s == *t; s++, t++)if (*s == '\0')

return 0;

return *s - *t;}

Page 21: 05 Pokazivaci i Nizovi (1)

NIZOVI POKAZIVAČA; POKAZIVAČI NA POKAZIVAČE

Pokazivači su promenljive, pa se i oni mogu čuvati u nizovima.

Primer: Sortiranje redova teksta po abecednom redu

pročitati sve redove ulaznog teksta sortirati ih prikazati ih po redu

Page 22: 05 Pokazivaci i Nizovi (1)

#include <stdio.h>#include <string.h>

#define MAXLINES 5000 /* maksimalan broj linija koje se mogu sortirati*/

char *lineptr[MAXLINES]; /* pokazivači na linije teksta */int readlines(char *lineptr[], int nlines);

void writelines(char *lineptr[], int nlines);void sort(char *lineptr[], int nlines);

/* sortiranje linija sa ulaza */main(){

int nlines; /* broj učitanih linija */

if ((nlines = readlines(lineptr, MAXLINES)) >= 0){

sort(lineptr, nlines);writelines(lineptr, nlines);return 0;

}else{

printf("greska: suvise veliki broj linija\n");return 1;

}}

Page 23: 05 Pokazivaci i Nizovi (1)

#define MAXLEN 1000 /* maksimalna duzina linije */

int getline(char *, int);char *alloc(int);

/* readlines: ucitava redove sa ulaza */int readlines(char *lineptr[], int maxlines){

int len, nlines;char *p, line[MAXLEN];

nlines = 0;while ((len = getline(line, MAXLEN)) > 0)

if (nlines >= maxlines || p = alloc(len) == NULL)return -1;

else{

line[len-1] = '\0'; /* brisanje znaka za novi red */strcpy(p, line);lineptr[nlines++] = p;

}

return nlines;}

Page 24: 05 Pokazivaci i Nizovi (1)

/* writelines: ispisivanje linija teksta */void writelines(char *lineptr[], int nlines){

int i;

for (i = 0; i < nlines; i++)printf("%s\n", lineptr[i]);

}

ili ovako

/* writelines: ispisivanje linija teksta */void writelines(char *lineptr[], int nlines){

while (nlines-- > 0)printf("%s\n", *lineptr++);

}

Page 25: 05 Pokazivaci i Nizovi (1)

/* sort: sortira niz v u rastucem redosledu */void sort(char *v[], int nlines){

int i, j;char *temp;void swap(char *v[], int i, int j);

for(i = 0; i < n-1; i++)for(j = i+1; j < n; j++)

if( strcmp(v[i],v[j]) > 0 ){

temp = v[i];v[i] = v[j];v[j] = temp;

}}

Page 26: 05 Pokazivaci i Nizovi (1)

VIŠEDIMENZIONALNI NIZOVI

Primer: Konverzija dana u mesecu u dan u godini i obratno funkcija day_of_year – pretvara mesec i dan u dan u godini funkcija month_day – pretvara dan u u godini u mesec i dan

static char daytab[2][13] = {{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

};

/* day_of_year: set day of year from month & day */int day_of_year(int year, int month, int day){

int i, leap;

leap = year%4 == 0 && year%100 != 0 || year%400 == 0;

for (i = 1; i < month; i++)day += daytab[leap][i];

return day;}

Page 27: 05 Pokazivaci i Nizovi (1)

/* month_day: set month, day from day of year */void month_day(int year, int yearday, int *pmonth, int *pday){

int i, leap;

leap = year%4 == 0 && year%100 != 0 || year%400 == 0;for (i = 1; yearday > daytab[leap][i]; i++)

yearday -= daytab[leap][i];

*pmonth = i;*pday = yearday;

}

Page 28: 05 Pokazivaci i Nizovi (1)

Pisanje indeksa

daytab[i][j] /* ispravno */

daytab[i,j] /* pogresno */

Prenošenje višedimenzionalnih nizova funkciji

f(int daytab[2][13]) { ... }

Ili ovako

f(int daytab[][13]) { ... }

a pošto je broj redova nebitan, može se napisati i ovako

f(int (*daytab)[13]) { ... }

što znači da je parametar pokazivač na niz od 13 celih brojeva.

Zagrade su neophodne zato što uglaste zagrade [] imaju veći prioritet od operatora *. Bez zagrada bi se dobila sledeća deklaracija

int *daytab[13]

što bi predstavljalo niz od 13 pokazivača na tip int.

Page 29: 05 Pokazivaci i Nizovi (1)

INICIJALIZACIJA NIZOVA POKAZIVAČA

Primer: Funkcija koja vraća naziv meseca u godini

/* month_name: return name of n-th month */char *month_name(int n){

static char *name[] = {"Illegal month","January", "February", "March","April", "May", "June","July", "August", "September","October", "November", "December"

};

return (n < 1 || n > 12) ? name[0] : name[n];}

Page 30: 05 Pokazivaci i Nizovi (1)

POKAZIVAČI ILI VIŠEDIMENZIONALNI NIZOVI?

int a[10][20];

int *b[10];

char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };

char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };