0% au considerat acest document util (0 voturi)
3 vizualizări17 pagini

CPP - Pointeri

Documentul explică conceptul de pointeri în C++, care sunt variabile ce stochează adresele altor variabile, permițând accesul direct la valorile acestora prin operatorii de adresă (&) și dereferențiere (*). Se discută despre declararea pointerilor, diferențele dintre tipurile de date la care pot indica și modul în care pot fi utilizați în combinație cu tablouri. Exemplul oferit ilustrează cum pointerii pot modifica indirect valorile variabilelor la care se referă.

Încărcat de

poemecuprobleme
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
3 vizualizări17 pagini

CPP - Pointeri

Documentul explică conceptul de pointeri în C++, care sunt variabile ce stochează adresele altor variabile, permițând accesul direct la valorile acestora prin operatorii de adresă (&) și dereferențiere (*). Se discută despre declararea pointerilor, diferențele dintre tipurile de date la care pot indica și modul în care pot fi utilizați în combinație cu tablouri. Exemplul oferit ilustrează cum pointerii pot modifica indirect valorile variabilelor la care se referă.

Încărcat de

poemecuprobleme
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd

Pointeri (Indicatori)

În capitolele anterioare, variabilele au fost explicate ca locații în memoria computerului care pot
fi accesate prin identificatorul lor (numele lor). În acest fel, programul nu trebuie să se intereseze
de adresa fizică a datelor din memorie; acesta folosește pur și simplu identificatorul ori de câte
ori trebuie să se refere la variabilă.

Pentru un program C++, memoria unui computer este ca o succesiune de celule de memorie,
fiecare având o dimensiune de un octet și fiecare cu o adresă unică. Aceste celule de memorie de
un singur octet sunt ordonate într-un mod care permite reprezentărilor de date mai mari de un
octet să ocupe celule de memorie care au adrese consecutive.

În acest fel, fiecare celulă poate fi ușor localizată în memorie prin intermediul adresei sale unice.
De exemplu, celula de memorie cu adresa 1776urmează întotdeauna imediat după celula cu
adresă 1775și o precede pe cea cu 1777și se află exact la o mie de celule după 776și exact la o
mie de celule înainte de 2776.

Când o variabilă este declarată, memoria necesară pentru a stoca valoarea sa i se atribuie o
locație specifică în memorie (adresa sa de memorie). În general, programele C++ nu decid în
mod activ adresele exacte de memorie unde sunt stocate variabilele sale. Din fericire, această
sarcină este lăsată la latitudinea mediului în care rulează programul - în general, un sistem de
operare care decide locațiile specifice de memorie în timpul execuției. Cu toate acestea, poate fi
util pentru un program să poată obține adresa unei variabile în timpul execuției pentru a accesa
celulele de date care se află la o anumită poziție față de aceasta.

Adresa operatorului (&)


Adresa unei variabile poate fi obținută prin plasarea semnului ampersand () înaintea numelui
variabilei &, cunoscut sub numele de operator address-of . De exemplu:

1 foo = &myvar;

Aceasta ar atribui adresa variabilei myvar; prin plasarea operatorului address-of ( ) fooînaintea
numelui variabilei , nu mai atribuim conținutul variabilei în sine , ci adresa acesteia. Adresa reală
a unei variabile din memorie nu poate fi cunoscută înainte de execuție, dar să presupunem,
pentru a clarifica unele concepte, că este plasată în timpul execuției în adresa de memorie . În
acest caz, să luăm în considerare următorul fragment de cod:myvar&foo

myvar1776

1 myvar = 25;
2 foo = &myvar;
3 bar = myvar;
Valorile conținute în fiecare variabilă după execuția acesteia sunt prezentate în următoarea
diagramă:

Mai întâi, am atribuit valoarea 25lui myvar(o variabilă a cărei adresă în memorie am presupus
că este 1776).

A doua instrucțiune atribuie fooadresa lui myvar, pe care am presupus că este 1776.

În cele din urmă, a treia instrucțiune, atribuie valoarea conținută în myvarlui bar. Aceasta este
o operație de atribuire standard, așa cum s-a făcut deja de multe ori în capitolele anterioare.

