-
CURS 7
Pointeri - tipuri speciale de variabile sau constante care au ca
valori adrese ale unor alte variabile,
constante, functii, structuri de date (adrese ale unor locaţii
de memorie)
- permit calcule cu adrese
- specifice limbajelor de asamblare
- 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.
- sunt în mod special utili la alocarea dinamică a memoriei şi
la apelul prin referinţă.
-
Pointerii - păstrează adresa începutului locaţiei în care este
stocată o anumită valoare. - pachet compus din două părţi:
- pointerul însuşi - care are ca valoare adresa primului octet
al locaţiei de memorie în care este
stocată o variabilă (sau constantă) - tipul valorii stocate în
locaţia de memorie la începutul căreia pointează
- spune computerului câtă memorie să citească după adresa la
care pointează şi cum să o interpreteze
-
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
Limbajul C foloseşte pointerii în trei moduri:
În cazul unui pointer la o funcţie, tipul pointerului va fi
tipul de dată returnat de către funcţie, iar valoarea sa va fi
adresa la care începe codul funcţiei respective
Pentru lucrul cu pointeri, limbajul C oferă doi operatori:
&
- &x înseamnă adresa variabilei x (de fapt adresa primului
octet al locaţiei de memorie în care este stocată variabila x)
* - *p1 valoarea stocată în locaţie de memorie la care pointează
pointerul p1.
-
Declararea şi iniţializarea pointerilor
tip *nume_pointer; sau tip* nume_pointer; tip - tip de date de
bază sau tip utilizator nume_pointer - identificator folosit pentru
pointerul care se declară
Declarare
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
struct agenda
{
char nume[20];
char tel[15];
};
struct agenda *psa;
//psa este un pointer la date de tip structură agenda.
Exemple
-
double a, *pd;
a=8.406;
pd=&a;
Exemple
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
float pi, *p;
p=π
*p=3.14159; // se atribuie lui pi valoarea 3.14159
int* p1, p2, p3; //numai p1 este pointer
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!!!
Iniţializare
- precizarea adresei unui obiect definit în memorie. - p -
numele unui pointer => *p reprezintă valoarea de la adresa la
care "pointează"
pointerul p.
-
Program exemplu
#include
#include
int 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);
return 0;
}
Atenţie la iniţializarea pointerilor !!! + referinta nevalida +
pointeri nuli
Exemplu //testare pointer
int* pi=0;
...
if(pi!=0)
n=*pi; //se foloseşte pointerul dacă pointează la o adresă
legală (validă)
-
#include
#include
void main()
{
int* pi=NULL;
int n;
scanf("%d",&n);
if(n
-
Operaţii cu pointeri
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 (4
octeti)
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.
Exemplu Fie declaraţiile: int n,*pin=&n;
float x,*pre=&x;
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 int num , *ptr1 ,*ptr2 ;
num=ptr1-ptr2; // rezultatul este un intreg, egal cu numarul de
obiecte (locatii de memorie) dintre valorile pointerilor
d) Compararea pointerilor
Comparaţiile logice !=, ==, = sunt valabile şi în cazul
pointerilor.
Exemplu // functie pentru determinarea lungimii unui sir de
caractere
int lsir(char *pc)
{
int i;
for(i=0;*pc!=NULL;pc++)
i++;
return i;
}
int main() { char sir[50]; gets(sir); printf(“Lungimea sirului
este %d”,lsir(sir)); return 0; }
Adunarea pointerilor nu este permisa!!! pmed=(pin+pfin)/2; //
Invalid pointer addition pmed=pin+(pfin-pin)/2; este o instructiune
corecta (la pin se adauga un intreg)
-
Pointeri şi tablouri Pointerii sunt intim legaţi de tablouri -
numele unui tablou este 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;
pc=text;
putchar(*pc); //se va tipări caracterul A
pc+=4;
putchar(*pc); //se va tipări caracterul 1
2. char sir[10]=“DevC++";
char *p;
p=sir;
while(*p)
putchar(*p++); //se va tipări sirul “DevC++”
3. double a[15],t;
t=*(a+3); //este echivalent cu t=a[3];
-
Program exemplu: adunarea elementelor unui şir folosind pointeri
#include
#include
#include
#include
int main()
{
float x[100], *y,v;
double s=0.0,med;
int i;
//genereaza aleator elementele sirului x
for(i=0;i
-
fără pointeri: #define DIM 50 int x[DIM],i; for(i=0;i
-
Apelul prin referinţă utilizând parametri de tip pointer
Apelul funcţiilor prin valoare funcţiei i se transmite valoarea
argumentului
Apel: f(x) Antet: void f(int x) void printsir(float sir[], int
dim)
{
int i;
for (i=0; i
-
Program exemplu: ordonarea unui şir folosind pointeri şi funcţie
care returnează un
pointer la şirul ordonat
#include
void printsir(float *p, int dim)
{
int i;
for (i=0; i
-
Program exemplu: funcţii swap cu apel prin valoare şi prin
referinţă
#include
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;
}
int main()
{
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);
return 0;
}
sc1 nu interschimbă valorile variabilelor din funcţia apelantă
ci numai copii ale acestora! sc2 schimba valorile variabilelor din
functia apelanta.
-
Tema 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 B.W.
Kernighan and D.M. Ritchie, The C
Programming Language, pag. 81
Pointeri la funcţii
-numele unei funcţii este un pointer la funcţia respectivă
-numele funcţiei este o reprezentare în mod intern a adresei la
care începe codul funcţiei
- se utilizează pentru transmiterea funcţiilor ca parametri ai
altor funcţii.
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)= eps);
return c;
}
//=================Apelul functiei
bisect=======================
s=bisect(A,B,f);
-
Structuri de date dinamice în C
- structuri care îşi modifică dimensiunea prin alocarea şi
eliberarea (dealocarea) memoriei dintr-o zonă specială de memorie
numită "heap“ - permit programatorului să controleze exact consumul
de memorie al unui program. - managementul structurilor dinamice se
face folosind pointeri
=> 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.
Memoria heap - 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. - este
disponibilă programelor de aplicaţii în timpul execuţiei acestora
folosind funcţiile malloc şi free.
“read only”
-
Memoria stack (stiva) - regiune speciala de memorie care
stocheaza variabilele temporare (locale) create de functii
(inclusiv de catre functia main()) - variabilele din memoria
“stack” dispar la iesirea din functie - este limitata (dimensiunea
ei depinde de sistemul de operare)
=> erori de tipul “stack overflow” - managementul ei este
facut de catre CPU (nu necesita managementul programatorului) -
acces foarte rapid - “bug”: incercarea de a accesa o variabila
locala a unei functii care a iesit din executie, de catre o alta
functie.
Memoria heap - variabilele pot fi accesate global, de catre
orice functie - managementul ei trebuie facut de catre programator
altfel => “memory leak” deoerece memoria alocata de o anumita
aplicatie nu va deveni disponibila pentru alte aplicatii - se
acceseaza folosind pointeri - nu este limitata d.p.d.v. al
numarului de date stocate (evident, e limitata fizic) - accesul
este mai putin rapid decat in cazul memoriei “stack” - recomandat
sa fie folosita cand se doreste stocarea unui tablou sau a unei
structuri de dimensiuni mari
-
Funcţia malloc - prototipul în şi în - alocă o zonă contiguă de
memorie şi este de forma: (void *) malloc(dimensiune)
dimensiune - mărimea blocului de memorie alocat în octeţi. -
returnează un pointer la primul octet al primei locaţii a blocului
de memorie alocat - tipul pointerului se va converti la tipul dorit
al datelor care se vor stoca: int *pi,n; pi=(int *) malloc
(n*sizeof(int));
- in caz de eroare, sau daca dimensiune este egală cu zero,
returnează pointerul NULL.
Funcţia free - eliberează un bloc de memorie alocat cu funcţia
malloc () - apelul acesteia se face cu un parametru care reprezintă
pointerul la originea blocului de memorie care se eliberează.
-
Program exemplu: folosirea funcţiilor malloc şi free
//Se aloca dinamic un tablou de n elemente;
#include
#include
int main()
{
int n,i;
float *p,*p0;
printf("Introduceti dimensiunea tabloului: ");
scanf("%d",&n);
p=(float *)malloc(n*sizeof(float));
//(float *) converteste pointerul void
//returnat de catre malloc intr-un pointer la tipul float
p0=p;
if (p==NULL)
{
printf("Eroare: Memorie nedisponibila\n");
exit(1);
}
for(i=0;i