Sunteți pe pagina 1din 105

CURS 8-9

Pointeri. Alocare dinamica.


8. TABLOURI ŞI POINTERI
 In cadrul limbajului C/C++ există o strânsă legătură între
tablouri şi pointeri

 Un nume de tablou este un identificator care specifică


adresa primului element din tablou, adresă ce nu poate fi
modificată
 această valoare (adresa) poate fi atribuită unui alt
pointer sau poate fi folosită pentru accesul la
elementele tabloului folosind aritmetica pointerilor

2
TABLOURI ŞI POINTERI

 Deosebirea între variabile de tip pointeri şi nume de


tablouri constă în următoarele:

 unei variabile pointer i se pot atribui valori diferite în


timpul execuţiei

 numele unui tablou are tot timpul drept valoare adresa


primului său element şi de aceea această valoare nu
poate fi modificată (se comportă din acest punct de
vedere ca un pointer constant)

3
TABLOURI ŞI POINTERI
 Exemplu:
float ftab[20], *fptr, ftemp;
int l;
...
fptr = ftab; // fptr contine adresa primului element din ftab
ftemp = ftab[0]; // este echivalent cu ftemp = *fptr

 De asemenea:
&ftab[0] este identic cu ftab
ftab[0] *ftab
&ftab[i] ftab+i
ftab[i] *(ftab+i)
iar expresia: (&ftab[i] - ftab) are întotdeauna valoarea i
4
TABLOURI ŞI POINTERI
 Indexarea unui pointer asemenea unui tablou are sens
doar dacă referă un tablou:

const char str[ ] = "Pointeri in C/C++"; // str – identificatorul


unui sir constant

int main(void){
const char *p; //pointer aritmetic catre un sir const
int i;
p = str; // atribuire pointeri
for(i=0; p[i]; i++)
printf("%c",p[i]);
}

 Dacă p pointează pe un tablou, compilatorul C/C++


generează cod executabil mai scurt pentru *(p+i) decât 5

pentru p[i]
TABLOURI ŞI POINTERI

 Dacă un pointer nu pointează pe un tablou, el poate fi


indexat, dar rezultatul este imprevizibil:

char *p, ch;


int i;
...
p=&ch;
for(i=0;i<10;i++)
p[i] = 'A'+i;

6
TABLOURI ŞI POINTERI

 Numele unui tablou conține adresa primului element, care


nu poate fi modificată (deci numele tabloului nu poate fi
incrementat/decrementat, spre deosebire de numele unui
pointer), dar poate fi folosit ca pointer aritmetic în locul
indexării:

const char str[ ]="Pointeri in C/C++";


...
*(str+1)=‘p';
printf("%c ", *(str+1)); // dar nu e permis str++

7
ORGANIZAREA IN MEMORIE A UNUI TABLOU
 Un tablou este definit in memorie prin:
- numele tabloului, ce are ca valoare adresa primului
element din tablou
- elementele tabloului memorate în mod contiguu
- se rezervă o zonă de dimensiunea unui element după
ultimul element efectiv din tablou

 Exemplu:
int varsta[ ] = {18,19,20,5,10};
varsta[0] = *varsta = 18
varsta[1] = *(varsta+1) = (varsta +1)[0] = 19
8
 varsta[2] = *(varsta+2) = (varsta +1)[1] =
(varsta +2)[0] =20
 varsta[3] = *(varsta+3) = (varsta +1)[2] = (varsta+2)[1]
= (varsta+3)[0] = 5
 varsta[4] = *(varsta+4) = (varsta +1)[3] = (varsta+2)[2]
= (varsta+3)[1] = (varsta+4)[0] = 10

 Obs: dacă un pointer referă un tablou, adică apare


operatorul de indexare [ ], operatorul de indirectare *
nu mai e folosit

9
TABLOURI ŞI POINTERI
 Tablouri de pointeri
 Tablourile de pointeri se definesc astfel:

tip *nume_ptr[DIM];

 Exemplu:

int *pa[20];
int adrpoint;
...
pa[7] = &adrpoint;
*pa[7] = 127;

10
TABLOURI ŞI POINTERI
 Tablourile de pointeri se folosesc mai ales la crearea de
tabele de şiruri de caractere care pot fi selectate in
funcţie de anumite valori

 Exemplu:

const char *p[ ] = { "Out of range", "Disk full", "Paper out" };

void Err(int nr_err)


{
printf(p[nr_err]);
}

11
TABLOURI ŞI POINTERI

 Tablourile de pointeri pot fi accesate cu pointeri dubli


 Exemple:

char *a[5];
char **p; // pointer (dublu) catre caractere
p = a;

 p va conţine adresa primului element al tabloului a,


unde vom avea un pointer spre un caracter, astfel încât
(p+1) va indica elementul a[1], etc.

12
 Exemplu:
Fie 10 șiruri de caractere ale căror adrese se află într-un
tablou de pointeri char *sir[10] și construim o funcție care
afișează aceste șiruri de caractere la un apel. Acest lucru îl
putem realiza în două moduri:
-primul mod, cu tablouri de pointeri;
-al doilea, cu pointeri dubli

13
A) TABLOURI DE POINTERI
#include <iostream>
using namespace std;
void afis1_sir(char *tp[ ], int n);
const char *sir[10]={"a", "b", "c", "d", "e",
"f", "g", "h", "i", "j" };

int main(){
afis1_sir(sir,10);
return 0;
}

