Upload
biljana-maksic
View
47
Download
0
Embed Size (px)
DESCRIPTION
f
Citation preview
STRUKTURE PODATAKA I ALGORITMI 1Pokazivači i nizovi
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] */
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 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);...
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 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
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č 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 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
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 */
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 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 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 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){
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*)
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č */
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++;
}}
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++);}
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];}
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;}
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 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;
}}
#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;}
/* 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++);
}
/* 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 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;}
/* 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 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 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", "Jan", "Feb", "Mar" };
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };