Sunteți pe pagina 1din 49

Programare orientata pe obiecte

Curs 3 – Pointeri

C
++{};
C++ Acest curs
› Definitie
› Initializare
› Tip de date
› Dereferentiere
› Pointeri si const
› Pointer nul
› Pointer la pointer
C++ C Pointers Explained
Laurentiu Petrea
Senior Software Architect
ACCESS - Browser Product Department
C++ Definitie
› Variabilă utilizată pentru a stoca adresa de memorie a unei
date sau functie.
.
.
.

0x00394768 x = 40

.
.
› Declaratie .

Datatype* variableName;
C++ Initializare

int x = 40;
int *ptr;
int *ptr;
ptr = (int *) malloc(sizeof(int) * count);
ptr = &x;

Memory from
0x00394768 x = 40 0x00394768 heap

0x0012FF60 ptr = 0x00394768 0x0012FF60 ptr = 0x00394768


C++ Dimensiune
#include <stdio.h>

int main()
{
char c_var; Dimensiune char pointer = 4 value = 4061659
int i_var; Dimensiune integer pointer = 4 value = 4061644
double d_var; Dimensiune double pointer = 4 value = 4061628
char *char_ptr;
int *int_ptr;
double *double_ptr;
char_ptr = &c_var;
int_ptr = &i_var;
double_ptr = &d_var;
printf(“Dimensiune char pointer = %d value = %u\n", sizeof(char_ptr), char_ptr);
printf(“Dimensiune integer pointer = %d value = %u\n", sizeof(int_ptr), int_ptr);
printf(“Dimensiune double pointer = %d value = %u\n",
sizeof(double_ptr),double_ptr);
}
C++ Dereferentiere
I
int x = 10; x = 10 0x12345678

int *ptr = &x; ptr = 0x12345678 0x87654321

int y = *ptr; y = 10 0x21435687

II
int *ptr = 10; ptr = junk value 0x32142658

int y = *ptr; // In acest punct programul se va bloca


incercand sa acceseze o locatie de memorie nevalida
C++ Transferul parametrilor - valoare
void calling_function(void)
Variabile locale ale functiei apelante
{
int t1, t2, t3;
t1 = 10;
t2 = 20; t1 = 10
t3 = called_function(t1, t2); t2 = 20
} t3 = 30

int called_function(int x, int y)


{ Variabile locale ale functiei apelate
int t1, t2, t3;
t2 = x;
t1 = y; t1 = 20
t3 = t1 + t2; t2 = 10
return t3; t3 = 30
}
C++ Transferul parametrilor - adresa
void calling_function(void)
{
int t1; Variabile locale ale functiei apelante
int *t2;
t1 = 10;
t2 = called_function(&t1);
} t1 = 10 ---0x12345678
t2 = 0x87654321
int* called_function(int* x)
{
int t2;
Variabile locale ale functiei apelate
int *t1;
int *t3;
t1 = x;
t1 = 0x12345678
t2 = 10;
t2 = 10
t3 = (int*)malloc(sizeof(int)); t3 = 0x87654321
*t3 = *t1 + t2;
return t3;
}
C++ Pointeri şi constante
› Prin folosirea unui pointer sunt implicate două obiecte: pointerul însuşi şi
obiectul pointat
› Precedarea declaraţiei unui pointer cu un const face obiectul (nu pointerul)
constant.
› Declararea unui pointer constant presupune utilizarea *const şi nu doar *.
void f1(char* p){
char s[] = "Gica";
const char* pc = s; //pointer catre constant
pc[3] = ´g´; //error: pc pointeaza catre constant
pc = p; //ok
char *const cp = s; //pointer constant
cp[3] = ´a´; //ok
cp = p; //error: cp este constant
const char *const cpc = s;//pointer constant catre constant
cpc[3] = ´a´; //error: cpc pointeaza catre constant
cpc = p; //error: cpc este constant
}
C++ Pointeri si const
› Operatorul care face un pointer constant este *const. Nu există
declaratorul const*. Dacă este întâlnit este considerat că face parte din
tipul de bază.
char *const cp; //pointer constant catre char
char const* pc; //pointer catre char constant
const char* pc2; //pointer catre char constant

› Modificatorul const se foloseşte frecvent la funcţii, la declararea


parametrilor formali de tip pointer sau referinţe, pentru a interzice
funcţiilor respective modificarea datelor spre care pointează parametrii
respectivi.
char* strcpy(char* p, const char* q); //nu poate modifica (*q)

› Atentie ! - Protecţia datelor cu ajutorul const nu este totală pentru toate


compilatoarele
C++ Const pointer

int main() Mem addr Valori


{
int num1 = 10; 0x123456 num1 = 10
int num2 = 20;
int* const ptr1 = &num1; //initializarea const 0x654321 num2 = 20
ptr
ptr1 = &num2; // nu se poate 0x111122 const ptr1 = 0x123456
printf(“Valoarea stocata la adresa =
%d\n",*ptr1); 0x123456 num1 = 10
}
0x654321 num2 = 20
x
0x111122 const ptr1 = 0x123456
C++ Pointer to const
int main() Mem addr Valori
{
int num1 = 10;
0x123456 num1 = 10
const int* ptr1;
int* ptr2;
ptr1 = &num1;
*ptr1 = 20; //nu se poate 0x111122 const ptr1 = 0x123456
num1 = 20; //se poate
printf(“Valoarea stocata la adresa =
0x123456 num1 = 10
%d\n",*ptr1);
}

0x111122 const ptr1 = 0x123456


C++ Const pointer to const

int main()
{
int num1 = 10;
int num2 = 20;
const int* const ptr1 = &num1;
*ptr1 = 20; //nu se poate - pointer catre const
num1 = 20; //se poate
ptr1 = &num2; //nu se poate - pointer constant
printf("Valoarea stocata la adresa = %d\n",*ptr1);
}
C++ Pointeri către un obiect de tip necunoscut
› Într-o secvenţă de cod low level este necesar a transmite
adresa unei locaţii de memorie fără a cunoaşte tipul
datei/datelor stocate în acea locaţie.
› Pentru asta se foloseşte pointerul către tipul de date
necunoscut void*
› Un pointer către orice tip de dată poate fi atribuit unei
variabile de tip void*.
› Un pointer pe funcţie sau la un membru al unei clase nu
poate fi atribuit unei variabile de tip void*.
› Un pointer de tip void* poate fi atribuit/comparat unui/cu alt
pointer de tip void*.
› Un pointer de tip void* poate fi convertit explicit la orice tip
de date.
C++ Pointeri către un obiect de tip necunoscut
int f(int *p)
{
void *pv = p;//conversie implicita
*pv;//eroare, un pointer pe void* nu poate fi dereferentiat
++pv;//eroare, un pointer pe void* nu poate fi incrementat

int *pi = (int*)pv; //conversie explicita la int*


}

› Sfaturi:
– A nu se utiliza pointeri convertiţi explicit către un alt tip de date decât cel iniţial
– A se utiliza pointeri la void* doar pentru a fi:
› transmişi ca parametru funcţiilor iar tipul acestora nu este necesar a fi cunoscut;
› returnat de funcţii.
– A se converti explicit pointerii la void* atunci când sunt folosiţi;
C++ Pointeri către un obiect de tip necunoscut
› Având în vedere că pointerii la void* sunt folosiţi în
programarea low level, acolo unde resursele hardware sunt
manipulate, apariţia/utilizarea unor astfel de pointeri la nivel
high level trebuie privită cu suspiciune deoarece cu
siguranţă se datorează unor erori de proiectare.

Pointer nul
 nullptr reprezintă un pointer nul literal, un pointer ce nu
pointează către ceva.
 Poate fi atribuit oricărui tip de pointer
int ∗pi = nullptr;
double ∗pd = nullptr;
int i = nullptr; // eroare : i nu este un pointer
C++ Pointer nul
› Înainte de a fi introdus nullptr, era folosit zero (0) ca notaţie pentru
pointer nul. int *x = 0;
› Nici un obiect nu este alocat la adresa 0, iar zero (0) este cea mai
comună reprezentare a nullptr.
› Zero (0) este un int. Totuşi în conversiile standard 0 este folosit ca o
constantă a unui pointer sau pointer la membru.
› O altă reprezentare a unui pointer nul a fost macrodefiniţia NULL.
int *p = NULL;

› Totuşi există diferenţe in definiţia NULL în diferite implementări:


#define NULL 0
#define NULL 0L
#define NULL (void*)0 /*C style*/
int ∗p = NULL; //eroare: nu poate fi atribuit un void* la un int*
C++ Pointer to pointer
› Pointer ce contine adresa unui al pointer ce contine adresa
unei variabile.

Mem addr Valori

int a = 10; 0x123456 a = 10


int *ptr = &a;
int **ptr1 = &ptr; 0x111122 ptr = 0x123456

0x111133 ptr2 = 0x111122


C++ Alocare memorie pt. o matrice – metoda student
//alocare a discrete a zonelor de memorie (C ANSI) //dealocare (C ANSI)
void **Alocare2D(size_t n, size_t m, size_t dim) void Dealoca2D(void **p, size_t n)
{ {
void **p = nullptr; int i = 0;
p = malloc(n * sizeof(void *)) ;
int i = 0; for(i = 0; i < n; i++)
{
for(i = 0; i < n; i++) free(p[i]);
{ p[i] = 0;
*(p + i) = malloc( m * dim); }
} free(p);
}
return p;
}
... ...
//apel //apel
v = (short int**)Alocare2D(n, m, sizeof(short int)); Dealoca2D((void**)v, n);
v = 0;
C++
C++
C++
C++
C++
C++
C++
C++ Alocare memorie pt. o matrice – zona continua
//alocare continua a memoriei (C ANSI) //dealocare (C ANSI)
void **Alocare2D(size_t n, size_t m, size_t dim) void Dealoca2D(void **p)
{ {
size_t i = 0; int i = 0;
void **p = nullptr;
free(*p);
p = malloc(n * sizeof(void *)); *p = 0;
*p = malloc( n * m * dim); free(p);
}
for(i = 1; i < n; i++)
{
*(p + i) = *p + i * m * dim;
}

return p; ...
}
... //apel
//apel Dealoca2D((void**)v);
v = (short int**)Alocare2D(n, m, sizeof(short int)); v = 0;
C++
C++
C++
C++
C++
C++
C++
C++ Alocare memorie pt. o matrice – un singur bloc
//alocare un singur bloc de memorie (C ANSI) // dealocare (C ANSI) -
void **Alocare2D(size_t n, size_t m, size_t dim) nu este nevoie de functie
{ de dealocare
void **p = nullptr;
p = malloc(n * sizeof(void *) + n * m * dim);
size_t i = 0;
const size_t dimvp = n * sizeof(void*);

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


{
*(p + i) = (void*)((unsigned char*)p + dimvp + i * m * dim);
}

return p;
}
... ...
//apel //apel
v = (short int**)Alocare2D(n, m, sizeof(short int)); free(v);
v = 0;
C++
C++
C++
C++
C++
C++
C++
C++ Struct/class layout
› O structură/clasă îşi păstrează membri în ordinea în care au fost
declaraţi: struct DataCalendaristica
{
char zi; // [1:31]
int an;
char luna; // [1:12]
};

› Aranjarea în memorie a membrilor ar putea fi aceasta:

› Totuşi, dimensiunea unui obiect nu este suma dimensiunilor membrilor


C++ Struct/class layout
› Ex: pe 32 de biţi dimensiunea unui obiect de tip DataCalendaristica
este de 12 octeţi şi nu de 6.

› Optimizare:
struct Data
{
int an;
char zi; // [1:31]
char luna; // [1:12]
};
C++ Definiţia şi utilizarea unei structuri
› Un nume (identificator) devine accesibil imediat ce a fost scris şi nu după
declarare completă:
struct Link
{
Link∗ previous;
Link∗ successor;
};
› Totuşi nu se poate declara un obiect de un anumit tip dacă declaraţia tipului
respectiv nu este finalizată
struct Link
{
Link data;
};

› Eroare, deoarece compilatorul nu poate determina dimensiunea tipului Link.


C++ Forward declaration
› Referiri simultane:
struct List; // declararea numelui unei structuri: Va fi
//definita mai tarziu

struct Link
{
Link∗ prev;
Link∗ next;
List∗ member_of;
int data;
};

struct List
{
Link∗ head;
};

› Numele unei structuri poate fi folosit înainte de a fi definită


structura, dar nu poate instanţia un obiect.
C++ Câmpuri de biţi
› Tipul de date char este cel mai mic obiect ce poate fi alocat
independent
› Se poate considera risipă de spaţiu utilizarea unui char în declararea
unei variabile binare (singurile valori pe care le poate avea sunt 0 şi 1)
› Există posibilitatea de împacheta astfel de variabile ca fiind câmpuri de
biţi într-o structură/clasă.
› Un membru este definit ca fiind un câmp de biţi prin specificarea
numărului de biţi pe care îl ocupă
› Un câmp de biţi trebuie sa fie de tip int sau enum
› Nu se poate obţine adresa unui câmp de biţi
› Sunt permise câmpurile fără nume
C++ Câmpuri de biţi
struct student
{
unsigned int nrMatricol;
int : 8;
bool promovat : 1; //[0..1]
bool camin : 1; //[0..1]
unsigned int grupa : 11; //[1101..1410]
unsigned int nota : 4; // [1..10]
unsigned int vârsta : 7; // [1..127]
};

› Utilizarea câmpurilor de biţi pentru împachetarea variabilelor salvează câţiva


octeţi în detrimentul dimensiunii şi vitezei de execuţie a unui program.
› Pot fi o alternativă convenabilă pentru lucru pe biţi.

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