14
void afis1_sir(const char *tp[], int n)
{
int i;
for(i=0; i < n; i++)
if (tp[i] != NULL) //if (tp[i])
printf("%s\n", tp[i]);
}//afis1_sir

 Apel ambele cazuri: afis1(2)_sir(sir, 10);

15
B) POINTERI DUBLI
void afis2_sir(const char **tp, int n)
{
char *p;
while (n--){
p=*tp;
while (*p)
putchar( *p++); //caracterul urmator
putchar(‘\n’);
tp++; // trecerea la un nou sir de caract.
}//while
}//afis2_sir

16
TABLOURI ŞI POINTERI
 Pointeri către şiruri de caractere:
int main(void){
const char *p1;
p1 = "unu doi trei";
printf("%s", p1);

const char *p2 = "unu doi trei";


printf("%s", p2);

const char str[] = "unu doi trei", *p3;


p3 = str;
printf("%s", p3);

return 0;
}
17
TABLOURI ŞI POINTERI
/* valoarea minima dintr-un tablou de numere intregi*/
#include <stdio.h>
#define DIM 20
int DetMin(int *x, int dim);
int main(void){
int i, dim, min;
int x[DIM];
printf("\nIntroduceti dimensiunea tabloului (<=20): ");
scanf("%d", &dim);
if(dim<0 || dim > DIM)
printf("\n Dimensiune prea mare sau negativa !\n");
return 1;
}
printf("\n Introduceti elementele tabloului:\n");

18
TABLOURI ŞI POINTERI
for(i=0; i<dim; i++) {
printf("\tx[%d] = ", i);
scanf("%d", (x+i));
}
min = DetMin(x, dim);
printf("\n Cel mai mic element din tablou este:
%d\n", min);
return 0;
}//main

int DetMin(int *x, int n){


int i, min;
min = *x++;
for(i=1; i<n; i++) {
if(*x < min)
min = *x;
x++;
}
return min; 19
} //DetMin
 Pointerii către tablouri de tip T au o aritmetică în care
unitatea este:
dim_tab * sizeof(T),
 în timp ce pointerii spre tipul T au o aritmetică în care
unitatea este sizeof (T).

Exemplu:
 int (*p)[10]; //p este un pointer spre un tablou de 10
întregi cu unitatea 10*sizeof(int)
int(*p)[3];
int tab[3][3] = { { 1,2,3 }, { 4, 5,6 }, {7,8,9} };
p = &tab[0]; // *p[0]=1, *p[1]=4, *p[2]=7

 int *p[10]; //p este un tablou de 10 pointeri spre intregi


20
 în cazul în care avem tablouri cu mai multe
dimensiuni care se declară prin:
T nume_tab [d1][d2]…[dn];
unde T este tipul elementelor și d1, d2, …, dn sunt
constante întregi și pozitive, atunci elementele tabloului
nume_tab vor ocupa o zonă contiguă de memorie de
dimensiunea:
d1*d2*…*dn * sizeof(T),
deci tabloul va fi alocat la adrese succesive de
memorie.

21
 Fie un tablou a de tip T și dimensiuni: d1, d2, …, dn:
T a[d1][d2]…[dn];
și presupunem indicii: i1, i2, …, in astfel încât
0 <= ik <= dk-1, unde k ia valori între 1 și n.

Funcția f care realizează corespondența între indicii (i1, i2,


…, in ) și adresa lui a[i1][i2]…[in] se numeste funcție de
alocare a memoriei pentru tabloul a, funcția de alocare
fiind identică pentru toate tablourile cu aceeași dimensiune
și același tip T.

22
Notând cu d și i multiindicii:
d = (d1, d2, …, dn)
i = (i1, i2, …, in), și cu
T tipul de bază al tabloului,
atunci funcția de alocare a memoriei pentru un tablou
oarecare a se notează cu:
fd, T (i, a)
adică adresa elementului i al tabloului a de tipul T și
multiindici d este dată în limbajul C prin:

23
fd, T (i,a) = baza (a) + sizeof(T) *[ i1d2d3…dn +
i2d3d4…dn +
i3d4d5…dn +…+
in-1dn +
in
]

deci tabloul este alocat la adrese succesive de memorie,


ultimul indice variind cel mai repede, baza(a) este
&a[0][0]…[0].

24
Observații:
 Funcția de alocare nu depinde de prima dimensiune, d1,
de aceea uneori putem să o ignorăm
 La tablourile unidimensionale (T a[M]) funcția de alocare
nu depinde de M:
&a[i] este egala cu: baza(a) + sizeof (T) * i
 Un tablou cu mai multe dimensiuni este tratat de către
compilator ca un tablou cu o dimensiune (și anume prima),
care are ca elemente un tablou cu restul de dimensiuni, deci
nu există limitări sintactice ale numărului de dimensiuni în C.
La un tablou bidimensional, T a[M][N], funcţia de alocare nu
depinde de M:
&a[i][j] este egala cu: baza(a) + sizeof (T) * [ i * N + j ]
25
 În cazul transmiterii către o funcție a unui tablou cu mai
multe dimensiuni, este de preferat să folosim ca
parametru formal un pointer către tablou, adică adresa
de început a acelui tablou.

 De asemenea, se poate folosi ca parametru un pointer


de tip void, în cadrul funcției folosindu-se un pointer
specific către tipul T și cu conversia explicită către acel
tip (vedeti exemplul urmator).

26
AFIŞARE MATRICE LINIE CU LINIE
#include <stdio.h>
#include <stddef.h>
#define FORMAT "%6d"

