Alocare dinamică
Cu toții știm să alocăm memorie static (de ex: int v[100]
), unii poate ați mai lucrat și cu STL (de ex: vector<int> v
), dar v-ați întrebat vreodată ce face STL în spate? În acest capitol vom începe să răspundem la întrebare.
Memorie
Atunci când declarăm variabile, avem câteva opțiuni pentru unde să fie mai exact memoria alocată.
TODO desen memorie si toate tipurile de memorie Memorie pe heap, stivă, globală, constantă
C style
Pentru a aloca memorie dinamic în C, există câteva funcții pe care ne bazăm.
malloc
void *malloc(std::size_t size)
ia ca argument un întreg size
și întoarce un pointer la începutul unui bloc contiguu de memorie de size
bytes. Întoarce nullptr
dacă eșuează.
malloc
e cel mai de bază mod de a aloca memorie pe heap în limbajul C. Dacă vrem să alocăm un vector de 100 de numere întregi și să îl folosim ca pe un array, putem aborda astfel:
uint32_t *v = (uint32_t *) malloc(100 * sizeof(uint32_t));
if (v == nullptr) {
std::cerr << "Failed to allocate";
}
Acest array poate fi folosit mai departe cum sunteți deja obișnuiți cu un array alocat static:
for (int i = 0; i < 100; ++i) {
v[i] = i;
}
calloc
void *calloc(size_t num, std::size_t size)
ia ca argument întregii num
și size
și întoarce un pointer la începutul unui bloc contiguu de memorie de size * num
bytes inițializat pe 0.
Un exemplu similar cu cel de la malloc
de mai sus:
uint32_t *v = (uint32_t *) calloc(100, sizeof(uint32_t));
va rezulta într-un array de 100 de întregi, toți setați pe 0.
free
Atunci când alocăm memorie dinamic, este responsabilitatea noastră să o și eliberăm. Dacă omitem să facem asta, riscăm să consumăm din ce în ce mai multă memorie cât timp rulează programul fără a o folosi la ceva, ocupând inutil RAMul utilizatorului și uneori chiar cauzând probleme mai grave. De exemplu, când crasheaza un joc, adesea este din cauza unor memory leakuri (memorie alocată care nu mai poate fi accesată) pentru că a ajuns să consume mai multă memorie decât îi poate oferi sistemul de operare.
void free(void *ptr)
ia ca argument pointer către începutul unui bloc de memorie pe care îl va elibera.
realloc
void *realloc(void *ptr, std::size_t new_size)
ia ca argument un pointer către începutul unui bloc de memorie pe care îl va muta și dimensiunea pe care trebuie să o aibă noul bloc. Întoarce adresa de memorie de la începutul noului bloc de memorie sau nullptr
dacă eșuează. În cazul în care eșuează, blocul original de memorie este păstrat, altfel este eliberat.
Având acces la realloc
, putem aloca puțină memorie pentru o structură de date inițial, după care o putem realoca într-o zonă mai mare atunci când este nevoie. De exemplu, vectorii din STL își dublează dimensiunea de fiecare dată când se apelează un push_back
care nu ar mai putea fi efectuat în memoria deja existentă.
memset
void* memset(void* dest, int ch, std::size_t count)
ia ca argument o adresă de memorie, o valoare ch
și un număr de bytes și setează count
bytes începând cu adresa dest
pe ultimul byte al valorii ch
.
De exemplu, pentru a seta un array static v
de numere naturale pe 1061109567
, putem apela:
memset(v, 0x3f, sizeof(v));
Atenție! Pentru uint32_t v[100]
, sizeof(v)
întoarce 100 * sizeof(uint32_t) = 400
. Pentru uint32_t *v = (uint32_t *) malloc(100 * sizeof(uint32_t))
, sizeof(v)
întoarce dimensiunea unui pointer pe platforma pe care este rulat codul (cel mai probabil 4 sau 8).
Exerciții
-
Se citesc până la
EOF
numere între1
și1000000
din fișierulcramschool.in
. Afișați la consolă numărul de numere și numerele fără să citiți de mai multe ori din fișier. Exemplu Input5 8 7 1 231 5343 11 112 998 4
Output
10 5 8 7 1 231 5343 11 112 998 4
-
Se dă
n
număr natural care se poate reprezenta pe 32 de biți fără semn. Fără a declara tablouri statice sau cu STL, creați o matrice bidimensionalămat
den
linii șin
coloane astfel încât următoarea secvență de cod să compileze și să ruleze cu outputul dat: Exemplu Codfor (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { mat[i][j] = i + 1; } } for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { std::cout << mat[i][j] << ' '; } std::cout << '\n'; }
Input
5
Output
1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5