You are on page 1of 14

Curs 7 Pointeri

Pointeri
Pointerii sunt variabile sau constante care au ca valori adrese ale unor variabile,
adică adrese ale unor locaţii de memorie. Ei permit calcule cu adrese, calcule care sunt
specifice limbajelor de asamblare şi sunt folosiţi în scopul scrierii unor programe mai
eficiente atât din punctul de vedere al timpului de execuţie cât şi din punctul de vedere al
utilizării resurselor hard, în mod concret al utilizării memoriei computerelor.
Pointerii sunt în mod special utili la alocarea dinamică a memoriei [Beu99] şi la apelul prin
referinţă.
Pentru a înţelege mai uşor pointerii este convenabil ca aceştia să fie discutaţi prin
comparaţie cu variabilele normale.
O variabilă "normală" este o locaţie de memorie care păstrează o anumită valoare. Am
văzut în capitolele precedente că la declararea unei variabile, acesteia i se alocă un anumit
număr de octeţi, iar referirea la valoarea stocată în acei octeţi se face prin numele
respectivei variabile. La nivel maşină, respectiva locaţie are o adresă de memorie. Dacă de
exemplu, variabila declarată este s de tip float, cei 4 octeţi care încep la adresa locaţiei
respective sunt cunoscuţi programatorului ca şi s şi pe aceşti 4 octeţi pot fi stocate valori
reale în simplă precizie.
Pointerii nu păstrează o valoare în sens tradiţional ci adresa unei variabile. Un
pointer indică ("pointează") o altă variabilă păstrând o copie a adresei acestei variabile.
Deoarece pointerii păstrează adrese şi nu valori "tradiţionale", ei pot fi văzuţi ca fiind

Exemple de locaţii de memorie, adrese, pointer


compuşi din două părţi: pointerul însuşi şi valoarea pe care o indică; pointerul însuşi
păstrează o adresă, iar adresa respectivă indică o valoare.
Limbajul C foloseşte pointerii în trei moduri:
a) pentru a crea structuri dinamice de date construite din blocuri de memorie
b) pentru a opera cu parametrii pasaţi funcţiilor la apelul acestora
c) pentru a accesa informaţia stocată în tablouri.
In C, există o legătură intimă între tablouri şi pointeri.

Declararea şi iniţializarea pointerilor


Pointerii se declară ca orice variabilă, deosebirea constând doar în faptul că numele
lor este precedat de caracterul * care reprezeintă operatorul unar de indirectare sau de

1
Curs 7 Pointeri

dereferenţiere. Pentru iniţializarea pointerilor se poate folosi operatorul unar &, numit
operator de referenţiere şi care returnează adresa argumentului său.
Declararea pointerilor se face în modul următor:
tip *nume_pointer; sau tip* nume_pointer;
unde tip este un tip de date de bază sau tip utilizator, iar nume_pointer este un
identificator folosit pentru pointerul care se declară.

Exemple
1. float *pf; //pf poate conţine adrese la care se află memorate date de tip float
int *pi; //pi poate conţine adrese la care se află memorate date de tip int
char* pc; //pc poate conţine adrese la care se află memorate date de tip char

2. struct agenda
{
char nume[20];
char tel[15];
};

struct agenda *psa;


//psa este un pointer la date de tip structură agenda.

Iniţializarea pointerilor constă în precizarea adresei unui obiect definit în memorie. Dacă p
este numele unui pointer, atunci *p reprezintă valoarea de la adresa la care "pointează"
pointerul p.

Exemple
1. double a, *pd;
a=8.406;
pd=&a;

2. int n=1, m=2, s[10];


int *pi; // pi este un pointer la tipul întreg
pi = &n; // pi pointează acum la n
m = *pi; // m are valoarea de la adresa lui n, adică 1
*pi = 0; // n are acum valoarea 0
pi = &s[0]; // pi pointează acum la s[0], adică are ca valoare
// adresa elementului s[0]
*pi=5*m; // s[0] are acum valoarea 5*1=5

3. float pi, *p;


p=π
*p=3.14159;
Prin ultima instrucţiune i se atribuie de fapt lui pi valoarea 3.14159.
La declarea şi iniţializarea pointerilor constanţi se va ţine cont de faptul că printr-o
construcţie de forma:
2
Curs 7 Pointeri

const int* pi=&i;


