Sunteți pe pagina 1din 91

Pointeri

M2
8. Tablouri şi pointeri
• In cadrul limbajului C/C++ există o strânsă legătură între
tablouri şi pointeri

• Un nume de tablou ce nu este urmat de operatorul de


indexare este un pointer constant de tipul elementelor
tabloului ce are ca şi valoare adresa primului element din
tablou
– această valoare 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 ca şi valoare adresa primului


său element şi de aceea se zice că el este un pointer constant

3
Tablouri şi pointeri

• De asemenea mai avem:


&ftab[0] este identic cu ftab
ftab[0] *ftab
&ftab[i] ftab+i
ftab[i] *(ftab+i)
iar expresia: (&ftab[i] - ftab) are totdeauna valoarea i

• 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

4
Tablouri şi pointeri
• Un pointer se poate indexa ca şi când ar fi un tablou
doar daca refera un tablou:

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

void main(void)
{
char *p;
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) faţă de
p[i]
5
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 este un pointer de tip constant deci nu


se poate modifica dar poate fi folosit ca şi pointer aritmetic
în locul indexării:

char str[ ];
...
*(str+1)='A';
printf("%c ", *(str+1)); // dar nu e permis str++

7
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;

8
Tablouri şi pointeri
– Tablourile de pointeri se folosesc mai ales la crearea de
tabele de şiruri care pot fi selectate funcţie de anumite
valori

– Exemplu:

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

void Err(int nr_err)


{
printf(p[nr_err]);
}

9
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


o adresă spre un caracter, astfel încât (p+1) va indica pe a[1]

10
• Exemplu:
Fie 10 siruri de caractere ale caror adrese
se afla intr-un tablou de pointeri, char
*sir[10] si construim o functie care afiseaza
aceste siruri de caractere la un apel. Acest
lucru il putem realiza in doua moduri:
• -primul mod clasic:
- al doilea, cu pointeri dubli
11
a) clasic
• #include <iostream>
• using namespace std;
• #include <conio.h>
• void afis1_sir(char *tp[], int n);
• char *sir[10]={"a","b","c","d","e","f","g","h","i","j"};

• void main(){
• afis1_sir(sir,10);
• getch();
• }
12
• void afis1_sir(char *tp[], int n)
• {
• int l;
• for(l=0;l < n; l++)
• //if (tp[l] != NULL) //if (tp[l])
• printf("%s\n", tp[l]);
• }//afis1_sir

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

13
b) Pointeri dubli
• void afis2_sir(char **tp, int n)
• {char *p;
• while (n--)
• {p=*tp;
• while (*p) putchar( *p++);
• putchar(„\n‟);
• tp++;
• }//while
• }//afis2_sir
14
Tablouri şi pointeri
• Pointeri către şiruri constante
void main(void)
{
char *p;
p = "unu doi trei";
printf(“%s”, p);
}

void main(void)
{
char *p = "unu doi trei";
printf(“%s”, p);
}

void main(void)
{
char str[ ] = "unu doi trei", *p;
p = str;
printf(“%s”, p);
}
15
Tablouri şi pointeri
#include <stdio.h>
#define DIM 20

int DetMin(int *x, int dim);

void main(void)
{
int i, dim, min;
int x[DIM];
printf("\nIntroduceti dimensiunea vectorului: ");
scanf("%d", &dim);
if(dim > DIM) {
printf("\n Dimensiune prea mare !\n");
return;
}

printf("\n Introduceti elementele vectorului:\n");


for(i=0; i<dim; i++) {
printf("\tx[%d] = ", i);
scanf("%d", (x+i));
}
16
Tablouri şi pointeri
min = DetMin(x, dim);
printf("\n Cel mai mic element din vector este: %d\n", min);
}

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;
}

17
• Pointerii catre tablouri de tip T au o aritmetica in
care unitatea este:
• dim_tab * sizeof(T),
• in timp ce pointerii spre tipul T au o aritmetica in
care unitatea este sizeof (T).
• Exemplu:
• int (*p)[10];//p este un pointer spre un tablou de
10 intregi cu unitatea 10*sizeof(int) ce rezulta
usor tinind cont de faptul ca (*p) poate aparea in
ori ce context unde apre x, => int x[10].
• int *p[10];//p este un tablou de 10 pointeri spre
intregi 18
• In cazul in care avem tablouri cu mai multe
dimensiuni care se declara prin:
• T nume_tab [d1][d2]…[dn]; //unde T este tipul
elementelor si d1, d2, …, dn sunt constante
intregi si pozitive, atunci elementele tabloului
nume_tab vor ocupa o zona contigua de
memorie de dimensiunea:
• d1*d2*…*dn * sizeof(T), deci tabloul va fi
alocat la adrese succesive de memorie.