Principala diferență dintre a doua și a treia instrucțiune este apariția operatorului de tip address-
of ( &).

Variabila care stochează adresa unei alte variabile (ca fooîn exemplul anterior) este ceea ce în
C++ se numește pointer . Pointerii sunt o caracteristică foarte puternică a limbajului care are
multe utilizări în programarea de nivel inferior. Puțin mai târziu, vom vedea cum se declară și se
utilizează pointerii.

Operator de dereferențiere (*)


După cum tocmai s-a văzut, o variabilă care stochează adresa unei alte variabile se
numește pointer . Se spune că pointerii „indică” variabila a cărei adresă o stochează.

O proprietate interesantă a pointerilor este că pot fi utilizați pentru a accesa variabila la care
indică direct. Acest lucru se face prin plasarea operatorului de dereferențiere ( *) înainte de
numele pointerului. Operatorul în sine poate fi citit ca „valoarea indicată de”.

Prin urmare, continuând cu valorile din exemplul anterior, se formează următoarea instrucțiune:

1 baz = *foo;

Aceasta ar putea fi citită ca: „ bazegal cu valoarea indicată de foo”, iar instrucțiunea ar atribui
de fapt valoarea 25lui baz, deoarece fooeste 1776, iar valoarea indicată de 1776(urmând
exemplul de mai sus) ar fi 25.
Este important să se diferențieze clar că foose referă la valoarea 1776, în timp ce *foo(cu un
asterisc *precedent al identificatorului) se referă la valoarea stocată la adresa 1776, care în acest
caz este 25. Observați diferența dintre includerea sau neincluderea operatorului de
dereferențiere (am adăugat un comentariu explicativ despre modul în care ar putea fi citită
fiecare dintre aceste două expresii):

1 baz = foo; // baz equal to foo (1776)


2 baz = *foo; // baz equal to value pointed to by foo (25)

Prin urmare, operatorii de referință și dereferențiere sunt complementari:


 &este operatorul adresă-de și poate fi citit simplu ca „adresă de”.
 *este operatorul de dereferențiere și poate fi citit ca „valoare indicată de”

Prin urmare, ele au semnificații oarecum opuse: O adresă obținută cu &poate fi dereferențiată
cu *.

Anterior, am efectuat următoarele două operații de atribuire:

1 myvar = 25;
2 foo = &myvar;

Imediat după aceste două afirmații, toate expresiile următoare ar da ca rezultat adevărat:

1 myvar == 25
2 &myvar == 1776
3 foo == 1776
4 *foo ==

Prima expresie este destul de clară, având în vedere că operația de atribuire efectuată pe myvara
fost myvar=25. A doua folosește operatorul de adresare ( &), care returnează adresa lui myvar,
despre care am presupus că are valoarea 1776. A treia este oarecum evidentă, deoarece a doua
expresie a fost adevărată, iar operația de atribuire efectuată pe fooa fost foo=&myvar. A patra
expresie folosește operatorul de dereferențiere ( *) care poate fi citit ca „valoarea indicată de”,
iar valoarea indicată de fooeste într-adevăr 25.
Deci, după toate acestea, puteți deduce și că, atâta timp cât adresa indicată de foorămâne
neschimbată, următoarea expresie va fi, de asemenea, adevărată:

1 *foo ==

Declararea pointerilor
Datorită capacității unui pointer de a se referi direct la valoarea la care indică, un pointer are
proprietăți diferite atunci când indică charcătre un intsau un float. Odată dereferențiat,
tipul trebuie să fie cunoscut. Și pentru aceasta, declararea unui pointer trebuie să includă tipul de
date la care urmează să indică pointerul.

Declararea pointerilor urmează această sintaxă:

type * name;

unde typeeste tipul de date la care indică pointerul. Acest tip nu este tipul pointerului în sine, ci
tipul datelor la care indică pointerul. De exemplu:

1 int * number;
2 char * character;
3 double * decimals;

