STRUKTURE PODATAKA I ALGORITMI 1 Pokazivai i nizovi

  • Slides: 30
Download presentation
STRUKTURE PODATAKA I ALGORITMI 1 Pokazivači i nizovi

STRUKTURE PODATAKA I ALGORITMI 1 Pokazivači i nizovi

POKAZIVAČI I ADRESE Dobijanje adrese nekog objekta (operator &) p = &c; /* p

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; y = *ip; *ip = 0; ip = &z[3]; /* /* ip sada pokazuje na x */ y je sada 1 */ x je sada 0 */ ip sada pokazuje na z[3] */

 Deklaracija pokazivača int *ip; double *dp, atof(char *); Ako ip pokazuje na celobrojnu

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;

POKAZIVAČI I ARGUMENTI FUNKCIJA Pozvana funkcija ne može direktno da promeni promenljivu u funkciji

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; }

 Promena se može postići slanjem pokazivača na promenljive koje treba promeniti swap(&x, &y);

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

POKAZIVAČI I NIZOVI U C-u postoji jaka veza između pokazivača i nizova Deklaracija int

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.

 Ukoliko je p pokazivač na ceo broj, deklarisan kao int *p; onda linija

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

 Sada linija x = *p; dodeljuje promenljivoj x vrednost elementa a[0]. Ukoliko pokazivač

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

 Indeksiranje nizova i aritmetika pointera su veoma bliski. Po definiciji, naziv niza je

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 nizova p=a a=p i i p++ a++ je dozvoljeno nije dozvoljeno

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

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 != ''; s++) n++; return n; }. . . duzina("Zdravo, drugari!"); /* konstantan string */ duzina(niz); /* char niz[100]; */ duzina(pok); /* char *pok */

 U definiciji funkcija dozvoljeno je deklarisanje formalnih parametara na oba načina; i kao

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.

ADRESNA ARITMETIKA Ako je p pokazivač na neki element niza, tada p++ uvećava p

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

PRIMER: FUNKCIJE ZA ALOKACIJU MEMORIJE alloc(n) – obezbeđuje memoriju za n uzastopnih znakovnih pozicija

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

#define ALLOCSIZE 10000 /* size of available space */ static char allocbuf[ALLOCSIZE]; /* storage

#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; }

PRIMER: FUNKCIJA STRLEN /* strlen: return length of string s */ int strlen(char *s)

PRIMER: FUNKCIJA STRLEN /* strlen: return length of string s */ int strlen(char *s) { char *p = s; while (*p != '') 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*)

ZNAKOVNI POKAZIVAČI I FUNKCIJE "Ja sam string" je string konstanta printf("hello, worldn"); char *pmessage;

ZNAKOVNI POKAZIVAČI I FUNKCIJE "Ja sam string" je string konstanta printf("hello, worldn"); char *pmessage; pmessage = "now is the time"; char amessage[] = "now is the time"; /* niz */ char *pmessage = "now is the time"; /* pokazivač */

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

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]) != '') 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) != '') s++; t++; } } {

 Iskusni C programeri bi napisali /* strcpy: copy t to s; pointer version

Iskusni C programeri bi napisali /* strcpy: copy t to s; pointer version 2 */ void strcpy(char *s, char *t) { while ((*s++ = *t++) != ''); } Ili još kraće /* strcpy: copy t to s; pointer version 3 */ void strcpy(char *s, char *t) { while (*s++ = *t++); }

PRIMER: FUNKCIJA STRCMP Funkcija strcmp(s, t) poredi stringove s i t i vraća negativnu

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] == '') return 0; return s[i] - t[i]; }

 Verzija sa pokazivačima /* strcmp: return <0 if s<t, 0 if s==t, >0

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 == '') return 0; return *s - *t; }

NIZOVI POKAZIVAČA; POKAZIVAČI NA POKAZIVAČE Pokazivači su promenljive, pa se i oni mogu čuvati

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

#include <stdio. h> #include <string. h> #define MAXLINES 5000 /* maksimalan broj linija koje

#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 linijan"); return 1; } }

#define MAXLEN 1000 /* maksimalna duzina linije */ int getline(char *, int); char *alloc(int);

#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] = ''; /* brisanje znaka za novi red */ strcpy(p, line); lineptr[nlines++] = p; } return nlines; }

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

/* writelines: ispisivanje linija teksta */ void writelines(char *lineptr[], int nlines) { int i; for (i = 0; i < nlines; i++) printf("%sn", lineptr[i]); } ili ovako /* writelines: ispisivanje linija teksta */ void writelines(char *lineptr[], int nlines) { while (nlines-- > 0) printf("%sn", *lineptr++); }

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

/* 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; } }

VIŠEDIMENZIONALNI NIZOVI Primer: Konverzija dana u mesecu u dan u godini i obratno funkcija

VIŠEDIMENZIONALNI NIZOVI Primer: Konverzija dana u mesecu u dan u godini i obratno funkcija day_of_year – pretvara mesec i 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}, {0, 31, 29, 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; }

/* month_day: set month, day from day of year */ void month_day(int year, int

/* 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; }

 Pisanje indeksa daytab[i][j] /* ispravno */ daytab[i, j] /* pogresno */ Prenošenje višedimenzionalnih

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.

INICIJALIZACIJA NIZOVA POKAZIVAČA Primer: Funkcija koja vraća naziv meseca u godini /* month_name: return

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]; }

POKAZIVAČI ILI VIŠEDIMENZIONALNI NIZOVI? int a[10][20]; int *b[10]; char *name[] = { "Illegal month",

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" };