19
• Fie un tablou a de tip T si dimensiuni: d1, d2, …,
d n:
• T a[d1][d2]…[dn]; si presupunem indicii:
• i1, i2, …, in astfel incit 0 <= ik <= dk-1, unde k ia
valori intre 1 si n.
• functia f care realizeaza corespondenta intre
indicii (i1, i2, …, in) si adresa lui a[i1][i2]…[in] se
numeste functie de alocare a memoriei pentru
tabloul a, functia de alocare fiind identica pentru
toate tablourile cu aceiasi dimensiune si acelas
tip T.

20
• Notind cu d si i multiindicii:
• d = (d1, d2, …, dn)
• i = (i1, i2, …, in), si cu
• T tipul de baza al tabloului atunci functia
de alocare a memoriei pentru un tablou
oarecare a se noteaza cu:
• fd, T (i,a) , adica adresa elementului i al
tabloului a de tipul T si multiindici d este
data in limbajul C prin:
21
• 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].
22
• Observatii:
• -Functia de alocare nu depinde de prima
dimensiune, d1, de aceea uneori putem sa o
ignoram
• -La tablourile unidimensionale functia de
alocare devine:
• a[i] = baza(a) + sizeof (T) * i
• -Un tablou cu mai multe dimensiuni este
tratat de catre compilator ca un tablou cu o
dimensiune (si anume prima) care are ca si
elemente un tablou cu restul de dimensiuni, deci
nu exista limitari sintactice ale numarului de
dimensiuni in C 23
• In cazul transmiterii unui tablou cu mai
multe dimensiuni la o functie, este de
preferat a folosi ca si parametru formal un
pointer catre tablou, adica adresa de
inceput a acelui tablou. De asemenea se
prefera folosirea pointerului de tip void, in
cadrul functiei folosindu-se un pointer
specific catre tipul T si cu conversia
explicita catre acel tip.
24
Afisare 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 si pointer
diferenta
• while ((d=v-(int*)a) < mn) printf ((d%n == n-1) ?
FORMAT “\n” : FORMAT, *v++); 25
Declaratori complecsi
• Un declarator complex este un identificator
insotit de mai mult de un modificator de tipurile:
• -de date, []
• -pointer, *
• -functie, ()
• Aceste combinatii variate pot fi aplicate uneia si
aceleiasi variabile (identificator) cu unele
exceptii:
• -un tablou nu poate fi compus din functii
• -o functie nu poate returna o functie sau un
tablou 26
Interpretarea declaratorilor
complecsi:
• -parantezele () si [] din dreapta (functii si
tablouri) identificatorului au prioritatea mai mare
decat * din stanga identificatorului
• -parantezele () si [] au aceiasi prioritate
asocierea facandu-se de la S ->D
• -specificatorul de tip este aplicat in ultimul pas
cand declaratorul a fost complet evaluat
• -parantezele () ca si operator, se pot folosi
pentru a schimba asocierea implicita, fortand
una explicita
27
Regula interpretarii (citire din
interior spre exterior)
• 1. Se citeste identificatorul si se cauta
paranteze [] si () spre dreapta care se
interpreteaza, respectiv * spre stanga
• 2. daca se intalneste operatorul () la ori ce
nivel se revine si se aplica aceleasi reguli
pentru elementele din interiorul
parantezelor rotunde
• 3. In final se aplica specificatorul de tip
28
Exemple:
a)char * (* (*var)( ))[10];
• -var este: un pointer la
• -o functie, care returneaza
• -un pointer la
• -un tablou de 10 elemente
• -fiecare element fiind un pointer de tip char
b) int * var[5];
-var este un tablou
-de 5 pointeri de tip intreg 29
c) int (*var)[5];
-var este un pointer
-la un tablou de 5 elemente de tip int
d) long * var(int, int);
-var este o functie cu doi parametrii int
-care returneaza un pointer de tip long

