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
EOFnumere între1și1000000din 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 4Output
10 5 8 7 1 231 5343 11 112 998 4 -
Se dă
nnumă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ămatdenlinii șincoloane 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
5Output
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