Sunteți pe pagina 1din 96

Pointeri i tablouri

1. Pointerii i adresele
2. Pointerii i tablourile
3. Aritmetica adreselor
4. Pointerii spre caractere
5. Alocarea dinamic
6. Tablouri de pointeri; pointeri la pointeri
7. Tablouri multidimensionale
8. Iniializarea tablourilor de pointeri
9. Pointeri vs. tablouri multidimensionale
Pointeri
Un pointer este o variabil care conine adresa unui
obiect(alt variabil sau funcie).
Orice variabil are dou elemente caracteristice: valoarea
coninut n variabil (rvalue) i valoarea adresei locaiei de
memorie (lvalue).
Exemplu: a = a + b;
Pointerii sunt folosii mult n C deoarece:
- permit exprimarea unor operaii la un nivel mai sczut;
- conduc la un cod mai compact i mai eficient.
Trebuie folosii cu grij pentru a nu afecta claritatea i
simplitatea programelor.
Pointeri
Adrese de memorie
Adresa locaiei de memorie n care este stocat o variabil se
poate obine aplicnd operatorul de adres (operatorul &)
naintea numelui variabilei (operaie ntlnit frecvent n
exemplele anterioare, n cazul apelrii funciei scanf).
Operatorul de adres poate fi utilizat pentru obinerea valorii
adresei oricrei variabile. Secvena urmtoare ilustreaz acest
lucru: se tipresc valorile adreselor a dou variabile, ca
numere ntregi fr semn, n reprezentare hexazecimal:
int a;
float x;
...
printf("adresa lui a este %x \n", &a);
printf("adresa lui x este %x \n", &x);
Pointeri
Adrese de memorie
Valorile i formatul adreselor de memorie depind de arhitectura
calculatorului i de sistemul de operare sub care ruleaz. => Din
motive de portabilitate a programelor, dac se dorete declararea
unei variabile care s conin o adres de memorie, nu se vor utiliza
tipurile ntregi.
n C s-a definit un specificator de format special: %p, pentru
tiprirea valorilor reprezentnd adrese de memorie.
int a;
float x;
printf("adresa lui a este %p \n", &a);
printf("adresa lui x este %p \n ", &x);
Se recomand utilizarea specificatorului de format %p pentru
tiprirea adreselor (este o variant portabil).
Pointeri
Variabile pointer
Un pointer este o variabil care are ca valoare o adres de
memorie.
Spunem c variabila pointer indic locaia de
memorie de la adresa pe care o conine.
Cel mai comun caz este acela n care valoarea unei
variabile pointer este adresa unei alte variabile.
n C, pointerii se refer la un anumit tip: tipul datelor
coninute n locaia de memorie indicat de variabila pointer.
=> O variabil pointer n C este de regul pointer la un
anumit tip.
Pointeri
Variabile pointer
Declararea variabilelor pointer se face n felul urmtor:
tip * nume_variabila_pointer;
variabila nume_variabila_pointer va conine adrese de
zone de memorie alocate unor date de tipul tip.
* semnific faptul c variabila este pointer la
tipul respectiv.
De exemplu,
int *p;
definete o variabil p pointer la ntreg. Variabila p poate
conine adrese de locaii n care se memoreaz valori ntregi.
Pointeri
Variabile pointer
Declaraiile variabilelor pointer la un anumit tip pot
s apar n aceeai list cu variabilele obinuite de acel
tip:
int x, *p;
definete o variabil x de tip ntreg i o variabil p de
tip pointer la ntreg.
Se pot declara i pointeri generici, de tip void *
( tipul datelor indicate de ei nu este cunoscut).
Pointeri
Variabile pointer
Un pointer de tip void reprezint doar o adres de memorie
a unui obiect oarecare:
- dimensiunea zonei de memorie indicate i
interpretarea informaiei coninute, nu sunt definite;
- poate apare n atribuiri, n ambele sensuri, cu pointeri
de orice alt tip;
- folosind o conversie explicit de tip cu operatorul
cast : (tip *), el poate fi convertit la orice alt tip de
pointer (pe acelai calculator, toate adresele au aceeai
lungime).
Pointeri
Utilizare operatorii de adresare i derefereniere
Ca i n cazul variabilelor de orice tip, i variabilele pointer
declarate i neiniializate conin valori aleatoare.
Pentru a atribui variabilei p valoarea adresei variabilei x, se
folosete operatorul de adres ntr-o expresie de atribuire de forma:
p=&x;
n urma acestei operaii, se poate spune c pointerul p indic spre
variabila x.
Pentru a obine valoarea obiectului indicat de un pointer se
utilizeaz operatorul de derefereniere (indirectare) (operatorul *):
Dac p este o variabil de tip pointer care are ca valoare adresa
locaiei de memorie a variabilei ntregi x (p indic spre x) atunci
expresia *p reprezint o valoare ntreag (valoarea variabilei x).
Pointeri
Utilizare operatorii de adresare i derefereniere
int x, *p;
x=3;
p=&x;
printf("%d", *p);
*p=5;
printf("%d", x);
Expresia *p care se afieaz cu primul printf reprezint
valoarea obiectului indicat de p, deci valoarea lui x. Se va afia
valoarea 3.
Atribuirea *p=5; modific valoarea obiectului indicat de p,
adic valoarea lui x. Ultimul printf din secven afieaz 5 ca
valoare a lui x.
Pointeri
Utilizare operatorii de adresare i derefereniere
ntotdeauna cnd se aplic operatorul * asupra unui pointer ptr
declarat ca i tip * ptr;
expresia obinut prin derefereniere (*ptr) este de tipul tip.
n cazul unui pointer generic (de tip void *), dereferenierea
trebuie precedat de cast (altfel nu tim ce tip de valori indic
pointerul).
Cnd se utilizeaz operatorul de derefereniere trebuie avut grij
ca variabila pointer asupra creia se aplic s fi fost iniializat,
adic s conin adresa unei locaii de memorie valide. O secven
ca cea de mai jos este incorect i poate genera erori grave la
execuie: int *p; *p=3;
Pentru a indica adres inexistent, se utilizeaz ca valoare a unui
pointer, constanta NULL.
Pointeri
Utilizare operatorii de adresare i derefereniere
Variabilele pointer pot fi utilizate n expresii i direct,
fr indirectare.
int x, *p1, *p2;
x=3;
p1=&x;
p2=p1; /* atribuire de pointeri */
Atribuirea p2=p1; copiaz coninutul lui p1 n p2, deci p2
va fi fcut s indice spre acelai obiect ca i p1. n contextul
secvenei, p2 va indica tot spre x.
Pointeri
Pointerii i adresele
Exemple:
int x = 1, y = 2, z[10];
int *ip; /* ip este un pointer la int */
ip = &x; /* ip indica acum spre x */
y = *ip; /* y este acum 1 */
*ip = 0; /* x este acum 0 */
ip = &z[0]; /* ip indica acum spre z[0] */
Dac ip indic spre x, atunci *ip poate aprea n orice
context n care ar putea aprea x, deci
*ip = *ip + 10; l incrementeaz pe x cu 10.
Pointeri
Pointerii i adresele
Operatorii unari * i & au o preceden mai mare dect
operatorii aritmetici, n consecin:
y = *ip + 1
preia obiectul spre care indic ip, l adun cu 1 i atribuie
rezultatul lui y, iar
*ip += 1
incrementeaz obiectul spre care indic ip, ca i
++ *ip i
(*ip )++
Operatorii unari precum * i ++ se asociaz de la dreapta la
stnga.
Pointeri
Pointerii i adresele
Pointerii fiind variabile, pot fi folosii i fr a fi
derefereniai.
Dac iq este un alt pointer spre tipul int,
iq = ip
copiaz coninutul lui ip n iq, fcnd astfel ca iq s indice
spre ceea ce indic ip.
Operatorul de adres (&) se aplic numai obiectelor din
memorie: variabile i elemente de tablou. Operatorul & nu
poate fi aplicat expresiilor, constantelor sau variabilelor de
tip register.
Tablouri
Exemple de utilizare
Prelucrarea unui tablou unidimensional:
#define N 10
int t[N]; /* tablou de N elemente */
int i; /* variabila contor */
for (i = 0; i < n; i++)
*prelucrare t[i]
Exemplul 1: S se citeasc N numere ntregi i s se
memoreaz n irul t1. S se construiasc apoi irul t2 cu
elementele lui t1 n ordine invers.
Tablouri
Exemple de utilizare
#include <stdio.h>
#define N 20
int main (void) {
int t1[N], t2[N];
int i;
/* citeste elementele sirului t1 */
printf(Introduceti cele %d elemente ale sirului \n, N);
for (i = 0; i < N; i++)
scanf(%d, &t1[i]);
Tablouri
Exemple de utilizare
/* construieste t2 */
for (i = 0; i < N; i++)
t2[i] = t1[N 1 - i];
/* afiseaza elementele sirului t2 */
printf(Sirul in ordine inversa este: \n);
for(i = 0; i < N; i++)
printf(%d , t2[i]);
printf(\n);
return 0;
}
Tablouri
Exemple de utilizare
Citirea elementelor unei matrici cu N linii i Mcoloane:
printf(Introduceti elementele matricii: \n);
for(i = 0; i < N; i++)
for(j = 0; j< M; j++)
scanf(%d, &a[ i ][ j ]);
Afiarea elementelor unei matrici:
for(i = 0; i < N; i++) {
for(j = 0; j < M; j++)
printf(%4d, a[ i ][ j ]);
printf(\n);
}
Tablouri
Exemple de utilizare
Exemplul 2: S se calculeze suma elementelor de sub
diagonala principal a unei matrici ptrate cu n (n 20) linii i
coloane.
#include <stdio.h>
int main(void)
{
int a[20][20];
int suma, i, j, n;
printf(Introduceti dimensiunea matricii (<=20):);
scanf(%d, &n);
Tablouri
Exemple de utilizare
/* citirea elementelor matricii */
for(i = 0; i < n; i++)
for(j = 0; j< n; j++) {
printf(a[%d][%d] = , i+1, j+1);
scanf(%d, &a[ i ][ j ]);
}
/* calculul si afisarea sumei */
for(i = 0, suma = 0; i < n; i++)
for(j = 0; j< i; j++)
suma += a[ i ][ j ];
printf(Suma calculata este: %d\n, suma);
return 0;
}
Tablouri
Exemple de utilizare
Transmiterea tablourilor ca parametri la funcii.
Exemplul 3: S se scrie un program care calculeaz suma a
dou matrici, S1=A+B precum i suma S2=S1+A.
#include <stdio.h>
int n, m;
void citire (int a[10][10])
{
int i, j;
printf(Introduceti elementele:);
Tablouri
Exemple de utilizare
for(i = 0; i < n; i++)
for(j = 0; j< m; j++) {
printf(a[%d][%d] = , i+1, j+1);
scanf(%d, &a[ i ][ j ]); }
}
void suma(int a[10][10], int b[10][10], int s[10][10]) {
int i, j;
for(i = 0; i < n; i++)
for(j = 0; j< m; j++)
s[ i ][ j ] = a[ i ][ j ] + b[ i ][ j ];
}
Tablouri
Exemple de utilizare
void tiparire(int a[10][10]) {
int i, j;
for(i = 0; i < n; i++) {
for(j = 0; j< m; j++)
printf(%8d, a[ i ][ j ]);
printf(\n);
}
int main(void) {
int a[10][10], b[10][10], s1[10][10], s2[10][10];
printf(Introduceti dimensiunile matricilor:);
scanf(%d%d, &n, &m);
Tablouri
Exemple de utilizare
/* Citirea matricilor */
printf(Introduceti elementele matricii a \n);
citire(a);
printf(Introduceti elementele matricii b \n);
citire(b);
/* Tiparirea matricilor */
printf(Matricea a este :\n);
tiparire(a);
printf(Matricea b este :\n);
tiparire(b);
Tablouri
Exemple de utilizare
/* Calculul si afisarea matricilor suma */
suma(a, b, s1);
printf(Matricea suma dintre a si b este:\n);
tiparire(s1);
suma(s1, a, s2);
printf(Matricea suma dintre s1 si a este:\n);
tiparire(s2);
return 0;
}
Pointeri i tablouri
Pointerii i tablourile
Orice operaie care poate fi realizat cu ajutorul tablourilor
(variabilelor cu indici) poate fi, de asemenea, efectuat cu
ajutorul pointerilor.
Declaraia
int a[10];
definete un tablou de dimensiune 10, adic un bloc de 10
elemente consecutive notate a[0], a[1], ..., a[9].
a:
a[0] a[1] a[9]
Pointeri i tablouri
Pointerii i tablourile
Dac pa este un pointer spre un ntreg, declarat ca
int *pa;
atunci atribuirea
pa = &a[0];
l seteaz pe pa s indice spre elementul zero al lui a; mai
precis, pa conine adresa lui a[0].
pa:
a:
a[0]
Pointeri i tablouri
Pointerii i tablourile
Instruciunea
x = *pa;
va copia coninutul lui a[0] n x.
Dac pa indic spre un anumit element de tablou, atunci,
prin definiie, pa + 1 indic spre urmtorul element, pa + i
indic spre o locaie aflat la i elemente dup pa, iar pa i
indic spre o locaie aflat cu i elemente nainte.
pa:
pa+1:
pa+2:
a[0]
a:
Pointeri i tablouri
Pointerii i tablourile
Remarcile anterioare sunt valabile indiferent de tipul sau
mrimea variabilelor din tabloul a.
nelesul expresiei adun 1 la un pointer i, prin extensie,
ntreaga aritmetic a pointerilor este c pa + 1 indic spre
urmtorul obiect, iar pa + i indic spre al i-lea obiect de dup
pa.
Prin definiie, valoarea unei variabile sau expresii de tip
tablou este adresa elementului zero al tabloului.
pa = &a[0];
poate fi scris
pa = a;
Pointeri i tablouri
Pointerii i tablourile
O referin la a[i] poate fi scris i ca *(a+i); cele dou
forme sunt echivalente.
Aplicnd operatorul & ambelor pri ale echivalenei,
rezult c &a[i] i a+i sunt forme identice.
Dac pa este un pointer, n expresii acesta poate fi folosit
cu un indice; pa[i] este identic cu *(pa+i).
Concluzie: o expresie cu tablou i indice este echivalent
cu una scris ca pointer i distan de deplasare.
Pointeri i tablouri
Pointerii i tablourile
Exist totui o diferen ntre un nume de tablou i un
pointer.
Un pointer este o variabil:
pa=a i pa++ sunt expresii legale
Un nume de tablou nu este o variabil:
a=pa i a++ sunt expresii ilegale
Cnd un nume de tablou este transmis unei funcii, ceea ce
se transmite este o adres reprezentnd locaia elementului
iniial.
n interiorul funciei apelate, acest argument este o variabil
local; deci un nume de tablou sub form de parametru
este un pointer, adic o variabil care conine o adres.
Pointeri i tablouri
Pointerii i tablourile
n limbajul C, numele unui tablou este echivalent cu un pointer
care conine adresa primului su element.
Fie urmtoarele declaraii: tab un tablou de elemente de tip T i p
un pointer la tipul T:
T tab[N];
T * p;
Cele 3 atribuiri urmtoare sunt echivalente, si au ca efect faptul c
p va indica adresa primului element al tabloului tab:
p=tab; p=&tab; p=&tab[0];
Atribuirea tab=p; nu este permis.
Numele tabloului indic o adres constant: adresa primului
element al tabloului este o valoare constant din momentul n care
tabloul a fost alocat static, deci nu mai poate fi modificat printr-o
atribuire ca cea de mai sus.
Pointeri i tablouri
Pointerii i tablourile
/* strlen: returneaza lungimea sirului s */
int strlen(char *s) {
int n;
for (n = 0; *s !=\0; s++)
n++;
return n; }
Sunt corecte toate apelurile urmtoare:
strlen(buna ziua); /* constanta sir */
strlen(tablou); /* char tablou[100]; */
strlen(ptr); /* char *ptr; */
Pointeri i tablouri
Pointerii i tablourile
Ca parametri formali din definiia unei funcii, declaraiile
char s[ ]; i char *s; sunt echivalente.
Se prefer char *s; deoarece precizeaz ntr-un mod mai
explicit faptul c parametrul este un pointer.
Cnd numele unui tablou este transmis unei funcii, funcia
poate considera, dup cum prefer, c i-a fost transmis fie un
tablou, fie un pointer. Poate folosi chiar ambele notaii dac
acest lucru este considerat potrivit i clar.
Se poate transmite funciei doar o parte dintr-un tablou:
f(&a[2]) sau f(a+2)
n interiorul lui f declaraia parametrului este:
f(int tabl[ ]) { ... } sau f(int *tabl) { ... }
Pointeri i tablouri
Aritmetica adreselor
Toate operaiile de manipulare a pointerilor iau n
considerare n mod automat dimensiunea obiectului spre care
se indic.
Operaiile permise cu pointeri sunt:
- atribuirea pointerilor de acelai tip;
- adunarea unui pointer cu un ntreg sau scderea unui ntreg;
- scderea sau compararea a doi pointeri care indic spre
elemente ale aceluiai tablou;
- atribuirea valorii zero (NULL) sau compararea cu aceasta.
Pointeri i tablouri
Aritmetica pointerilor-incrementare i decrementare
Operatorii unari de incrementare i decrementare se pot
aplica asupra variabilelor de tip pointer, n format prefix i
postfix.
T * p;
p++; p--;
++p; --p;
Operatorul de incrementare aplicat asupra unui operand de
tip pointer la tipul T mrete adresa coninut de operand cu
numrul de octei necesar pentru a pstra o dat de tipul T (se
adun sizeof(T)):
T tab[N];
T * p;
int i;
p=&tab[i];
p++;
p va contine adresa elementului tab[i+1];
Pointeri i tablouri
Aritmetica pointerilor Adunarea i scderea unui ntreg
Dac p este un pointer la tipul T i n o valoare ntreag,
expresia p+n mrete valoarea lui p cu n*sizeof(T). Analog,
expresia p-n micoreaz valoarea lui p cu n*sizeof(T);
Dac se declar un tablou tab i se utilizeaz ca n secvena
de mai jos:
T tab[N];
T * p;
p=tab;
expresia p+n va indica spre elementul tab[n].
Valoarea elementului tab[n] poate fi reprezentat i
prin expresia *(p+n).
Variabilele cu indici se pot nlocui prin expresii cu pointeri.
Pointeri i tablouri
Aritmetica pointerilor - iniializare
n general, un pointer poate fi iniializat ca orice alt
variabil dei, n mod normal, singurele valori care au sens
sunt zero sau o expresie care implic adresele unor date
definite anterior.
Constanta zero poate fi atribuit unui pointer i un pointer
poate fi comparat cu constanta zero. Constanta simbolic
NULL (definit n <stdio.h>) este adesea folosit n locul
lui zero, ca o modalitate de a indica mai clar c aceasta este
o valoare special pentru un pointer.
Pointeri
Aritmetica pointerilor
Compararea a doi pointeri
Doi pointeri care indic spre elementele aceluiai tablou pot
fi comparai, folosind operatorii de relaie i de egalitate.
Dac p i q sunt doi pointeri spre elementele tab[i] respectiv
tab[j] ale unui tablou, comparaia p<q este adevarat dac i
numai dac i<j.
Diferena a doi pointeri
Doi pointeri care indic spre elementele aceluiai tablou pot
fi sczui. Dac p i q sunt doi pointeri spre elementele tab[i]
i respectiv tab[i+n], diferena q-p are valoarea egal cu n.
Pointeri i tablouri
Aritmetica pointerilor
Dac p i q indic spre elemente ale aceluiai tablou i p<q,
atunci q-p+1 reprezint numrul de elemente de la p la q
inclusiv.
/* strlen: returneaza lungimea sirului s */
int strlen(char *s)
{
char *p = s; /* initializare cu primul element al sirului*/
while (*p != \0)
p++;
return p-s; /* returneaza nr. de caractere parcurse*/
}
Pointeri i tablouri
Aritmetica pointerilor
Sunt ilegale urmtoarele operaii aritmetice asupra
pointerilor:
- adunarea a doi pointeri;
- nmulirea sau mprirea pointerilor;
- deplasarea sau aplicarea unor mti;
- adunarea cu valori de tip float sau double ;
- atribuirea a doi pointeri de tipuri diferite fr a folosi
operatorul cast (cu excepia tipului void *).
Pointeri
Prelucrarea tablourilor
Exemplu: parcurgerea elementelor unui tablou.
void tiparire1(int tab[ ], int N) {
int i;
for (i=0; i<N; i++)
printf("%d ",tab[ i ]); }
void tiparire2(int tab[ ],int N) {
int * ptr;
for (ptr=tab; ptr<tab + N; ptr++)
printf("%d ", *ptr); }
void tiparire3(int *tab, int N) {
int * ptr;
for (ptr=tab; ptr<tab + N; ptr++)
printf("%d ", *ptr); }
Pointeri
Prelucrarea tablourilor
void tiparire4(int *tab, int N) {
int i;
for (i=0; i<N; i++, tab++)
printf("%d ", *tab);
}
Apelurile celor 4 funcii au acelai efect:
void main(void) {
int a[5]={1,2,3,4,5};
tiparire1(a,5);
tiparire2(a,5);
tiparire3(a,5);
tiparire4(a,5);
}
Pointeri i tablouri
Pointerii spre caractere
O constant ir, scris sub forma:
Eu sunt un sir de caractere
este un tablou de caractere. n reprezentarea intern, tabloul se
termin cu caracterul \0, pentru ca n program s se poat
gsi sfritul irului. Lungimea spaiului de stocare este de
aceea cu unu mai mare dect numrul de caractere aflate ntre
ghilimele.
Poate cea mai frecvent utilizare a constantelor ir este ca
argumente ale funciilor:
printf(Buna ziua\n);
Accesul la acest ir se face printr-un pointer la tipul caracter;
funcia printf primete un pointer ctre nceputul tabloului de
caractere. Pointerul acceseaz deci primul element al irului.
Pointeri i tablouri
Pointerii spre caractere
Constantele ir nu trebuie s fie neaprat argumente de
funcii. Dac pmesaj este declarat ca:
char *pmesaj;
atunci instruciunea
pmesaj = acum este timpul;
i atribuie lui pmesaj un pointer ctre tabloul de caractere.
a c um e s t e t i mp u l \0
pmesaj:
Pointeri i tablouri
Pointerii spre caractere
Exist o deosebire important ntre urmtoarele definiii:
char tmesaj[ ] = acum este timpul; /* un tablou */
char *pmesaj = acum este timpul; /* un pointer */
- Caracterele din cadrul tabloului pot fi modificate individual,
dar tmesaj va indica ntotdeauna ctre acelai spaiu de stocare.
- pmesaj este un pointer iniializat s indice ctre o constant
ir; poate fi modificat ulterior astfel nct s indice alt locaie,
dar rezultatul este nedefinit dac se modific coninutul irului.
acum este timpul \0
acum este timpul \0 pmesaj:
tmesaj:
Pointeri i tablouri
Pointerii spre caractere
Exemplul 1: funcia strcpy, care copiaz un ir n alt ir.
/* strcpy: copiaza t in s; versiunea cu indici de tablou */
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((s[ i ] = t[ i ]) != \0)
i++;
}
Pointeri i tablouri
Pointerii spre caractere
/* strcpy: copiaza t in s; versiunea cu pointeri */
void strcpy(char *s, char *t)
{
while ((*s = *t) != \0) {
s++;
t++;
}
}
Deoarece argumentele sunt transmise prin valoare, strcpy
poate folosi parametrii s i t cum dorete.
Pointeri i tablouri
Pointerii spre caractere
n practic, strcpy nu ar fi scris ca n exemplele anterioare.
Programatorii C experimentai ar prefera:
/* strcpy: copiaza t in s; versiunea cu pointeri 2*/
void strcpy(char *s, char *t)
{
while ((*s++ = *t++) != \0)
;
}
Aceast versiune mut incrementarea lui s i t n partea de
testare a ciclului. n final se copiaz n s inclusiv terminatorul
\0.
Pointeri i tablouri
Pointerii spre caractere
Putem realiza nc o compactare, observnd c nu este
necesar comparaia cu \0 pentru a controla ciclul (ntrebarea
este doar dac expresia este zero). Efectul final este copierea
caracterelor din t n s, pn la i inclusiv terminatorul \0.
/* strcpy: copiaza t in s; versiunea cu pointeri 3*/
void strcpy(char *s, char *t)
{
while (*s++ = *t++)
;
}
- Convenia de notaie este demn de luat n seam, iar stilul
din acest variant trebuie nsuit (se ntlnete frecvent).
Pointeri i tablouri
Pointerii spre caractere
Exemplul 2: funcia strcmp, care compar lexicografic
dou iruri de caractere (s i t) i returneaz o valoare:
- negativ, dac s < t
- zero, dac s == t
- pozitiv, dac s > t.
Valoarea se obine prin scderea caracterelor de la prima
poziie n care t i s difer.
Pointeri i tablouri
Pointerii spre caractere
/* strcmp: compara sirurile de caractere s si t;
versiunea cu indici de tablouri */
int strcmp (char *s, char *t)
{
int i;
for (i = 0; s[ i ] == t[ i ]; i++)
if (s[ i ] == \0)
return 0;
return s[ i ] t[ i ];
}
Pointeri i tablouri
Pointerii spre caractere
/* strcmp: compara sirurile de caractere s si t;
versiunea cu pointeri */
int strcmp (char *s, char *t)
{
for ( ; *s == *t; s++, t++)
if (*s == \0)
return 0;
return *s *t;
}
Pointeri i tablouri
Pointerii spre caractere
ntre operatorii *, ++ i -- pot s apar urmtoarele
combinaii. De exemplu:
*--p
l decrementeaz pe p nainte de a prelua caracterul spre care
indic p.
Perechea de expresii:
*p++ = val; /* introdu val in stiva */
val = *--p; /* scoate varful stivei si copiaza-l in val */
constituie instruciunile standard pentru introducerea i
scoaterea unui element din stiv.
Pointeri i tablouri
Alocarea dinamic
Multe aplicaii pot fi optimizate dac memoria necesar
stocrii datelor lor este alocat dinamic n timpul execuiei
programului.
Alocarea dinamic de memorie nseamn alocarea de zone
de memorie i eliberarea lor n timpul execuiei programelor.
Zona de memorie n care se aloc, la cerere, datele
dinamice este diferit de zona de memorie n care se aloc
datele statice(stiva de date) i se numete HEAP.
Spaiul de memorie alocat dinamic, la cerere, este accesibil
programatorului printr-un pointer care conine adresa sa.
Pointerul care indic un obiect din HEAP este de regul
stocat ntr-o variabil alocat static.
Pointeri i tablouri
Alocarea dinamic
Momentele alocrii i cel al eliberrii zonei de memorie pot
fi aleatorii pe durata execuiei programului i sunt stabilite de
programator.
Pentru alocarea i eliberarea memoriei alocate dinamic exist
funcii oferite de biblioteca C standard.
Funciile de gestionare a memoriei au prototipurile n
fiierele alloc.h i stdlib.h. Cele mai utilizate dintre acestea
sunt:
malloc alocarea zonei de memorie
free eliberarea zonei de memorie ocupat cu malloc
Pointeri i tablouri
Alocarea dinamic
Funcia de alocare dinamic a memoriei (malloc)
void * malloc(size_t size);
Funcia aloc n HEAP un bloc de dimensiune size; dac
operaia reuete returneaz un pointer la blocul alocat,
altfel returneaz NULL.
- Este posibil ca cererea de alocare s nu poat fi satisfcut
dac nu mai exist suficient memorie n zona de HEAP sau
nu exist un bloc compact de dimensiune cel puin egal cu
size.
- Dimensiunea size se specific n octei.
Pointeri i tablouri
Alocarea dinamic
Rezultatul funciei malloc este un pointer de tip void.
Se va folosi operatorul cast pentru conversii de tip la
atribuirea rezultatului funciei ctre un pointer de un anumit
tip.
Alocarea dinamic a unei zone pentru o valoare de tip int :
int * ip;
ip=(int * )malloc(sizeof(int));
*ip=5;
Pointeri i tablouri
Alocarea dinamic
Chiar dac variabila pointer care indic o astfel de zon de
memorie nu mai exist (i-a depit durata de via) zona
alocat rmne n continuare ocupat, devenind ns
inaccesibil pentru program.
O eroare frecvent de acest gen poate apare la utilizarea
variabilelor dinamice n cadrul funciilor:
void fct()
{
int * p;
p=(int *)malloc(10* sizeof(int));
}
Pointeri i tablouri
Alocarea dinamic
Funcia de eliberare dinamic a memoriei (free)
void free(void *p);
Funcia elibereaz un bloc alocat anterior cu malloc;
adresa de nceput a blocului este transmis ca argument, la
apelul funciei.
- Transferul unei valori invalide (un pointer la un bloc de
memorie care nu a fost rezultatul unui apel al funciei
malloc) poate compromite funcionarea sistemului de
alocare.
Pointeri i tablouri
Alocarea dinamic
Un tablou poate fi alocat dinamic printr-o secven ca cea de
mai jos:
TIP * p;
p= (TIP *) malloc(N*sizeof(TIP));
Pointerul p va indica un bloc suficient de mare pentru a
conine N elemente de tipul TIP.
n continuare, variabila p poate fi utilizat ca i cum ar fi fost
declarat ca un tablou de forma:
TIP p[ N ];
Avantajul alocrii dinamice a unui tablou este c
dimensiunea sa poate fi specificat doar n timpul execuiei.
Pointeri i tablouri
Alocarea dinamic
Exemplul 1: Alocarea dinamic de memorie pentru un tablou
de n numere ntregi:
#include <stdio.h>
#include <stdlib.h>
main(void)
{
int n;
int * tab;
int i;
printf("Introduceti numarul de elemente: \n");
scanf("%d", &n);
Pointeri i tablouri
Alocarea dinamic
if ((tab=(int *)malloc(n * sizeof(int)))==NULL) {
printf("Eroare alocare dinamica memorie !\n");
exit(1);
}
for (i=0; i<n; i++)
scanf("%d", &tab[ i ]);
for (i=0; i<n; i++)
printf("%d ", tab[ i ]);
free(tab);
}
Pointeri i tablouri
Alocarea dinamic
Exemplul 2:S se citeasc de la tastatur un ir de caractere sir1.
S se creeze o copie dinamic a acestuia, notat sir2:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
main(void) {
char sir1[40], *sir2;
printf("introduceti un sir de caractere: \n");
scanf("%40s", sir1);
if ((sir2=(char *)malloc(strlen(sir1)+1))==NULL) {
printf("eroare aloc. dinamica memorie !\n");exit(1);}
strcpy(sir2, sir1);
printf("copia sirului este: %s \n", sir2); .......... }
Pointeri i tablouri
Alocarea dinamic
Exemplul 3: Definirea si utilizarea unui vector alocat dinamic
( varianta 2 ):
main( ) {
int n,i; int * a;
printf ("n="); scanf ("%d", &n);
a=(int *) calloc (n, sizeof(int));
// sau: a=(int*) malloc (n*sizeof(int));
printf ("componentele vectorului: \n");
for (i=0; i<n; i++)
scanf ("%d", &a[ i ]); // sau scanf ("%d", a+i);
for (i=0; i<n; i++)
printf ("%d ",a[ i ]); // sau printf ("%d ", *(a+i));
}
Pointeri i tablouri
Alocarea dinamic
Exemplul 4: Alocarea dinamic a unei matrici:
main () {
int ** a; int i, j, nl, nc;
printf ("nr. linii="); scanf ("%d",&nl);
printf ("nr. col. ="); scanf ("%d",&nc);
// memorie pentru vectorul de pointeri la linii
a = (int**) malloc (nl*sizeof(int*));
for (i=0; i < nl; i++)
// aloca memorie pentru fiecare linie i
a[i] = (int*) calloc (nc, sizeof(int)); // o linie
..........................
}
Pointeri i tablouri
Alocarea dinamic
Pentru o matrice alocat dinamic, cu elemente
ntregi, notaia a[i][j] este interpretat astfel :
- a[i] conine un pointer (o adresa b)
- b[j] sau b+j conine ntregul din poziia
"j" a vectorului cu adresa "b".
Pointeri i tablouri
Alocarea dinamic
Exemplul 5: Se citete un numr necunoscut de valori ntregi
ntr-un vector extensibil:
#define INCR 100 //cu cat creste vectorul la fiecare realocare
main() {
int n, i;
float x, * v; // v = adresa vector
n=INCR; i=0;
v = (float *)malloc (n*sizeof(float)); //alocare initiala
while ( scanf("%f",&x) != EOF) {
if (++i == n) { // daca este necesar
n= n+ INCR; //creste dimensiunea vectorului
v=(float *) realloc (v, n*sizeof(float)); }
v[i]=x; // memorarea lui x in vector }...........
}
Pointeri i tablouri
Alocarea dinamic - concluzii
Variabilele alocate n HEAP sunt dinamice. Ele se numesc
astfel pentru apar i pot s dispar pe parcursul execuiei
programului, n mod dinamic. Alocarea spaiului necesar
pentru o asemenea variabil ca i relocarea (eliberarea) lui, se
efectueaz n mod explicit n program, prin apelurile malloc i
free.
Timpul scurs din momentul crerii locaiei unei variabile i
pn la desfiinarea acestei locaii se numete durata de via
a variabilei. Pentru variabilele obinuite (automatice), durata
de via coincide cu durata de activare a blocului de care
aparin. Pentru variabilele dinamice apariia i dispariia lor
are loc independent de structura textului programului, iar
durata de via este intervalul de timp scurs ntre apelurile
funciilor malloc i free pentru acele variabile.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
Deoarece pointerii sunt la rndul lor variabile, ei pot fi stocai
n tablouri, ca i alte variabile.
Exemplu: sortarea unor linii de text de lungimi diferite.
- Dac liniile de sortat sunt stocate una n continuarea alteia
ntr-un lung ir de caractere, atunci fiecare linie poate fi
accesat printr-un pointer ctre primul su caracter.
- Pointerii pot fi stocai ntr-un tablou.
- Dou linii pot fi comparate transmind pointerii lor funciei
strcmp. Cnd dou linii trebuie interschimbate, se
interschimb pointerii acestora din tabloul de pointeri, nu
liniile n sine.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
Acest lucru elimin dubla problem a gestionrii
complicate a spaiului de stocare i a resurselor mari
necesare mutrii liniilor n sine.
defghi
jklmnopqr
abc
defghi
jklmnopqr
abc
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
Procesul de sortare se desfoar n trei pai:
citeste toate liniile de la intrare
sorteaza-le
tipareste-le in ordine
mprim programul n funcii care s corespund acestei
diviziuni naturale, funcia principal controlnd celelalte
funcii.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
Rutina de citire trebuie:
- s culeag i s salveze caracterele de pe fiecare linie i s
creeze un tablou de pointeri ctre linii;
- s numere liniile citite, deoarece aceast informaie este
necesar pentru sortare i tiprire.
- s returneze, de exemplu, -1 (numr de linii greit), dac
exist prea multe date de intrare.
Rutina de scriere trebuie doar s tipreasc liniile n ordinea
n care apar acestea n tabloul de pointeri.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
#include <stdio.h>
#include <string.h>
#define MAXLINII 5000 /* nr maxim de linii ce vor fi
sortate */
char *ptrlinie[MAXLINII]; /*pointeri catre liniile de text */
int citestelinii(char *ptrlinie[ ], int nlinii);
void tiparestelinii(char *ptrlinie[ ], int nlinii);
void qsort(char *ptrlinie[ ], int stanga, int dreapta);
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
int main()
{
int nlinii; /* numarul de linii de intrare citite */
if ((nlinii = citestelinii(ptrlinie, MAXLINII)) >=0) {
qsort (ptrlinie, 0, nlinii-1);
tiparestelinii(ptrlinie, nlinii);
return 0;
} else {
printf(eroare: prea multe date de sortat\n);
return 1;
} }
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
#define MAXLUNG 1000 /* lungimea maxima a
unei linii de intrare */
int preialinie(char *, int);
/* citestelinii: citeste liniile de intrare */
int citestelinii(char *ptrlinie[ ], int maxlinii)
{
int lung, nlinii;
char *p, linie[ MAXLUNG ];
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
nlinii = 0;
while ((lung = preialinie(linie, MAXLUNG)) > 0)
if (nlinii >=maxlinii ||(p=(char *)malloc(lung))==NULL)
return -1;
else {
linie[lung-1] = \0; /* sterge caracterul rand nou */
strcpy(p, linie);
ptrlinie[nlinii++] = p;
}
return nlinii;
}
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
/* tiparestelinii: tipareste liniile de iesire */
void tiparestelinii(char *ptrlinie[ ], int nlinii)
{
int i;
for (i = 0; i < nlinii; i++)
printf(%s\n, ptrlinie[ i] );
}
Funcia preialinie este cea descris anterior.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
Principala noutate este declaraia lui ptrlinie:
char *ptrlinie[MAXLINII]
precizeaz c:
- ptrlinie este un tablou de MAXLINII elemente, fiecare
element fiind un pointer ctre char;
- ptrlinie[i] este un pointer spre un caracter, iar *ptrlinie[i]
este caracterul spre care indic pointerul, adic primul caracter
al celei de a i-a linii de text salvat.
ptrlinie fiind el nsui numele unui tablou, poate fi tratat ca
un pointer, iar funcia tiparestelinii poate fi scris i altfel:
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
/* tiparestelinii: tipareste liniile de iesire */
void tiparestelinii(char *ptrlinie[ ], int nlinii)
{
while (nlinii -- > 0)
printf(%s\n, *ptrlinie++);
}
La nceput, *ptrlinie indic spre prima linie; fiecare
incrementare l avanseaz pe acesta la urmtorul pointer de
linie, n timp ce nlinii descrete de fiecare dat cu unu.
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
/* qsort: sorteaza v[stanga] ... v[dreapta] in ordine
crescatoare */
void qsort(char *v[ ], int stanga, int dreapta)
{
int i, ultim;
void swap(char *v[ ], int i, int j);
if (stanga >= dreapta) /* nu face nimic daca tabloul */
return; /* contine mai putin de 2 elemente */
swap(v, stanga, (stanga + dreapta)/2);
ultim = stanga;
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
for (i = stanga + 1; i <= dreapta; i++)
if (strcmp(v[i], v[stanga]) < 0)
swap(v, ++ultim, i);
swap(v, stanga, ultim);
qsort(v, stanga, ultim-1);
qsort(v, ultim+1, dreapta);
}
Pointeri i tablouri
Tablouri de pointeri; pointeri la pointeri
/* swap: interschimba v[i] cu v[j] */
void swap(char *v[ ], int i, int j)
{
char *temp;
temp = v[ i ];
v[ i ] = v[ j ];
v[ j ] = temp;
}
Pointeri i tablouri
Tablouri multidimensionale
Limbajul C admite i tablouri multidimensionale
dreptunghiulare dei, n practic, acestea sunt mult mai puin
folosite dect tablourile de caractere.
Exemplu: conversia datei din zi a lunii n zi a anului i
invers, prin intermediul a dou funcii:
zi_a_anului convertete ziua i luna n ziua din an
luna_zi convertete ziua din an n lun i zi. Argumentele
corespunztoare lunii i zilei vor fi pointeri.
static char tabzi[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
Pointeri i tablouri
Tablouri multidimensionale
/* zi_a_anului: calculeaza ziua din an avand luna si
ziua */
int zi_a_anului (int an, int luna, int zi)
{
int i, bisect;
bisect = an%4 == 0 && an%100 != 0 || an%400 == 0;
for (i = 1; i < luna; i++)
zi += tabzi[bisect] [i];
return zi;
}
Pointeri i tablouri
Tablouri multidimensionale
/* luna_zi: calculeaza luna si ziua avand ziua din an */
void luna_zi(int an, int zi_an, int *pluna, int *pzi)
{
int i, bisect;
bisect = an%4 == 0 && an%100 != 0 || an%400 == 0;
for (i = 1; zi_an > tabzi[bisect] [i]; i++)
zi_an -= tabzi[bisect] [i];
*pluna = i;
*pzi = zi_an;
}
Pointeri i tablouri
Tablouri multidimensionale
Valoarea aritmetic a unei expresii logice (variabila bisect),
este fie zero (fals) fie unu (adevrat), deci poate fi folosit ca
indice pentru tabloul tabzi.
S-a folosit tipul char pentru tabloul tabzi, pentru a se ilustra
o ntrebuinare specific acestui tip, stocarea ntregilor mici,
chiar dac nu sunt caractere.
n C, un tablou bidimensional este de fapt un tablou
unidimensional, ale crui elemente reprezint fiecare cte un
vector. Indicii sunt scrii
tabzi[i][j] /* [linie] [col] /*
i nu
tabzi[i,j] /* GRESIT */
Pointeri i tablouri
Tablouri multidimensionale
Elementele se stocheaz pe linii, deci cel mai din dreapta indice
(coloana ), variaz cel mai repede dac elementele sunt accesate n
ordinea n care sunt stocate.
Un tablou se iniializeaz cu o list de valori de iniializare
cuprins ntre acolade, fiecare linie a unui tablou bidimensional
este iniializat cu sublista corespunztoare. La nceputul tabloului
s-a plasat o coloan de zerouri pentru ca numerele coloanelor s
poat varia ntre 1 i 12, n loc de 0 i 11.
Dac un tablou bidimensional trebuie transmis unei funcii,
declaraia de parametru din funcie trebuie s conin, obligatoriu,
numrul de coloane; numrul de linii este irelevant, deoarece ceea
ce se transmite este un pointer la un tablou de linii, n care fiecare
linie este un vector de 13 elemente de tip int.
Pointeri i tablouri
Tablouri multidimensionale
Se transmite un pointer ctre obiecte care sunt vectori de 13
elemente de tip int.
Dac tabloul tabzi ar trebui transmis unei funcii f, declaraia
lui f ar putea fi
f(int tabzi[2][13]) { ... }
dar poate fi i
f(int tabzi[ ][13]) { ... }
care precizeaz c parametrul este un pointer la un vector de
13 ntregi.
Pointeri i tablouri
Iniializarea tablourilor de pointeri
Exemplu: o funcie nume_luna(n), care returneaz un
pointer la un ir de caractere ce conine numele celei de a n-a
luni.
char *nume_luna(int n) {
static char *nume[ ] = {
Luna inexistenta,
Ianuarie, Februarie, Martie, Aprilie,
Mai, Iunie, Iulie, August,
Septembrie, Octombrie, Noiembrie,
Decembrie };
return (n < 1 || n > 12) ? nume[0] : nume[n];
}
Pointeri i tablouri
Iniializarea tablourilor de pointeri
Valorile de iniializare ale tabloului de pointeri la
caractere, nume, este o list de iruri de caractere;
fiecare dintre acestea este plasat n poziia
corespunztoare din tablou.
Caracterele din cel de al i-lea ir sunt plasate
undeva n stiva de date i un pointer la acest ir este
stocat n nume[i].
Deoarece dimensiunea tabloului nu este precizat,
compilatorul numr valorile de iniializare i
deduce valoarea corect.
Pointeri i tablouri
Pointeri vs. tablouri multidimensionale
nceptorii n C au uneori nelmuriri cu privire la
deosebirea dintre un tablou bidimensional i un tablou de
pointeri.
Fie declaraiile:
int a[10][20];
int *b[10];
atunci a[3][4] i b[3][4] sunt din punct de vedere sintactic
referine legale ctre un singur int.
a este ns un tablou bidimensional veritabil: au fost
alocate 200 de locaii fiecare de dimensiunea unui int, iar
pentru gsirea elementului a[linie][col] se folosete formula:
20 x linie + col
Pointeri i tablouri
Pointeri vs. tablouri multidimensionale
Pentru b definiia aloc doar 10 pointeri (locaii de tip
adres) i nu i iniializeaz; iniializarea trebuie efectuat fie
la definire fie prin cod.
Presupunnd c fiecare element al lui b indic spre un tablou
de douzeci de elemente, atunci se va aloca spaiu pentru ct
pentru 200 de elemente de tip int, plus zece locaii pentru
pointeri.
Avantajul cel mai important al tablourilor de pointeri este c
liniile tabloului pot avea lungimi diferite. adic nu este necesar
ca toate elementele lui b s indice spre cte un vector de
douzeci de elemente; unele pot indica spre dou elemente,
altele spre cincizeci i altele spre nici unul.
Pointeri i tablouri
Pointeri vs. tablouri multidimensionale
Cea mai frecvent ntrebuinare a tablourilor de pointeri
este stocarea irurilor de caractere de lungimi diferite, ca n
funcia nume_luna din exemplul anterior.
Exemplu 1: tablou de pointeri
char *nume[ ] = {Luna inexistenta, Ian, Feb, Mar};
Luna inexistenta \0
Ian \0
Feb \0
Mar \0
nume:
Pointeri i tablouri
Pointeri vs. tablouri multidimensionale
Exemplu 2: tablou bidimensional
char unnume[ ] [17] = {Luna inexistenta, Ian, Feb,
Mar};
Luna inexistenta\0 Ian\0 Feb\0 Mar\0
0
17
34 51
unnume:

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