void afis_mat_int(void *a, int m, int n){


int *v = (int *)a;
int mn = m*n;
ptrdiff_t d; //definire din stddef, d ca pointer dif.
while ((d=v-(int*)a) < mn)
printf ((d%n == n-1) ? FORMAT "\n" : FORMAT, *v++);
}

27
DECLARATORI COMPLECȘI
 Un declarator complex este un identificator însoțit de mai
mult de un modificator de genul:
-de date, [ ]
-pointer, *
-functie, ( )
 Aceste combinații variate pot fi aplicate uneia și aceleiași
variabile (identificator) cu unele excepții:
-un tablou nu poate fi compus din funcții
-o funcție nu poate returna o funcție sau un tablou

28
INTERPRETAREA DECLARATORILOR COMPLECSI

 parantezele ( ) și [ ] din dreapta (funcții și tablouri)


identificatorului au prioritate mai mare decât * din stânga
identificatorului
 parantezele ( ) și [ ] au aceeași prioritate, asocierea
făcându-se de la S ->D
 specificatorul de tip este aplicat în ultimul pas, când
declaratorul a fost complet evaluat
 parantezele ( ) ca operator, se pot folosi pentru a
schimba asocierea implicită, forțând una explicită

29
REGULA INTERPRETĂRII (CITIRE DIN INTERIOR SPRE
EXTERIOR)

 1. Se citește identificatorul și se caută paranteze [ ] și


( ) spre dreapta identificatorului care se interpretează,
respectiv * spre stânga

 2. dacă se întâlnește operatorul ( ) la orice nivel, se


revine și se aplică aceleași reguli pentru elementele
din interiorul parantezelor rotunde

 3. În final se aplică specificatorul de tip

30
EXEMPLE:
a) int * var[5];
-var este un tablou
-de 5 pointeri de tip intreg

b) int (*var)[5];
-var este un pointer
-la un tablou de 5 elemente de tip int

31
c) long * var(int, int);
-var este o funcție cu doi parametri int
-care returnează un pointer de tip long

d) char * (* (*var)( ))[10];


-var este: un pointer la
-o funcție, care returnează
-un pointer la
-un tablou de 10 elemente
-fiecare element fiind un pointer de tip char
32
9. FUNCŢII PENTRU LUCRUL CU ŞIRURI DE
CARACTERE

 Biblioteca standard C/C++ are mai multe funcţii pentru


lucrul cu şirurile de caractere

 Majoritatea acestor funcţii îşi au prototipul în fişierul antet


<string.h> sau mai nou <cstring> la VC++

 Alte funcţii care lucrează cu şiruri sunt declarate în


<stdio.h>, <stdlib.h>

33
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Afișarea unui şir la stdout:

int puts(const char *s); (stdio.h)

 Adaugă caracterul NewLine


 In caz de succes returnează ultimul caracter scris, altfel
returnează EOF

34
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Citirea unui şir de la stdin:

char *gets(char *string); (stdio.h)

 Citeşte intrările de la stdin până la întâlnirea unui caracter


‘\n’
 acest caracter nu este pus în şir

 Returnează un pointer la şirul dat ca argument


gets() nu este disponibila in VC++19, se va folosi:
char *gets_s( char *buffer, size_t sizeInCharacters );
sau
char *fgets(char *str, int n, stdin); 35
Exemplu gets_s( )

#include <iostream>
using namespace std;
#include <regex>

int main() {
//versiunea C
printf("\nGets: ");
char buf[20];
char *adr = gets_s(buf, _countof(buf));
printf(buf);
printf("\nAdr. init %p Adr. ret: %p", buf, adr);
double d_var;
printf("\nIntoduceti o valoare reala: ");
scanf_s("%lf", &d_var);
printf("\nValoarea reala este: %lf \n", d_var);
/* printf("\nIntroduceti sir nou: ");
char *aadr = gets_s(buf, _countof(buf));
printf(buf); 36
printf("\nAdr. init %p Adr. ret: %p", buf, aadr);*/
// versiunea C++
cout << "\n C++ - Introduceti numele si prenumele
studentului: ";
cin.ignore();
cin.getline(buf, 30);
regex pattern(" ");
//verifica separare nume prenume cu spatiu
while (!regex_search(buf, pattern)){
cout << "\nNu ati introdus corect!!";
cin.getline(buf, 30);
}
cout<<"\nIntroduceti o valoare double: ";
cin>>d_var;
cout<<"\n Studentul este: " << buf<< "\t Valoarea
double e: " << d_var<<endl; 37
cout << "\n\nIntroduceti inca o data numele si
prenumele studentului (nu se valideaza
spatiu intre nume si prenume): ";
cin.ignore();
cin.getline(buf, 30);

cout << "\nIntrodu inca o valoare double: ";


cin >> d_var;
cout << "\n Studentul este: " << buf << "\t Valoarea
double e: " << d_var << endl;
cin.ignore();
cin.get();
}

38
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Convertirea unui şir: (stdlib.h, math.h)

a) int atoi(const char *s);


b) float atof(const char *s);
c) long atol(const char *s);

 Funcţiile returneaza valoarea convertită sau 0 în caz că nu se


poate face conversia

39
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Lungimea unui şir:

unsigned strlen(const char *);

 Returnează numărul de caractere din şir, mai puţin


terminatorul de şir (caracterul NULL)

40
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Copierea unui şir: (string.h)

char * strcpy(char *dest, const char *src);


 Copiază şi terminatorul de şir NULL
! Obs. _strcpy() in Visual Studio 2019

char * strncpy(char *dest, const char * src, unsigned n);


 Copiază cel mult n caractere
 Dacă n < lungimea şirului sursă, nu se copiază terminatorul