se declară un pointer neconstant la o constantă întreagă, iar printr-o construcţie de forma:
int* const pi=&i;
se declară un pointer constant la un întreg.
În primul caz pointerul pi poate lua diferite valori în timp ce i va fi o constantă. În al doilea
caz, întregul i poate fi modificat prin intermediul pointerului dar nu poate fi modificat
pointerul pentru a pointa la altceva.
De asemenea, la declararea pointerilor într-o singură declaraţie se va ţine cont de faptul că
printr-o declaraţie de forma:
int* p1, p2, p3;
numai p1 este pointer, în timp ce p2 şi p3 sunt întregi. Astfel dacă se doreşte declararea
tuturor celor trei pointeri se poate proceda fie sub forma:
int *p1, *p2, *p3;
fie sub forma:
int* p1;
int* p2;
int* p3;
Prin declaraţia următoare:
float *pf1, fact(int *);
se declară pointerul pf1 la tipul float şi o funcţie fact care returnează o valoare de tip
float şi care are ca argument un pointer la tipul int.
Un pointer este constrâns să pointeze la un anumit tip de date. Aşadar, dacă a fost declarat
un pointer la tipul float, ca şi în declaraţia de mai sus, atunci el nu va putea avea ca valori
adrese ale unor locaţii de memorie unde sunt stocate date de tip întreg.

Program exemplu

#include <stdio.h>
#include <math.h>

void main()
{
float a,b;
float* p;

p = &a;
*p=4*atan(1.0);
b=2*a;
printf("a=%f b=%f",a,b);
printf("\nValoarea din locatia de memorie la care pointeaza p este %f
\nAdresa lui a: %X", *p, p);
}

Output-ul acestui program este:

şi rezultă după parcurgerea etapelor schiţate în figura următoare:


După secţiunea de declaraţii, variabilele a şi b au valori arbitrare, iar pointerul p, nefiind
iniţializat, poate pointa oriunde.
3
Curs 7 Pointeri

După instrucţiunea p=&a; p va pointa la


locaţia de memorie rezervată variabilei a şi
va avea ca valoare adresa primului octet al
acestei locaţii.
După instrucţiunea *p=4*atan(1.0);
conţinutul locaţiei de memorie la care
pointează p, adică valoarea lui a, va fi de 4
ori arcul al cărui tangentă este 1 (în radiani),
adică 3.141593, iar după instrucţiunea
b=2*a;, valoarea variabilei b va fi 6.283185.
O atenţie specială va fi acordată
iniţializării pointerilor deoarece pointerii
neiniţializaţi indică adrese nevalide. Astfel,
în urma unei declaraţii şi iniţializări de
forma:
int *p;
*p = 4;
Etapele execuţiei programului precedent
pointerul neiniţializat p pointează la o locaţie
aleatoare de memorie care poate fi una din zona variabilelor globale sau în spaţiul
programului obiect sau chiar în zona sistemului de operare. Prin atribuirea *p=4;
programul va încerca să scrie valoarea 4 în locaţia arbitrară la care pointează p ceea ce
poate duce la ieşirea din execuţie sau la blocarea programului sau chiar la coruperea unor
date ale sistemului de operare şi blocarea acestuia. Aşadar, înainte de dereferenţierea
pointerilor aceştia trebuie să fie iniţializaţi cu adrese valide.
O altă eroare care se poate face cu pointerii este o referinţa nevalidă care poate fi
produsă printr-o atribuire de forma p1=p2;, unde p1 şi p2 sunt pointeri, iar p2 este
neiniţializat. Orice referinţă la *p1 va produce o referinţă nevalidă.
Un alt tip de eroare cu pointeri este referinţa la pointeri nuli. O astfel de eroare se
produce când un pointer care pointează la zero, respectiv la pointerul NULL este folosit
pentru a face referinţă la un bloc de date. Astfel, dacă p este un pointer la tipul întreg,
secvenţa de cod
p = 0;
*p = 12;
este nevalidă deoarece nu există nici un bloc de date la care să pointeze p. In consecinţă,
încercarea de a citi sau scrie acel bloc de date conduce la o referinţă nevalidă.
Menţionăm că există cazuri în care un pointer poate pointa la zero, doar dereferenţierea
unui astfel de pointer fiind nevalidă.
Un program poate testa dacă un pointer va pointa la o adresă legală sau nu.

Exemplu

int* pi=0;
...
if(pi!=0)
n=*pi; //se foloseşte pointerul dacă pointează la o adresă legală

4
Curs 7 Pointeri

În loc de valoarea 0 (zero) se poate folosi macroul NULL, această valoare pentru un pointer
indicând de asemenea o adresă nevalidă. Astfel, exemplul precedent poate fi rescris sub
forma:

#include <stdio.h>
#include <math.h>

void main()
{
int* pi=NULL;
int n;

scanf("%d",&n);
if(n<10)
pi=&n;
if(pi)
{
printf("dublul lui n este %d",2*(*pi));
printf("\nAdresa lui n este %X",pi);
}
else
printf("Adresa nevalida: \\x%X",pi);
}

Două exemple de output ale acestui program sunt:

respectiv:

In exemplele discutate până acum, am văzut că pointerii por fi iniţializaţi fie prin
asignarea valorilor adreselor unor variabile sub forma pi=&i, fie prin asignarea valorii
NULL. Pointerii mai pot fi iniţializaţi cu ajutorul funcţiilor care returnează un pointer. Vom
discuta aceste cazuri în capitolul dedicat managementului fişierelor.

Operaţii cu pointeri
Cu pointeri pot fi efectuate următoarele operaţii:
a) Incrementare şi decrementare
Fie declaraţia:
int *p;
Instrucţiunile:
++p; şi p++;
respectiv:
--p; şi p--;
măresc, respectiv micşorează valoarea lui p cu o unitate. În acest caz, unitatea este de 2
octeţi deoarece aceasta este lungimea locaţiilor de memorie în care se păstrează datele de
tip int.

5
Curs 7 Pointeri

b) Adunarea şi scăderea unui întreg dintr-un pointer


Rezultatul operaţiei p+n, unde p este un pointer şi n este un întreg este: p+n·r, unde
r reprezintă numărul de octeţi folosiţi pentru păstrarea în memorie a datelor de tipul celor
spre care pointează p.

Fie declaraţiile:
int n,*pin=&n;
float x,*pre=&x;
atunci, dacă pin are valoarea 1BBC, expresia pin+3 va avea valoarea 1BBC+6, adică 1BC2.
Dacă pre are valoarea 1BB6, atunci expresia pre-5 va avea valoarea 1BB6-20, adică 1BA2.

c) Scăderea pointerilor
În limbajul C nu este permisă adunarea pointerilor dar este permisă scăderea
acestora. Prin scăderea a doi pointeri se obţine un întreg care reprezintă numărul locaţiilor
de memorie aflate între adresele la care pointează cei doi pointeri care se scad.
Astfel, o instrucţiune de forma:
pmed=(pin+pfin)/2;
va semnala o eroare de forma "Invalid pointer addition", în timp ce instrucţiunea:
pmed=pin+(pfin-pin)/2;
este corectă deoarece în acest caz, la pin se adună întregul rezultat din diferenţa pfin-pin
împărţită la 2.

d) Compararea pointerilor
Comparaţiile logice !=, ==, <, <=, >, >= sunt valabile şi în cazul pointerilor.

Mai jos este dat ca exemplu o funcţie care determină lungimea unui şir de caractere.
Menţionăm că numele unui tablou, deci şi al unui şir, este un pointer constant care are ca
valoare adresa primului element al tabloului.
int lsir(char *pc)
{
int i;
for(i=0;*pc!=NULL;pc++)
i++;
return i;
}

Când funcţia va fi apelată cu numele unui şir, pointerul pc va avea ca valoare adresa
primului caracter al şirului. În funcţia lsir se incrementează i cât timp caracterul la care
pointează pc este nenul. Orice şir se încheie cu caracterul NULL.

Pointeri şi tablouri
Aşa cum am mai spus, în limbajul C, pointerii sunt intim legaţi de tablouri, numele
unui tablou fiind chiar un pointer constant care are ca valoare adresa elementului de index
0 al tabloului respectiv. Orice operaţie care se face folosind indicii tablourilor poate fi
făcută, chiar mai rapid, prin folosirea pointerilor.

Exemple
1. char text[10]="Anul1Fizica", *pc;
6
Curs 7 Pointeri

pc=text;
putcahr(*pc); //se va tipări caracterul A
pc+=4;
putchar(*pc); //se va tipări caracterul 1

2. char sir[10]="BorlandC";
char *p;
p=sir;
while(*p)
putchar(*p++); //se va tipări sirul BorlandC
3. double a[15],t;
t=*(a+3); //este echivalent cu t=a[3];

Program exemplu: adunarea elementelor unui şir folosind pointeri

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

void main()
{

float x[100], *y,v;


double s=0.0,med;
int i;

//genereaza aleator elementele sirului x


randomize();
for(i=0;i<100;i++)
{
v=rand();
x[i]=(v*1000)-500;
}
//se aduna elementele sirului x; pointerul y a fost initializat cu adresa
//primului element al sirului x
for(y=x;y!=&x[100];)
s+=*y++;
med=s/100;
printf("\ns= %lg\tmedia= %lg",s,med);
}

Programul de mai sus generează 100 de valori aleatoare în intervalul [-500,500] după care
calculează suma şi media acestor valori.

Exemplu
Iniţializarea elementelor unui tablou fără pointeri şi cu pointeri

fără pointeri: cu pointeri:

#define DIM 50 #define DIM 50

int x[DIM],i; int x[DIM], *pi;


for(i=0;i<DIM;i++) for(pi=x;pi<&x[DIM];pi++)
x[i]=0; *pi=0; 7
Curs 7 Pointeri

În primul caz, pentru aflarea adresei elementului x[i] compilatorul generează un cod care
înmulţeşte pe i cu dimensiunea unui element al tabloului. În varianta cu pointeri,
compilatorul evaluează o singură dată indicele şirului, salvând 49 de operaţii de
multiplicare.
Referirea la elementul unui tablou sub forma a[i] este identică cu *(a+i). De fapt,
imediat ce se întâlneşte o referinţă de forma a[i], compilatorul C converteşte aceasta la
*(a+i). Evident, construcţiile &a[i] şi a+i sunt de asemenea identice.

Apelul prin referinţă utilizând parametri de tip pointer


Apelul funcţiilor poate fi făcut în două moduri şi anume prin valoare, caz în care
funcţiei i se transmite valoarea argumentului şi respectiv prin referinţă, caz în care
funcţiei i se transmite adresa argumentului.
Astfel, un apel de forma:
f(x)
este un apel prin valoare deoarece funcţiei f i se transmite valoarea argumentului x, iar un
apel de forma:
g(&x)
este un apel prin referinţă deoarece funcţiei g i se transmite adresa variabilei x.
Antetele celor două funcţii vor fi de forma:
void f(int x)
respectiv:
void g(int *pi)
Un tablou poate fi transferat unei funcţii în două moduri.
Să presupunem că vrem să construim o funcţie care tipăreşte elementele unui şir la ieşirea
standard. O versiune a acestei funcţii ar putea fi de forma:

void printsir(float sir[],int dim)


{
int i;
for (i=0; i<dim; i++)
printf("%f\n",sir[i]);
}
Funcţiei i s-a transferat tabloul sir şi dimensiunea acestuia.

O a doua versiune a acestei funcţii poate fi de forma:

void printsir(float *p,int dim)


{
int i;
for (i=0; i<dim; i++)
printf("%f\n",*p++);
}

În acest caz, funcţiei i se transferă un pointer şi anume tocmai numele tabloului care
trebuie tipărit.
8
Curs 7 Pointeri

Program exemplu: ordonarea unui şir folosind pointeri şi funcţie care returnează un
pointer la şirul ordonat

#include <stdio.h>

void printsir(float *p, int dim)


{
int i;
for (i=0; i<dim; i++)
printf("%f\n",*p++);
}

float* ordsir(float *p0, int dim)


{
/*functia ordsir returnează un pointer la tipul float şi are doi parametri,
din care unul este un pointer la tipul float şi unul de tip întreg*/
float *q,*p=p0,t;
//p se va modifica in interiorul acestei functii, de aceea se pastreaza
//pointerul initial p0
int k,i;

do //ordonare folosind metoda bulelor


{
k=1;
p=p0;
while(p<(p0+dim-1))
{
q=p+1;
if(*p<*q)
{
t=*p;
*p=*q;
*q=t;
k=0;
}
p++;
}
}
while(k==0);

return p0;
}

void main()
{

float x[100];
int n,i;

scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("x[%d]= ",i);
scanf("%f",&x[i]);
}
ordsir(x,n);

9
Curs 7 Pointeri

printsir(x,n);
}

Un alt exemplu în care este necesar ca apelul funcţiei trebuie să se facă prin valoare este
acela al unei funcţii care să realizeze interschimbarea a două valori.

Program exemplu: funcţii swap cu apel prin valoare şi prin referinţă


#include <stdio.h>

void sc1(double v1, double v2)


{
double t;
t=v1;v1=v2;v2=t;
}

void sc2(double *i, double *j)


{
double t;
t=*i;*i=*j;*j=t;
}

