Sunteți pe pagina 1din 32

Limbaje de programare

Cuprins
Tipul pointer (continuare)
Tablouri și pointeri
Aritmetica pointerilor
Alocare dinamica a memoriei
Pointeri la funcții
Bibliografie selectivă
Tipul pointer
Să ne amintim:
Orice variabilă are o adresă, la care se memorează valoarea ei.
Operatorul prefix & dă adresa operandului: &x e adresa variabilei x.
În cazul tablourilor, numele unui tablou este chiar adresa sa.
Tipul pointer
O adresă poate fi tipărită (în #include <stdio.h>
hexazecimal – baza 16) int main( void )
folosind formatul %p în {
apelul funcției printf: double d ; int a [ 6 ] ;
printf ("Adresa lu i d : %p\n" , &d ); // folosim operatorul &
printf ("Adresa lu i a : %p\n" , a ); // a e deja adresa , nu e nevoie de &
return 0;
}
Tipul pointer
Orice variabilă x are o adresă &x, unde se memorează valoarea ei.
Fiind o expresie &x are un tip: tipul pointer
Pentru o variabilă declarată:
Nume_tip x;

Adresa &x are tipul nume_tip * = pointer la nume_tip, adresa unui obiect de tip nume_tip
Tipul pointer
Numele unui tablou are tipul pointer la tipul elementelor:
int a[4]; ⇒ a are tipul int *
char s[8]; ⇒ s are tipul char *

La declararea parametrilor funcției:


void f(tip a[ ]) înseamnă de fapt void f(tip *a)

Tipul unei constante șir de caractere ”sir” este char * = adresa unde se găsește
șirul în memorie
Valoarea specială NULL e folosită pentru a indica o adresă invalidă.
Tipul pointer
Pointer = o variabilă care conține adresa altei variabile.
Declararea unei variabile de tip pointer:
Nume_tip *nume_variabila
= nume_variabila e pointer la o valoare de tip nume_tip
= nume_valiabila conține adresa unei valori de tip nume_tip
int x; //variabila de tip int
int *p; //variabila pointer la int
p=&x; //lui p i se atribuie adresa lui x
Tipul pointer
Dereferențierea unui pointer
Operatorul de dereferențiere *
◦ Operator prefix: *nume_pointer
◦ Operand: pointer
◦ Rezultat: valoarea de la adresa conținută de pointer

Observație: dacă p este un pointer, *p este lvalue (locator value) - i se pot atribui valori
Operatorul * este inversul operatorului &
int x,*p;
*&x este chiar x (valoarea de la adresa lui x)
&*p este p (adresa valorii de la adresa p)
Tipul pointer #include <stdio.h>
#include <string.h>
O funcție care primește adresa unei /*functia calculeaza atat suma cat si diferenta a doua numere*/
variabile îi poate citi și modifica void sum_dif(int a, int b, int *sum, int *dif)
valoarea memorată. {
*sum = a + b;
Obs: În cazul parametrilor de tip *dif = a - b;
pointer valoarea transmisă este o }
adresă (adresa nu se modifică!) int main() {
int s, d;
Folosim parametri de tip pointer:
sum_dif(5, 3, &s, &d);
◦ când ne obligă limbajul (tablouri ca printf("5+3=%d\n5-3=%d\n", s, d);
parametri la funcții) return 0;
◦ când dorim să întoarcem mai multe }
rezultate (folosind return putem
întoarce doar un rezultat)
Tipul pointer #include <stdio.h>
#include <string.h>
Schimbarea valorilor de la două adrese: // schimba valorile de la 2 adrese
void swap(int * pa, int * pb)
{
int tmp; // variabila temporara
tmp = *pa;
*pa = *pb;
*pb = tmp;
}

int main()
{
int a = 5, b = 3;
Urmăriți cu ajutorul depanatorului de programe (debugger) swap(&a, &b);
modificarea valorilor variabilelor a și b! printf("a=%d,b=%d", a, b);
return 0;
}
Tablouri și pointeri
În C numele unui tablou este un pointer (adresa blocului de elemente/a primului element).
Dacă declarăm Diferența:
tip a[LEN], *pa;
Adresa a este o constantă, pa este o variabilă
Putem atribui pa=a;
&a[0] a iar a[0]  *a Nu putem atribui a=adresă, dar putem atribui pa=adresă