de şir NULL

41
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Concatenarea şirurilor: (string.h)

char * strcat(char *dest, const char * src);


 Copiază şi terminatorul de şir NULL

char * strncat(char *dest, const char * src, unsigned n);


 Concatenează cel mult n caractere din şirul sursă

42
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Compararea şirurilor: (string.h)


 s1 = s2 dacă s1[i] = s2[i] pentru orice i.
 s1 < s2 dacă s1[i] = s2[i] ,i=0,..,k si s1[k] < s2[k]

 s1 > s2 dacă s1[i] = s2[i] ,i=0,..,k si s1[k] > s2[k]

 Compararea se face cu semn


int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, unsigned n);
int stricmp(const char *s1, const char *s2);
int strincmp(const char *s1, const char *s2, unsigned n);

 Funcţiile returnează: 0 dacă şirurile sunt egale


-1 dacă s1 < s2
1 dacă s1 > s2
43
 Comparările se opresc dacă se întâlneşte o condiţie > sau <
FUNCŢII PENTRU LUCRUL CU ŞIRURI DE CARACTERE

 Setarea unui şir:

char *strset(char *s, int ch); (string.h)

 Funcţia setează toate caracterele unui şir la valoarea dată de


al doilea parametru
 Şirul trebuie să conţină marcajul de sfârşit de şir
 Funcţia returnează un pointer la şirul iniţial

44
10. POINTERI SPRE FUNCŢII

 O funcţie, la fel ca un tablou, are asociată o adresă fixă de


memorie

 Un pointer către o funcţie va conţine această adresă

 Declararea unui pointer spre o funcţie trebuie să precizeze


tipul returnat de funcţie şi numărul, respectiv tipul
parametrilor
tip_rezultat (*pf) (lista_param_formali);

45
POINTERI SPRE FUNCŢII

 Exemplu:
char * (*pf) (char *, const char *);

 în care pf se defineşte ca fiind un pointer la o funcţie


care:
 are doi parametri, unul char * , altul const char *
 returnează un pointer la șir de caractere, char *

46
POINTERI SPRE FUNCŢII

 Numele unei funcţii fiind sinonim cu adresa de început a


acesteia, putem avea:
pf = strcpy; sau pf = &strcpy;

 Apelul unei funcţii prin intermediul unui pointer se face cu


construcţia:
(*pf) (lista_param_actuali);

 Dacă funcţia returnează o valoare, aceasta se poate atribui


unei variabile:
var = (*pf) (lista_param_actuali);

47
POINTERI SPRE FUNCŢII

 Observaţii:
 Standardul ANSI permite a scrie apelul fără a folosi
operatorul de indirectare *, adică:
pf (lista_param_actuali);
sau
var = pf (lista_param_actuali);

 Parantezele permit distingerea între declaraţia unui


pointer de funcţie şi prototipul unei funcţii care
returneaza un pointer:
tip_rez * nume_f(...); tip_rez (*nume_pf)(...);

48
POINTERI SPRE FUNCŢII

 Pointerii la funcţii se folosesc la:


 transferul adresei unei funcţii ca parametru;

 construirea unor tablouri care să conţină adrese


de funcţii care pot fi apelate indirect;

49
POINTERI SPRE FUNCŢII
int test(char*, char*, int (*)(char*, char*));
int comp1(char *, char *);
int comp2(char *, char *);

int main(void){
char s1[30], s2[30];
int (*pf)(char *, char *);
pf = comp1;
...
if ( test(s1, s2, pf) == 0 )
printf("sirul s1 este egal cu sirul s2");
else
printf("sirul s1 este diferit de sirul s2");
return 0;
50
}
int test(char *a, char *b, int (*cmp)(char *, char*)){
if(!(*cmp)(a,b))
return(0);
else
return(1);
}

int comp1(char *s1, char *s2){


return(s1[0] - s2[0]);
}

int comp2(char *s1, char *s2){


return(strlen(s1) – strlen(s2));
} 51
POINTERI SPRE FUNCŢII

#include <stdio.h>
#include <conio.h>
#define DIM 10

float serie(int *, int);


float paralel(int *, int);
float calcul(int*, int, float (*)(int*, int));

int main(void){
int i, n, tab[DIM];
char grp;
float (*pf)(int*, int);

printf("\nIntroduceti numarul de rezistente <=10 : ");


scanf_s("%d", &n);
if(n==0) return; 52
POINTERI SPRE FUNCŢII
printf("\nIntroduceti %d valori de rezistente :\n", n);
for(i=0; i<n; i++) {
printf("Rezistenta %d :", i+1);
scanf_s("%d", &tab[i]);
}
printf("\nGrupare serie/paralel (s/p)? ");
grp = getche();
switch(grp)
{
case 'S':
case 's': pf = serie;
break;
case 'P':
case 'p': pf = paralel;
break;
default: printf("\n Operatie invalida !");
53
return;
}//end switch
POINTERI SPRE FUNCŢII

printf("\n\tRezistenta echivalenta este: %f\n", calcul (tab, n, pf));


return 0;
}//end main

float calcul(int *tab, int n, float (*pf)(int *, int)) {


// alte prelucrari
return(pf(tab, n));
}//end calcul

54
float serie(int *tab, int n) {
float suma = 0;
while(n)
suma += tab[--n];
return(suma);
}//end serie

float paralel(int *tab, int n) {


float suma = 0;
while(n)
suma += 1./tab[--n];
return(1./suma);
}//end paralel

