Upload
others
View
17
Download
3
Embed Size (px)
Citation preview
Programski jezik C -pokazivači
Pokazivači i adrese Memorija računara organizovana je u niz uzastopnih bajtova Memoriji se pristupa preko adresa Adrese su brojevi koji se najčešće zapisuju u heksadekadnom sistemu Uzastopni bajtovi memorije mogu da se tretiraju kao jedinstven podatak Na primer, dva (ili četiri, u zavisnosti od sistema) uzastopna
bajta mogu da se tretiraju kao jedinstveni podatak celobrojnogtipa
2
Pokazivači i adrese Pokazivači (engl. pointer) predstavljaju tip podataka u C-u čije
su vrednosti memorijske adrese Pokazivačka promenljiva (promenljiva pokazivačkog tipa) sadrži
adresu memorijske lokacije neke (druge) promenljive Na 16-bitnim sistemima adrese zauzimaju dva bajta, na 32-
bitnim sistemima četiri, na 64-bitnim osam Iako su pokazivačke vrednosti (adrese) celi brojevi, pokazivački
tipovi se razlikuju od celobrojnih
3
Primer#include <stdio.h>void f(int a[]) {
printf("f: %d\n", sizeof(a));}int main() {
int a[] = {1, 2, 3, 4, 5};printf("main: %d\n", sizeof(a));f(a);return 0;
}
4
Pokazivači C razlikuje više pokazivačkih tipova Za svaki tip podataka (i osnovni i korisnički – int, float, double,
struct tacka, enum karta itd.) postoji odgovarajući pokazivačkitip
Na primer, tip pokazivača koji ukazuje na podatak tipa int zapisuje se
int * Slično važi i za druge tipove, npr float *, double *, char *...
ALI: zvezdica (*) vezuje se za ime (identifikator) pokazivača, ne za sam tip (bez obzira na razmake tj. položaj zvezdice)
5
Pokazivači - primeri
int *p1; /* pokazivac na int */ int* p2; /* pokazivac na int */ int* p3, p4; /* p3 je pokazivac na int, p4 je int */
6
Pokazivači - sadržaj Pokazivačka promenljiva može da sadrži adrese promenljivih ili
elemenata niza Adresa promenljive tj. elementa niza dobija se operatorom &
primenjenim na tu promenljivu tj. element niza
Operator & je unaran, naziva se i operatorom referenciranja ili adresnim operatorom
Adresni operator vraća adresu svog operanda Adresni operator može da se primeni samo na promenljive i
elemente niza, a ne i na izraze ili konstante
7
Primer int a=10; int *p; p = &a;
Kako izgleda memorija?
8
Operator dereferenciranja
Unarni operator * (koji zovemo „operator dereferenciranja“) primenjuje se na pokazivačku promenljivu i vraća sadržaj sa adrese koja je vrednost te promenljive, vodeći računa o tipu
Dereferencirani pokazivač može biti l-vrednost
Dereferencirani pokazivač nekog tipa može da se pojavi u bilokom kontekstu u kojem može da se pojavi podatak tog tipa
9
Primer
int a=10, *p; p = &a; *p = 5; *p = *p+a+3;
Kako izgleda memorija i koja je vrednost promenljive a?
10
Pokazivači i celi brojevi
Pokazivački i celobrojni tipovi su različiti pa je naredni kodneispravan:
int *pa, a;pa = a;a = pa;pa = 1234;
11
Pokazivači i celi brojevi (0), NULL
Pokazivač može da ima vrednost 0; Nije ga moguće dereferencirati („segmentation fault“)
Ćešće se koristi simbolička konstanta NULL Definisana u zaglavlju <stdio.h>
Na primer, int* p = NULL;
12
NULL, primer
int* a = NULL, b = 2; a = &b; *a = 3; b++;
Koja je vrednost promenljive b?
13
Konverzije
Pokazivači mogu da se konvertuju iz jednog u drugi pokazivački tip, na primer:
int a; char* p = (char*)&a;
Vrši se samo prosta dodela adrese, bez konverzije podataka na koje pokazivač ukazuje
Često dovodi do neželjenih efekata Oprez!
14
Konverzije - primer
int* p = NULL;float f = 5;p = &f;printf("%d\n", *p);
15
Konverzije - primer
int* p = NULL;float f = 5;p = (int *)&f;printf("%d\n", *p);
Program će ispisati 1084227584 - broj koji odgovara celobrojnoj vrednosti zapisa u pokretnom zarezu broja 5
16
Pokazivači na void U nekim slučajevima, poželjno je imati mogućnost „opšteg“
pokazivača, tj. pokazivača koji može da ukazuje na promenljiverazličitih tipova
Za to se koristi tip void*void* p = NULL;int a = 3, b = 5, c;float f = 3.0, g;p = &a;c = *(int*)p + b;p = &f;g = *(float*)p + 1;
17
const I na pokazivače može da se primeni ključna reč const
/*promenljiva i*/int i;
/*vrednost od a je 5 i ne moze se menjati*/const int a = 5;
/*pokazivac na konstantan int, npr p=&a; */const int* p = &a;
/*vrednost p1 ne moze se menjati*/int* const p1 = &i;
/*p2 se ne moze menjati, pokazuje na const int*/const int* const p2 = &a;
18
Kopiranje pokazivača
Vrednost pokazivača može da se dodeli pokazivaču istog tipa Kako izgleda memorija? Taj način kopiranja nekada se naziva plitkim kopiranjem Duboko kopiranje podrazumeva da se napravi kopija sadržaja
koji se nalazi na adresi na koju pokazivač pokazuje, a da se pokazivač usmeri ka kopiji tog sadržaja
19
Kopiranje pokazivača - primerint a = 5;int *p = NULL, *q = NULL;p = &a;q = p;*p = 2;*q = *p + 5;
Koja je vrednost promenljive a? Kako izgleda memorija?
20
Pokazivači i argumenti funkcija Već smo napomenuli da C prenosi argumente funkcija po vrednosti,
tj. ne omogućuje direktnu promenu vrednosti argumenata od stranepozvane funkcije.
Na primer, funkcija za razmenu vrednosti x, y (swap):void swap(int x, int y){ int temp;
temp =x;x = y;y = temp;
} i njen poziv swap(a, b); neće razmeniti vrednosti argumenata a i b (zbog prenosa
argumenata po vrednosti, funkcija swap razmenjuje samo sopstvenekopije promenljivih a i b iz svog "stek okvira") 21
Pokazivači i argumenti funkcijavoid swap(int x, int y){ int temp;
temp =x;x = y;y = temp;
}main() {
int a = 3, b = 5;swap(a, b);printf("a = %d, b = %d\n", a, b);
}
22
Pokazivači i argumenti funkcija Željeni efekat može se postići prenosom pokazivača na argumente
umesto samih argumenata, tj. pozivom funkcije swap(&a, &b) definisane sledećom definicijom (parametri funkcije su pokazivači na celobrojne promenljive):
void swap(int *px, int *py) / razmena *px i *py /{ int temp;
temp = *px;*px = *py;*py = temp;
} deklaracija (prototip): void swap(int *, int *) poziv swap(&a, &b)
23
Pokazivači i argumenti funkcija
24
Pokazivači i argumenti funkcija -primer
#include <stdio.h>void deljenje(unsigned a, unsigned b, unsigned* pk, unsigned* po) {
*pk = 0; *po = a;while (*po >= b) {
(*pk)++;*po -= b;
}}int main() {
unsigned k, o;deljenje(14, 3, &k, &o);printf("%d %d\n", k, o);return 0;
}25
Pokazivači i strukturestruct razlomak *pa = &a;(*pa).imenilac = 2;(*pa).brojilac = 1;
Zagrade su neophodne zbog prioriteta operatora Umesto kombinacije operatora * i ., moguće je koristiti operator
-> koji je najvišeg prioriteta
struct razlomak *pa = &a;pa->imenilac = 2;pa->brojilac = 1;
26
Pokazivači, strukture i funkcije Strukture se kao argumenti u funkciju prenose po vrednosti Često zauzimaju veliki prostor Umesto struktura u funkcije mogu da se proslede njihove adrese
- pokazivači na strukture
void saberi_razlomke(const struct razlomak *pa, const struct razlomak *pb, struct razlomak *pc)
{pc->brojilac = pa->brojilac*pb->imenilac +
pa->imenilac*pb->brojilac;pc->imenilac = pa->imenilac*pb->imenilac;
}27
Pokazivačka aritmetika Razlikuje se od običnog izračunavanja
Izraz p+1 označava dodavanje dužine jednog objekta tipa na koji ukazuje p
NE označava dodavanje vrednosti 1 na p
Primer: int* p; p+1 i p mogu da se razlikuju za dva ili četiri, tj za sizeof(int)
Ako p sadrži adresu 100, i ako je sizeof(int) jednako 4, vrednost p+3 je adresa 100 + 3*4 = 112
28
Pokazivačka aritmetika Izraz p-n označava oduzimanje n dužina objekta tipa na koji
pokazuje p
Prefiksni i postfiksni operatori ++ i - – (slično značenje)
Dva pokazivača je moguće oduzimati- vrši se deljenje razlike veličinom tipa pokazivača
Dva pokazivača ne mogu da se sabiraju
29
Operatori poređenja
Ako su pokazivači istog tipa: relacijski operatori
Npr. p1 < p2, p1 == p2, . . . (značenje)
30
Prioriteti operatora
Unarni operatori & i * imaju viši prioritet nego binarni aritmetičkioperatori
Na primer, *p+1 je zbir sadržaja lokacije na koju ukazuje p i vrednosti 1 (ne sadržaj na adresi p+1)
Unarni operatori &, *, i prefiksni ++ se primenjuju zdesna nalevo, pa naredba
++*p; inkrementira sadržaj lokacije na koju ukazuje p
Postfiksni operator ++ se primenjuje sleva nadesno ali ima viši prioritet od drugih unarnih operatora koji se primenjuju zdesnanalevo
*p++ vraća sadržaj na lokaciji p kao bočni efekat p se inkrementira
31
Primer
int a = 3, *p; p = &a; *p = *p+1; ++*p; Koja je vrednost promenljive a?
32
Nizovi
Postoji tesna veza između pokazivača i nizova int a[10]; deklariše niz od 10 elemenata tipa int Izraz sizeof(a) ima istu vrednost kao izraz 10*sizeof(int)
Početni element niza je a[0], a deseti element je a[9] i oni su u memoriji poređani uzastopno
U fazi kompilacije, imenu niza a pridružena je informacija o adresi početnog elementa niza, o tipu elemenata niza i o broju elemenata niza
33
Pokazivači i nizovi
Ime niza (vrednost) a nije pokazivačkog tipa Nije l-vrednost, ne može mu se promeniti vrednost
int a[50]; int b[50]; a = b; /*neispravna dodela*/
34
Pokazivači i nizovi
a <=> &a[0] *a <=> a[0] a+1 <=> &a[1] *(a+1) <=> a[1] ... a+i <=> &a[i] *(a+i) <=> a[i] ... a+n-1 <=> &a[n-1] *(a+n-1) <=> a[n-1] Nema provere granica niza, pa je dozvoljeno pisati (tj. prolazi fazu
prevođenja) i a[100], *(a+100), a[-100], *(a-100), iako je veličina niza samo 50
35
Pokazivači i nizovi Elementima niza može se pristupati korišćenjem pokazivačke
aritmetike Na pokazivač može da se primeni nizovski indeksni pristup Na primer, int* p; p[3]... Vrednost izraza p[3] poklapa se sa elementom p[3] niza koji bi
počinjao na adresi p (bez obzira što p nije niz nego pokazivač)
Na primer, ako je sizeof(int) 4 i ako pokazivač p ukazuje na adresu 100 na kojoj počinje niz celih brojeva, tada je p[3] ceo broj koji se nalazi u memoriji počevši od adrese 112
Isto: *(p+3) (korišćenje pokazivačke aritmetike)
36
Pokazivači i nizovi Dakle, bez obzira da li je x pokazivač ili niz, važi
x[n] <=> *(x+n) i x+n <=> &x[n]
Ako funkcija prima pokazivač na prvi element niza, implementacijafunkcije može da koristi indeksni pristup kao da je u pitanju niz
Oprez! Mora da bude obezbeđen memorijski prostor!
Primer: int a[10]; int *b; a[3] = 5; b[3] = 8; // nije dobro
37
Pokazivači i nizovi Izraz a ima vrednost adrese početnog elementa i tip int [10] Izraz &a ima istu vrednost (tj. sadrži istu adresu) kao i a, ali
drugačiji tip - tip int (*)[10] tip pokazivača na niz od 10 elemenata koji su tipa int
int a[10]; /*niz celobrojnih vrednosti*/ int *b[10]; /*niz pokazivaca*/ int (*c)[10]; /*pokazivac na niz sa 10 elemenata*/ c = &a; (*c)[0] = 0; /*a[0] = 0;*/
38
Pokazivači i nizovi
Niz ne može da se prenese kao argument funkcije Kao argument funkcije može da se navede ime niza i time se
prenosi samo pokazivač koji ukazuje na početak niza Funkcija koja prihvata takav argument može da primi tip char [] ili
char * (npr. char a[] ili char * a) Ovim se u funkciju kao argument prenosi (po vrednosti) samo
adresa početka niza, ne i informacija o dužini niza
39
Pokazivači i nizovi#include <stdio.h>int main() {int a[] = {8, 7, 6, 5, 4, 3, 2, 1};int *p1, *p2, *p3;/* Ispis elemenata niza */printf("%d %d %d\n", a[0], a[3], a[5]);/* Inicijalizacija pokazivaca -ime niza se konvertuje u pokazivac */p1 = a; p2 = &a[3]; p3 = a + 5;printf("%d %d %d\n", *p1, *p2, *p3);/* Pokazivaci se koriste u nizovskojsintaksi */printf("%d %d %d\n", p1[1], p2[2], p3[-1]);
40
Pokazivači i nizovi/* Pokazivacka aritmetika */p1++; --p2; p3 += 2; /* a++ nije dozvoljeno */printf("%d %d %d %lu\n", *p1, *p2, *p3, p3 - p1);
*p1++; printf("%d ", *p1);*(p1++); printf("%d ", *p1);(*p1)++; printf("%d\n", *p1);return 0;} 8 5 3 8 5 3 7 3 4 7 6 1 6 6 5 6 41
Pokazivači na funkcije Funkcije se ne mogu direktno prosleđivati kao argumenti drugim
funkcijama, vraćati kao rezultat funkcija i ne mogu se dodeljivati promenljivima
Moguće posredno korišćenjem pokazivača na funkcije
Više o tome u okviru kursa Programiranje 2
42
Dinamička alokacija memorije
Više o tome u okviru kursa Programiranje 2
43
Funkcije za rad sa stringovima -primeri
/ strcpy: kopira t u s; verzija sa indeksiranim nizovima /
char* strcpy(char *s, const char *t) // parametri mogu biti i char s[ ], const char t[ ]
{int i;i = 0;while ((s[i] = t[i]) != ‘\0')
i++;return s;
}
44
Funkcije za rad sa stringovima -primeri
char *strcpy(char *s, const char *t){ char * temp=s;
while ((*s = *t) != ‘\0') {s++;t++;
}return temp;
}…while ((*s++ = *t++) != ‘\0');return temp;…while (*s++ = *t++);return temp;
45
Funkcije za rad sa stringovima -primeri
/ strcmp: vraca <0 ako je s<t, 0 ako je s==t, >0 ako je s>t /int strcmp(char *s, char *t){
int i;for ( ; *s == *t; s++, t++)
if (*s == ‘\0')return 0;
return *s - *t;}
46
Funkcije za rad sa stringovima -primeri
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];}
47