Pointer (tip de date)

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită la 11 aprilie 2021; verificările necesită 11 modificări .

Pointer ( în engleză  pointer ) este o variabilă al cărei interval de valori constă din adrese de celule de memorie sau o valoare specială - adresa zero . Acesta din urmă este folosit pentru a indica faptul că pointerul nu se referă în prezent la niciuna dintre celulele valide. Indicatorii au fost inventați de Ekaterina Logvinovna Iuscenko în limbajul de programare a adreselor (1955), și nu de Harold Lawson în 1964, așa cum s-a crezut mult timp în străinătate [1] . În 1955, conceptele de adresare indirectă și de adresare a rangurilor superioare au fost introduse în Address Programming Language , care acoperă conceptul de pointer și domeniul său de aplicare în limbajele de programare moderne.

Domeniul de aplicare

Indicatorii sunt utilizați în două domenii:

Acțiuni pe pointeri

Limbajele de programare care prevăd tipul de pointeri conțin, de regulă, două operații de bază asupra acestora: atribuire și dereferire .

În 1955, limbajul de programare a adreselor (URSS) a introdus „operarea cu bară” (dereferențierea pointerului), care a fost implementată în hardware prin operarea F a procesorului în computerul Kiev (1955) și mai târziu în M-20 calculatoare , „ Dnepr ”, calculatoare din familia BESM (BESM-2, BESM-3, BESM-3M și BESM-4), familiile Minsk și Ural, precum și alte computere de fabricație sovietică. Utilizarea multiplă a dereferențării pointerului a fost, de asemenea, implementată în hardware-ul acestor computere prin operațiuni de actualizare a adreselor de grup pentru a accelera lucrul cu formate de tip arbore ( listele și alte tipuri de date abstracte sunt un caz special de formate asemănătoare arborelui).

Primul atribuie o anumită adresă indicatorului. Al doilea este folosit pentru a accesa valoarea din memorie indicată de indicator. Dereferențiarea poate fi explicită sau implicită; în majoritatea limbajelor de programare moderne, dereferențiarea are loc numai atunci când este specificat explicit[ ce? ] .

Un exemplu de lucru cu pointeri în limbajul C :

int n = 6 ; // Declararea unei variabile n de tip int și atribuirea acesteia la valoarea 6 int * pn = malloc ( sizeof ( int ) ); // Declararea pointerului pn și alocarea memoriei pentru acesta * pn = 5 ; // Dereference pointer și atribuiți valoarea 5 n = * pn ; // Atribuiți n valorii (5) indicată de pn free ( pn ); // Eliberează memoria ocupată pn = & n ; // Atribuiți pointerul pn la adresa variabilei n (pointerul va indica către n) n = 7 ; // *pn a devenit și el egal cu 7

Operatorul unar &returnează adresa variabilei, iar operatorul *este folosit pentru a dereferi:

int sourceNum1 = 100 ; int sourceNum2 = 200 ; int * pNum1 = & sourceNum1 ; int * pNum2 = & sourceNum2 ; printf ( "Valoarea indicatorului de 1-%d, 2-%d \n " , * pNum1 , * pNum2 ); pNum1 = pNum2 ; printf ( "Valoarea indicatorului de 1-%d, 2-%d \n " , * pNum1 , * pNum2 );

Dacă pointerul stochează adresa unui obiect, atunci se spune că pointerul se referă la acest obiect .

Limbile care prevăd utilizarea pointerilor pentru alocarea dinamică a memoriei trebuie să conțină un operator pentru alocarea explicită a variabilelor în memorie. În unele limbi, pe lângă acest operator, există și un operator pentru ștergerea explicită a variabilelor din memorie. Ambele operațiuni iau adesea forma unor rutine încorporate (funcțiile malloc și free în C, operatorii new și delete în C++ și așa mai departe). Când utilizați un indicator simplu, mai degrabă decât un indicator inteligent , ar trebui să ștergeți întotdeauna variabila din memorie în timp util pentru a evita scurgerile de memorie .

Pointer to void

Un indicator de tip void vă permite să vă referiți la orice tip de date , inclusiv la o clasă . Această tehnologie stă la baza oricărui tip de bibliotecă Boost .

clasa A { câmp int ; }; AclA ; _ void * pA = ( gol * ) & clA ; // pointerul pA se referă la un obiect din clasa A

Pointer to pointer (adresându-se rangurilor mai înalte)

Există, de asemenea, pointeri către pointeri în programare. Ele stochează adrese de memorie unde există pointeri către memoria în care se află obiectul de date sau un alt pointer. Înlănțuirea unui pointer la un pointer care indică din nou către un pointer ne permite să introducem conceptul de dereferențiere multiplă de pointer (în limbajul de programare a adresei : „adresarea rangurilor mai înalte” ) și acțiunea corespunzătoare asupra pointerelor: Indirecție multiplă.

int x , * p , ** q ; x = 10 ; p = & x ; q = & p ; // pointer la pointer printf ( "%d" , ** q );

Pointer nul

Un pointer nul este un pointer care deține o valoare specială care indică faptul că variabila pointer dată nu se referă la (nu indică) niciun obiect. În limbajele de programare, este reprezentată printr-o constantă specială [4] :

Principalele probleme ale aplicației

Indicatorii sunt greu de gestionat. Este destul de ușor să scrieți o valoare greșită într-un indicator, ceea ce poate provoca o eroare greu de reprodus. De exemplu, ați schimbat din greșeală adresa unui pointer în memorie sau ați alocat incorect memoria pentru informații și aici vă poate aștepta o surpriză: o altă variabilă foarte importantă care este folosită doar în interiorul programului va fi suprascrisă. Înțelegerea exactă a erorii și reproducerea acesteia nu va fi ușor, iar eliminarea unor astfel de erori nu este întotdeauna o sarcină banală, uneori trebuie să rescrieți o parte semnificativă a programului [6] .

