Documente Academic
Documente Profesional
Documente Cultură
- SDA -
1
Organizare:
Curs – 3h/sapt.
Laborator – 2h/sapt. – pentru promovare maxim 3 absente
*nu se recupereaza cu alte grupe
Nota finala:
Examen - 30% nota - probleme
Lucrari - 20% nota - 2 lucrari grila (2*10%)
__________________________
- pentru promovare min ½ punctaj examen+lucrari
•…
Bibliografie:
Alte resurse:
- SDA drive
Azi - Recapitulare
Managementul memoriei
6
De la codul sursa la program executabil
Etape
editarea codului sursa - programator
salvarea fisierului cu extensia .c /.cpp
preprocesarea - preprocesor
efectuarea directivelor de preprocesare (#define MAX 100)
ca un editor – sunt inlocuite directivele preprocesor in codul sursa
compilarea ( C - limbaj compilat ) - compilator
verificarea sintaxei
transformare in modul/cod obiect (limbaj masina) cu extensia .o /.obj
editarea legaturilor - editor de legaturi (linker)
combinarea modulului obiect cu alte module obiect (ex: al bibliotecilor asociate
fisierelor header incluse)
transformarea adreselor simbolice in adrese reale
Variabile, tipuri de date si cuvinte rezervate
date intrare -> program -> date iesire
variabile + operatori -> expresii + instructiuni -> descriu pasii unui program
Variabilele
- reprezinta spatiul de stocare in memoria calculatorului (stocheza datele manipulate)
- fiecare are un nume convenabil in codul sursa (ex. rezultat, suma, nume, vector)
- in momentul rularii/executiei (runtime) fiecare variabila foloseste o zona/ spatiu din
memorie ca sa isi stocheze valoarea
9
Tipuri de date de baza (fundamentale)
o int – numere intregi
o float – numere reale reprezentate in virgula mobila (floating point)
o double – numere reale mari in virgula mobila (de doua ori mai mult spatiu rezervat ca in cazu
tipului float)
o char –defineste caractere
o void - tip special - multimea valorilor e vida; specifica absenta oricarei valori
Tablouri – stocheaza mai multe elemente de acelasi tip folosind o zona continuua de memorie
#include <stdio.h>
int main() {
int i, j, k, test[2][3][2];
// Afisare valori:
for(i = 0; i < 2; ++i) {
for (j = 0; j < 3; ++j) {
for(k = 0; k < 2; ++k ) {
printf("test[%d][%d][%d] = %d\n", i, j, k, test[i][j][k]); }
}
}
return 0; 12
}
Tipuri de date derivate
struct persoana{
char nume[50];
int varsta;
} p;
// main
struct persoana p1, p2, p3[20];
/*sau*/ /*sau*/
typedef struct /*pers*/{ typedef struct pers persoana;
char nume[50]; struct pers{
int varsta; char nume[50];
} persoana; int varsta;
};
//main
persoana p1, p2, p3[20]; //main 13
persoana p1, p2, p3[20];
Variabile globale
definite in afara oricarei functii
accesibile de oriunde din program
durata de viata este egala cu cea a programului
daca existenta unei variabile dintr-un fisier este declarata folosind cuvantul extern
in alt fisier - > poate fi folosita in cel de-al doilea fisier
extern - spune compilatorului ca variabila globala exista in alt fisier decat cel compilat.
//exemplu
int patrat(int numar)
{
int rezultat; //variabila locala
rezultat=numar*numar;
return rezultat;
}
15
• sunt numite locale pentru ca durata lor de viata este legata de functia in care sunt
declarate.
• de fiecare data cand functia este executata, variabilele sale locale sunt alocate.
• la iesirea din functie (finalul executiei codului aferent; exit) variabilele locale elibereaza
spatiul ocupat.
parametrul numar din functia patrat este tot o entitate locala si primeste un spatiu de
memorie la apelul functiei
diferenta intre numar si rezultat este ca numar va avea stocata valoarea copiata din
apelul functiei:
int patrat(int numar)
{
int x= patrat(3); int rezultat;
rezultat=numar*numar;
return rezultat;
}
Parametrii locali sunt copii locale ale informatiei cu care se apeleaza functia.
Acest mecanism se numeste transmitere prin valoare (pass by value).
Parametrii sunt variabile locale initializate cu o operatie de tip copiere la apelul functiei.
Avantaj : Functia va detine propria copie - pe care poate sa o manipuleze fara sa interfere
cu variabila din care se face copierea.
int * f(){
int temp=10;
return(&temp);
}
//returneaza un pointer catre variabila locala de tip int
int main()
{
int * ptr=f();
….
(*ptr)=(*ptr)+1;//?
return 0;
}
o Functia este in regula pe durata rularii, problema apare in momentul in care se iese din
functie deoarece se returneaza un pointer la un int – dar unde este acest int alocat?
o Problema: variabila locala temp este alocata doar cat timp functia este executata; la iesirea
din functie, spatiul este eliberat automat.
o Cand rulam aceasta mica bucata de cod, eroarea nu este vizibila, dar daca aplicatia18se
complica, sistemul va face solicitari asupra memoriei ocupate de temp.
Daca totusi vrem ca functia de mai sus sa intoarca un pointer la o variabila
locala - aceasta trebuie declarata static.
int * pointer_local ()
{
static int temp=10; //valoarea initiala(la primul apel) este 10
return(&temp); //returneaza un pointer catre variabila locala de tip int
}
#include <stdio.h>
Daca variabila i nu era
void func() { declarata static se afisa:
static int i = 0; i=1
i++; i=1
printf("i = %d\n" , i); i=1
} i=1
i=1
int main()
{
for (int j=0; j<5; j++)
func(); Afisaza:
i=1
return 0; i=2
} i=3
i=4 20
i=5
II. O functie sau variabila globala definita folosind cuvantul cheie static semnaleaza ca acel
nume e rezervat si nu este accesibil/utilizabil in afara fisierului in care apare (file scope –
vizibilitate la nivel de fisier)
Chiar daca var_stat este declarata ca externa, nu se poate face legatura (linkage) din cauza ca
a fost declarata static in f1.cpp. 21
Variabile registru
22
Variabile volatile
- cuvantul cheie volatile ii spune compilatorului ca este vorba de o variabila care isi
poate modifica valoarea oricand
- se foloseste cand se citesc valori din afara programului si nu exista control din cod
asupra valorii lor
23
Transmiterea parametrilor prin valoare
VS
Transmiterea parametrilor prin referinta
DAR, uneori, vreau sa le modific (sa schimb valorile stocate la adresele variabilelor
respective).
24
Pentru asta parametrii pot sa fie transmisi in functie prin referinta (pass-by-reference).
Utilizarea pointerilor ca parametrii in functii
Cand apelez functia o voi face cu adresa variabilei (adresa e copiata si transmisa in functie) –
iar in functie lucrez cu un pointer catre variabila care vreau sa fie manipulata/modificata.
//main //main
int a; int a;
scanf(“%d ”,&a); //5 scanf(“%d”,&a); //5
f(a); f(&a); //6 //transmit in functie adresa lui a
printf(“%d ”, a); printf(“%d”, a); //6//valoarea stocata in a - a fost modificata
//ce se afisaza? x
65
//Cum procedez dar vreau ca a 65
//valoarea lui a sa fie modificata?
25
Ex: Implementati o functie care interschimba valorile a 2 variabile. Apelati-o in
main!
void interschimba(int *pa, int *pb)
void interschimba(int a, int b) {//primesc adresele variabilelor si lucrez direct
{ int temp = *pa; //la acele adrese
int temp = a; *pa = *pb;
a = b; *pb = temp;
b = temp; }
}
//…main()
//…main() int a,b;
int a,b; a = 3; b = 4;
a = 3; b = 4; printf(“%d %d\n”,a,b);
printf(“%d %d\n”,a,b); interschimba(&a, &b); //transmit adresele
interschimba(a, b); //variabilelor a si b
printf(“%d %d\n”, a , b); printf(“%d %d\n”, a , b);
// Ce se afisaza? De ce? // Ce se afisaza? De ce?
Ex: Considerati 3 vectori de intregi: x,y si z cu n1, n2 si, respectiv, n3 elemente. Pentru
fiecare vector trebuie citite valorile elementelor si apoi afisate. Creati functii pentru citirea
elementelor unui vector de lungime l>0 si afisarea elementelor unui vector de lungime l.
Apelati-le pentru x, y si z.
// main
int x[10];
afisare(x,10);
Spatiul (virtual) de adresa al unui proces
Regiunea Text este utilizata pentru stocarea de:
• cod (memoria este alocata de compilator cand incepem un program C)
• cod compilat (assembly)
Diagrama memoriei folosite
de un proces
Regiunea Date este utilizata pentru stocarea:
• variabilelor globale initializate explicit
• variabilelor statice locale initializate explicit
Stiva sau stocarea automata contine stack frame-uri (create la un apel de functii).
Un stack frame contine:
• variabilele locale declarate ca parte a unei functii
• parametrii (argumentele) functiei
• rezultatul returnat de functia apelata
void f1(){
int a;
char b[4];
float c;
}
char : 1 byte
int : 2 bytes
float : 4 bytes
double : 8 bytes
void f2(){
int x;
char *y;
char z[2][2];
f3();
}
void f3(){
float m[3];
int n;
}
sp pentru executia lui f1 : f1() -> f1.f2() -> f2.f3() -> f2.f3() se termina ->f1.f2() se termina
->f1.f3()-> f1.f3() se termina->f1 se termina
• !!! dupa f1.f2(), cand se apeleaza f1.f3(),variabilele functiei f3() sunt suprascrise pe zona pana
32 atunci
ocupata de variabilele lui f2().
Stack
Ex: Apel functie recursiva.
Stack
n=3
int num; 3+sum(2)
n=2
int sum(int n) 2+sum(1)
{
n=1
if(n==0) return n;
1+sum(0)
else
return n+sum(n-1); n=0
0
}
int main()
{
num=3; sum(3) Heap - gol
sum(num); =3+sum(2)
=3+2+sum(1) BSS num
=3+2+1+sum(0) Date - gol
return 0;
} =3+2+1+0 Text(cod)
=3+2+1
=3+3
33
Ce se intampla daca scot conditia n==0? =6
Stack
Ex: Cum este ocupata memoria in cazul programului de mai jos?
#include <stdio.h>
void func() {
static int i = 0;
// zona de date
i++;
printf("i = %d \n“, i);
}
int main()
{ //main – in stack
int j;
for (j=0; j<5; j++) func();
// j – in stack; apel func() – stack; iesire din func() x 5 ori – sp nu se modifica
return 0;
}
ALOCARE DINAMICA
(vs. – tot ce se aloca pe stack – ALOCARE STATICA)
Arhitecturile moderne asigneaza adresa cea mai de jos pentru heap, si cea mai de sus
pentru stiva.
Deoarece managemenul zonei de memorie Heap nu e automat, pot aparea erori de tipul:
pierderea referintelor la zonele alocate (memory leaks) si referirea de zone nealocate
35
sau insuficient alocate (accese nevalide).
Spatiul (virtual) de adresa al unui proces
Heap
Avantajele : - durata de viata - programatorul controleaza exact cand memoria e alocata si
eliberata.
- dimensiunea spatiului de memorie alocat poate fi controlata in detaliu.
Ex. Se poate aloca spatiu pentru un vector de intregi de lungime dorita in momentul rularii
(run-time) – iar spatiul ocupat va fi exact cel ocupat; pe cand, daca se foloseste memoria
locala(stiva), s-ar aloca un spatiu pentru, de exemplu, 100 de intregi, sperand ca aceasta va
fi destul sau nu prea mult, respectiv ca lungimea vectorului nu se va schimba de-a lungul
programului.
Dezavantajele - mai multa munca: alocarea in heap se face explicit de catre programator
- mai multe erori (logice) posibile - pot aparea alocari si adresari incorecte
Heap - concluzie
• o zona mare de memorie pusa la dispozitia programului
• acesta poate solicita blocuri de memorie
• pentru alocarea unui bloc de dimensiune oarecare programul face o cerere explicita prin
apelul functiei de alocare heap
• functiile de alocare rezerva un bloc de memorie de dimensiunea ceruta si intoarce un
36
pointer catre aceast spatiu (proces invers pentru eliberarea de spatiu)
Spatiul (virtual) de adresa al unui proces
Managerul memoriei heap
managerul memorie heap aloca blocuri oriunde - atata timp cat acestea nu se suprapun si
au cel putin lungimea solicitata.
la orice moment de timp, unele din zonele memoriei heap sunt alocate programului si se
afla in starea de utilizare (in use).
alte zone nu au fost alocate si se gasesc in starea libera (free), ele pot fi alocate in mod
sigur
37
Spatiul (virtual) de adresa al unui proces
Managerul memoriei heap
• are structuri private de date pentru a tine evidenta zonelor libere si ocupate
• cand programul nu mai foloseste o zona de memorie -> face o cerere de eliberare
explicita catre managerul memoriei heap -> acesta isi updateaza structurile de date
private astfel incat blocul de memorie eliberat sa fie considerat liber si reutilizat.
• dupa eliberare, pointerul continua sa pointeze catre blocul eliberat (dangling pointer), iar
programul trebuie sa NU acceseze obiectul care ocupa spatiul tocmai eliberat.
Are o dimensiune mare DAR poate sa fie ocupata in totaliate -> noi cereri de alocare nu
mai pot fi satisfacute -> functia de alocare va intoarce aceasta stare in rularea
38
programului –> NULL sau va arunca o exceptie de runtime specifica limbajului.
Alocare dinamica
void* malloc(size_t )
void* calloc (size_t , size_t )
void* realloc (void * , size_t )
daca exista suficient spatiu liber in HEAP -> atunci un bloc de memorie continuu de
dimensiunea specificata va fi marcat ca ocupat (in use), iar functia malloc va returna un
pointer ce contine adresa de inceput a acelui bloc.
daca nu se poate face alocarea (nu exista spatiu), functia malloc intoarce NULL.
accesarea blocului alocat se realizeaza printr-un pointer (din afara heap-ului de cele mai
multe ori) catre adresa de inceput a blocului (din heap).
C++: tipul generic void * returnat de functia malloc face obligatorie utilizarea unei
conversii de tip atunci cand respectivul pointer este asignat unui pointer de un anumit tip.
Functia free
functia free elibereaza zona de memoria alocata dinamic a carei adresa de inceput este
data de p.
zona de memorie alocata este marcata ca fiind disponibila (not in use) pentru o noua
alocare.
printf(%d”,dp);//adresa variabilei dp
//in C este considerata bad practice conversia tipului void* returnat de malloc la tipul
//pointerului (double*) ; in C++ conversia e necesara
Ex: Am nevoie un vector v a carui lungime -n- variaza de-a lungul executiei programului =>
alocare dinamica.
//in main sau alta functie
int n, i;
int *v;
//…initial nu stiu care trebuie sa fie dimensiunea lui v …dupa care aflu
n=3;
for ( i=0;i<n;i++)
scanf(“%d”, &v[i]); //lucrez cu aceasta variabila la fel ca si cu vectorul alocat static
scanf(“%d”,&n);
free(v);
// alte operatii
printf(“%d”, v[1]); //adresarea unei zone de memorie marcata ca fiind libera; oricine putuse
//sa vina si sa o ocupe; nu avem o eroare, dar nu avem cum sa stim ce e stocat la acea
//ca sa nu am astfel de probleme – dupa eliberarea de spatiu - fac pointerul NULL - >
v=NULL;
Ex: Vreau sa aloc spatiu pentru un sir de n caractere – sa il citesc de la tastatura,
dupa care vreau ca in acelasi vector sa salvez n+1 caractere.
daca exista suficient spatiu liber in HEAP atunci un bloc de memorie continuu de
dimensiunea specificata va fi marcat ca ocupat, iar functia calloc va returna un pointer ce
contine adresa de inceput a acelui bloc.
diferenta intre malloc si calloc e ca a doua functie initializeaza toate elemente pentru care
se aloca spatiu cu 0 sau NULL
Exemplu:
int *v;
int n=4;
v= (int*)calloc(n, sizeof(int)); // aloc spatiu pentru n=4 intregi si ii initializez cu 0
Functia realloc
daca exista suficient spatiu liber in HEAP atunci un bloc de memorie continuu de
dimensiunea specificata va fi marcat ca ocupat, iar functia realloc va returna un pointer
ce contine adresa de inceput a acelui bloc. Continutul blocului de memorie initial –
p- se copiaza la noua locatie(daca e cazul). Spatiul ocupat initial de p (si neutilizat in
continuare) e marcat ca liber (free).
daca nu exista suficient spatiu liber realloc intoarce NULL, iar p nu este modificat.
Ex: Cititi de la tastatura un sir de numere intregi pana la intalnirea lui 0. Afisati numerele in
ordinea inversa a citirii. Implementati afisarea ca functie.
#include <stdlib.h>
#include <stdio.h>
int main(){
int *p = NULL; // nu pot sa transmit in realloc un pointer neinitializat=>initializez cu NULL
int val, nr_elem=0;//dimensiunea lui p e nr_elem=0
scanf(“%d”,&val); //in val citesc noi elemente pentru p
while (val!=0)
{ nr_elem++;
p=(int*)realloc(p,nr_elem);
p[nr_elem-1]=val; Ce se intampla daca nu pot sa
scanf(“%d”,&val); realoc memorie?
} - p devine NULL (am pierdut
afis(p,nr_elem); tot continutul de pana atunci).
return 0; Ce putem face?
}
Testez ca se realoca spatiu. Daca nu se poate realoca, afisez ce informatie detin.
int main(){
int *p=NULL,*aux=NULL;
int val, nr_elem=0;
scanf(“%d”,&val);
while (val!=0)
{ nr_elem++;
aux=(int*)realloc(p,nr_elem);
//if (nr_elem==2) aux=NULL; //daca vreti sa testati comportamentul decomentati
if (aux!=NULL) { //p nu mai poate sa fie folosit
p=aux;
p[nr_elem-1]=val;
scanf(“%d”,&val);
}else { //p poate sa fie folosit, e un pointer valid
printf("eroare“);
afis(p, nr_elem);
exit(0);
}
}
afis(p,nr_elem); //..vezi functia afis pe slideul precedent
//pot sa eliberez spatiul ocupat de aux si p daca sunt nenuli; dar oricum se iese din program
return 0;
}
Alocarea dinamica a unui tablou bidimensional (matrice)
int main(){
void afisare(int**m, int lin, int col){
int **matr;
int i, j; int l, c, i;
for (i=0; i<lin; i++){ scanf(“%d”,&l);
for (j=0; j<col; j++) scanf(“%d”,&c);
printf(“%d “,m[i][j]);
printf(“\n“);
matr= (int**) malloc(l*sizeof(int*));
}
} for (i=0; i<l; i++)
matr[i] = (int*) malloc(c*sizeof(int));
// matr[i] = (int*) calloc(c,sizeof(int));
void citire(int**m, int lin, int col){
citire(matr, l, c);
int i, j; afisare(matr, l, c);
for (i=0; i<lin; i++) return 0;
for (j=0; j<col; j++) }
scanf(“%d”,m[i][j]);
}
#include <stdlib.h>
int main(){
#include <stdio.h>
int *p=NULL, nr_elem;
scanf(“%d”,&nr_elem);
void aloc(int* v,int dim){
aloc(p,nr_elem);
v=(int*)malloc(dim*sizeof(int));
citire(p,nr_elem);
}
afisare(p,nr_elem);
return 0;
void citire(int* v,int dim){
}
int i;
for (i=0; i<dim; i++) scanf(“%d”,v[i]);
} Ce se intampla in functia aloc()?
•v primeste o noua valoare pe care sa o
void afisare(int* v,int dim){ stocheze ca adresa la care pointeaza.
int i; •dar v a fost transmis ca o copie a lui p.
for (i=0; i<dim; i++) printf(“%d “,v[i]); •la iesirea din functie copia se pierde –
} spatiul alocat cu malloc ramane alocat, dar
pierd adresa sa.
//….
int *p=NULL, nr_elem;
scanf(“%d”,&nr_elem);
p=aloc(p, nr_elem);
void modif1(int*x){
*x=(*x)+1;
}
Fie structura:
typedef struct /*pers*/{
char nume[50];
int varsta;
} persoana;
//main
persoana p1, p2, p3[20];
scanf (“%s”, p1.nume);
scanf (“%d”, &p1.varsta);
scanf (“%s”, p2.nume);
scanf (“%d”, &p2.varsta);
p1=p2; // reprezentati in memorie
57
Problema 3
1.Alocati dinamic spatiu pentru un vector de dimensiune n de
intregi si pentru o matrice lxc de intregi.
2. Alocati astfel incat elementele tablourilor sa fie initializate cu 0.
Problema 4
Alocati dinamic spatiu pentru o variabila numar complex, un
vector/matrice de numere complexe – unde, numar complex e o
structura.
Problema 5
Realocati spatiu pentru tablourile din Problema 3– dimensiunile
noi sunt n-1 si (l+1)*c.
Problema 6
Alocati dinamic spatiu pentru siruri de caractere. Utilizati functiile
58
strlen, strcat, strcmp, strcpy, etc
Anexa I - Tipul pointer
Ce este un pointer?
num 5
Dereferentiere
Dereferentierea este o operatie prin care se urmareste referinta unui pointer
- ca sa se recupereze valoarea catre care se pointeaza.
Atribuirea de pointeri
• Operatorul = intre 2 pointeri -> ii face sa pointeze catre aceeasi zona de
memorie:
second= numPtr
Variabilele de tip pointer se declara ca orice alta variabila: tip, nume, posibila
initializare.
OBS: se ocupa in memorie spatiu pentru pointer, dar nu si pentru elementul catre
care se va pointa
Tipul pointer
Operatorul referinta &
Exista mai multe feluri in care se poate calcula referinta catre un/adresa unui element
catre care se pointeaza.
Cel mai simplu, aceasta se afla cu operatorul &:
int num;
int*numPtr;
num=5;
numPtr=# // calculeaza referinta catre num (adresa lui num)
//si o stocheaza in numPtr
Operatorul de dereferentiere *
Operatorul * - dereferentiaza un pointer.
Este un operator unar si se plaseaza la stanga operandului.
*numPtr <=> num <=> *(&num)
second=numPtr;//copie superficiala
numPtr
num++;
second=&num1; second
numPtr = #
num++;
numPtr
printf(“%d %d\n” , *second ,*numPtr);
num 65
Tipul pointer
“Bad pointers”
int *p;
int a = 1;
int b = 2;
int c = 3;
int* p;
int* q;
p = &a;
q = &b;
c = *p;
p = q;
*p = 13;
p = &a; // p pointeaza la a
q = &b; // q pointeaza la b
int *p;
char *c;
printf(“%d\n” , sizeof(p));
printf(“%d\n” , sizeof(c));
Numele unui tablou este un pointer constant care are ca valoare adresa primului
element din tablou.
tip_date m[l][c];
Rezumat
De ce sa folosim pointeri ?
Acelasi efect se poate obtine copiind informatia in toate locurile necesare, dar asta
asta implica mai mult timp consumat si mai mult spatiu de memorie ocupat.