Pointeri – Operatori specifici, tablouri, functii

Un pointer este o variabilă ce conţine adresa unei alte variabile, numită statică, căreia i se alocă un spaţiu de memorie pe toată durata de viaţă a blocului în care a fost declarată. Deoarece un pointer conţine adresa unei variabile, cu ajutorul lui putem avea acces, indirect, la acea variabilă.
Pointerii se utilizează la transmiterea parametrilor şi la transmiterea funcţiilor ca şi parametri. Facilităţile oferite de pointeri în limbajul C permit:
• crearea de noi variabile în timpul de execuţie al programului, folosind alocarea dinamică a memoriei
• accesarea directă a diverselor zone de memorie permiţând astfel definirea şi exploatarea structurilor dinamice înlănţuite
• accesarea directă a unor elemente din diverse structuri de date precum tablourile
• prelucrarea şirurilor de caractere

Pointerii prezintă şi dezavantaje: utilizarea unui pointer care nu este riguros controlat poate duce la blocarea sistemului. De asemenea, programele scrise utilizând pointeri C sunt mai greu de depanat.

Variabilele de tip pointer se declară prin construcţii de forma:

tip *nume;

Tip reprezintă orice tip C valid indicând tipul de variabilă spre care pointează pointerul respectiv iar nume este numele variabilei de tip pointer. Indiferent de tipul variabilei spre care pointează pointerul, pentru un pointer se rezervă 2 octeţi în care urmează să se păstreze o adresă.

Operatori specifici pointerilor
Operatorii specifici pointerilor sunt: ‘*’ şi ‘&’
Operatorul ‘&’ este un operator unar care returnează adresa variabilei asupra căreia se aplică.
Operatorul ‘*’este tot un operator unar care returnează valoarea ce se găseşte la adresa indicată de variabila asupra căreia se aplică.

Exemplu:

int *p, n=5, m;
//declarăm p ca pointer la întreg şi n şi m întregi
p=&n;
//p va conţine adresa variabilei n deci p va pointa (indica) spre n
m=*p;
//lui m i se da valoarea care se găseşte la adresa indicată de p
//deci m va conţine valoarea lui n; în consecinţă m=5
m=*p+1;
//m va conţine valoarea 6

Uneori faptul că se foloseşte acelaşi simbol pentru valoarea de la adresa indicată de un pointer (operatorul “*”) şi pentru înmulţirea a 2 numere (“*”), respectiv adresa indicată de un pointer (operatorul “&”) şi operatorul “şi pe biţi” (“&”) poate produce confuzii. Operatorii specifici pointerilor sunt operatori unari, în timp ce operatorul aritmetic de înmulţire şi cel pentru şi pe biţi sunt operatori binari. De asemenea, prioritatea operatorilor specifici pointerilor este mai ridicată decât cea a operatorilor aritmetici sau logici pe biţi (cu excepţia lui minus unar cu care au aceeaşi prioritate). O mare atenţie trebuie acordată tipului variabilei spre care pointează un pointer. Următorul exemplu este sugestiv în acest sens:


int *p;
float x=1.23, y;
p=&x;
y=*p;
//valoarea lui y va fi total eronată datorită tipului pointerului p
//p este pointer spre întreg; y va lua din x doar primii 2 octeţi considerându-l număr
//întreg; compilarea acestui program în C++ va genera eroare

Exemplu:

int *a,**b,c=1,d;
//declarăm a pointer la întreg, b pointer la pointer, c întreg
//iniţializat cu valoarea 1 şi d întreg
a=&c;
//pointerul a va primi adresa variabilei întregi c
b=&a;
//pointerul la pointer b va primi adresa pointerului a
d=**b;
//variabila d primeşte valoarea 1, adică valoarea lui c

Pointeri şi tablouri
Printr-o declaraţie de forma:


tip v[lim1][lim2]...[limn];

(declaraţia unui tablou multidimensional), numele tabloului este unpointer constant la primul element din tablou. Alocarea de spaţiu pentru un tablou folosind pointeri se face astfel:

tip *v;

urmată apoi de alocarea de spaţiu pentru elementele tabloului, alocare care se poate face în cursul execuţiei programului folosind instrucţiunea malloc.
Astfel echivalentul declaraţiei de forma:

int v[10];

este:

//se declară v ca pointer la întreg; v va conţine adresa primului element al tabloului,
//şi va fi un pointer constant spre acest element (nu poate fi modificat)
int *v;
//alocăm spaţiu pentru io elemente de tip întreg
v=malloc(10*sizeof(int));

Exemple:

int i;
//declarăm un întreg i folosit ca indice al tabloului
double v[100],x,*p;
//declarăm un tablou de no elemente de tip double, o variabilă x de tip double şi p
//pointer spre double
p=&v[0];
//o atribuire de acest gen este corectă dar neelegantă
p=v;
//atribuire identică cu cea de mai sus; p va conţine adresa primului element al tabloului v
x=v[5];
//x va conţine valoarea elementului 6 al tabloului
x=*(v+5);
//atribuire identică cu precedenta
v++;
//o asemenea instrucţiune este incorectă, v este pointer constant
p++;
//instrucţiune corectă; înainte de atribuire p conţinea aceeaşi adresă ca şi v, iar pointerul
//p poate fi incrementat şi va conţine adresa următorului element din tabloul v

C nu controlează limitele unei matrici, deci controlul defectuos al indicelui unui tablou poate duce la referirea unor zone de memorie ce conţin valori necunoscute, conducând spre rezultate eronate ale programului. Un indice incorect sau un pointer care nu este riguros controlat pot duce până la blocarea sistemului. De aceea, unui pointer care la un anumit moment nu indică o locaţie de memorie validă, i se atribui valoarea nuli (care este o). Un pointer care este nuli practic nu indică spre nimic.

Pointeri la funcţii
Deşi o funcţie nu este o variabilă, ea are o adresă în memorie care poate fi atribuită unui pointer. Adresa unei funcţii este punctul de intrare în funcţie. De aceea, un pointer către funcţie poate fi utilizat pentru a apela o funcţie. în momentul compilării, fiecărei funcţii i se stabileşte un punct de intrare care se găseşte la o adresă de memorie. Un pointer care conţine adresa punctului de intrare, poate fi folosit pentru a apela acea funcţie.

Adresa unei funcţii se obţine utilizând numele funcţiei fără paranteze sau argumente (există o similaritate cu modul de a obţine adresa unui tablou, ca fiind numele tabloului). Următorul exemplu de program sperăm că este edificator.


#include "stdio.h"
int suma(int a, int b)
{
  return(a+b);
}
int produs(int a, int b)
{
  return(a*b);
}
void main(void)
{
  int a,b;
  //p este pointer spre o funcţie cu 2 parametri întregi
  int (*p) (int,int);
  //citim valorile a 2 întregi
  scanf("%d %d",&a,&b);
  //pointerul p primeşte adresa funcţiei suma
  p=suma;
  //folosind un pointer la funcţii vom obţine suma celor 2 numere citite
  printf("suma este %d\n",p(a,b));
  //pointerul p primeşte adresa funcţiei produs
  p=produs;
  //folosind un pointer la funcţii vom obţine produsul celor 2 numere citite
  printf("produsul este %d\n",p(a,b));
}

Pointerii la funcţii pot fi folosiţi şi ca parametri ai unei funcţii. Datorită dificultăţilor de a înţelege un program ce utilizează pointeri la funcţii, în general aceştia se evită. Totuşi, în anumite cazuri ale unor probleme complexe, se impune folosirea pointerilor la funcții.

Leave a Reply