Pointeri
Orice variabila are: continut
adresa (de multe ori e nevoie de lucrul cu adresele variabilelor)
Def:
Pointerii sunt variabile sau constante care au ca valori adrese ale unor variabile, adica adrese ale unor locatii de memorie.
Pointerii permit calcule cu adrese
folositi pt. scrierea unor programe mai eficiente d.p.d.v. al timpului de executie
memoriei utilizate
Tipuri de pointeri:
D.p.d.v. al continutului zonei de memorie adresate:
- pointeri de date (obiecte) - contin adresa unei variabile din memorie;
- pointeri generici (numiti si pointeri void) - contin adresa unui obiect oarecare, de tip neprecizat;
- pointeri de functii - contin adresa codului executabil al unei functii.
Utilizare in C pentru a crea structuri dinamice de date construite din blocuri de memorie
pentru a opera cu parametrii transferati functiilor la apelul acestora
pentru a accesa informatia stocata in tablouri.
Declararea si initializarea variabilelor pointer
Pentru pointerii de date:
tip nume_pointer;
in care tip (care se mai numeste si tip de baza sau tip utilizator) reprezinta tipul valorii care se gaseste la adresa pastrata in nume_pointer, deci indica tipul de variabile catre care poate sa pointeze pointerul respectiv.
Exemple:
int a, b, *p, *q; // p, q pointeaza catre variabile de tip int
float c, d, *p1, *q1; // p1, q1 pointeaza catre variabile de tip float
Obs:
Caracterul "*" poate fi plasat in mai multe feluri, a. i. de declaratiile urmatoare sunt echivalente:
int *p;
int * p;
int* p;
Pentru pointerii generici:
void nume_pointer;
Un pointer generic nu are asociat un tip de date precis. De aceea, in cazul unui pointer vid, dimensiunea zonei de memorie adresate si interpretarea informtiei nu sunt definite.
Initializarea pointerilor consta in precizarea adresei unui obiect definit in memorie. Daca p este numele unui pointer, atunci *p reprezinta valoarea de la adresa la care 'pointeaza' pointerul p.
Variabilele de tip pointer pot fi initializate la declarare. De asemenea, li se pot atribui adrese pe parcurs.
Exista doi operatori unari care permit utilizarea variabilelor pointer:
q & - operatorul adresa (de referentiere) - pentru aflarea adresei din memorie a unei variabile;
Ex
Se
atribuie pointerului p adresa variabilei x ( p pointeaza catre x)
int x;
p = &x;
q - operatorul de indirectare (de dereferentiere) - care furnizeaza valoarea din zona de memorie spre care pointeaza pointerul operand.
Ex
*p reprezinta valoarea variabilei x.
Obs: Operatorii * si & sunt complementari.
Exemplu:
int a=5, b=10, v[10];
int *p; // p pointeaza catre variabile de tip intreg
p = &a; // p pointeaza la a
b = *p; // b are valoarea de la adresa lui a , adica 5
*p = 0; // a are acum valoarea 0
p = &v[0]; // p pointeaza acum la v[0], deci are ca valoare adresa elementului v[0]
*p = 10*b; // v[0] are acum valoarea 10*5 = 50
Obs: p este de tipul int * (p este de tip pointer spre int)
1. declaratia int *p poate fi interpretata: *p este de tipul int (continutul adresei spre care
pointeaza variabila p este de tipul int)
2. atribuirea a = 10 este echivalenta cu p = &a; *p = 10;
Erori in cazul utilizarii pointerilor
1. absenta initializarii - eroarea cea mai frecventa in cazul utilizarii pointerilor
Astfel, in urma unei declaratii si initializari de forma:
int *p;
*p = 2;
pointerul p este neinitializat => valoarea 2 va fi scrisa la o adresa de memorie necunoscuta => coruperea memoriei, rezultare eronate sau imprevizibile, terminarea
fortata a programului
Deci, inainte de dereferentierea pointerilor acestia trebuie sa fie initializati cu adrese valide.
2. o referinta nevalida care poate fi produsa printr-o atribuire de forma:
p1=p2;
unde p1 si p2 sunt pointeri, iar p2 este neinitializat.
Orice referinta la *p1 va produce o referinta nevalida.
referinta la pointeri nuli. Astfel, daca p este un pointer la tipul intreg, secventa:
p = 0;
*p = 10;
este nevalida deoarece nu exista nici un bloc de date la care sa pointeze p.
Astfel, incercarea de a citi sau scrie acel bloc de date conduce la o referinta nevalida.
Operatii cu pointeri
1) Compararea pointerilor
Comparatiile logice = =, !=, <, >, <=, >= sunt valabile si in cazul pointerilor.
Exemplu:
int p1, p2;
if (p1<p2) cout<<"p1="<<p1<<"<"<<"p2="<<p2<<'n';
else cout<<"p1="<<p1<<">="<<"p2="<<p2<<'n';
De asemenea, se poate realiza compararea unui pointer cu valoarea nula,
pentru a verifica daca acesta adreseaza un obiect. Compararea se face cu
Exemplu:
if (!p1)
. . . . . ; // pointer nul
else . . . . ; // pointer nenul
2) Adunarea si scaderea unui intreg dintr-un pointer
Rezultatul operatiei p+n, respectiv p-n, unde p este un pointer si n este un intreg este: p+n*r, respectiv p-n*r, unde r reprezinta numarul de octeti folositi pentru pastrarea in memorie a datelor de tipul celor spre care pointeaza p.
Un caz particular al adunarii sau scaderii dintre un pointer de date si un intreg (n=1) il reprezinta incrementarea si decrementarea unui pointer de date:
Fie declaratia:
int *p;
Instructiunile:
++p; si p++; respectiv: --p; si p--;
maresc, respectiv micsoreaza valoarea lui p cu o unitate.
3) Scaderea pointerilor
In limbajul C nu este permisa adunarea pointerilor, dar este permisa scaderea a doi pointeri de obiecte de acelasi tip, rezultatul fiind o valoare intreaga care reprezinta numarul locatiilor de memorie aflate intre adresele la care pointeaza cei doi pointeri care se scad.
Exemplu:
Fie p un pointer spre elementul v[i] al tabloului v si q un pointer spre elementul v[i+n] al aceluiasi tablou. Atunci diferenta q-p are valoarea n.
Pointeri si tablouri
Numele unui tablou este un pointer constant (valoarea lui nu poate fi schimbata) care are ca valoare adresa primului element din tablou.
Diferenta dintre numele unui tablou si o variabila pointer este aceea ca unei variabile de tip pointer i se pot atribui valori la executie, lucru imposibil pentru numele unui tablou. Acesta tot timpul are ca valoare adresa primului sau element.
Tinand cont de acest lucru si de operatiile aritmetice aplicabile variabilelor pointer, orice operatie care se face folosind indicii tablourilor poate fi facuta, chiar mai rapid, prin folosirea pointerilor.
Obs: legatura dintre pointeri si tablouri unidimensionale poate fi evidentiata astfel:
Fie v un tablou (vector) cu 10 elemente intregi (fie declaratia: int v[10])
v[0] v[1] ........ v[9]
v=&v[0] v+1=&v[1] . . . v+9=&v[9]
v=v[0] (v+1)=v[1] . . . (v+9)=v[9]
In concluzie, deoarece numele tabloului este un pointer constant, avem echivalentele:
v+i & v[i]
v[i] (v+i)
Deci variabilele indexate pot fi transformate in expresii cu pointeri si avem echivalentele:
Adresa |
Valoare |
||
Notatie indexata |
Notatie cu pointeri |
Notatie indexata |
Notatie cu pointeri |
&v |
v |
v[0] |
*v |
&v[1] |
v+1 |
v[1] |
*(v+1) |
&v[i] |
v+i |
v[i] |
*(v+i) |
&v[n-1] |
v+n-1 |
v[n-1] |
*(v+n-1) |
Intr-adevar:
v[i]=*(v+i)=*(i+v)=i[v]
Obs: in C, daca v este un tablou de intregi avem
urmatoarea echivalenta:
v[i] º i[v]
Exemplu: initializarea elementelor unui tablou:
int x[10], *pi; for(pi=x;pi<&x[10];pi++) *pi=0; int x[10], i; for(i=0;i<10;i++) x[i]=0;
fara
pointeri: cu pointeri:
Exemplu: adunarea elementelor unui tablou cu 10 componente intregi folosind pointeri:
#include <iostream.h>
void main()
Exemplu: Sa se citeasca elementele unui vector cu maxim 10 elemente intregi si sa se inlocuiasca elementul minim din vector cu o valoare introdusa de la tastatura, folosind lucrul cu pointeri.
#include <iostream.h>
void main()
aflarea elementului minim din
vector si a pozitiei acestuia Am utilizat: v=v[0] (v+i)=v[i]
min=v;
indice=0;
for (i=0; i<n; i++)
if (min>=(v+i))
citirea valorii cu care se va inlocui elementul minim si inlocuirea
acestuia
cout<<"valoarea de inlocuire:"; cin >> val;
(v+indice)=val;
afisarea noului
vector
for (i=0; i<n; i++)
cout<<(v+i)<<" ";
Obs: legatura dintre pointeri si tablouri multidimensionale poate fi evidentiata astfel:
Fie A un tablou bidimensional(matrice) cu 4 linii si 5 coloane (fie declaratia: int A[4][5])
Matricea A are
4 linii si 5 coloane. Numele tabloului bidimensional,
A, refera intregul tablou; A[0] refera prima linie din tablou; A[0][0]
refera primul element al
tabloului.
A[
A[
A[
A[
In concluzie, deoarece numele tabloului este un pointer constant, avem echivalentele:
A = (A + 0) A[0]
A=(A)(A[0])=(A[0]+0) A[0][0]
*(*(A+i)+j) *(A[i]+j) A[i][j]
Exemplu: Sa se citeasca elementele unei matrici cu maxim 10 randuri si 10 coloane cu elemente intregi si sa se afiseze, folosind lucrul cu pointeri.
#include <iostream.h>
void main()
afisarea elementelor matricii
utilizand *(*(a+i)+j)=a[i][j]
for (i=0; i<n; i++)
Tablouri de pointeri
Deoarece pointerii sunt variabile si ei pot fi stocati in tablouri. Un tablou de pointeri se declara la fel ca orice tablou, diferenta constand in faptul ca elementele tabloului sunt pointeri.
Obs: tablourile de pointeri se utilizeaza frecvent pentru a prelucra succesiuni de siruri de caractere. Intr-un program se intalnesc adesea siruri de caractere care trebuie tratate in mod unitar. Atunci se poate defini un tablou de pointeri, fiecare element al tabloului fiind un pointer spre un astfel de sir de caractere.
Efectul unei astfel de declaratii
este alocarea de memorie pentru un tablou cu "dim" componente care pastreaza adrese ale unor valori
de tipul "tip".
Modul general
de declarare a unui tablou de pointeri:
tip nume_tablou[dim];
Initializarea componentelor tabloului se va face prin atribuiri de adrese de variabile de acelasi tip cu tipul de baza declarat:
nume_tablou[indice]=&variabila;
Pointeri la pointeri
O secventa de forma:
tip **nume_variabila;
declara variabila "nume_variabila" ca pointer catre un pointer de tipul "tip".
Valoarea variabilei nume_variabila va putea fi o adresa la care se pastreaza adresa unei valori de tipul "tip".
Valoarea de tipul "tip" se va putea obtine prin apelul:
**nume_variabila.
Exemplu: Functia main( ) urmatoare acceseaza aceeasi zona de memorie in care este depozitata valoarea reala 10 in moduri diferite, cu ajutorul a trei variabile p, q, r.
#include <iostream.h>
void main()
|