Acestea sunt trei declarații de pointeri. Fiecare dintre ele este destinată să indice către un tip de
date diferit, dar, de fapt, toate sunt pointeri și probabil toate vor ocupa aceeași cantitate de spațiu
în memorie (dimensiunea în memorie a unui pointer depinde de platforma pe care rulează
programul). Cu toate acestea, datele la care indică nu ocupă aceeași cantitate de spațiu și nici nu
sunt de același tip: prima indică către un int, a doua către un char, iar ultima către
un double. Prin urmare, deși aceste trei variabile de exemplu sunt toate pointeri, ele au de fapt
tipuri diferite: int*, char*, și double*respectiv, în funcție de tipul la care indică.

Rețineți că asteriscul ( *) utilizat la declararea unui pointer înseamnă doar că este un pointer
(face parte din specificatorul său compus de tip) și nu trebuie confundat cu operatorul de
dereferențiere văzut puțin mai devreme, dar care este scris și cu un asterisc ( *). Sunt pur și
simplu două lucruri diferite reprezentate cu același semn.

Să vedem un exemplu despre pointeri:

// my first pointer prima valoare este 10


#include <iostream> a doua valoare este 2
using namespace std;

int main ()
{
int firstvalue, secondvalue;
int * mypointer;

mypointer = &firstvalue;
*mypointer = 10;
mypointer = &secondvalue;
*mypointer = 20;
cout << "firstvalue is " << firstvalue << '\n';
cout << "secondvalue is " << secondvalue << '\n';
return 0;
}
Editează și rulează pe [Link]

Observați că, deși nici , firstvaluenici nu secondvaluesunt setate direct nicio valoare în
program, ambele ajung la o valoare setată indirect prin utilizarea mypointer. Iată cum se
întâmplă:

Mai întâi, mypointeri se atribuie adresa lui firstvalue folosind operatorul address-of ( &).
Apoi, valorii indicate de mypointeri se atribuie o valoare de 10. Deoarece, în acest
moment, mypointerindică locația de memorie a lui firstvalue, aceasta modifică de fapt
valoarea lui firstvalue.

Pentru a demonstra că un pointer poate indica diferite variabile pe parcursul existenței sale într-
un program, exemplul repetă procesul cu secondvalueși același pointer, mypointer.

Iată un exemplu puțin mai elaborat:

// more pointers prima valoare


#include <iostream> a doua valoar
using namespace std;

int main ()
{
int firstvalue = 5, secondvalue = 15;
int * p1, * p2;

p1 = &firstvalue; // p1 = address of firstvalue


p2 = &secondvalue; // p2 = address of secondvalue
*p1 = 10; // value pointed to by p1 = 10
*p2 = *p1; // value pointed to by p2 = value
pointed to by p1
p1 = p2; // p1 = p2 (value of pointer is
copied)
*p1 = 20; // value pointed to by p1 = 20

cout << "firstvalue is " << firstvalue << '\n';


cout << "secondvalue is " << secondvalue << '\n';
return 0;
}
Editează și rulează pe [Link]

Fiecare operație de atribuire include un comentariu despre cum ar putea fi citită fiecare linie: de
exemplu, înlocuirea semnelor ampersand ( &) cu „adresa lui” și a asteriscurilor ( *) cu „valoarea
la care indică”.

Observați că există expresii cu pointeri p1și p2, atât cu, cât și fără operatorul de
dereferențiere ( *). Semnificația unei expresii care utilizează operatorul de dereferențiere (*)
este foarte diferită de una care nu îl folosește. Când acest operator precede numele pointerului,
expresia se referă la valoarea la care indică pointerul, în timp ce atunci când numele unui pointer
apare fără acest operator, se referă la valoarea pointerului în sine (adică adresa la care indică
pointerul).

Un alt lucru care v-ar putea atrage atenția este linia:

1 int * p1, * p2;

Aceasta declară cei doi pointeri utilizați în exemplul anterior. Observați însă că există un asterisc
( *) pentru fiecare pointer, astfel încât ambele să aibă tipul int*(pointer către int). Acest lucru
este necesar datorită regulilor de precedență. Rețineți că, dacă, în schimb, codul a fost:

