programski jezik c - apeg.ac.meapeg.ac.me/nastava/spr_2019_c_pokazivaci i dinamicka memorija.pdf ·...
TRANSCRIPT
Pokazivači (Pointeri)
• Pojam pointera
• Operatori za rad sa pointerima
• Pokazivači na vektor i matricu
• Funkcije i pokazivači
Za kompajliranje koda koristen DEV-C++ 4.9.9.2 Compiler !!!!
Pokazivači (pointeri) POINTERI?
Pokazivači (Pointeri)*
Pokazivači obezbjeđuju indirektan pristup memoriji (putem adrese)
Pokazivač je promjenljiva koja sadrži memorijsku adresu nekog objekta
ako je u pitanju polje – tada je to adresa početnog elementa polja
ako je u pitanju funkcija – tada je to adresa početka funkcije
Ako promjenljiva p sadrži adresu objekta q, kažemo da p pokazuje na q
Primjer:
MEMORIJA
adresa
101
100
99
q 65
Deklaracija
pokazivača:
tip *ime;
Primjer:
int *pi;
char *pc,
*tekst;
100 p
* Dio slajdova od: Dražen Brđanin. Elektrotehnički fakultet Banja Luka, sa dopustenjem.
Pokazivači (pointeri) POINTERI?
Operatori za rad sa pokazivačima
Adresni operator &
daje adresu nekog objekta u
memoriji
Ako je q neka promjenljiva tada
&q daje adresu te promjenljive
Ako želimo da pokazivač p
pokazuje na promjenljivu q
imamo p=&q
MEMORIJA
adresa
101
100
99
q 65
Operator indirekcije *
omogućava indirektan pristup
promjenljivoj koristeći pokazivač na tu
promjenljivu
Ako je s neka promjenljiva, a p
pokazivač na isti tip, tada se može pisati
s=*p .
To znači da će q dobiti vrijednost koja
se nalazi na lokaciji koju pokazuje p
MEMORIJA
adresa
101
100
99
q 65
100 p
p=&q 100 p
p=&q
65 s
s=*p
Pokazivači (pointeri) POINTERI?
Operatori za rad sa pokazivačima Adresni operator & i operator indirekcije * su INVERZNI OPERATORI
y = *& x ili y = x
ili
int x,y,*z;
z = &x;
y = *z; y = *z = *&x = x
Primjer:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x, prom=5, *pokaz;
pokaz = &prom;
x = *pokaz;
printf("prom=%d\npokaz=%d\nx=%d\n",prom, pokaz, x);
return 0;
}
prom=5
pokaz=2293568
x=5 Adresa od prom
Pokazivači (pointeri) POINTER NA POLJE?
Pokazivač na vektor (jednodimenzionalno polje)
Ime vektora (niza) predstavlja adresu početnog elementa vektora i
već je samo po sebi pokazivač (statički pokazivač)
a[2] a[0] a[1]
0 0 0 0
a[3]
0
a[4]
int a[5]={0};
int *p, *q;
p = a;
ili
p = &a[0];
q = a+3;
ili
q = &a[3];
Napomena:
a je statička promjenljiva
Nije dozvoljeno a=a+2; ili
a++;
Napomena:
p je dinamička promjenljiva
Dozvoljeno je p=p+2; ili p++;
Pokazivači (pointeri) POINTER NA POLJE?
Pokazivač na vektor (jednodimenzionalno polje)
Primjer:
#include <stdio.h>
static int x[5] = {1,2,3,4,5};
main()
{
int *p;
p = x;
printf(”%d %d %d\n”, x[0], x[1], x[2]);
printf(”%d %d %d\n”, *p, *(p+1), *(p+2) );
}
1 2 3
1 2 3
Pokazivači (pointeri) POINTER NA POLJE?
Prenos vektora u funkciju pomoću pokazivača
Pretpostavimo da imamo funkciju
int funkcija (niz, n)
int niz[], n;
{ ... }
pristup elementima u nizu
niz[indeks]
int funkcija (p, n)
int *p, n;
{ ... }
pristup pomoću pointera
*(p+indeks)
ili
Primjer:
int suma (int niz[], int
n)
{
int s=0, i;
for (i=0; i<n; i++)
s += niz[i];
return (s);
}
Primjer:
int suma (int *p, int n)
{
int s=0, i;
for (i=0; i<n; i++)
s += *(p+i);
return (s);
}
Pokazivači (pointeri) POINTER NA POLJE?
Pokazivač na matricu (dvodimenzionalno polje)
... 0 1 j ... m-1
0
1
i
n-1
mat[i][j]
mat[0][0]
int *p;
p = mat; ili
p = &mat[0][0];
*( mat + m*i + j ) ili
*( &mat[0][0] + m*i + j )
Pokazivači (pointeri) ADRESNA
ARITMETIKA?
Adresna aritmetika Neka je:
a – vektor sa elementima tipa T
v – izraz tipa T
n – cjelobrojni izraz
p, q – pokazivači na tip T (pokazuju na elemente niza a)
Tada važe sljedeća pravila:
a – pokazivač na početak niza
&a[0] – pokazivač na početak niza
*p – element niza a na koji pokazuje p
*p=v – elementu na koji pokazuje p dodjeljuje vrijednost v
++p – pokazuje na sljedeći element niza
--p – pokazuje na prethodni element niza
*++p – inkrementira p, pa pristupa tom (sljedećem) elementu
*p++ - pristupa elementu na koji pokazuje p, a zatim pokazuje
na sljedeći element
p+n - pokazuje na n-ti naredni element niza
*(p+n)=v – dodjeljuje vrijednost v n-tom narednom elementu u nizu u
odnosu na element na koji pokazuje p
q-p - određuje broj elemenata niza smještenih između adresa na
koje pokazuju q i p
Pokazivači (pointeri) ADRESNA
ARITMETIKA?
Primjer:
#include <stdio.h>
main()
{
static char c[] = {’a’,’b’,’c’,’d’};
static int i[] = { 1, 2, 3, 4};
char *pc1=&c[0], *pc2=&c[3];
int *pi1=&i[0], *pi2=&i[3];
printf(”*c=%c *i=%d\n”, *c, *i);
printf(”*pc1=%c *pi1=%d\n”, *pc1, *pi1);
printf(”*(pc1+1)=%c *(pi1+2)=%d\n”, *(pc1+1), *(pi1+2));
printf(”*++pc1=%c *++pi1=%d\n”, *++pc1, *++pi1);
printf(”*pc1++=%c *pi1++=%d\n”, *pc1++, *pi1++);
printf(”*pc1=%c *pi1=%d\n”, *pc1, *pi1);
pc1-=2; pi1-=2;
printf(”*pc1=%c *pi1=%d\n”, *pc1, *pi1);
printf(”pc2-pc1=%d pi2-pi1=%d\n”, pc2-pc1, pi2-pi1);
}
*c=a *i=1
*pc1=a *pi1=1
*(pc1+1)=b *(pi1+2)=3
*++pc1=b *++pi1=2
*pc1++=b *pi1++=2
*pc1=c *pi1=3
*pc1=a *pi1=1
pc2-pc1=3 pi2-pi1=3
Funkcije i pokazivači
• Poziv funkcije po vrijednosti i referenci
– Funkcija se poziva po vrijednosti ako joj se predaju argumenti osnovnog tipa (a ne adrese).
– U tom slučaju, funkcija može koristiti vrijednosti stvarnih argumenata (kopije sadržaja lokacija u memoriji) ali nema mogućnost njihove izmjene (jer su joj nepoznate adrese lokacija).
– Funkcija se može pozvati po referenci, ako je deklarisana i definisana sa argumentima pokazivačima na odgovarajuće tipove.
Funkcije i pokazivači
#include <stdio.h>
int povrsina_1(int a, int b)
{
return a*b;
}
void povrsina_2(int *p, int a, int b)
{
*p= a * b;
}
int main()
{
int a,b, po;
a=2;
b=3;
// POZIV PO VRIJEDNOSTI
printf("\npovr_1= %d",povrsina_1(a,b));
// POZIV PO REFERENCI
printf("\npovr_2= %d",povrsina_2(&po,a,b));
getchar();
getchar();
return 0;
}
Proslijedi adresu promenljive po i na toj
adresi smjesti a*b
Nema return
Funkcije i pokazivači REFERISANJE?
Prenos argumenata u funkciju referisanjem
Argumenti se u funkciju standardno prenose putem vrijednosti
Funkcija nema mogućnost da promijeni vrijednost stvarnog argumenta!!!
Da bi se omogućilo da funkcija mijenja vrijednost stvarnog argumenta:
1. Prilikom poziva funkcije treba slati adresu!
2. Formalni argument treba definisati kao pokazivač na tip!
3. U tijelu funkcije treba koristiti operator indirekcije!
REFERISANJE = PRENOS ADRESE (REFERENCE) stvarnog argumenta
Referisanje omogućava da funkcija mijenja vrijednost stvarnog argumenta!
Funkcije i pokazivači REFERISANJE?
Primjer:
#include <stdio.h>
void suma ( niz, n, s )
int *niz, n, *s;
{
int i;
*s = 0;
for (i=0; i<n; i++)
*s += *niz++ ;
}
main()
{
static int i[]={1,2,3,4};
int x;
suma (i, 4, &x);
printf(”Suma: %d”, x);
}
Suma: 10
šalje se adresa
Formalni argument je
pokazivač
Korišćenje
operatora
indirekcije
Funkcije i pokazivači GORNJI PRIMJER
BEZ POKAZIVACA?
#include <stdio.h>
int niz[]={1,2,3,4};
int suma ( int n)
{
int i, suma1=0;
for (i=0; i<n; i++)
suma1= suma1+niz[i];
return suma1;
}
int main()
{
int suma2, n;
n=4;
suma2=suma(n);
printf("Suma: %d", suma2);
getchar();
return 0;
}
Niz je sada globalna
promenljiva
Poziv po vrijednosti
Dinamička dodjela memorije • Statička i dinamička dodjela memorije
– Naredbe statičke deklaracije - na početku programa
(globalne) ili na početku određene funkcije (lokalne).
– Statička deklaracija - pod kontrolom prevodioca, unaprijed rezervisan prostor.
– Naredbe dinamičke deklaracije - bilo gdje u programu,unutar bilo koje funkcije i na bilo kom mjestu.
– Dinamička deklaracija – tokom izvršavanja programa se rezervise prostor.
Dinamička dodjela memorije...
• Statička dodjela memorije
• char x; 1 bajt
• int y; 4 bajta
• int niz_1d[100]; 400 bajtova
• double niz_2d[50][100]; 40 000 bajtova
• Na početku programa/funkcije zna se tačan broj bajtova koji će biti rezervisan za pojedine promenljive.
Dinamička dodjela memorije...
• Statička dodjela memorije • Nije pogodan oblik deklaracije kada se:
• zadaje i koristi mnogo manji broj podataka od predviđenog.
• Od 5000 promjenljivih ne koriste se sve.
Dinamička dodjela memorije...
• Statička dodjela memorije
• Nije pogodan oblik deklaracije kada se:
• formira kolekcija promenljivih koja predstavlja presjek od zadatih kolekcija promenljivih.
• Možda u presjeku nije ni 500 elemenata (1. niz).
Dinamička dodjela memorije...
• Statička dodjela memorije • Nije pogodan oblik deklaracije kada se: • Formira kolekcija promenljivih koja predstavlja uniju
od zadatih kolekcija promenljivih.
• Možda u uniji nije svih 1500 elemenata od oba niza
Dinamička dodjela memorije...
• Statička dodjela memorije • Nije pogodan oblik deklaracije kada se: • Od određenog mjesta u programu dio zadatih
podataka više ne koristi.
• Od 5000 promenljivih program dalje koristi dio.
Dinamička dodjela memorije...
• Statička dodjela memorije
• Nedostaci:
• Na početku programa neophodno je donijeti odluku o veličini prostora potrebnog u memoriji – za sve objekte tog programa.
• Ne može se promijeniti veličina jednom dodeljenog prostora u memoriji.
• Ne može se prostor koji više nije od koristi jednom objektu koristiti za neki drugi objekat programa.
Dinamička dodjela memorije...
• Statička dodjela memorije
• Koristi se:
• kod pojedinačnih promenljivih osnovnog tipa,
• kada niz bilo kog tipa ima relativno mali broj promenljivih,
• kada je kompletan niz promjenljivih potreban u svim djelovima jedne funkcije.
Dinamička dodjela memorije...
• Dinamička dodela (alokacija) memorije
• Koristi se:
• za uzimanje dijela memorijskog prostora rezervisanog za podatke, tokom izvršavanja programa.
• za vraćanje iskorišćenog memorijskog prostora, tokom izvršavanja programa da ne bi bilo nepotrebnog velikog trošenja memorije od početka do kraja izvršavanja jednog programa.
Dinamička dodjela memorije...
• Dinamička dodjela (alokacija) memorije i zone u operativnoj memoriji
operativna(radna) memorija
Permanent storage
za instrukcije programa i za
globalne statički deklarisane
promenljive
Heap
za dinamičku dodelu memorije
tokom izvršavanja programa
Stack
za poziv / povratak iz funkcija i
za lokalne statički deklarisane
promenljive
Dinamička dodjela memorije...
• Dinamička dodjela (alokacija) memorije u “C-u”.
• 4 funkcije iz C biblioteke <stdlib.h>
• malloc()
• calloc()
• realloc()
• free()
Dinamička dodjela memorije...
• malloc(), dinamička dodjela memorije za jedan blok od traženog broja bajtova (za n bajtova).
• calloc(), dinamička dodjela memorije za veći broj blokova i inicijalizacija svakog bajta u svakom bloku na 0 (za mxn bajtova, sa vrednošću 0).
• realloc(), izmena veličine prethodno dinamički dodijeljene memorije (za n2 bajtova).
• free(), Oslobađanje prethodno dinamički dodijeljene memorije (0 bajtova).
Dinamička dodjela memorije...
• Za funkcije malloc(), calloc() i realloc() važi:
– Ako je pomoću neke od funkcija nađen i dodijeljen odgovarajući prostor u memoriji – funkcija vraća u pokazivaču adresu dodeljenog prostora.
– Ako se pomoću neke od funkcija traži veći prostor od raspoloživog u memorijskoj heap zoni funkcija vraća NULL pokazivač.
Dinamička dodjela memorije...
• Deklaracija funkcije
• void *malloc( unsigned int vel);
• Traži se alokacija bloka u memoriji od vel bajtova i dobija:
– NULL ako nema dovoljno memorije ili
– adresa bloka (generički pokazivač void *)
Dinamička dodjela memorije...
• Primjer
int *ptr;
ptr = malloc( 100 * sizeof(int));
• Uspješno izvršena naredba rezerviše memorijski blok za 100 promenljivih tipa int i adresu ovog bloka dodeljuje pokazivaču ptr, ali ne inicijalizuje ovaj memorijski blok.
Dinamička dodjela memorije...
• Deklaracija funkcije
• void *calloc(unsigned int n, unsigned int vel);
• Traži se alokacija n blokova u memoriji, svaki od vel bajtova i dobija se od ove funkcije:
– NULL ako nema dovoljno memorije ili
– adresa 1. bloka (generički pokazivač void *), pri čemu je svaki bajt u svakom bloku inicijalizovan na vrijednost 0.
Dinamička dodjela memorije...
• Primjer
int *ptr;
ptr = calloc( 10, 100 * sizeof(int));
• Uspješno izvršena naredba rezerviše i inicijalizuje (na vrijednost 0) 10 memorijskih blokova, svaki od 100 promenljivih tipa int i adresu prvog od ovih blokova dodeljuje pokazivaču ptr.
Dinamička dodjela memorije...
• Deklaracija funkcije
• void *realloc(void *ptr, unsigned int vel);
• Traži se izmjena veličine dodeljenog prostora u memoriji, počev od adrese ptr, na vel bajtova, i dobija:
– NULL ako nema dovoljno memorije za izmjenu ili
– adresa prostora izmjenjene veličine (generički pokazivač void *).
Dinamička dodjela memorije...
• Primjer 1:
int *ptr;
ptr = malloc( 100 * sizeof(int));
.....
ptr = realloc( ptr, 500 * sizeof(int));
• Uspješno izvršena naredba mijenja veličinu dodijeljenog prostora od adrese ptr, sa 100 na 500 promenljivih tipa int i adresu ovog bloka dodeljuje pokazivaču ptr (ne mijenja se postojeći sadržaj od adrese ptr).
Dinamička dodjela memorije...
• Primjer 2:
int *ptr;
ptr = malloc( 100 * sizeof(int));
ptr = realloc( ptr, 10 * sizeof(int));
• Uspješno izvršena naredba mijenja veličinu dodijeljenog prostora od adrese ptr, sa 100 na 10 promenljivih tipa int i adresu ovog bloka dodeljuje pokazivaču ptr (ne mijenja se sadržaj zadržanih bajtova od adrese ptr).
Dinamička dodjela memorije...
• Deklaracija funkcije
• void free(void *ptr);
• Oslobađa se prostor u memoriji, počev od adrese ptr, samo ako je dinamički dodijeljen.
Dinamička dodjela memorije...
• Primjer:
int *ptr;
ptr = malloc( 100 * sizeof(int));
...
ptr = realloc( ptr, 500 * sizeof(int));
...
free(ptr);
• Oslobadja memoriju od adrese ptr.
Dinamička dodjela memorije... #include <stdio.h>
#include <string.h>
#include <stdlib.h>
main()
{
char *bafer;
bafer = (char *)malloc( 12 ); // Umjesto char bafer[12];
if( bafer == NULL ) { printf("\nGreska alokacije"); exit(1); }
printf( "\n Kreiran bafer velicine 12 bajtova");
strcpy(bafer, "Jedan tekst");
printf("\nSadrzaj bafera: %s", bafer);
bafer =(char *)realloc( bafer, 24 ); // Umjesto char bafer[24];
if(bafer == NULL) { printf("\nGreska realokacije"); exit(1); }
printf( "\nVelicina bafera promenjena na 24 bajtova" );
printf("\nBafer jos uvek sadrzi: %s", bafer);
strcat(bafer, " Drugi tekst");
printf("\nBafer sada sadrzi: %s\n", bafer); //Ne bi bilo sa char bafer[12];
free(bafer);
getchar();
}