47
Programski jezik C - pokazivači

Programski jezik C - pokazivačipoincare.matf.bg.ac.rs/~gordana/programiranjeI/Programski_Jezik_C_pokazivaci.pdfPokazivači i adrese Pokazivači (engl. pointer) predstavljaju tip podataka

  • Upload
    others

  • View
    8

  • Download
    1

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