1 int * p1, p2;

p1ar fi într-adevăr de tip int*, dar p2ar fi de tip int. Spațiile nu contează deloc în acest scop.
Însă, oricum, simpla amintire de a pune un asterisc per pointer este suficientă pentru majoritatea
utilizatorilor de pointeri interesați să declare mai mulți pointeri per instrucțiune. Sau și mai bine:
folosiți o instrucțiune diferită pentru fiecare variabilă.

Pointeri și matrice
Conceptul de tablouri este legat de cel de pointeri. De fapt, tablourile funcționează foarte
asemănător pointerilor către primele lor elemente și, de fapt, un tablou poate fi întotdeauna
convertit implicit în pointerul de tipul corespunzător. De exemplu, luați în considerare aceste
două declarații:

1 int myarray [20];


2 int * mypointer;

Următoarea operație de atribuire ar fi validă:

1 mypointer = myarray;
După aceea, mypointerși myarrayar fi echivalente și ar avea proprietăți foarte similare.
Principala diferență fiind că mypointeri se poate atribui o adresă diferită, în timp
ce myarraynu i se poate atribui niciodată nimic și va reprezenta întotdeauna același bloc de 20
de elemente de tip int. Prin urmare, următoarea atribuire nu ar fi validă:

1 myarray = mypointer;

Să vedem un exemplu care combină tablouri și pointeri:

// more pointers 10, 20, 30, 40, 50,


#include <iostream>
using namespace std;

int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;
p++; *p = 20;
p = &numbers[2]; *p = 30;
p = numbers + 3; *p = 40;
p = numbers; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}
Editează și rulează pe [Link]

Pointerii și tablourile acceptă același set de operații, cu aceeași semnificație pentru ambele.
Principala diferență constă în faptul că pointerilor li se pot atribui adrese noi, în timp ce
tablourilor nu le este posibil.

În capitolul despre tablouri, parantezele ( []) au fost explicate ca specificând indexul unui
element al tabloului. Ei bine, de fapt, aceste paranteze sunt un operator de dereferențiere
cunoscut sub numele de operator offset . Ele dereferențiază variabila pe care o urmează, la fel
cum *o face și el, dar adaugă și numărul dintre paranteze la adresa dereferențiată. De exemplu:

1 a[5] = 0; // a [offset of 5] = 0
2 *(a+5) = 0; // pointed to by (a+5) = 0

Aceste două expresii sunt echivalente și valide, nu numai dacă aeste un pointer, ci și dacă aeste
un tablou. Rețineți că, dacă este un tablou, numele său poate fi folosit la fel ca un pointer către
primul său element.
Inițializarea pointerului
Pointerii pot fi inițializați să indice către locații specifice chiar în momentul în care sunt definiți:

1 int myvar;
2 int * myptr = &myvar;

Starea rezultată a variabilelor după acest cod este aceeași ca după:

1 int myvar;
2 int * myptr;
3 myptr = &myvar;

Când pointerii sunt inițializați, ceea ce este inițializat este adresa la care indică (adică myptr),
niciodată valoarea la care indică (adică *myptr). Prin urmare, codul de mai sus nu trebuie
confundat cu:

1 int myvar;
2 int * myptr;
3 *myptr = &myvar;

Ceea ce oricum nu ar avea prea mult sens (și nu este cod valid).

Asteriscul ( *) din declarația pointerului (linia 2) indică doar că este un pointer, nu este
operatorul de dereferențiere (ca în linia 3). Ambele lucruri se întâmplă să folosească același
semn: *. Ca întotdeauna, spațiile nu sunt relevante și nu schimbă niciodată sensul unei expresii.

Pointerii pot fi inițializați fie la adresa unei variabile (cum ar fi în cazul de mai sus), fie la
valoarea unui alt pointer (sau matrice):

1 int myvar;
2 int *foo = &myvar;
3 int *bar = foo;

