Alociranje memorije se odnosi na proces rezervisanja dela memorije za skladištenje podataka tokom izvršavanja programa.
Neoslobađanje memorije nakon završetka korišćenja može dovesti do curenja memorije. Kako bismo mogli da dinamički alociramo memoriju u C-u neophodno je da učitamo biblioteku stdlib.h
.
Stack (stek) je deo memorije u kojem se čuvaju lokalne promenljive i povratne adrese funkcija tokom izvršavanja programa. Promenljive alocirane na steku automatski se oslobađaju kada funkcija završi izvršavanje. Stek obično ima fiksnu veličinu koja se određuje prilikom pokretanja programa.
Heap (hip) je deo memorije u kojem se dinamički alocira memorija tokom izvršavanja programa. Memorija na heap-u se može koristiti za skladištenje podataka koji su potrebni tokom dužeg vremenskog perioda, kao što su globalne promenljive i dinamički alocirane strukture podataka. Alocirana memorija na hipu se ne oslobađa automatski kada funkcija završi izvršavanje, već mora biti eksplicitno oslobođena pomoću funkcije free(). Heap je obično veći i fleksibilniji od steka, ali upravljanje njime zahteva pažljivo praćenje alocirane i oslobođene memorije kako bi se izbegli curenja memorije i fragmentacija.
Potpis funkcije: void free( void *ptr )
Funkciji se kao parametar prosleđuje pokazivač na memoriju koja je dinamički alocirana. Ta memorija se oslobađa i može da je zauzme neki drugi proces ili čak i isti proces, samo za neku drugu namenu. Korišćenje ove funkcije sprečava “curenje” memorije. “Curenje” memorije nastaje kada zauzimamo memoriju koja nam treba, a ne oslobađamo memoriju koja nam treba.
Primer korišćenja: Pogledati primer korišćenje za funkciju malloc.
Funkcija malloc
se koristi za dinamičko alociranje memorije.
Potpis funkcije: void* malloc( size_t size )
size
je broj bajtova koji hoćemo da rezervišemo.
Ako imamo neki tip T i želimo da dinamički rezervišemo niz od 10 elemenata to možemo uraditi na sledeći način:
T *ptr = (T*) malloc(10 * sizeof(T));
int *ptr2 = (int*) malloc(10 * sizeof(int));
malloc
će u skoro svim slučajevima raditi bez kastovanja.
Ukoliko operativni sistem nađe odgovarajući komad memorije koji može da dodeli procesu, vratiće mu odgovarajuću adresu. Ukoliko nije u mogućnosti da mu dodeli memoriju vratiće mu NULL
. NULL
je specijalna vrednost koja označava da pokazivač ne pokazuje ni na jednu adresu. NULL
se tretira kao false
u uslovima.
Primer korišćenja:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p1 = malloc(4*sizeof(int)); // alocira se dovoljno memorije za niz od 4 int-a
int *p2 = malloc(sizeof(int[4])); // isto, samo što se tip imenuje direktno
int *p3 = malloc(4*sizeof *p3); // isto, samo bez ponavljanja imena tipa
if(p1) { // ako OS nije mogao da dodeli memoriju vratiće NULL i neće ući u if
for(int n=0; n<4; ++n) // popuni niz
p1[n] = n*n;
for(int n=0; n<4; ++n) // štampaj ga
printf("p1[%d] == %d\n", n, p1[n]);
}
free(p1);
free(p2);
free(p3);
}
Funkcija calloc
se koristi za dinamičko alociranje memorije. Prilikom alociranja memorije, svi bajtovi se setuju na nulu.
Potpis funkcije: void* calloc( size_t num, size_t size )
num
je broj elemenata koji hoćemo da rezervišemo.
size
je veličina jednog elementa u bajtovima.
Ako imamo neki tip T i želimo da dinamički rezervišemo niz od 10 elemenata to možemo uraditi na sledeći način:
T *ptr = (T*) calloc(10, sizeof(T));
int *ptr2 = (int*) calloc(10, sizeof(int));
calloc
će u skoro svim slučajevima raditi bez kastovanja.
Ukoliko operativni sistem nađe odgovarajući komad memorije koji može da dodeli procesu, vratiće mu odgovarajuću adresu. Ukoliko nije u mogućnosti da mu dodeli memoriju vratiće mu NULL
. NULL
je specijalna vrednost koja označava da pokazivač ne pokazuje ni na jednu adresu. NULL
se tretira kao false
u uslovima.
Primer korišćenja:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p1 = calloc(4, sizeof(int)); // alocira se dovoljno memorije za niz od 4 int-a
int *p2 = calloc(1, sizeof(int[4])); // isto, samo što se tip imenuje direktno
int *p3 = calloc(4, sizeof *p3); // isto, samo bez ponavljanja imena tipa
if(p1) { // ako OS nije mogao da dodeli memoriju vratiće NULL i neće ući u if
for(int n=0; n<4; ++n) // štampaj ga
printf("p1[%d] == %d\n", n, p1[n]);
}
free(p1);
free(p2);
free(p3);
}
Funkcija realloc
se koristi za dinamičko alociranje memorije. Ova fukcija služi za proširivanje već alociranog prostora.
Ako u novoj memoriji ima dovoljno slobodnog prostora odmah iza originalnog bloka, funkcija jednostavno proširi blok memorije i vrati pokazivač na početak novog bloka.
Ukoliko nema dovoljno slobodnog prostora odmah iza originalnog bloka, funkcija može alocirati novi blok na drugoj lokaciji, kopirati sadržaj originalnog bloka u novi blok, a zatim osloboditi originalni blok. U ovom slučaju, funkcija vraća pokazivač na početak novog bloka.
Ako nema dovoljno slobodne memorije, stara memorija neće biti oslobođena, a funkcija će vratiti NULL.
Potpis funkcije: void *realloc( void *ptr, size_t new_size )
ptr
je pokazivač na memoriju koja je već rezervisana.
size
je broj bajtova koji hoćemo da rezervišemo.
Ako imamo neki tip T i želimo da dinamički proširimo niz koji je već alociran i ima 10 elemenata, tako da nakon proširivanja ima 100 elemenata, to možemo uraditi na sledeći način:
T *ptr1 = (T*) malloc(10 * sizeof(T));
T *ptr2 = (T*) realloc(ptr1, 100 * sizeof(T));
int *ptr11 = (int*) malloc(10 * sizeof(int));
int *ptr22 = (int*) realloc(ptr11, 100 * sizeof(int));
realloc
će u skoro svim slučajevima raditi bez kastovanja.
Prilikom statičke alokacije matrice, rezerviše se veliki komad memorije. Svi vrste matrice se nalaze jedna pored druge.
#include <stdio.h>
int main() {
int i, j;
int a[3][3] = {
{ 0, 1, 2 },
{ 10, 11, 12 },
{ 20, 21, 22 }};
int (*pa)[3]; // int *pa[3]; -> pogresno!!!
pa = a; // možemo da ga koristimo kao da pristupamo matrici a
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++)
printf("%5d", pa[i][j]);
printf("\n");
}
printf("%p\n", pa);
printf("%p\n", pa[0]);
printf("%p\n", pa[1]);
return 0;
}
Kada imamo pokazivač na niz integer-a koji ima 3 člana, pa[1] (ili *(pa + 1)) će biti pokazivač na vrstu sa indeksom 1. Slično kao kod pokazivača na int, pa + 1 će zapravo uvećati adresu za 12 (3 int * 4 byte).
#include <stdio.h>
int main() {
int i, j;
int a[3][3] = {
{ 0, 1, 2 },
{ 10, 11, 12 },
{ 20, 21, 22 }};
int *pa[3];
// umesto pa = 1 kao u prethodnom primeru, svakom članu niza pa moramo da kažemo na koji niz pokazuje
for (i = 0; i < 3; i++)
pa[i] = a[i];
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++)
printf("%5d", pa[i][j]); // možemo da ga koristimo kao da pristupamo matrici a
printf("\n");
}
printf("%p\n", pa);
printf("%p\n", pa[0]);
printf("%p\n", pa[1]);
return 0;
}
Nizove pokazivača ćete koristiti sigurno u radu sa matricom. Pokazivač na niz najverovatnije nećete koristiti, ali je potrebno znati šta je i koja je razlika između niza pokazivača i pokazivača na niz.