55
11. TRANSFERUL DE ARGUMENTE CĂTRE
FUNCŢIA MAIN

 La lansarea în execuţie multe aplicaţii permit specificarea


unor argumente în linia de comandă

 Exemplu:
arj e chess.arj

56
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN
 Introducerea de argumente în linia de comandă, se face
prin definirea unor parametri pentru funcţia main( ):
int main(<int argc, char *argv[ ]> <,char *env[ ]>)
{ ... }
 argc, conţine numărul argumentelor ( >= 1)
 argv, este un tablou de pointeri către şiruri de
caractere, unde:
 argv[0] pointează pe un şir de caractere cu numele şi
calea programului care se lansează în execuţie
 argv[1] pointează pe un şir de caractere cu primul
argument, ş.a.m.d.
 env, este un tablou de pointeri către şiruri de
caractere care reprezintă o listă de parametri ai SO
57
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN

 Intre argumente se acceptă ca separator spaţiu şi tab,


iar dacă un argument va conţine spaţiu(tab), acel
argument va fi încadrat între ghilimele
 Numele argc, argv, env nu sunt impuse
 Pentru că aceste argumente se primesc sub forma de
şiruri de caractere, aceste şiruri pot fi convertite spre
un format intern de reprezentare în memorie cu
funcţiile atoi( ), atof( ), atol( )
 Indiferent de modul de utilizare a funcţiei main( ) de
către utilizator, cei trei parametri sunt alocaţi pe stivă

58
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN

 Funcţia main( ) poate returna un rezultat către


programul apelant (SO)
 In general, un rezultat 0 indică terminarea normală a
programului, iar orice altă valoare indică terminare
anormală
 Returnarea rezultatului se face cu ajutorul instrucţiunii
return (expresie), ca în orice altă funcţie C/C++:

int main(void)
{
...
return (0);
}
59
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN

int main (int argc, char *argv[ ])


{
if (strcmp(argv[0], "Test") != 0)
{
printf(" Numele programului nu este \"Test\" !");
exit(1);
}
...
return 0;
}//main

60
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[ ]){
int i, suma=0, n;
if (argc == 1) {
printf("\n\n Nu aţi introdus numerele de adunat !"); exit(1);
}
else {
for(i=1; i<argc; i++) {
n = atoi(argv[i]);
suma += n;
} // for
printf("\nSuma argumentelor din linia de comandă este: %d", suma);
} // else
return 0; } /main
61
TRANSFERUL DE ARGUMENTE CĂTRE FUNCŢIA MAIN

 Funcţia exit(int) permite întreruperea execuţiei unui


program şi transmiterea unui rezultat către programul
apelant

 Întreruperea programului se face indiferent de unde


este apelată funcţia exit( ) (din funcţia main( ) sau din
orice altă funcţie C/C++)

62
12. ALOCAREA DINAMICĂ A MEMORIEI

 Alocarea dinamică permite crearea, exploatarea şi


distrugerea unor obiecte în timpul execuţiei programului,
la iniţiativa programatorului

 Compilatorul face alocări dinamice pentru obiectele


locale unei funcţii sau unui bloc, dar alocarea se face pe
stivă

 Zona de date globale este inclusă în dimensiunea


programului, iar pe de altă parte dimensiunea stivei nu
poate depăşi anumite limite
63
ALOCAREA DINAMICĂ

 De aceea în multe cazuri se impune folosirea alocării


dinamice

 In cazul alocării dinamice făcută de programator,


alocarea se face într-o zonă de memorie numită "heap“,
care diferă atât de stivă, cât şi de zona de date globale a
programului

64
 HEAP – ce este?
• zona de memorie e controlată de programator
• structură arborescentă
• nu are restricții la dimensiunea memoriei
• accesul la variabile se face mai lent și neapărat prin pointeri
• variabilele stocate pe heap pot fi accesate din orice regiune
a programului
 Stiva
• stochează variabile temporare create de fiecare funcție
(inclusiv main())
• structură LIFO (last in first out)
• la ieșirea din funcție, toate variabilele sunt șterse din stivă
• memoria este controlată automat de compilator
• are dimensiuni reduse
65
• acces rapid la variabile
ALOCAREA DINAMICĂ

 Alocarea dinamică implică mărirea codului sursă al


programului, de aceea se foloseşte dacă se poate obţine o
reducere a dimensiunii programului prin reducerea zonei
de date incluse în program

 Alocarea dinamică se foloseşte în cazul datelor de


dimensiune variabilă sau în cazul unor structuri cu număr
variabil de elemente, număr cunoscut abia la execuţie

 Odată alocată memoria, acea zona poate fi tratată ca


fiind un tablou și accesată astfel, pentru că alocarea se
face în mod secvențial.

66
ALOCAREA DINAMICĂ C

Alocarea dinamică în limbajul C

 Limbajul C nu dispune de un sistem încorporat pentru


alocarea şi eliberarea variabilelor dinamice (dar are
pentru variabile statice şi locale)

 Biblioteca standard C oferă un set de funcţii pentru


alocarea şi eliberarea variabilelor dinamice

 Funcţiile ce permit alocarea dinamică sunt declarate în


fişierele antet stdlib.h şi malloc.h

67
ALOCAREA DINAMICĂ C

void * malloc(unsigned nr_oct);

 Descriere:
 alocă o zonă de "nr_oct" octeţi în memoria heap, în cadrul
segmentului de date curent
 returnează un pointer generic în caz de succes sau
valoarea NULL în caz de eşec
 dimensiunea maximă a blocului alocat este de 64Ko