Aritmetica pointerilor
Efectuarea operațiilor aritmetice pe pointeri este puțin diferită față de efectuarea lor pe tipuri
întregi obișnuite. Pentru început, sunt permise doar operațiile de adunare și scădere; celelalte nu
au sens în lumea pointerilor. Dar atât adunarea, cât și scăderea au un comportament ușor diferit
cu pointerii, în funcție de dimensiunea tipului de date la care indică.

Când au fost introduse tipurile de date fundamentale, am văzut că tipurile au dimensiuni diferite.
De exemplu: charare întotdeauna o dimensiune de 1 octet, shorteste în general mai mare
decât aceasta și intși longsunt chiar mai mari; dimensiunea exactă a acestora depinzând de
sistem. De exemplu, să ne imaginăm că într-un sistem dat, charocupă 1 octet, shortocupă 2
octeți și longocupă 4 octeți.

Să presupunem acum că definim trei pointeri în acest compilator:

1 char *mychar;
2 short *myshort;
3 long *mylong;

și că știm că acestea indică către locațiile de memorie 1000, 2000, și 3000, respectiv.

Prin urmare, dacă scriem:

1 ++mychar;
2 ++myshort;
3 ++mylong;

mychar, așa cum era de așteptat, ar conține valoarea 1001. Dar nu atât de evident, myshortar
conține valoarea 2002 și mylongar conține 3004, chiar dacă fiecare a fost incrementat o singură
dată. Motivul este că, atunci când se adaugă unul la un pointer, pointerul este făcut să indice
către următorul element de același tip și, prin urmare, dimensiunea în octeți a tipului la care
indică este adăugată la pointer.

Acest lucru este aplicabil atât la adunarea, cât și la scăderea oricărui număr dintr-un pointer. S-ar
întâmpla exact la fel dacă am scrie:

1 mychar = mychar + 1;
2 myshort = myshort + 1;
3 mylong = mylong + 1;

În ceea ce privește operatorii de incrementare ( ++) și decrementare ( --), ambii pot fi utilizați
fie ca prefix, fie ca sufix al unei expresii, cu o mică diferență de comportament: ca prefix,
incrementarea are loc înainte de evaluarea expresiei, iar ca sufix, incrementarea are loc după
evaluarea expresiei. Acest lucru este valabil și pentru expresiile care incrementează și
decrementează pointeri, care pot deveni parte a unor expresii mai complicate, care includ și
operatori de dereferențiere ( *). Reamintind regulile de precedență a operatorilor, putem reaminti
că operatorii postfix, cum ar fi incrementarea și decrementarea, au o precedență mai mare decât
operatorii prefix, cum ar fi operatorul de dereferențiere ( *). Prin urmare, următoarea expresie:

1 *p++

este echivalent cu *(p++). Și ceea ce face este să crească valoarea lui p(deci acum indică
elementul următor), dar deoarece ++este folosit ca postfix, întreaga expresie este evaluată ca
valoarea indicată inițial de pointer (adresa la care a indicat înainte de a fi incrementată).

În esență, acestea sunt cele patru combinații posibile ale operatorului de dereferențiere atât cu
versiunea prefix, cât și cu cea sufix ale operatorului de incrementare (aceleași lucru fiind valabil
și pentru operatorul de decrementare):

1 *p++ // same as *(p++): increment pointer, and dereference


2 unincremented address
3 *++p // same as *(++p): increment pointer, and dereference
4 incremented address
++*p // same as ++(*p): dereference pointer, and increment
the value it points to
(*p)++ // dereference pointer, and post-increment the value it
points to

O afirmație tipică - dar nu atât de simplă - care implică acești operatori este:

1 *p++ = *q++;

Deoarece ++are o precedență mai mare decât *, ambele operatoare de incrementare pși qsunt
incrementate, dar pentru că ambii operatori de incrementare ( ++) sunt utilizați ca postfix și nu ca
prefix, valoarea atribuită lui *peste *qînaintea ambelor operatoare de incrementare pși q. Și
apoi ambele sunt incrementate. Ar fi aproximativ echivalent cu:

1 *p = *q;
2 ++p;
3 ++q;