30
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>

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


stdio.h, stdlib.h

31
Funcţii pentru lucrul cu şiruri de caractere

• Afisarea unui şir la stdout:

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

– Adaugă caracterul NewLine


– In caz de succes returneaza ultimul caracter scris altfel returneaza
EOF

32
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

– Returneaza un pointer la şirul dat ca argument

33
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

34
Funcţii pentru lucrul cu şiruri de caractere

• Lungimea unui şir:

unsigned strlen(const char *);


– Returneaza numărul de caractere din şir mai puţin terminatorul
de şir (caracterul NULL)

35
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
• Conţinutul şirului destinaţie se pierde

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


• Copiază cel mult n caractere
• Dacă se copiază n caractere nu se copiază terminatorul de şir NULL

36
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ă

37
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 returneaza:
• 0 dacă şirurile sunt egale
• -1 dacă s1 < s2
• 1 dacă s1 > s2

– Comparările se opresc dacă se întâlneşte o condiţie > sau <


38
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

39
10. Pointeri spre funcţii
• O funcţie, ca şi 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);

40
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 caracter, "char *"

41
Pointeri spre funcţii

• Numele unei funcţii fiind sinonim cu adresa acesteia de


început 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);

42
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 returneza
un pointer:
tip_rez * nume_f(...); tip_rez (*nume_pf)(...);

– 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;
43
Pointeri spre funcţii

int test(char*, char*, int (*)(char*, char*));

int comp1(char *, char *);


int comp2(char *, char *);

void main(void)
{
char s1[30], s2[30];
int (*pf)(char *, char *);
pf = comp1;
...
if(test(s1, s2, pf) == 0)
printf("sirul s1 este diferit de sirul s2");
else
printf("sirul s1 este egal cu sirul s2");
} 44
Pointeri spre funcţii

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));
} 45
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));

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

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


scanf("%d", &n);
if(n==0) return;

46
Pointeri spre funcţii
printf("\nIntroduceti %d valori de rezistente :\n", n);
for(i=0; i<n; i++) {
printf("Rezistenta %d :", i+1);
scanf("%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 !");
return;
}//end switch
47
Pointeri spre funcţii
printf("\n\tRezistenta echivalenta este: %f\n", calcul (tab, n, pf));
}//end main

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


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

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 48
11. Transferarea 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

49
Transferarea de argumente către funcţia main

• Introducerea de argumente în linia de comandă, se face


prin definirea unor parametri pentru funcţia main( ):
void 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
50
Transferarea de argumente către funcţia main

• Intre argumente se acceptă ca şi 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 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ă

51
Transferarea de argumente către funcţia main

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


{
if (strcmp(argv[0], "Test") != 0)
{
printf("Ati modificat numele programului !");
exit(1);
}
...
}//main

52
Transferarea de argumente către funcţia main
#include <stdio.h>
#include <stdlib.h>
void 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;
}
printf("\nSuma argumentelor din linia de comandă este: %d", suma);
}
}
53
Transferarea 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(1);
}

54
Transferarea 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

• Intreruperea programului se face indiferent de unde este


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

55
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ă

56
Alocarea dinamică

• In cazul alocării dinamice, 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

• Zona de date globale este inclusă în dimensiunea


programului iar pe de altă parte dimensiunea stivei nu
poate depăşi anumite limite

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


dinamice

57
Alocarea dinamică

• Alocarea dinamică implică mărirea codului 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

• Odata alocata memoria, acea zona poate fi tratata ca fiind


un tablou, si accesata astfel

58
Alocarea dinamică C

Alocarea dinamică în limbajul C


– Limbajul C nu dispune de un sistem încorporat pentru
alocare ş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 alloc.h şi malloc.h

59
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 pe 16 biţi în caz de succes sau
valoarea NULL în caz de eşec
• dimensiunea maximă a blocului alocat este de 64Ko

60
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, *(p+i)

• Se recomandă testarea pointerului obţinut înainte de