68
ALOCAREA DINAMICĂ C

 Pointerul generic este convertit cu operatorul cast, iar


dimensiunea memoriei este calculată cu operatorul sizeof
astfel:
int *p, n=20;
...
p = (int *) malloc(n * sizeof(int));

 Zona de memorie alocată este echivalentă cu un tablou


de 20 de întregi, iar elementele lui pot fi referite:
 cu operatorul de indexare: p[0], p[i]
 sau cu operatorul de indirectare: *p, sau mai general,
*(p+i)

69
 Se recomandă testarea pointerului obţinut înainte
de folosire:

if (!(p = (int *)malloc(n * sizeof(int))))


{
// mesaj de eroare
}

70
ALOCAREA DINAMICĂ C
void * calloc(unsigned nr_elem, unsigned dim_elem);

 Descriere:
 alocă o zonă de "nr_elem*dim_elem" octeţi în memoria
heap, în cadrul segmentului de date curent
 iniţializează zona de memorie cu 0
 returnează un pointer generic în caz de succes sau
valoarea NULL în caz de eşec;
 exemplu:
int *p, n=20;
...
p = (int *)calloc(n, sizeof(int));

71
ALOCAREA DINAMICĂ C

void free(void *);

 Permite eliberarea zonelor de memorie alocate anterior,


prin specificarea unui pointer ce conţine adresa de
început a zonei
 Dacă se apelează aceste funcţii cu un parametru NULL
rezultatele sunt imprevizibile

72
ALOCAREA DINAMICĂ C

 Dacă datele din heap nu mai sunt necesare se recomandă


eliberarea zonei aferente
 După eliberare:

- nu pot folosi din nou acea zonă de memorie cu pointerul


eliberat
- pointerul eliberat rămâne vizibil, dar valoarea lui nu are nici o
semnificație
- până la sfârșitul blocului acest pointer poate fi refolosit la o
altă alocare sau ca simplu pointer către o dată căreia i se
asociază
 Regulă practică: numărul de apeluri către funcţii de alocare
(malloc( ), calloc( )) trebuie să fie egal cu numărul de apeluri către
funcţii de dealocare (free( ))
73
ALOCAREA DINAMICĂ C

void *realloc(void *block, size_t size); //(malloc.h, stdlib.h)

 Funcţia permite realocarea memoriei prin extinderea sau


comprimarea unui bloc alocat anterior cu una din funcţiile
malloc( ), calloc( ) sau chiar realloc( )
 Dacă size == 0 blocul de memorie este eliberat şi se
returnează valoarea NULL
 Dacă block == NULL, funcţia realloc( ) lucrează la fel ca şi
funcţia malloc( )
 În urma ajustării dimensiunii blocului de memorie este
posibilă mutarea conţinutului memoriei în altă zonă
 Funcţia returnează un pointer la zona de memorie
realocată în caz de succes sau valoarea NULL în caz de 74
eşec sau dacă size == 0
ALOCAREA DINAMICĂ C
1. Alocarea unui tablou unidimensional (vector)
#include <stdio.h>
#include <alloc.h>