Ca întotdeauna, parantezele reduc confuzia prin adăugarea de lizibilitate expresiilor.

Pointeri și constante
Pointerii pot fi utilizați pentru a accesa o variabilă prin adresa sa, iar acest acces poate include
modificarea valorii indicate. Dar este posibil și să se declare pointeri care pot accesa valoarea
indicată pentru a o citi, dar nu pentru a o modifica. Pentru aceasta, este suficient să se califice
tipul indicat de pointer ca const. De exemplu:
1 int x;
2 int y = 10;
3 const int * p = &y;
4 x = *p; // ok: reading p
5 *p = x; // error: modifying p, which is const-
qualified

Aici pindică o variabilă, dar indică către aceasta într-o constmanieră calificată cu -, ceea ce
înseamnă că poate citi valoarea indicată, dar nu o poate modifica. Rețineți, de asemenea, că
expresia &yeste de tip int*, dar aceasta este atribuită unui pointer de tip const int*. Acest
lucru este permis: un pointer către non-const poate fi convertit implicit într-un pointer către
const. Dar nu invers! Ca o caracteristică de siguranță, pointerii către constnu sunt implicit
convertibili în pointeri către non- const.

Unul dintre cazurile de utilizare a pointerilor către constelemente este ca parametri de funcție:
o funcție care primește un pointer către non- constca parametru poate modifica valoarea
transmisă ca argument, în timp ce o funcție care primește un pointer către constca parametru
nu poate.

// pointers as arguments: 11
#include <iostream> 21 de ani
using namespace std; 31 de ani

void increment_all (int* start, int* stop)


{
int * current = start;
while (current != stop) {
++(*current); // increment value pointed
++current; // increment pointer
}
}

void print_all (const int* start, const int* stop)


{
const int * current = start;
while (current != stop) {
cout << *current << '\n';
++current; // increment pointer
}
}

int main ()
{
int numbers[] = {10,20,30};
increment_all (numbers,numbers+3);
print_all (numbers,numbers+3);
return 0;
}
Editează și rulează pe [Link]

Rețineți că print_allfolosește pointeri care indică către elemente constante. Acești pointeri
indică către conținut constant pe care nu îl pot modifica, dar nu sunt ei înșiși constanți: adică,
pointerii pot fi în continuare incrementați sau li se pot atribui adrese diferite, deși nu pot
modifica conținutul la care indică.

Și aici se adaugă o a doua dimensiune a constanței pointerilor: Pointerii pot fi, de asemenea, ei
înșiși const. Și acest lucru este specificat prin adăugarea lui const la tipul indicat (după asterisc):

1 int x;
2 int * p1 = &x; // non-const pointer to non-const
3 int
4 const int * p2 = &x; // non-const pointer to const int
5 int * const p3 = &x; // const pointer to non-const int
const int * const p4 = &x; // const pointer to const int

Sintaxa cu constpointeri și este cu siguranță dificilă, iar recunoașterea cazurilor care se


potrivesc cel mai bine fiecărei utilizări tinde să necesite o oarecare experiență. În orice caz, este
important să obțineți consecvența cu pointerii (și referințele) corect cât mai curând posibil, dar
nu ar trebui să vă faceți griji prea mult cu privire la înțelegerea tuturor informațiilor dacă aceasta
este prima dată când sunteți expus la combinația de constpointeri și . Mai multe cazuri de
utilizare vor apărea în capitolele următoare.

Pentru a adăuga puțin mai multă confuzie sintaxei lui constpointeri cu , constcalificativul
poate fie să precede, fie să urmeze tipul indicat, cu exact aceeași semnificație:

1 const int * p2a = &x; // non-const pointer to const int


2 int const * p2b = &x; // also non-const pointer to const int

Ca și în cazul spațiilor care înconjoară asteriscul, ordinea constantelor în acest caz este pur și
simplu o chestiune de stil. Acest capitol folosește un prefix const, deoarece, din motive
istorice, acesta pare a fi mai extins, dar ambele sunt exact echivalente. Meritele fiecărui stil sunt
încă intens dezbătute pe internet.

Pointeri și literali de șir