void main()
{

double* pd;
double a,b;
printf("Adresa la care incepe codul functiei sc1 este: %X\n",sc1);
scanf("%lf%lf",&a,&b);
sc1(a,b);
printf("\nApelul functiei sc1:\na= %lg\tb=%lg",a,b);
sc2(&a,&b);
printf("\nApelul functiei sc2:\na= %lg\tb=%lg",a,b);
}

Un exemplu de output al acestui program este:

După cum se vede, funcţia sc1 nu interschimbă valorile variabilelor din funcţia apelantă ci
numai copii ale acestora. Pe de altă parte, funcţia sc2, al cărui apel se face prin referinţă,
realizează interschimbarea celor două valori. Aşadar, argumentele de tip pointer permit
funcţiilor să acceseze şi să modifice valorile variabilelor funcţiei apelante.

Exerciţiu
Să se scrie o funcţie care să returneze 1 dacă se citeşte un număr de la tastatură şi să
returneze 0 în caz contrar. Vezi funcţia getint din [Ker88].

10
Curs 7 Pointeri

Pointeri la funcţii
La fel ca şi numele unui tablou şi numele unei funcţii este un pointer la funcţia
respectivă, adică numele funcţiei este o reprezentare în mod intern a adresei la care începe
codul funcţiei. Pointerii la funcţii se utilizează pentru transmiterea funcţiilor ca parametri
ai altor funcţii.

etc....

Exemplu
Dacă dorim ca funcţia f să apeleze funcţia g sub forma f(g), funcţia g având antetul:
float g(int x)
atunci antetul lui f trebuie să fie de forma:
double f (float (*) (int))
Exemplu
//Funcţia bisect are ca parametru o altă funcţie f

//======================= Functia f =========================


double f(double x)
{
return x-sqrt(2);
}

//======================Functia bisect =======================


double bisect(double inf, double sup, double (*pf)(double))
{
double c,sol;
if((*pf)(inf)==0) return inf;
if((*pf)(sup)==0) return sup;
if((*pf)(inf)*(*pf)(sup)>0)
{
printf("\n\a\aNu exista sol sau exista sol. multiple");
getch();
exit(1);
}
do
{
c=(inf+sup)/2.0;
if((*pf)(c)==0) return c;
if((*pf)(inf)*(*pf)(c)<0)
sup=c;
else
inf=c;
}
while(fabs((*pf)(c)) >= eps);
return c;
}

//=================Apelul functiei bisect=======================


s=bisect(A,B,f);

Tablouri de pointeri; pointeri la pointeri


11
Curs 7 Pointeri

Deoarece pointerii sunt variabile şi ei pot fi stocaţi în tablouri. Un astfel de tablou de


pointeri ar putea fi util la scrierea unui program care să sorteze într-o anumită ordine
liniile unui text de input (vezi [Ker88], pag.91).
Un tablou de pointeri se declară la fel ca orice tablou, diferenţa constând în faptul că
elementele tabloului sunt pointeri.

Exemple
char *pr[1000]; //tablou cu maxim 1000 de elemente; elementele sunt pointeri la tipul char
int *ptr[100]; //tablou cu maxim 100 de elemente; elementele sunt pointeri la tipul int

Structuri de date dinamice în C


Structurile de date dinamice sunt structuri care îşi modifică dimensiunea prin
alocarea şi dealocarea memoriei dintr-o zonă specială de memorie numită "heap". Acestea
sunt extrem de importante în C deoarece permit programatorului să controleze exact
consumul de memorie al unui program.
Structurile dinamice alochează blocuri de memorie din heap în funcţie de necesităţi, iar
managementul acestor blocuri se face folosind pointeri. Când anumite structuri de date nu
mai sunt folosite, ele vor returna blocurile corepunzătoare memoriei heap, pentru a fi
refolosite, realizându-se astfel o utilizare eficientă a memoriei.
Memoria heap este o zonă aparte de memorie corespunzătoare unui program, folosită
pentru crearea şi distrugerea structurilor de date care au timp de viaţă limitat.
După cum este schematizat în Figura 1, conceptual, memoria alocată unui program este
împărţită în patru segmente: segmentul corespunzător codului executabil, segmentul
corespunzător variabilelor globale, segmentul de memorie stivă şi segmentul memoriei
heap.

Fig.1

Segmentul cod conţine biţii corespunzători instrucţiunilor programului şi acest


