12. Dinamička alokacija memorije¶
Podeli svoje utiske o poglavlju na anketi https://docs.google.com/forms/d/1v87L2JkRzXnvglKgZtM-rPJjTuzB-93z9LHasXQMbA0
Preduslovi za rad:
Poznavanje funkcija
Poznavanje nizova
Poznavanje pokazivača
Dinamičku alokaciju koristimo kada ne znamo unapred, pre pokretanja programa, koliko nam je memorije potrebno za rad. Do sada smo u radu sa nizovima uvek unapred određivali veličinu niza. Kada ovako jednom odredimo veličinu niza, ne možemo je kasnije menjati. Ta memorija nam može postati mala u nekom trenutku. Problem nedovoljne memorije rešavamo tako što je zauzimamo u toku izvršavanja programa, tj. alociramo je dinamički. Dinamički alociranu memoriju je potrebno uvek nakon korišćenja osloboditi.
Funkcije za dinamičku alokaciju memorije su malloc
, calloc
, realloc
i free
. Sve funkcije se nalaze u biblioteci stdlib.h
,
pa je potrebno uključiti je kako bi ih koristili. Funkcije malloc
i calloc
služe za zauzimanje memorije, a funkcija realloc
za proširenje/smanjenje prethodno dinamički zauzete memorije. Ove tri funkcije vraćaju adresu početka memorijskog bloka ukoliko je memorija uspešno
zauzeta ili NULL vrednost ukoliko se memorija ne može zauzeti. Imajući ovo u vidu, uvek je potrebno proveriti da li su ove funkcije uspešno
zauzele memoriju i izaći iz programa ako nisu. Funkcija free
služi za oslobađanje dinamički zauzete memorije.
12.1. Malloc¶
Funkcija malloc
se koristi za dinamičku alokaciju memorijskog bloka određene veličine. Funkcija vraća generički pokazivač
tipa void *
, koji se može pretvoriti u bilo koji pokazivač. Ne inicijalizuje zauzetu memoriju na početnu vrednost.
Osnovna forma funkcije je:
void *malloc(size_t size);
gde size_t size
predstavlja veličinu memorije koju će malloc
pokušati da zauzme.
1#include <stdio.h>
2#include <stdlib.h>
3
4int main()
5{
6 int *a, n;
7
8 do
9 {
10 printf("Unesite duzinu niza: ");
11 scanf("%d", &n);
12
13 } while(n <= 0);
14
15 a = (int *) malloc(n * sizeof(int));
16
17 if (a == NULL)
18 {
19 printf("Memorija se ne moze zauzeti.\n");
20 exit(EXIT_FAILURE);
21 }
22
23 for(int i = 0; i < n; i++)
24 {
25 printf("a[%d] = ", i);
26 scanf("%d", &a[i]);
27 }
28
29 printf("Uneti niz je:\n");
30 for(int i = 0; i < n; i++)
31 {
32 printf("a[%d] = %d\n", i, a[i]);
33 }
34
35 free(a);
36 return 0;
37}
U primeru iznad prvo unosimo dužinu niza, a zatim zauzimamo memoriju za tačno toliko članova niza korišćenjem funkcije malloc
.
Za određivanje veličine memorijskog bloka koji nam je potreban, koristimo operator sizeof
. Ovaj operator može se koristiti sa
tipovima podataka ili sa promenljivama, tako da je jedan od načina da mu prosledimo tip podatka. Na primer, sizeof(int)
nam daje
veličinu jednog integer-a u bajtovima, dok sa izrazom n * sizeof(int)
dobijamo ukupnu veličinu memorije potrebnu za ceo niz.
Nakon što pokušamo da zauzmemo memoriju, potrebno je da proverimo da li je to uspešno učinjeno. Ako malloc
vrati NULL, to znači
da je zauzimanje memorije neuspešno i u tom slučaju treba da izađemo iz programa. Ako je memorija uspešno zauzeta, unosimo članove
niza, a zatim ih ispisujemo.
Kada nam više nije potrebna, ili kada smo završili sve što smo planirali u vezi sa dinamičkom memorijom, oslobađamo zauzetu
memoriju korišćenjem funkcije free
. Iako u primerima često oslobađamo memoriju na kraju programa, u stvarnim aplikacijama,
posebno onima koje rade u realnom vremenu ili u dugoročnim procesima (npr. serveri), važno je da oslobađamo memoriju čim nam više
nije potrebna, kako bismo izbegli curenje memorije.
Ispis ovog programa je:
Unesite duzinu niza: 3
a[0] = 7
a[1] = 2
a[2] = 9
Uneti niz je:
a[0] = 7
a[1] = 2
a[2] = 9
12.2. Free¶
Funkcija free
služi za oslobađanje dinamički zauzete memorije. Memorija se zauzima nekom od funkcija za dinamičku alokaciju,
koje vraćaju pokazivač na zauzetu memoriju. Ova funkcija očekuje pokazivač na tu memoriju kako bi je oslobodila.
Osnovna forma funkcije je:
void free(void *ptr);
12.3. Calloc¶
Funkcija calloc
se koristi za dinamičku alokaciju memorijskog bloka određene veličine. Blokovi memorije koje alocira funkcija
calloc
su sukcesivni, što znači da su raspoređeni uzastopno u memoriji. Ovo je bitna karakteristika, jer omogućava
efikasno korišćenje memorije i lakše upravljanje sa skupovima podataka koji se koriste u kontinuitetu. Funkcija vraća generički
pokazivač tipa void *
, koji se može pretvoriti u bilo koji pokazivač. Ukoliko dođe do greške u alokaciji, funkcija će vratiti
NULL
vrednost. Za razliku od funkcije malloc
, koja samo alocira memoriju bez inicijalizacije, calloc
automatski postavlja
sve bajtove u dodeljenoj memoriji na nulu. To znači da će svaka pozicija u memoriji biti postavljena na 0 (ili NULL
u slučaju pointera).
Osnovna forma funkcije je:
void *calloc(int n, size_t size);
gde size_t size
predstavlja veličinu jednog memorijskog bloka, a n broj blokova koje će calloc
pokušati da zauzme.
1#include <stdio.h>
2#include <stdlib.h>
3
4int main()
5{
6 int *a, n;
7
8 do
9 {
10 printf("Unesite duzinu niza: ");
11 scanf("%d", &n);
12
13 } while(n <= 0);
14
15 a = (int *) calloc(n, sizeof(int));
16
17 if (a == NULL)
18 {
19 printf("Memorija se ne moze zauzeti.\n");
20 exit(EXIT_FAILURE);
21 }
22
23 for(int i = 0; i < n; i++)
24 {
25 printf("a[%d] = ", i);
26 scanf("%d", &a[i]);
27 }
28
29 printf("Uneti niz je:\n");
30 for(int i = 0; i < n; i++)
31 {
32 printf("a[%d] = %d\n", i, a[i]);
33 }
34
35 free(a);
36 return 0;
37}
U primeru iznad je sve isto kao u primeru sa malloc
funkcijom, uz razliku da je memorija ovde zauzeta korišćenjem calloc
funkcije. Opet je potrebno proveriti da li je memorija uspešno zauzeta, kao i osloboditi je na kraju programa.
Ispis ovog programa je:
Unesite duzinu niza: 3
a[0] = 1
a[1] = 2
a[2] = 3
Uneti niz je:
a[0] = 1
a[1] = 2
a[2] = 3
12.4. Realloc¶
Funkcija realloc
koristi se za promenu veličine prethodno alociranog bloka memorije. Memorija se prethodno mora zauzeti
korišćenjem funkcija malloc
ili calloc
. Realokacijom memorije se zadržavaju prethodne vrednosti i ne inicijalizuje se memorija na početnu vrednost.
Ukoliko je potrebno proširiti alocirani blok memorije (povećate njegovu veličinu), realloc
će to i pokušati. Ako nije moguće
proširiti memoriju na postojećoj lokaciji (zbog fragmentacije ili drugih razloga), realloc
može alocirati novi blok u nekoj
drugoj lokaciji i prekopirati sadržaj iz stare memorije u novu. Ukoliko je potrebno smanjiti veličinu memorijskog bloka, realloc
jednostavno seče blok i oslobađa višak memorije, čuvajući samo prvih new_size bajtova.
Ako je alokacija uspešna, realloc
vraća generički pokazivač na prošireni ili skraćeni memorijski blok tipa void *
.
Ovaj pokazivač može biti isti kao prethodni ili može biti nova adresa u slučaju premeštanja bloka. Ukoliko dođe do greške i nije
moguće alocirati memoriju, realloc
vraća NULL, a originalni blok memorije ostaje nepromenjen.
Osnovna forma funkcije je:
void *realloc(void *ptr, size_t new_size);
gde size_t new_size
predstavlja novu veličinu memorije koju je potrebno zauzeti, a void *ptr
pokazivač na prethodno zauzetu
memoriju koju je potrebno smanjiti ili povećati.
1#include <stdio.h>
2#include <stdlib.h>
3
4int main()
5{
6 int *a, n;
7
8 do
9 {
10 printf("Unesite duzinu niza: ");
11 scanf("%d", &n);
12
13 } while(n <= 0);
14
15 a = (int *) malloc(n * sizeof(int));
16
17 if (a == NULL)
18 {
19 printf("Memorija se ne moze zauzeti.\n");
20 exit(EXIT_FAILURE);
21 }
22
23 for(int i = 0; i < n; i++)
24 {
25 printf("a[%d] = ", i);
26 scanf("%d", &a[i]);
27 }
28
29 int m;
30
31 do
32 {
33 printf("Unesite novu duzinu niza: ");
34 scanf("%d", &m);
35
36 } while(m <= 0);
37
38 // Pokušaj da se poveća veličina niza koristeći realloc
39 int *tmp = (int *) realloc(a, m * sizeof(int));
40
41 // Provera da li realloc nije uspeo
42 if (tmp == NULL)
43 {
44 // Greška pri alokaciji, originalni blok je i dalje validan
45 free(a); // Oslobađanje prethodno alocirane memorije
46 printf("Memorija se ne moze zauzeti.\n");
47 exit(EXIT_FAILURE);
48 }
49
50 // Ako realloc uspe, dodeljujemo novi pokazivač
51 a = tmp;
52
53 if (m > n)
54 {
55 for(int i = n; i < m; i++)
56 {
57 printf("a[%d] = ", i);
58 scanf("%d", &a[i]);
59 }
60 }
61
62 printf("Uneti niz je:\n");
63 for(int i = 0; i < m; i++)
64 {
65 printf("a[%d] = %d\n", i, a[i]);
66 }
67
68 free(a);
69 return 0;
70}
U primeru iznad unosimo prvo dužinu niza, nakon čega zauzimamo memoriju za tačno toliko članova niza korišćenjem funkcije malloc
.
Kada smo pokušali da zauzememo memoriju, potrebno je da proverimo da li je ona uspešno zauzeta. Ukoliko je malloc
vratio NULL
,
izlazimo iz programa, ukoliko nije, unosimo članove niza. Nakon toga, unosimo novu dužinu niza i zauzimamo memoriju za promenjenu
dužinu korišćenjem funkcije realloc
. Funkciji realloc
prosleđujemo pokazivač na niz i m * sizeof(int)
gde je m
nova
dužina niza. Potrebno je da ponovo proverimo da li je memorija uspešno zauzeta ili je funkcija realloc
vratila NULL
. Ukoliko je
memorija zauzeta, proveravamo da li je nova dužina niza veća od stare i u tom slučaju unosimo ostale elemente niza. Za dužinu niza
sada uzimamo m
, pa će for
petlja za ispis svih elemenata niza ići do ove vrednosti. Na kraju programa, oslobađamo dinamički
zauzetu memoriju sa funkcijom free
.
Ispis ovog programa je:
Unesite duzinu niza: 3
a[0] = 1
a[1] = 2
a[2] = 3
Unesite novu duzinu niza: 5
a[3] = 4
a[4] = 5
Uneti niz je:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5