Așa cum s-a menționat anterior, literalii de șir de caractere sunt tablouri care conțin secvențe de
caractere terminate cu null. În secțiunile anterioare, literalii de șir de caractere au fost utilizați
pentru a fi inserați direct în cout, pentru a inițializa șiruri de caractere și pentru a inițializa
tablouri de caractere.

Dar pot fi accesați și direct. Literalii de șir de caractere sunt tablouri de tipul corespunzător
pentru a conține toate caracterele sale plus caracterul null terminal, fiecare dintre elemente fiind
de tipul const char(ca literali, nu pot fi modificați niciodată). De exemplu:

1 const char * foo = "hello";

Aceasta declară un tablou cu reprezentarea literală pentru "hello", iar apoi un pointer către
primul său element este atribuit lui foo. Dacă ne imaginăm că "hello"este stocat în locațiile
de memorie care încep de la adresa 1702, putem reprezenta declarația anterioară ca:

Rețineți că aici fooeste un pointer și conține valoarea 1702, și nu 'h', nici "hello", deși
1702 este într-adevăr adresa ambelor.

Pointerul fooindică o secvență de caractere. Și deoarece pointerii și tablourile se comportă în


esență în același mod în expresii, foopoate fi folosit pentru a accesa caracterele în același mod
ca și tablourile de secvențe de caractere terminate cu null. De exemplu:

1 *(foo+4)
2 foo[4]

Ambele expresii au valoarea 'o'(al cincilea element al matricei).

Pointeri către pointeri


C++ permite utilizarea pointerilor care indică spre pointeri, iar aceștia, la rândul lor, indică spre
date (sau chiar spre alți pointeri). Sintaxa necesită pur și simplu un asterisc ( *) pentru fiecare
nivel de indirecționare în declararea pointerului:

1 char a;
2 char * b;
3 char ** c;
4 a = 'z';
5 b = &a;
6 c = &b;

Presupunând că locațiile de memorie sunt alese aleatoriu pentru fiecare variabilă


a 7230, 8092și 10502, aceasta ar putea fi reprezentată astfel:
Cu valoarea fiecărei variabile reprezentată în interiorul celulei corespunzătoare, iar adresele lor
respective în memorie reprezentate de valoarea de sub ele.

Noutatea din acest exemplu este variabila c, care este un pointer către un pointer și poate fi
utilizată la trei niveluri diferite de indirectare, fiecare dintre ele corespunzând unei valori diferite:

 ceste de tip char**și are o valoare de8092


 *ceste de tip char*și are o valoare de7230
 **ceste de tip charși are o valoare de'z'

indicatori void
Tipul voidde pointer este un tip special de pointer. În C++, voidreprezintă absența tipului.
Prin urmare, voidpointerii sunt pointeri care indică o valoare fără tip (și, prin urmare, o lungime
nedeterminată și proprietăți de dereferențiere nedeterminate).

Acest lucru oferă voidpointerilor o mare flexibilitate, putând indica orice tip de date, de la o
valoare întreagă sau un număr cu număr mobil până la un șir de caractere. În schimb, au o mare
limitare: datele la care indică nu pot fi dereferențiate direct (ceea ce este logic, deoarece nu avem
un tip la care să facem dereferențiere) și, din acest motiv, orice adresă dintr-un voidpointer
trebuie transformată într-un alt tip de pointer care indică un tip de date concret înainte de a fi
dereferențiată.

Una dintre posibilele sale utilizări poate fi transmiterea de parametri generici unei funcții. De
exemplu:

// increaser anul 1603


#include <iostream>
using namespace std;

void increase (void* data, int psize)


{
if ( psize == sizeof(char) )
{ char* pchar; pchar=(char*)data; ++(*pchar); }
else if (psize == sizeof(int) )
{ int* pint; pint=(int*)data; ++(*pint); }
}

int main ()
{
char a = 'x';
int b = 1602;
increase (&a,sizeof(a));
increase (&b,sizeof(b));
cout << a << ", " << b << '\n';
return 0;
}
Editează și rulează pe [Link]