a[0] a[1] a[2] a[3] pa


a

... 5C0

Adresa(hex) 5C0
Tablouri și pointeri
Ca parametri la funcții, următoarele două scrieri înseamnă același lucru:
size_t strlen(char s[]); sau size_t strlen(char *s);
Totuși între tablouri și pointeri există unele diferențe:
char s [ ] = ”test” ; // adresa constanta

( char ∗) char ∗p = ”test” ; // variabila de tip char ∗


Tablouri și pointeri
TABLOU POINTER

char s[] = "test"; char *p = "test";


s este o adresă constantă nu este o adresă constantă ci o variabilă cu loc în memorie
NU se poate atribui s = “text” , se poate atribui p = "ana"; sau p = s; și apoi p[0] = ’f’ ,
dar se poate atribui s[0] = ’f’ Dar NU se poate atribui direct p[0] = ’f’ (”test” e o constantă
șir),
sizeof(s) e 5 * sizeof(char)
sizeof(p) e sizeof(char *)
&s e chiar s (dar are alt tip, adresă de
tablou de 5 char: char (*)[5]) &p NU e p ⇒
GREȘIT: scanf("%4s", &p); CORECT: scanf("%4s", p);
Aritmetica pointerilor
O variabilă de un anumit tip ocupă sizeof(tip) octeți/bytes.
◦ &v – adresa lui v
◦ &v+1 – adresa la care s-ar putea memora următoarea variabilă de același tip (adresa cu
sizeof(tip) mai mare decât &v).

Adunarea unui întreg la un pointer:


◦ Are ca rezultat tot o adresă de același tip. a[0] a[1] a[2] a[3]
a
◦ Se poate folosi pentru parcurgerea de tablouri
a+i  &a[i]
...
*(a+i)  a[i]
Adresa(hex) 5C0
#include <stdio.h>
#include <string.h>
Aritmetica pointerilor char * final(char * s)
{
// returneaza pointer la ultimul caracter al cuvantului
Observație: char *p = s;
Tipul char fiind reprezentat pe un octet, //echivalent cu : char *p ; p = s ;
adresa p creste ca valoare cu 1 while(*p) //echivalent cu while (*p != ‘\0’)
(sizeof(char)=1) in cazul operatiei p++. p++;
//adica pana ajunge la pozitia
//din sir marcata cu ’ \0 ’ (∗ p = = 0 )
//adresa memorata in p creste cu sizeof(char)
return (p-1);
}

int main()
{
printf("%s\n", final("Programare"));
return 0;
}
Aritmetica pointerilor
Operația de scădere:
Se poate calcula doar diferența între doi pointeri de același tip
tip *p, *q;
p-q = numărul de valori de tip tip care încap între cele două adrese
Pentru a se obține diferența numerică în octeți:
a[0] a[1] a[2] a[3]
a
Se pot converti ambii pointeri la char *
sau se înmulțește rezultatul cu sizeof(tip) ...
Următoarele operații sunt echivalente:
p - q  ((char *)p - (char *)q) / sizeof(tip) Adresa(hex) 5C0
Aritmetica pointerilor
Atenție: Nu sunt definite alte operații aritmetice pentru pointeri!
Dar se pot efectua operații logice de comparație (cu operatorii relaționali ==, !=, <, etc.)
Aritmetica pointerilor
PARCURGERE TABLOU FOLOSIND INDICI PARCURGERE TABLOU FOLOSIND POINTERI

//cauta un caracter intr-un sir //cauta un caracter intr-un sir


char *strchr_i(char *s, int c) char *strchr_p(char *s, int c)
{ int i = 0; {
while (s[i] != '\0') while (*s != '\0')
{ {
if (s[i] == c) if (*s == c)
return &s[i]; //s-a gasit, returneaza adresa return s; //s-a gasit, returneaza adresa
i++; s++;
} }
return NULL; //nu s-a gasit, returneaza NULL return NULL; //nu s-a gasit, returneaza NULL
}
}
Alocare dinamica a memoriei
Să ne amintim:
Este o EROARE să folosim o variabilă neinițializată!
int sum; //gresit
int sum=0; //corect
for (i=0; i<10;i++)
sum+=a[i];