segment de memorie este determinat de către compilator şi link-editor. Tot aici sunt
incluse funcţiile de bibliotecă ce sunt adăugate programului. In general, această memorie
este de tip "read-only".
Segmentul de memorie corespunzător variabilelor globale ale programului, numit şi
segment de date statice, este folosit pentru stocarea variabilelor globale.
Segmentul memoriei stivă păstrează variabilele locale şi parametrii unei funcţii. Un
cadru stivă este creat la apelul unei funcţii şi este eliberat la încetarea execuţiei funcţiei
12
Curs 7 Pointeri

respective. La fiecare apel al unei funcţii, variabilele sale locale şi parametrii săi sunt
împinse în memoria stivă. La ieşirea din funcţie, aceste variabile şi parametri sunt eliminaţi
din stivă. Din această cauză, mărimea memoriei stivă a unui program, chiar dacă are o
valoare maximă, se modifică continuu în timpul execuţiei programului.
Memoria heap este o zonă de memorie complet separată, controlată de către un
manager de memorie de tip "run-time", care face managementul memoriei în timpul
execuţiei programului. De fapt acest manager de memorie este o funcţie de bibliotecă
adăugată programului aflat în execuţie.
Dacă un program foloseşte structuri dinamice în memoria heap, atunci când aceste
structuri nu mai sunt folosite, managerul de memorie va marca blocurile de memorie
corespunzătoare acestora ca fiind libere. Atunci când un program de aplicaţii îşi încheie
execuţia, sistemul de operare va descărca acel program din memorie, precum şi variabilele
sale globale şi stiva sa, astfel încât un alt program va putea folosi spaţiul corespunzător de
memorie. În acest fel, memoria computerelor este în permanenţă reciclată şi refolosită de
către programe pe măsură ce acestea se află în execuţie şi se încheie.
Memoria heap este disponibilă programelor de aplicaţii în timpul execuţiei acestora
folosind funcţiile malloc şi free. Acestea permit ca unui program să i se aloce memorie
exact atunci când este nevoie în timpul execuţiei programului.

Funcţiile Malloc şi free


Să presupunem că dorim alocarea unei anumite zone de memorie în timpul
execuţiei unui program. Acest lucru se face cu ajutorul funcţiei malloc, care va aloca un
bloc de memorie din heap. Sistemul de operare va rezerva acel bloc de memorie pentru
program, bloc care va putea fi folosit în orice fel în program. Când acel bloc nu mai este
necesar, el va fi returnat sistemului de operare pentru a fi folosit de către alte aplicaţii.
Eliberarea (dealocarea) unui bloc de memorie se face folosind funcţia free.
Funcţia malloc îşi are prototipul în <stdlib.h> şi în <alloc.h> şi este de
forma:
(tip *) malloc(dimensiune)
unde dimensiune reprezintă mărimea blocului de memorie alocat în octeţi, iar tip
reprezintă tipul de date spre care va pointa pointerul returnat de către funcţie.
În caz de succes, funcţia malloc returnează un pointer la primul octet al primei locaţii a
blocului de memorie alocat, iar în caz de eroare returnează pointerul NULL. Tot pointerul
NULL este returnat şi când dimensiune este egală cu zero.
Funcţia free eliberează un bloc de memorie alocat cu funcţia malloc şi apelul
acesteia se face cu un parametru care reprezintă pointerul la originea blocului de memorie
care se eliberează.
Mai jos este prezentat un program exemplu în care se folosesc funcţiile malloc şi free.
Până acum, când am lucrat cu tablouri, acestea au fost declarate de o anumită dimensiune
maximă DIM, iar programul putea lucra cu tablouri având maxim DIM elemente. De cele
mai multe ori însă, programul cere numărul de elemente al tabloului, n, care este de obicei
mai mic decât DIM.
În programul exemplu de mai jos, memoria necesară tabloului va fi alocată după citirea
numărului său de elemente şi va fi exact cât este necesară.

Program exemplu: folosirea funcţiilor malloc şi free

13
Curs 7 Pointeri

//Se aloca dinamic un tablou de n elemente;


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

void main()
{
int n,i;
float *p,*p0;

printf("Introduceti dimensiunea tabloului: ");


scanf("%d",&n);
p=(float *)malloc(n*sizeof(float));
p0=p;
if (p==NULL)
{
printf("Eroare: Memorie nedisponibila\n");
exit(1);
}
for(i=0;i<n;i++)
{
*p=i;
printf("\n%X\t%g",p,*p);
p++;
}
free(p0);
}

Un exemplu de output al acestui program este:

14