Pentru a rezolva unele dintre probleme, există metode de protecție și asigurare:

Inițializați pointerii

Un exemplu de eroare cu un pointer neinițializat:

/* programul este invalid. */ int main ( void ) { int x , * p ; // Memorie alocată pentru x, dar nu pentru *p x = 10 ; // Memoria se scrie 10 * p = x ; // 10 este scris într-o locație nedefinită din memorie, ceea ce poate duce la blocarea programului. returnează 0 ; }

Într-un program atât de mic, problema poate trece neobservată. Dar, când programul crește, poate deveni brusc clar că variabila este scrisă între alte blocuri de date care sunt importante pentru program. Pentru a evita această situație, trebuie doar să inițializați indicatorul [6] .

Utilizați corect indicatorii

Utilizarea incorectă a unui indicator:

#include <stdio.h> /* programul este nevalid */ int main ( void ) { int x , * p ; x = 10 ; p = x ; printf ( "%d" , * p ); returnează 0 ; }

Apelul printf()nu afișează pe ecran valoarea lui х, care este 10. În schimb, o valoare necunoscută este scoasă - aceasta este rezultatul unei utilizări incorecte a operatorului de atribuire ( р = х;). Acest operator atribuie valoarea 10 indicatorului р, care ar trebui să conțină adresa, nu valoarea. Din fericire, eroarea din acest program este detectată de compilator - emite un avertisment despre o conversie neobișnuită a pointerului. Pentru a remedia eroarea, scrieți p = &х;[6] .

Utilizarea corectă a indicatorului
  • încercați să inițializați variabilele imediat când declarați ( int x = 10;);
  • nu amestecați pointerii cu variabile obișnuite (cum ar fi int x, *p, y, *y_ptr;);
#include <stdio.h> int main ( void ) { int x = 10 ; int * p = & x ; printf ( "%d" , * p ); returnează 0 ; }

Scurgere de memorie

O scurgere de memorie  este un proces de scădere necontrolată a cantității de memorie cu acces aleatoriu liber (RAM) a unui computer asociat cu erori în rularea programelor care nu eliberează zonele de memorie inutile la timp sau cu erori în serviciile de control al memoriei sistemului.

char * pointer = NULL ; int i = 0 ; pentru ( i = 0 ; i < 10 ; i ++ ) { pointer = ( char * ) malloc ( 100 ); // Memoria se alocă de 10 ori } liber ( pointer ); // A este eliberat numai în ultimul caz

Comparație pointer

Adresele de memorie atribuite pointerilor pot fi comparate. Comparațiile formei pNum1 < pNum2și pNum1 > pNum2sunt adesea folosite pentru a itera secvențial peste elementele unui tablou într-o buclă : pNum1corespunde poziției curente în memorie și corespunde pNum2 sfârșitului matricei. pNum1 == pNum2va returna true dacă ambii pointeri indică către aceeași locație de memorie.

Aritmetica adresei

Aritmetica adresei a apărut ca o continuare logică a ideii de pointeri moștenite din limbajele de asamblare: în acestea din urmă, este posibil să se indice un decalaj față de poziția curentă.

Operații tipice ale aritmeticii adresei:

int * p ; // Să spunem p puncte la adresa 200 p ++ ; // După creștere, indică 200 + sizeof(int) = 204 p -- ; // Acum indică înapoi la 200.

Indicator inteligent

În unele limbaje de programare, există clase (de obicei șabloane) care implementează interfața pointer cu o nouă funcționalitate care corectează unele dintre deficiențele menționate mai sus.

Un indice în biologia umană

Creierul folosește grupuri de celule asemănătoare unui indicator pentru a îndeplini unele dintre sarcinile asociate cu memorarea informațiilor noi [7] .

Note

  1. Videla, Alvaro Kateryna L. Yushchenko - Inventor of  Pointers . https://medium.com/ . Un computer al propriilor pionieri ai erei calculatoarelor (8 decembrie 2018). Preluat la 30 iulie 2020. Arhivat din original pe 23 septembrie 2020.
  2. Pentru ce sunt folosite pointerii? . Consultat la 20 februarie 2013. Arhivat din original pe 26 februarie 2013.
  3. 14.1. Alocarea memoriei (downlink) . - „Adresa de început a memoriei alocate este returnată la punctul apelului funcției și scrisă în variabila pointer. O variabilă creată în acest fel se numește variabilă dinamică. Preluat la 22 februarie 2013. Arhivat din original la 25 iunie 2013. 
  4. Întrebarea 5.1 . comp.lang.c Întrebări frecvente. Consultat la 20 februarie 2013. Arhivat din original pe 26 februarie 2013.
  5. Un nume pentru pointerul nul:  nullptr . JTC1.22.32 . JTC1/SC22/WG21 - Comitetul pentru standarde C++ (2 octombrie 2007). Data accesului: 4 octombrie 2010. Arhivat din original la 11 februarie 2012.
  6. 1 2 3 Probleme legate de indicatori . Consultat la 22 februarie 2013. Arhivat din original pe 26 februarie 2013.
  7. Creierul folosește trucuri de programare pentru a rezolva probleme noi . RIA Novosti (23 septembrie 2013). Consultat la 13 septembrie 2016. Arhivat din original la 20 septembrie 2016.