sizeofeste un operator integrat în limbajul C++ care returnează dimensiunea în octeți a


argumentului său. Pentru tipurile de date nedinamice, această valoare este o constantă. Prin
urmare, de exemplu, sizeof(char)este 1, deoarece charare întotdeauna o dimensiune de un
octet.

Pointeri invalidi și pointeri nuli


În principiu, pointerii sunt meniți să indice către adrese valide, cum ar fi adresa unei variabile sau
adresa unui element dintr-un array. Dar pointerii pot indica de fapt orice adresă, inclusiv adrese
care nu se referă la niciun element valid. Exemple tipice în acest sens sunt pointerii
neinițializați și pointerii către elemente inexistente ale unui array:

1 int * p; // uninitialized pointer (local


2 variable)
3
4 int myarray[10];
int * q = myarray+20; // element out of bounds

Nici indicatorul, pnici qindicatorul nu indică adrese despre care se știe că conțin o valoare, dar
niciuna dintre instrucțiunile de mai sus nu provoacă o eroare. În C++, pointerii pot lua orice
valoare de adresă, indiferent dacă există sau nu ceva la acea adresă. Ceea ce poate cauza o eroare
este dereferențierea unui astfel de pointer (adică accesarea efectivă a valorii la care indică).
Accesarea unui astfel de pointer provoacă un comportament nedefinit, de la o eroare în timpul
execuției până la accesarea unei valori aleatorii.

Dar, uneori, un pointer trebuie să indice explicit nicăieri și nu doar o adresă invalidă. Pentru
astfel de cazuri, există o valoare specială pe care o poate lua orice tip de pointer: valoarea nulă a
pointerului . Această valoare poate fi exprimată în C++ în două moduri: fie cu o valoare întreagă
zero, fie cu nullptrcuvântul cheie:

1 int * p = 0;
2 int * q = nullptr;

Aici, ambele pși qsunt pointeri nuli , ceea ce înseamnă că nu indică în mod explicit nicăieri și
ambele se compară de fapt la fel: toți pointerii nuli se compară la fel cu alți pointeri nuli . De
asemenea, este destul de obișnuit să vedem constanta definită NULLutilizată în codul mai vechi
pentru a se referi la valoarea pointerului nul :
1 int * r = NULL;

NULLeste definit în mai multe anteturi ale bibliotecii standard și este definit ca un alias al unei
valori constante a unui pointer nul (cum ar fi 0sau nullptr).

Nu confundați pointerii nuli cu voidpointerii! Un pointer nul este o valoare pe care orice
pointer o poate lua pentru a reprezenta faptul că indică „nicăieri”, în timp ce un voidpointer este
un tip de pointer care poate indica undeva fără un tip specific. Unul se referă la valoarea stocată
în pointer, iar celălalt la tipul de date la care indică.

Pointeri către funcții


C++ permite operațiuni cu pointeri către funcții. Utilizarea tipică a acestei funcții este pentru
transmiterea unei funcții ca argument către o altă funcție. Pointerii către funcții sunt declarați cu
aceeași sintaxă ca o declarație de funcție obișnuită, cu excepția faptului că numele funcției este
cuprins între paranteze () și un asterisc ( *) este inserat înainte de nume:

1 // pointer to functions 8
2 #include <iostream>
3 using namespace std;
4
5 int addition (int a, int b)
6 { return (a+b); }
7
8 int subtraction (int a, int b)
9 { return (a-b); }
10
11 int operation (int x, int y, int (*functocall)
12 (int,int))
13 {
14 int g;
15 g = (*functocall)(x,y);
16 return (g);
17 }
18
19 int main ()
20 {
21 int m,n;
22 int (*minus)(int,int) = subtraction;
23
24 m = operation (7, 5, addition);
25 n = operation (20, m, minus);
26 cout <<n;
27 return 0;
}
Editează și rulează pe [Link]

În exemplul de mai sus, minuseste un pointer către o funcție care are doi parametri de tip int.
Acesta este inițializat direct să indice către funcție subtraction:

1 int (* minus)(int,int) = subtraction;

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