Pointerii, ca orice variabile, trebuie inițializați!


cu adresa unei variabile (sau cu alt pointer inițializat deja)
cu o adresă de memorie alocată dinamic
Alocare dinamica a memoriei
EROARE:
int *p; *p = 0;
EROARE:
char *p; scanf("%s", p);
p este neinițializat (eventual nul, dacă e variabilă globală)
⇒ valoarea e scrisă la o adresă de memorie necunoscută
⇒ memorie coruptă, vulnerabilități de securitate, terminare forțată
ATENȚIE: un pointer nu este un întreg orarecare.
Greșit: int *p = 640; ! NU putem alege adresa unei variabile (unde e pusă în memorie) ⇒ adresa
se determină la încărcarea programului / când se alocă memoria
Zona rezervată

Alocare dinamica a memoriei Stiva

Să ne amintim:
Organizarea memoriei unui program. …

Heap

Segmentul de date

Segmentul de cod
Zona rezervată

Alocare dinamica a memoriei Stiva

Alocarea dinamică a memoriei


◦ folosește zona Heap a memoriei

◦ folosește funcții din bibiloteca standard stdlib.h
◦ permite să obținem la rularea programului memorie de dimensiunea
dorită
Heap

Segmentul de date

Segmentul de cod
Alocarea dinamică a memoriei
Funcțiile de alocare dinamică a memoriei (declarate în stdlib.h) ne permit să creăm variabile noi
de dimensiunile necesare, la rularea programului:
void *malloc (size_t size); - alocă size octeți de memorie
void *calloc (size_t num, size_t size); - allocă num x size octeți și îi inițializează cu 0
void *realloc(void*ptr, size_t size); - modifică la size dimensiunea unei zone
alocate cu c/malloc, mută blocul de
memorie dacă este necesar
Alocarea dinamică a memoriei
Funcțiile m/c/realloc returnează:
◦ adresa de început a blocului unde a fost alocat numărul de octeți cerut, dacă alocarea de
memorie a fost posibilă
◦ NULL în caz de eroare (memorie insuficientă).
Trebuie testat rezultatul returnat (!= NULL).

Memoria alocată dinamic trebuie eliberată când nu mai este necesară, folosim
tot o funcție din stdlib.h
void free(void *ptr);
Alocarea dinamică a memoriei
Citirea elementelor unui tablou a cărui
dimensiune o citim de la tastatură. int i, n, *t;
printf("Dati numarul de elemente ");
scanf("%d", &n);
t = (int*)malloc(n*sizeof(int));
//alocam spatiu pentru n elemente de tip int
if (t != NULL)
{
//daca alocarea memoriei nu a dat eroare
printf("Dati %d elemente\n",n);
//citim elementele tabloului
for ( i = 0; i < n; i++)
scanf("%d", &t[i]);
}
Alocarea dinamică a memoriei
Exemple de utilizare greșită a adresei unei variabile.
// Vector cu cifrele unui nr intreg
NU se poate returna adresa unei variabile locale! int * cifre(int n) {
De ce? int k, c[5]; // Vector local
for (k = 4; k >= 0; k--)
O variabila locală este stocată pe stivă și are o {
existenţă temporară, garantată numai pe durata c[k] = n % 10;
execuţiei funcţiei în care este definită (cu excepţia n = n / 10;
variabilelor locale statice) }
return c; // Aici este eroarea !
Una din soluții, în acest caz, este tot utilizarea }
alocării dinamice
#include <stdio.h>
Alocarea dinamică #include <string.h>
#include <stdlib.h>
a memoriei char * strd(const char * s) {
// creeaza o copie a lui s
char *d = (char *)malloc(strlen(s) + 1);
Alocarea dinamică a memoriei pentru un obiect // loc pentru sir si ’\0’
în funcție. if(d != NULL){ // alocare ok
// face copia , returneaza d
strcpy(d, s);
return d;
} else // eroare la alocare
return NULL;
}
int main()
{
char *s="Program",*d;
d = strd(s);
printf("%s\n", d);
return 0;
}
#include <stdio.h>
Alocarea dinamică #include <stdlib.h>
int* creare_tablou(int *dimensiune)
a memoriei {
int i, n, *t;
void afisare_tablou(int *t, int n) printf("Dati numarul de elemente ");
Un exemplu mai complex: scanf("%d", &n);
{ //afisarea tabloului folosind o parcurgere cu pointeri
int *p,i; t = (int*)malloc(n*sizeof(int));
p=t; //alocam spatiu pentru n elemente de tip int
for (i=0;i<n;i++) if (t != NULL)
{ {
printf("Valoarea %d la adresa %p\n",*(p),p); //daca alocarea memoriei nu a dat eroare
p++; printf("Dati %d elemente\n",n);
} //citim elementele tabloului
} for ( i = 0; i < n; i++)
int main() scanf("%d", &t[i]);
{ }
int *tab, n; *dimensiune=n;
tab=creare_tablou(&n); //valoarea dimensiunii citite in functie
afisare_tablou(tab,n); //se va transmite in afara functiei
free(tab); //prin intermediul parametrului int*
return 0; return t;
} }
Pointeri la funcții
Uneori dorim să variem funcția apelată într-un punct de program.
Exemplu:
parcurgerea unui tablou pentru diverse prelucrări:
for (int i = 0; i < len; ++i)
f(tab[i]); //(cu diverse funcții f)