folosire:
if (!(p = (int *)malloc(n * sizeof(int)))
{
// mesaj de eroare
}
61
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;
– dimensiunea maximă a blocului alocat este de 64Ko
– exemplu:
int *p, n=20;
...
p = (int *)calloc(n, sizeof(int));
62
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

63
Alocarea dinamică C

• Pentru alocarea unor zone de memorie de dimensiuni mai


mari decât 64Ko se folosesc variantele far ale acestor
metode:

void far * farmalloc(unsigned long nr_oct);


void far * farcalloc(unsigned long nr_elem, unsigned long dim_elem);

void farfree(void far *);

• Funcţia free() se foloseşte împreună cu malloc() sau calloc()


• Funcţia farfree() împreună cu farmalloc() sau farcalloc()

64
Alocarea dinamică C

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


eliberarea zonei aferente
• Dupa eliberare:
• -nu pot folosi din nou acea zona de memorie cu pointerul
eliberat
• -pointerul eliberat ramane vizibil, dar valoarea lui nu are nici
o semnificatie
• -pana la sfarsitul blocului acest pointer poate fi refolosit la o
alta alocare sau ca simplu pointer catre o data la care se
asociaza

• Regulă practică:
– numărul de apeluri către funcţii de alocare (malloc( ), calloc( );
farmalloc( ), farcalloc( )) trebuie să fie egal cu numărul de apeluri
către funcţii de dealocare (free( ); farfree( )) 65
Alocarea dinamică C

void *realloc(void *block, size_t size); //(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( )
• In 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 eşec sau
dacă size == 0
66
Alocarea dinamică C

1. Alocarea unui tablou unidimensional (vector)


#include <stdio.h>
#include <alloc.h>

void main(void)
{
int i,n, *tab=NULL;
printf("\nIntroduceti dimensiunea tabloului : ");
scanf("%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("%d", tab+i);
// scanf("%d", &tab[i]);
} 67
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);
}

68
Alocarea dinamică C
2. Alocare tablou dinamic unidimensional exploatat ca un tablou
bidimensional

void 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;
}

// 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++)
scanf("%d ", p+i*n+j);
}
69
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));
}
}
else {
printf("\n Eroare de alocare !");
return;
}

// dealocare
if(p)
free(p);
}
70
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

71
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 şi 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
72
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ă
73
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];

74
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 !";
...

75
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:
float *p = new double[200];
...
delete [ ]p;

– Pointerul asociat trebuie să fie obţinut cu ajutorul


operatorului new şi trebuie să fie nenul ! 76
Alocarea dinamică C++
// Alocarea unui tablou unidimensional (vector)
#include <iostream.h>

void 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];
}
cout << "\n\n Elementele tabloului sunt : ";
for(i=0;i<n;i++)
cout << tab[i] << "\t";
}
else
cout << "Alocare nereusita !";
if(tab)
delete [ ]tab;
}
77
Alocarea dinamică C++
// Alocarea unui tablou bidimensional (matrice)
#include <iostream.h>

void 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;
for(i=0; i<m; i++) {
tab[i] = new int [n];
if(tab[i] == 0) {
cout << “\n Eroare de alocare !”
return;
}
}
cout << "\n Introduceti elementele tabloului : ";

78
Alocarea dinamică C++
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 : ";


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;
}
} 79
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

80
13. Funcţii pentru manipularea zonelor
de memorie
Iniţializări zone de memorie

#include <mem.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ă

81
Funcţii pentru manipularea zonelor de memorie

Copierea zonelor de memorie


#include <mem.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

82
Funcţii pentru manipularea zonelor de memorie

#include <mem.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

83
Funcţii pentru manipularea zonelor de memorie

Mutarea zonelor de memorie


#include <mem.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

84
Funcţii pentru manipularea zonelor de memorie

Compararea zonelor de memorie


#include <mem.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)

85
Funcţii pentru manipularea zonelor de memorie

Căutarea într-o zonă de memorie


#include <mem.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

86
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, intershimbarea se aplică la toţi octeţii, începând
cu primul octet
– dacă n este impar, intershimbarea se aplică numai la primii (n-1)
octeţi, începând cu primul octet

87
Funcţii pentru manipularea zonelor de memorie

#include <stdio.h>
#include <memory.h>

void 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");
}//end main

88
Funcţii pentru manipularea zonelor de memorie

#include <stdio.h>
#include <memory.h>

void main(void)
{
char *a = "AAA";
char *b = "BBB";
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)));
}//end main

89
Funcţii pentru manipularea zonelor de memorie

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

void 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");
}

90
Funcţii pentru manipularea zonelor de memorie

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

void 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..
}

91

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