int main(void){
int i,n, *tab=NULL;
printf("\nIntroduceti dimensiunea tabloului : ");
scanf_s("%d", &n);
tab = (int *)malloc(n * sizeof(int));
if(tab != NULL)
{
printf("\nIntroduceti elementele tablolului : ");
for(i=0; i<n; i++) {
printf("\n\t elementul %d : ", i+1);
scanf_s("%d", tab+i);
// scanf_s("%d", &tab[i]);
} 75
ALOCAREA DINAMICĂ C

for(i=0; i<n; i++)


{
printf("\n tab[%d] = %d : ", i, *(tab +i));
// printf("\n tab[%d] = %d : ", i, tab[i]);
}
}
else
printf("Alocare nereusita !");
if(tab)
free(tab);
return 0;
}
76
ALOCAREA DINAMICĂ C
2. Alocare tablou dinamic unidimensional exploatat ca un tablou
bidimensional
// !!! se vor include bibliotecile stdio.h si alloc.h
int main(void){
int *p, m, n, i, j;
printf("\n Dimensiuni matrice : ");
scanf("%d %d", &m, &n);
if((m <= 0) || (n <= 0)) {
printf("\nNumere invalide ! Mai incearca !");
return 1;
}
// alocare dinamică si test pointer
p = (int *)malloc(m * n * sizeof(int));
if(p != NULL) {
printf(“\nIntroduceţi elementele matricei :");
for(i=0; i<m; i++) {
printf(“\nLinia %d :", i+1);
for(j=0; j<n;j++)
77
scanf_s("%d ", p+i*n+j);
}
ALOCAREA DINAMICĂ C
printf(“\nMatricea arata astfel :");
for(i=0; i<m; i++) {
printf(“\n\t”);
for(j=0; j<n;j++)
printf("%d ", *(p+i*n+j));
}
} //if
else {
printf("\n Eroare de alocare !");
return 1;
}
// dealocare
if(p)
free(p);
return 0; }// main 78
ALOCAREA DINAMICĂ C

 Concluzii la alocarea dinamică în limbajul C:


 limbajul C nu are mecanisme încorporate (operatori)
pentru alocarea dinamică
 se utilizează funcţii din biblioteca standard
 alocarea dinamică implică folosirea pointerilor
 este necesară folosirea conversiilor de tip şi a
operatorului sizeof
 pointerii obtinuţi cu funcţiile malloc( ) şi calloc( ) se vor
dealoca numai cu funcţia free( )
 se recomandă testarea pointerilor obţinuţi înainte de
folosire şi înainte de dealocare

79
ALOCAREA DINAMICĂ C++

Alocarea dinamică în limbajul C++


 In C++ alocarea dinamică se poate face ca şi în
limbajul C (cu ajutorul aceloraşi funcţii), dar şi cu
operatorii new şi delete

 Operatorul new
 Este un operator unar şi permite alocarea dinamică în
heap
 Are ca valoare adresa de început a zonei de memorie
alocate (un pointer) în caz de succes sau valoarea 0 în
caz de eşec
 Obiectul alocat are durata de viaţă până la dealocare
cu operatorul delete sau până la sfârşitul programului
80
ALOCAREA DINAMICĂ C++

 Forme:
tip *nume_ptr = new tip;
tip *nume_ptr = new tip(expresie);
 unde:
 "tip" este un tip predefinit sau definit de utilizator
 "expresie" permite iniţializarea zonei alocate

 Alocarea tablourilor se face astfel:


tip *nume_ptr = new tip[expresie];
 unde "expresie" este de tip întreg şi va da dimensiunea
tabloului
 Operatorul new ştie câtă memorie trebuie alocată fiecărui tip
de variabilă
81
ALOCAREA DINAMICĂ C++

1. int *pi = new int;

2. int &r = * new int; // referinta


...
r = 100;

3. int *p = new int[10];

4. int m=2, n=3;


double *tab = new double[m*n];

82
ALOCAREA DINAMICĂ C++

 Se recomandă testarea variabilei dinamice înainte de


utilizare în raport cu valoarea 0:

int *ip;
long dim;
cout << "Dimensiune bloc : "
cin >> dim;
if((ip = new int[dim]))
{
cout << "Alocare reusita !";
exit(0);
}
else cout << "Alocare nereusita !"; 83

...
ALOCAREA DINAMICĂ C++

 Operatorul delete
 Permite eliberarea (dealocarea) memoriei atunci când
alocarea s-a făcut cu operatorul new:
int *p;
p = new int;
...
delete p;

 In cazul tablourilor:
double *p = new double[200];
...
delete [ ]p;

! Pointerul asociat trebuie să fie obţinut cu ajutorul 84

operatorului new şi trebuie să fie nenul !


ALOCAREA DINAMICĂ C++
// Alocarea unui tablou unidimensional (vector)

#include <iostream>
using namespace std;
int main(void)
{
int i, n, *tab;
cout << "\n Introduceti dimensiunea tabloului : ";
cin >> n;
tab = new int[n];
if(tab != 0) {
cout << "\n Introduceti elementele tabloului : ";
for(i=0;i<n;i++) {
cout << "\n\t Elementul " << i+1 << " : ";
cin >> tab[i]; 85
}
ALOCAREA DINAMICĂ C++

cout << "\n\n Elementele tabloului sunt : ";


for(i=0;i<n;i++)
cout << tab[i] << "\t";
}
else
cout << "Alocare nereusita !";
if(tab)
delete [ ]tab;
return 0;
}

86
ALOCAREA DINAMICĂ C++
// Alocarea unui tablou bidimensional (matrice)

#include <iostream>
using namespace std;
int main(void)
{
int i, j, m,n, **tab;

cout << "\n Introduceti numarul de linii : ";


cin >> m;
tab = new int*[m];
if(tab != 0)
{
cout << "\n Introduceti numarul de coloane : ";
cin >> n;
87
for(i=0; i<m; i++) {
tab[i] = new int [n];
if(tab[i] == 0) {
cout << "\n Eroare de alocare ! "
return 1;
}
}
cout << "\n Introduceti elementele tabloului : ";
for(i=0; i<m; i++)
for(j=0; j<n; j++) {
cout << "\n\t Elementul tab[" << i+1 << "][ "<<j+1<<”]:";
cin >> tab[i][j];
}

cout << "\n\n Elementele tabloului sunt : ";

88
ALOCAREA DINAMICĂ C++
for(i=0; i<m; i++) {
for(j=0; j<n; j++)
cout << tab[i][j]<<"\t";
cout << “\n”;
}
}
else
cout << "Alocare nereusita !";
if(tab) {
for(i=0; i<m; i++)
delete [ ] tab[i];
delete [ ] tab;
}
return 0;
}
89
ALOCAREA DINAMICĂ C++

 Concluzii la alocarea dinamică C++:


 C++ are mecanisme încorporate (operatori) pentru
alocarea dinamică: new, delete
 alocarea dinamică implică folosirea pointerilor
 nu este necesară folosirea conversiilor de tip şi nici a
operatorului sizeof
 pointerii obtinuţi cu operatorul new se vor dealoca
numai cu operatorul delete
 este recomandată testarea pointerilor obţinuţi înainte
de folosire şi înainte de dealocare

90
13. FUNCŢII PENTRU MANIPULAREA ZONELOR DE
MEMORIE

Iniţializări zone de memorie

#include <memory.h> // sau <string.h>


void *memset(void *dst, int c, size_t n);

 Funcţia setează primii n octeţi începând cu adresa dst la


valoarea dată de caracterul conţinut în parametrul c şi
returnează adresa şirului pe care lucrează

91
 TIPUL DE DATE SIZE_T – ce este?
• size_t • tip de date alias pentru unsigned integer •
returnat de operatorul sizeof
• utilizat de obicei în expresii ce se referă la
dimensiunea unui obiect
• disponibil în C: <stddef.h> <stdio.h> <stdlib.h>
<string.h> <time.h> <uchar.h> <wchar.h>
• disponibil în C++: <cstddef> <cstdio> <cstdlib>
<cstring> <ctime> <cwchar>

92
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

Copierea zonelor de memorie


#include <memory.h> // sau <string.h>
void *memcpy(void *dest, const void *src, size_t n);

 Se copiază un bloc de memorie de lungime n de la


adresa src la adresa dest
 Nu se garantează o funcţionare corectă în cazul în care
blocul sursa şi cel destinaţie se suprapun
 Funcţia returnează adresa blocului destinaţie

93
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

#include <memory.h> // sau <string.h>


void *_memccpy(void *dest, const void *src, int c, size_t n);

 Funcţia copiază n octeţi de la sursă la destinaţie, însă


copierea se poate opri în următoarele situaţii:
 la întâlnirea caracterului c (ce este copiat la destinaţie)
 s-au copiat deja n octeţi la adresa de destinaţie

 Funcţia returnează adresa din blocul destinaţie ce


urmează după caracterul c, dacă acesta a fost copiat
sau valoarea NULL în caz contrar
 ! In versiunea actuală (Visual Studio 2019) e definita
doar in string.h (sau cstring)
94
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

Mutarea zonelor de memorie


#include <string.h>
void *memmove(void *dest, const void *src, size_t n);

 Se mută un bloc de lungime n de la adresa sursă src la


adresa destinaţie dest
 Se garantează copierea corectă chiar dacă cele două
zone de memorie se suprapun
 Funcţia returnează adresa zonei destinaţie

95
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

Compararea zonelor de memorie


#include <memory.h> // sau <string.h>
int memcmp(const void *s1, const void *s2, unsigned n);
int _memicmp(const void *s1, const void *s2, unsigned n);

 Funcţia memcmp( ) compară primii n octeţi ai zonelor de


memorie s1 şi s2 şi returnează un întreg mai mic decât,
egal cu, sau mai mare decât zero dacă s1 este mai mic
decât, coincide, respectiv este mai mare decât s2
 Funcţia _memicmp( ) face acelaşi lucru, dar fără a ţine
cont de litere mari, respectiv litere mici (ignoring case) -
DOAR ÎN COMPILATOARE MICROSOFT!!
96
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

Căutarea într-o zonă de memorie


#include <memory.h> // sau <string.h>
void *memchr(const void *s, int c, unsigned n);

 Caută caracterul c în primii n octeţi de memorie indicaţi


de pointerul s
 căutarea se opreşte la primul octet care are valoarea c
(interpretată ca unsigned char)

 Funcţia returnează un pointer la octetul găsit sau NULL


dacă valoarea nu există în zona de memorie

97
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

Interschimbarea octeţilor
#include <stdlib.h>
void _swab( char *src, char *dest, int n );

 Preia primii n octeţi de la sursă, aplică interschimbarea


fiecărei perechi şi depune rezultatul la destinaţie
 dacă n este par, interschimbarea se aplică pentru toţi
octeţii, începând cu primul octet
 dacă n este impar, interschimbarea se aplică numai la
primii (n-1) octeţi, începând cu primul octet

98
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE
#include <stdio.h>
#include <string.h>
int main(void){
char alphabet[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char target[27];
char *result;
result =(char *) _memccpy(target, alphabet, 'K', sizeof(alphabet));
if (result)
*result = NULL;
printf(target);
printf("\n");
return 0;
}//end main
99
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE
#include <stdio.h>
#include <memory.h>

int main(void){
const char *a = "AAA";
const char *b = "BBB";
const char *c = "aaa";

printf("Rezultatul compararii %s cu %s folosind memcmp( ) este:


%d\n", a, b, memcmp(a, b, sizeof(a)));
printf("Rezultatul compararii %s cu %s folosind memicmp( ) este:
%d\n", a, c, _memicmp(a, c, sizeof(a)));
return 0;
}//end main
100
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

#include <stdio.h>
#include <string.h>

int main(void){
float values[ ] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
float empty[5];
int i;
memmove(empty, values, sizeof(values));
for (i = 0; i < 5; i++)
printf("%3.1f\t", empty[i]);
printf("\n");
return 0;
}
101
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "ABCDEFG";
char *ps = (char *) memchr(str,'D',strlen(str));
if (ps != NULL)
printf ("Caracterul cautat a fost gasit: %s\n", ps);
else
printf ("Caracterul cautat nu a fost gasit\n");
return 0;
}

102
FUNCŢII PENTRU MANIPULAREA ZONELOR DE MEMORIE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
char source[ ] = "Sir de initializare...";
char target[64];
memset(target, NULL, sizeof(target));
_swab(source, target, strlen(source));
printf("Sursa: %s \n", source);
printf("Destinatie: %s\n", target); // iS redi initlazira.e..
return 0;
}
103
Ce știm după acest curs…
 Tablouri și pointeri. Legături, accesul la elementele tabloului prin
intermediul pointerilor (indexare, operatori +, -, ++, --), respectiv
prin numele tabloului și operatorii ‘+’, ‘-’.
 Tablouri de pointeri. Utilizare.
 Alocarea în memorie a tablourilor. Funcția de alocare a memoriei
pentru un tablou (independența de prima dimensiune).
 Declaratori complecși. Reguli de interpretare.
 Funcții pentru lucrul cu șiruri de caractere.
 Pointeri către funcții. Declarare, utilizare.
 Argumentele funcției main()
 Alocarea dinamică a memoriei în C
 Alocarea dinamică a memoriei în C++
 Funcții pentru manipularea zonelor de memorie

104
105

S-ar putea să vă placă și