Numele unei funcții reprezintă adresa funcției.


Avem declarațiile:
de funcție: tip_rez fct (tip1, . . . , tipn);
de pointer la funcție: tip _rez (*pfct) (tip1, . . . , tipn);
ATENȚIE! Parantezele la (*pointer) sunt obligatorii!
Altfel: int *fct(void); e o funcție ce returnează pointer la întreg
#include <stdio.h>
#include <stdlib.h>
#define SIZE 5

Pointeri la funcții int compar(const void *a, const void* b)


{
int *pa, *pb;
Exemplu: //facem conversie de tip de la void * la int *
pa = (int*)a;
Funcția standard de sortare qsort (stdlib.h) pb = (int*)b;
void qsort(void *base, size_t num, size_t size, int if (*pa == *pb) //comparam elementele
(*compar)(void *, void *)); return 0;
if (*pa > *pb)
Parametri: return 1;
adresa tabloului de sortat, numărul și dimensiunea return -1;
elementelor adresa funcției de comparat }
elementele (returnează 0 în caz de egalitate) int main()
{
⇒ are argumente void *, compatibile cu pointeri la int tab[SIZE] = { 3, 2, 5, 1, 4 };
orice tip qsort(tab, SIZE, sizeof(int), compar);
//afisare(tab, SIZE);
Cum poate fi scrisă funcția compar mai compact?
return 0;
}
Exerciții
E7_1: Modificati solutia pentru exercitiul E5_3 (din cursul 5) astfel încât tablourile să fie alocate
dinamic.
E7_2: Să se scrie o funcţie pentru ştergerea (eliminarea) a n caractere dintr-o poziţie dată a unui
şir, ce returnează adresa șirului de caractere modificat
char * strdel(char *s, int pos, int n);
E7_3: Să se scrie o funcţie pentru inserarea unui şir s2 într-o poziţie dată pos dintr-un şir s1. Se
va presupune că există suficient loc în vectorul lui s1 pentru a face loc şirului s2. Funcţia
returnează adresa şirului s1.
char * strins(char *s1, int pos, char *s2)
Pentru mai multe informații legate de tipul pointer și operațiile aferente, parcurgeti capitolul 5
(subcapitolele 5.1-5.5) din:
◦ Kernighan, B. W., & Ritchie, D. ”The C programming language - Second edition”, 1988
Prentice Hall Software Series
Bibliografie selectivă
◦ Kernighan, B. W., & Ritchie, D. ”The C programming language - Second
edition”, 1988 Prentice Hall Software Series
◦ Minea, M., Limbaje de programare, materiale de curs
http://staff.cs.upt.ro/~marius/curs/lp/index.html
◦ Holotescu, C., Limbaje de programare, materiale de curs
http://labs.cs.upt.ro/~oose/pmwiki.php/LP/Lectures

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