Pointerii ne permit sa reprezentam tipuri de date complexe (structuri), sa realizam modificarea unor valori trimise ca parametrii unei functii, sa lucram cu zone de memorie alocate dinamic sau sa accesam mult mai eficient tablourile. Un pointer ne ofera mijlocul de a accesa indirect o valoare a unui tip de data.
Un pointer este de fapt o valoare. Este valoarea unei adrese de memorie unde se afla de fapt ceea ce ne intereseaza. Pointerii pot fi de tip near (au numai valoare de offset)sau de tip far (au si segment si offset).
int i = 10, *int_p;
declara un intreg i ce are valoarea 10, si un pointer la intreg numit int_p. * precde definirea unei variabile de tip pointer. Pentru a face legatura dintre i si int_p este folosit prefixul &.
int_p = &i;
Aceasta atribuie valoarea de adresa (adresa din memorie) a variabilei i pointerului. De acum inainte int_p va contine adresa zonei de memorie unde este plasata valoarea 10, valoare a variabilei i.
POINTERII CONTIN ADRESE DE MEMORIE !
Pentru a ne referi la valoarea lui i vom utiliza '*' :
x = *int_p;
Aceasta operatie atribuie valoarea de la adresa de memorie spre care indica int_p variabilei x.
#include <stdio.h>
main()
{
int i = 10, x, *int_p;
int_p = &i;
x = *int_p;
printf("i = %d, x = %d\n", i, x);
}
Transferul prin referinta al parametrilor unei functii
Comparati doua exemple :
void swap(int x, int y)
{
int tmp;
tmp=x;
x = y;
y = tmp;
}
cu
void swap(int *x, int *y)
{
int tmp;
tmp=*x;
*x = *y;
*y = tmp;
}
apel :
int i,j;
......
swap(&i,&j);
Operatiile aritmetice permise asupra pointerilor sunt adunarea/scaderea unei constante, incrementarea/decrementarea si scaderea a doi pointeri de acelasi tip.
Adunarea cu 1, face ca un pointer sa indice urmatorul obiect de acelasi tip iar decrementarea face ca el sa indice obiectul imediat anterior in spatiul de memorie.
int * pi; float *pf;
pi++ - pi va indica la urmatorul intreg. Ceea ce se scade sau se aduna efectic este sizeof(tip).
Semnificatia pointerilor poate fi alterata prin conversie explicita de tip cast.*((char*)pf) va furniza primul octet din reprezentarea variabilei de tip float.
Pointerul 0 este predefinit ca NULL si semnifica faptul ca nu indica nimic.
Deoarece void * inseamna un pointer de tip neprecizat, nu putem face operatii aritmetice asupra acestor pointeri. Pointerii void ne permite pastrarea gradului de generalitate al unui program la maximum.
Scaderea a doi pointeri este permisa numai in cazul pointerilor de acelasi tip. p-q reprezinta numarul de obiecte aflate intre p si q.
Fie declaratia :
int a[10], *p;
are sens
p=&a[0];
*p va indica catre primul element al tabloului a.
Iata doua moduri de a calcula lungimea unui sir de caractere :
int strlen(char s[])
{
int i;
for (i=0; s[i]!='\0';i++);
return i;
}
|
int strlen(char *s)
{
char *p;
while (*p++);
return p-s;
}
|
Obs Atentie la initializari de tipul :
char s[]={'a','b', 'c', 'd', '\0'};
char *p="abcd";
Pe cind prima initializeaza corect variabila s, alocindu-i spatiul necesar, cea de-a doua instructiune, va aloca un spatiu de memorie pentru constanta sir "abcd", iar adresa o va atribui pointerului p.
DYNAMIC MEMORY ALLOCATION (MALLOC, CALLOC, SIZEOF, FREE)
Alocarea dinamica este recomandata pentru rezervarea de spatiu pentru variabile. Este foarte greu de stiut dinainte care va fi cantitatea de date pe care o va procesa un program. Alocarea statica a unui tablou va conduce fie la risipirea in zadar a spatiului de memorie RAM (spatiu destul de scump) atunci cind se aloca o cantitate mult peste necesitati, fie la aparitia de erori atunci cind cantitatea de memorie alocata este mult sub necesitati. Alocarea statica se face in zona de date a programului in timpul generarii imaginii obiect a programului.
int a [20000]; float b[15000];
Este mult mai bine sa se aloce spatiu dupa necesitati (daca este posibil - cantitatea necesara de memorie sa nu fie mai mare decit cea disponibila).
Limbajul C permite programatorilor sa aloce si sa elibere dinamic zone de memorie, in timpul rularii programului. Functia care aloca memorie pentru o variabila este calloc(). Este necesara si utilizarea sizeof() ce determina necesarul de memorie. Free() elibereaza memoria alocata.
Un pointer poate fi declarat ca sa pointeze la o functie. Declararea unui astfel de pointer se face astfel :
int (*func_p)();
Parantezele din jurul *func_p sunt necesare,altfel compilatorul va intelege declaratia ca o declaratie de functie care returneaza un pointer la intreg. Pentru a atribui adresa unei functii pointerului, procedam astfel :
func_p = print;
unde print este numele functiei. Apelul functiei se realizeaza astfel (daca nu avem parametrii):
(*func_p)();
Daca functia returneaza valoare atunci avem :
i = (*func_p)();
Este posibil sa trimitem argumente unui program in C atunci cind sunt lansate in executie. Parantezele ce urmeaza functiei main sunt pentru acest scop. argc contine numarul de argumente primite iar argv[] este un tablou de pointeri fiecare continind cite un argument.
#include <stdio.h>
main( int argc, char *argv[] )
{
if( argc == 2 )
printf("Argumentul este : %s\n", argv[1]);
else if( argc > 2 )
printf("Prea multe argumente!\n");
else
printf("Nu avem nici un argument.\n");
}
Sa observam ca *argv[0] contine numele programului apelat, iar*argv[1] este un pointer la primul argument transmis si *argv[n] indica ultimul argument. Daca nu avem nici un argument, argc va avea valoarea 1, iar pentru n argumente argc va fi egal cu n+1.