Sunteți pe pagina 1din 31

C_POINTERIL l_2 Sda16 Tema “Analiza eficienţei prelucrării structurilor de date cu pointeri ”

Sarcina şi obiectivele:
1. De studiat şi însuşit materialul teoretic din lucrarea dată prin însușirea noțiunilor și lansarea exerciţiilor la
execuţie pentru analiza şi evidenţierea esenţialului prelucrării structurilor de date cu pointeri în elaborarea
modelelor soluţiei prin explicaţii, argumentări şi organigrame.
2. În partea practică, la prima lucrare, de organizat un program din trei exemple, propuse de profesor, analizând
specificul implementării pointerilor prin simulare numerică și rezultatele obținute.
3. În partea practică, la a doua lucrare, de organizat un program să se recapituleze materialul teoretic și varianta
din lucrarea de lab. 6 (PC), din semestrul I, şi să se analizeze algoritmii şi programele cu şi fără pointeri
(declarări şi parcurgeri cu pointeri). Pentru aprofundarea înţelegerii să se dezvolte algoritmii şi programele cu
pointeri pentru condiţiile problemelor şi să se elaboreze scenariile succinte de soluţionare prin pointeri cu
calculele de verificare şi explicaţii. Rularea programelor în limbajul C cu afişarea tuturor variabilor de intrare,
intermediare şi finale.
4. În raport să fie expuse toate programele şi calculele efectuate. Să se analizeze tehnica programării eficiente cu
pointeri în baza exerciţiilor şi variantelor problemelor efectuate pentru diverse situaţii cu argumentări.

Fiecare temă conţine două probleme care vor fi rezolvare cu mijloacele limbajului C (diferite modele de
date şi obiecte). Temele se iau în conformitate compartimentele propuse.
Lucrarea se consideră efecutată după ce studenţii demonstrează profesorului funcţionarea corectă a
programelor la calculator şi apoi prezintă darea de seamă cu analiza rezultatelor. Darea de seamă include: foaia
de titlu şi pentru fiecare lucrare să se descrie algoritmul de rezolvare a problemei, listingul programului, dotat cu
comentariile de rigoare, Datele de intrare şi rezultatele să fie folosite în simularea numerică şi să fie analizate în
comparaţie cu rezultatele obţinute, concluzia şi bibliografia studiată.

I. Consideraţii teoretice:
1. Noţiuni, exemple şi importanţa implementării pointelor

Variabile statice şi variabile dinamice. Variabilele pot fi statice sau dinamice. Cele statice au o zonă de
memorie bine definită. Structura, locul, tipul acestei zone nu poate fi schimbată în procesul execuţiei programului.
Accesul la variabilele statice se face direct, după nume. Structurile statice de date au un număr fix de elemente, care
nu poate fi modificat. Alocarea memoriei se face într-un spaţiu strict determinat al memoriei, care permite
adresarea directă.
O altă categorie de variabile, accesibile în limbaje este cea dinamică. Pentru acest tip de variabile poate fi
modificat volumul de memorie rezervat, ceea ce face mai flexibilă alocarea memoriei în procesul de lucru al
programului. Structurile dinamice pot acumula elemente în procesul de funcţionare al programului, sau pot lichida
elementele ce au devenit neutilizabile. Accesul la aceste variabile şi declararea lor se face în mod diferit de cel al
variabilelor statice. Accesarea (definirea) este indirectă, prin intermediul unui tip mediator de variabile – tipul
referinţă.
Variabile de tip referinţă conţin referiri (adresări indirecte) la variabilele dinamice. Referirea se realizează prin
memorarea în variabila referinţă a adresei unde e stocată variabila dinamică.
Pentru variabilele tip referinţă se alocă un spaţiu de memorie de 4 octeţi, care conţin adresa variabilei
dinamice. Memoria pentru variabilele dinamice se alocă dintr-o zonă specială, care foloseşte adresarea indirectă,
numită heap. Această zonă este diferită de zona pentru variabilele statice.
Variabilele dinamice ocupă un spaţiu de memorie în corespundere cu tipul lor: întreg (int), float (real),
şiruri de caractere (string) etc.
Memoria

zona HEAP

program variabila dinamică referită

zona variabilelor statice

variabilă tip referinţă (4 bytes)  adresa variabilei dinamice

 Zona de memorie este o succesiune de 1 , 2 , 4 , 8 sau mai multe locaţii ( octeţi ) consecutive de
memorie. Adresa unei zone de memorie este numarul de ordine a primei locatii de memorie ( cea mai din
stinga ) . Sunt doua mari categorii de pointeri : pointeri catre variabile si pointeri catre functii .
Mulţimea valorilor variabilelor tip referinţă este formată dintr-o mulţime de adrese, fiecare din ele identificînd o
variabilă dinamică. Mulţimea de valori mai e completată prin valoarea NULL, care nu conţine nici o adresă.
1.1. NOTIUNEA DE POINTER Un pointer este o variabila care are ca valoare adresa unei zone
de memorie.
Pointerii sunt variabile care contin adresa de memorie a unei alte variabile . Din aceste considerente, pointerii se
numesc si variabile de adresa.
Presupunem ca avem o variabila de tip întreg numita entitate localizata la adresa de memorie 0x1000 (adresele
sunt automat asigante variabilelor de catre compilator). Daca dorim sa referim aceasta variabila prin intermediul
unui pointer entitate_ptr, atunci valoarea pointerului va fi 0x1000 (entitate_ptr = 0x1000), astfel spunem
ca entitate_ptr "arata" spre variabila entitate (Pentru a se evita confuziile, se recomanda ca numele pointerilor sa
aiba sufixul _ptr).
Pentru a întelege mai bine mecanismul de functionare a pointerilor, introducem o analogie pointeri - adrese
postale, în care adresa de memorie este adresa postala, numele variabilei pointer este numele cladirii, iar entitatea
referita este cladirea propriu-zisa. Aceasta analogie este prezentata în tabelul 7.1. Se observa ca doi pointeri diferiti
(Fac_AC si Fac_ETC) au aceeasi valoare, indicând spre aceeasi locatie, astfel referind aceeasi entitate.
Tabela 7.1: Analogie pointeri - adrese postale
Variabila (nume pointer) Fac_CIM
Valoare adresa str. Studenţilor 3
Entitate Cladire FCIM
Pointerii se utilizeaza pentru a face referinţe ( a avea acces ) . la valoarea unei variabile atunci când se cunoaste adresa ei . Dar o variabila se
memoreaza intr - o zona de memorie de o anumita lungime , functie de tipul ei . De exemplu, o variabila de tip int se memoreaza pe doi octeti, pe
când una de tip float pe 4 octeti . De aici urmeaza ca un pointer nu reprezinta numai adresa unei variabile ci mai mult decit atât, anume:
 adresa unei zone de memorie;
 tipul variabilei ( int , char , double etc . ) care este memorata in acea zona de memorie.
Notiunea de pointer face ca limbajul C sa fie un puternic instrument de programare , mai ales la
indemâna programatorilor avansaţi . Recomandam ca utilizarea pointerilor sa se faca numai dupa intelegerea
clara a mecanismului de lucru cu adrese, intrucât, folositi fara discernamint, ( pointerii neinitializati, distrugerea
unor zone de memorie etc.).
Totusi folosiţi cu economie si disciplina, ei dau nastere la programe clare si simple, si de cele mai multe ori
mai rapide decit in varianta fara pointeri.
1.2. DECLARAREA POINTERILOR Ca si in cazul oricaror tipuri de variabile si pointerii trebuie
declarati. Ei se declara la fel, cu deosebirea ca numele pointerului este precedat de caracterul *.
Declaraţia de pointer este: tip *nume; si prin aceasta se precizeaza ca nume este un pointer catre o zona
de memorie care contine valoarea unei variabile de tipul tip.
In declaratia de mai sus tip poate fi : int, unsigned, char, float, double etc. sau un sablon de structura. Deci
constructia tip * introduce un nou tip de date anume pointer la tip.
In afara de tipul int, long, float etc., exista si un pointer special, anume de tip void (mai precis fara tip). care
se refera la o zona de memorie ce poate contine orice tip de variabila.
Exemple
long *pl ; /* pointer la long; */
char *pc; /* pointer la char; */
double *x; /* pointer la double; */
void *v ; /* pointer fara tip ; */
int *pi[ 8 ]; /* sir de 8 pointeri la int; */
unsigned *pm [ 5 ][ 2 ]; /*masiv bidimensional de pointer la unsigned. */
int *p; /* pointeri la int; */
*p++ operatorul ++ incrementeaza pointerul
(*p)++ operatorul ++ incrementeaza continutul pointerului
*++p operatorul ++ incrementeaza pointerul
++(*p) operatorul ++ incrementeaza continutul pointerului
1.3. OPERATORII unari: & si * Inainte de toate, pointerul este o variabila, ca orice variabila, el trebuie
initializat. O valoare valida pentru un pointer este fie o adresa, fie constanta 0 ( zero ), definita in fisierul antet “stdio.h”
prin NULL, si de aici urmeaza ca un pointer este initializat prin atribuirea unei adrese ( un alt pointer ) sau a constantei
simbolice NULL. Initializarea se poate face la declararea pointerului sau in decursul programului.
Adresa zonei de memorie, unde se păstrează valoarea variabilei x se poate obţine cu operatorul obţinerii adresei “&”.
Rezultatul operaţiei obţinerii adresei este adresa locaţiei de memorie ce a fost alocată pentru variabila respectivă. De exemplu:
presupunând că x e înscrisă în memorie pe adresa 21650, atunci &x va fi egală cu 21650. Este important, că &x este constantă
de tip indicator şi valoarea sa nu se schimbă în timpul execuţiei programului.
Exemplul 1 Rulaţi, afişaţi şi analizaţi:
# include <stdio.h>
void main (void) int x=5; float r=1.7; int *q; float *w; q=&x; w=&r;
printf (“%f se află pe adresa % d \n”,r,w); printf (“% d se află pe adresa %d \n”,x,q);}
Din exemplu uşor se observă că adresa celulei de memorie se reprezintă printr-o valoare de tip întreg. În acelaşi timp
această valoare nu poate fi schimbată în program şi expresia &x=55; este incorectă.
Exista totusi cateva restrictii , astfel daca ptr1 si ptr2 sunt pointeri de tip1 respectiv de tip2, atunci o atribuire:
ptr 1 = ptr 2 ; este permisa numai daca:
- tip 1 si tip 2 reprezinta acelasi tip, sau
- tip 1 este void , sau se face o conversie explicita ( cast ) a lui tip2 catre tip1.
Analizînd toate aceste noţiuni, apare întrebarea: “Cu ce scop se folosesc indicatori, dacă valoarea variabilei şi valoarea
adresei sale se poate păstra în variabile simple?”.
Prioritatea folosirii indicatorului constă în faptul, că la el se poate adresa în 2 moduri: q şi *q. Astericsul * în acest caz
indică că se apelează la conţinutul celulei de memorie, adresa căreia este valoarea indicatorului. Adică valoarea variabilei x de
tip întreg este 5; valoarea indicatorului q este 21650; iar valoarea lui *q este egală cu cifra de tip întreg 5 înscrisă pe adresa de
memorie 21650. În aşa mod:
1) Variabila q poate primi valori numai în formă de adresă q=&x şi atribuirea de forma q=21650; este incorectă din cauza
că aici se încearcă atribuirea unei valori întregi unui indicator şi nu a adresei.
2) Variabila *q poate primi valori de tip întreg. De exemplu: *q=6; Această atribuire se descifrează astfel: de amplasat
valoarea întreagă 6 în celula dememorie ce are adresa indicată în variabila q. Din cauza, că variabila q indică la celula cu
adresa 21650, valoarea variabilei ce-şi păstrează valoarea în celula de memorie cu această adresă va fi egală cu 6.
Exemplul 2. Rulaţi, afişaţi şi analizaţi:
#include <stdio.h>
main void (main){ int x=5; int *q; q=&x; printf (“x=%d\n”,x); // x=5;
*q=6; printf (“x=%d\n”,x) } // x= 6;
În rezultatul îndeplinirii exemplului vor fi afişate 2 expresii: x=5 şi x=6. Prima valoare primită de variabila x este valoarea
5 atribuită la momentul iniţializării acesteia. A doua valoare primită de variabila x este valoarea 6 atribuită celulei de memorie
la care indică indicatorul q.
Desigur variabila x ar putea primi valoare nouă prin simpla atribuire, x=6; dar efectul deplin de la folosirea indicatorilor se
poate observa la transmiterea lor în calitate de parametri unei funcţii, la crearea unui fişier, etc.
Adresare si indirectare Operatorii specifici pointerilor sunt: operatorul “de dereferentiere” *, sau “de indirectare”
(pentru un pointer, returneaza entitatea referita de pointer) si operatorul de adresa & (pentru o entitate, returneaza adresa
entitatii). Exemplul urmator explica folosirea acestor operatori. Variabila pointer obiect_ptr primeste adresa variabilei obiect,
astfel obiect_ptr va indica spre zona de memorie unde este memorata variabila obiect. În ultima linie, se stocheaza valoarea 5
la zona de memorie spre care arata obiect_ptr, adica în variabila obiect.
Am vazut ca operatorul de adresa & se aplica unei variabile si intoarce valoarea adresei sale din memorie. Operatorul de
indirectare (sau de dereferentiere) se aplica unui pointer si returneaza valoarea scrisa in memorie la adresa data de pointer. Intr-
un anumit sens, acesti doi operatori sunt inversi unul altuia. Pentru a intelege mai bine aceste notiuni, sa vedem pe un exemplu
ce se intampla in memorie: Exemplu: Presupunem ca avem declaratiile:
int obiect; /* variabila de tip intreg */
int * obiect_ptr; /* pointer la un intreg */
obiect=4; obiect_ptr=&obiect; /* obiect_ptr va indica spre obiect */
printf("%d\n", *obiect_ptr); /* afiseaza valoarea spre care indica obiect_ptr */
*obiect_ptr=5; /* atribuie lui obiect valoarea 5 */
Exemplu: Operatorii adresă & şi de indirectare * sunt unul inversul celuilalt:
*&x este chiar x, pentru orice obiect (variabilă) x;
&*p are valoarea p, pentru orice variabilă pointer p (dar p e o variabilă şi poate fi atribuită; &*p e o expresie şi nu poate).
Folosirea gresita a operatorilor poate duce la functionări incorecte a programului sau, în cazul fericit, la erori semnalate de
compilator: *variabila este ilegal, prin aceasta se solicita entitatea referita de o variabila care nu este un pointer;
&variabila_ptr este legal, dar "neobisnuit", se solicita adresa lui variabila_ptr, adica un pointer la un pointer.
Mai multi pointeri pot indica spre aceeasi zona de memorie:
int obiect; int *obiect_ptr1;int *obiect_ptr2; obiect = 1; /* se atribuie valoarea 1 lui obiect */
obiect_ptr1 = &obiect; obiect_ptr2 = obiect_ptr1; /* obiect_ptr1 si obiect_ptr2 vor indica spre
aceasi locatie de memorie */
printf("%d %d\n", *obiect_ptr1, *obiect_ptr2);
Amintim! Înainte de folosire, un pointer trebuie iniţializat, de ex. cu adresa unei variabile de tipul potrivit:
int x, *p, **pp; p = &x; pp = &p; Situaţia se poate reprezenta în următorul fel:

O referinţă *p poate fi folosită la stânga sau la dreapta unei atribuiri (în cazul de mai sus, *p se foloseşte absolut la fel
(sinonim) cu x): int x, y, z, *p; p = &x; z = *p; /* z = x */ *p = y; /* x = y */
Mentionam ca adresa unei variabile este dependenta de sistem (C aloca memorie acolo unde poate). Exemplu:
float x, y, *p; p = &x; y = *p; Mai intâi "p" se asigneaza cu adresa lui "x". Apoi, "y"
se asigneaza cu valoarea unui obiect la care pointeaza p (adica *p). Aceste doua instructiuni de asignare se pot scrie y = *&x;
care este echivalent cu y = x;
Am vazut mai sus ca un pointer se poate initializa in timpul declararii sale. Trebuie sa avem totusi grija ca variabilele din
membrul drept sa fie deja declarate. Exemplu: Unde este greseala ? int *p = &a, a;
Tabelul de mai jos ilustreaza modul de evaluare a expresiilor cu pointeri.
Exemplul 3: Presupunem ca avem declaratiile: int i = 3, j = 5, *p = &i, *q = &j, *r; double x;
----------------------------------------------------------------------
| Expresie | Expresie echivalenta | Valoare |
----------------------------------------------------------------------
p == & i p == (& i) 1
p=i+7 p = (i + 7) gresit
**&p * (* (& p)) 3
r=&x r = (& x) gresit
7**p/*q+7 (((7 * (* p))) / (* q)) + 7 11
*(r = & j) *= *p (* (r = (& j))) *= (* p) 15
---------------------------------------------------------------------
Spre deosebire de C traditional, in ANSI C, singura valoare intreaga care poate fi asignata unui pointer este 0
(sau constanta NULL). Pentru asignarea oricarei alte valori, trebuie facuta o conversie explicita (cast). In cele ce
urmeaza, vom scrie un program care ilustreaza legatura dintre valoarea unui pointer si adresa lui.
Exemplul : Un pointer special definit în limbajul C este pointerul NULL, care nu indica spre nimic (adresa
spre care arata acest pointer este 0). Acest pointer este definit în stdio.h si în stdlib.h ca fiind (void *) 0, adica
un pointer spre o locatie de tip void si care arata spre locatia de memorie 0.
Pentru a atribui unui pointer o adresa exista operatorul & , numit si “de adresa” , care aplicat unei variabile , unui
element al unui masiv sau unei structuri furnizeaza adresa zonei de memorie unde este memorat respectivul obiect .
Operatorul & este unar , are aceeasi prioritate cu a altor operatori unari ++, -- , ! , - (dupa paranteze) si se asociaza de la dreapta
la stanga. Nu se aplica expresiilor intre paranteze , constantelor sau variabilelor de tip register si de aceea utilizarile:
& (a + 2 * b ) sau & 13 sunt eronate.
Totusi , la lucru cu pointeri, cel mai mult intereseaza aflarea valorii din zona de memorie care are ca adresa pointerul,
deci actiunea complementara celei de aflare a adresei .
Exemplu 4. Rulaţi, afişaţi şi analizaţi
int x, y, *px ; / * x, y – intregi ; px – pointer la int */
px = &x ; /* px este initializat cu adresa lui x */
y = * px ; /* valoarea aflata la adresa px se atribuie lui y */
Se observa ca secventa de mai sus este echivalenta cu atribuirea : y = x ;
Este permisa si notiunea de “pointer la pointer”.Astfel prin : int * * ppi ;
se declara “ppi” ca fiind un pointer la un pointer de tip int. altfel spus ppi contine adresa unei zone de memorie ce contine
un numar de tip int. In acest sens urmariti exemplul urmator.
Exemplu 5. Rulaţi, afişaţi şi analizaţi
# include “ studio.h”
main ( )
{ int i, *pi, **ppi; /* se declara un intreg , un pointer la int. si un pointer la pointer la int. */
pi = & i ; /* initializarea pointerului pi cu adresa lui i */
ppi = & pi; /* initializarea lui ppi cu adresa lui pi */
printf ( “ pi = %p ppi = %p \ n” , pi, ppi) ;
*pi = 7; /*Echivalent cu i = 7 */
printf (“ i = %d \ n “, i );
i = *pi – 3 ; / * Echivalent cu i = i - 3 ; */
printf ( “ i = % d \ n “, i) ;
*pi = **ppi*2; /* Echivalent cu i = i* 2 ; */
printf ( “ i = % d \ n “, i) ;
}
La executia programului pe ecranul calculatorului se va afisa :
pi = FFDE ppi = FFEO
i= 7
i= 4
i= 8
In programul precedent se observa ca operatorul * aplicat unui pointer poate sa apara in partea stanga a unei
atribuiri.
Deci constructia “ * pointer “ este o valoare- stanga.
Exemplu 6. Rulaţi, afişaţi şi analizaţi
main()
{ int ivar, *iptr; iptr = &ivar; ivar = 421;
printf(" ivar: %p\n",&ivar);
printf(" Conţinutul ivar: %d\n", ivar);
printf("Conţinutul iptr: %p\n", iptr);
printf(" Valoarea adresată: %d\n",*iptr);
}
Exemplu 7. analizaţi
//Operatii indirecte asupra variabilelor x y x, y se adresează direct la variabila
#include<stdio.h> cu valoarea 7
7 97
void main() respectic 97.
{int x = 7, y = 97, s = 0;
int *px, *py; px x Fie ca variabila x se afla
pe adresa 6000, iar
5000 6000 6000 7 variabila pointer pe
//Luam adresa variabilelor x, y: adresa 5000. px se
px = &x; adresează indirect la variabila cu valoarea 7. Analog
py = &y; py se adresează indirect la variabila cu valoarea 97.
//Suma x + y
s = (*px) + (*py); Operatia *, returnează valoarea obiectului la care
operandul (pointerul) pointează: s = 7 + 97
//Afisarea Sumei
printf("\nSuma=%d\ ",s,); }

Exemplu 8. Rulaţi, afişaţi şi analizaţi In acest exemplu se va utiliza pointer de tip “ void “, pointer care nu se
poate dereferentia. Urmariti situatiile cand un pointer “ void “ se poate utiliza ca atare si cand este obligatorie o
conversie explicita ( cast ) , spre exemplu ( int * ).
# include “ stdio.h”
main ( )
{ int i, * pi ; /* se declara un intreg si un pointer la int */
void * pv ; /* se declara un pointer de tip void */
pi = & i; /* initializarea pointerului pi cu adresa lui i */
pv = pi; /* corect ! lui pv i se atribuie valoarea pointerului pi */
* pi = 10; /* Echivalent cu i = 10; */
i = * pv + 5; /* Gresit ! pv este pointer de tip nedeterminat (void) */
i = * (int *) pv + 5; /* corect ! pv este convertit explicit spre “ pointer la int” */
/* si astfel instructiunea este echivalenta cu i = i + 5; */
}
1.4. POINTER SI TABLOURI UNIDIMENSIONALE In urmatorul exemplu de program se defineste un sir de
caractere de dimensiune maxima 8 elemente initializate cu : abc).
Exemplu 9. Rulaţi, afişaţi şi analizaţi
main ( )
{ char s[ 8];
s[0] = ‘a’;
s[1] = ‘b’;
s[2] = ‘c’;
s[3] = ‘ ) ‘;
s[4] = ‘\ o’;
printf (“sirul S este % s \ n”, S); }
La executie se va afisa : abc ) adica elementele sirului pana la caracterul NULL (’ \0 ‘).
In exemplul de mai sus s s-a declarat ca masiv unidimensional (sir) de maxim 8 elemente , dar in program s-au
utilizat 5 elemente. Mai mult decat atat s se putea declara ca parametru la o functie ca tablou deschis :
char s[ ] ; pentru ca in aceasta situatie alocarea spatiului de memorie necesar se face dinamic . Acest lucru este
permis deoarece in limbajul C numele s al sirului este automat convertit la un pointer catre primul element al sirului. Acest
pointer este constant in sensul ca este ilegal a se modifica ( prin atribuiri) in program, deci nu este o parte stanga.
Inseamna ca declaratiile: char s[ ] ; si char *s ; sunt echivalente, cu observatia ca in primul caz s
este un pointer constant, initializat cu adresa lui s[0], iar in al doilea caz este un pointer obisnuit si neinitializat.
Aceasta echivalenta ne permite sa transmitem ca parametri unei functii siruri, matrici etc. prin pointeri catre primul lor
element. Deci o functie care prelucreaza elementele unui sir poate arata:
void f (char s[ ])
{
...
}
sau In ambele exemple, sfarsitul sirului s va fi determinat de detectarea
void f (char * s) caracterului ‘ \ o ‘ .
{ ... }
Al doilea tip de functie foloseste ca argument o adresa si de aceea in acest caz se mai spune ca apelul este prin
referinta ( in engleza call by adress).
Totusi, in cazul masivelor multidimensionale care sunt parametrii unei functii numai prin dimensiune poate lipsi.
Ex. int f ( float a [ ] [10])
In acest exemplu parametrul functiei f este un pointer catre primul element a unei matrici cu 10 coloane.
Exemplu 10. Sa se scrie o functie care verifica daca un şir de caractere x este sau nu palindrom (citit de la stânga la dreapta
sau de la dreapta la stânga este acelasi lucru: ex. cojoc, capac, cuc ). Rulaţi, afişaţi şi analizaţi
int palindrom (char *x)
 char *p,*q; for (p=x,q=x+strlen(x)-1;p<q;p++,q--) // se testeaza daca perechile de caractere egal
if (*p!=*q) return 0; //departate de extremitatile şirului coincid
return 1; 
Exemplu 11. Sa se analizeze modurile de realizare functiei strlen:
int strlen(s) char s[]; { int length = 0; for(; s[length] != '\0'; length++); return (length); }
int strlen(s) char *s; { int length; for(length=0; *s; length++, s++); return length; }
int strlen(register char *s) { register char *p = s; while(*p) p++; /* */ return (p - s); }
Organizaţi apelurile, afişând toate valorile.
1.5. OPERATII ARITMETICE CU POINTERI
Deoarece pointerii contin ca valoare o adresa de memorie, operatiile aritmetice permise sunt: atribuirea, adunarea cu
un intreg, scaderea cu un intreg, scaderea a doi pointeri, comparatia unui pointer cu constanta simbolica NULL si compararea a
doi pointeri.
Adunarea unui pointer cu un intreg Rezultatul adunarii este un pointer de acelasi tip marit cu intregul de adunat
inmultit cu numarul de octeti necesari pentru a memora o valoare de tipul pointerului. Astfel daca :
tip * ptr; atunci: ptr + i modifica ptr cu i * sizeof (tip).
Deci noua valoare a lui ptr este: ptr + i* sizeof (tip).
Daca intregul i este l atunci se poate folosi operatorul ++ prefixat sau postfixat: ++ ptr sau ptr ++. Noul ptr va fi :
ptr + sizeof (tip).
Ex. int * pi, i;
...
pi++; /* Aduna 2 la pi (sizeof (int) = 2) */
i = * (pi + 3); /* Aduna 2 * 3 la pi si intregul memorat la pi + 6 este atribuit lui i */
...
Daca avem de-a face cu un masiv unidimensional (sir) tip a [DIM]; atunci numele sirului neurmat de indici este
considerat pointer catre primul sau element, anume pointer constant ce contine intotdeauna adresa lui a[ 0]. .Altfel spus,
valoarea lui a este & a[0]. Deci inseamna ca adunarea unui intreg la acest tip de pointer este o constructie corecta si :
a + i este identic cu &a[ i ] iar
*(a+i) este identic cu a [ i ] .

1.5.1. Scaderea unui intreg dintr-un pointer Efectul scaderii unui intreg dintr-un pointer este acela ca valoarea
pointerului se micsoreaza cu intregul de scazut inmultit cu numarul de octeti necesari pentru a memora o valoare de tipul
pointerului.
Daca: tip *ptr; atunci noua valoare a pointerului dupa: ptr – i este : ptr – i * sizeof (tip).
Utilizand operatorul de decrementare -- ptr sau ptr – noua valoare pentru ptr va fi ptr – sizeof (tip).
Atat operatorul de incrementare ++, cat si cel de decrementare – se pot utiliza in expresii cu pointeri impreuna cu
operatorul de dereferentiere *. Pentru corectitudine trebuie tinut cont ca operatorii ++ , -- si * sunt de aceeasi prioritate si se
asociaza de la dreapata la stanga, deci se vor folosi parantezele pentru a forta ca un operator sa se execute primul.
Astfel daca: tip *ptr; atunci:
*ptr ++ respectiv *ptr – mai intai obtine valoarea de la adresa ptr si apoi incrementeaza , respectiv
decrementeaza pointerul;
(*ptr)++ respectiv (*ptr) – obtine valoarea de la adresa ptr pe care o incrementeaza , respectiv o
decrementeaza;
*++ptr respectiv * -- ptr mai intai incrementeaza, respectiv decrementeaza pointerul si apoi obtine
valoarea de la noua adresa ptr;
++*ptr respectiv --*ptr obtine valoarea de la adresa ptr pe care o incrementeaza , respectiv o decrementeaza .
Expresiile cu pointeri de mai sus permit o scriere mai compacta a programului si in comparatie cu utilizarea indicilor
la un tablou duc la o executie a programului intr-un timp mai scurt. Un motiv in plus pentru utilizarea acestor expresii este ca:
*ptr++, *ptr --, *++ptr si *-- ptr, intrucat se refera la obiectul catre care indica adresa din ptr, sunt valori-stanga (L value) si
deci pot fi utilizate la stanga operatorului de atribuire =. In acest caz operatorul = va modifica obiectul referit.

1.5.2. Scaderea a doi pointeri In general scaderea a doi pointeri este o operatie ilegala, cu cateva exceptii.
Astfel, daca pointerii sunt de acelasi tip , in sensul ca se refera la obiecte de acelasi tip, ei se pot scade si rezultatul
scaderii este un intreg int sau long ( functie de modelul de memorie) egal cu numarul de locatii de memorie capabile sa
memoreze o valoare de tipul comun al pointerilor existente intre adresa primului si adresa celui de-al doilea. Rezultatul este
pozitiv daca primul pointer este o adresa aflata la dreapta adresei din cel de-al doilea pointer si negativ in caz contrar.
Fie pointerii ptrl si ptr2:
tip *ptrl , *ptr2; atunci: ptr l – ptr 2 va fi egal cu (ptr l – ptr 2)/ sizeof (tip).
Alta scadere corecta este aceea cand cei doi pointeri indica spre elementele aceluiasi tablou si in aceasta situatie
rezultatul este egal cu numarul de elemente dintre cei doi indici: pozitiv daca indicele l > indicele 2 si negativ in caz contrar.
# include “ stdio.h”
main ( ) {int * pl, *p2; /* se declara doi pointer de tip int */
int x [ ] = { 0,1,2,3,4,5,6,7}; /* se declara un sir de 8 intregi care se initializeaza */
pl = & x [ 2 ]; /* Pointerul pl se initializeaza cu adresa lui x [2] . Echivalent cu : pl = x + 2 ; */
p2 = x + 7; /*Pointerul p2 se initializeaza cu adresa de inceput a lui x plus inca 7 locatii a cate
2 octeti adica cu adresa lui x [7]. Echivalent cu : p2 = & x [ 7]; */
printf (“x = %u pl = %u p2= %u \n”, x,pl,p2);
printf (“Diferente: pl – x = %u p2 – x = %u p2- p1 = %u \n”, pl – x, p2 – x, p2 – pl); }
La executia programului se va afisa:
x = 65494 pl = 65498 p2= 65508
Diferente: pl – x = 2 p2 – x = 7 p2 – p1 = 5
1.5.3. Compararea a doi pointeri. Pentru ca operatia de comparare sa fie valida, cei doi pointeri trebuie sa indice
catre doua elemente ale aceluiasi tablou si rezultatul comparatiei este egal cu rezultatul comparatiei celor doi indici.
Daca pointerul pl indica pe a [i], iar pointerul p2 indica pe a [j] atunci:
pl < p2 = 1 (adevarata) daca i < j;
= 0 (falsa) daca i > j;
pl > = p2 =l (adevarata) daca i > j;
= O (falsa) daca i < j;
pl = = p2 = l (adevarata) daca i = j; şi = 0 (falsa) daca i  j
Siruri cu index negativ sau mai mare decat dimensiunea maxima
La adunarea unui intreg la un pointer spunem ca daca a este un sir: tip a[ DIM]; atunci:
a + i este identic cu &a[ i ], iar *(a + i ) este identic cu a [i].
Intregul i care se aduna la pointerul constant a poate fi mai mare sau egal cu dimensiunea maxima a
sirului.Compilatorul C nu face verificarea depasirii dimensiunii maxime.
De asemenea ,din pointerul a se poate scade orice intreg si compilatorul C nu verifica depasirea limitei din stanga a
sirului.Intr-adevar la fel ca si la adunare cu intrgi :
a – j este identic cu &a[-j], iar (a-j) este identic cu a[-j].
Deci, in limbajul C utilizarea indexului negativ sau a indexului mai mare decat dimensiunea maxima este permisa asa cum
se ilustreaza in urmatorul exemplu.
Exemplu 12. Rulaţi, afişaţi şi analizaţi
# include “stdio.h”
main ( ) { int i, a [8];
for (i = -3; i<=12; i++) /* se initializeaza : */
a[i] = i; /* a [-3] = -3; a [-2] =-2; */
/* .. a[0]=0; a[1]=1;… */
/* a[7]=7;…; a [12]=12; */
printf (“a[-3]= %d a [2] = %d a [12] = %d \n”, *(a-3), *(a+2), a [12] );
printf (“ zona neinitializata a[-4] = %d”, a[-4] ); }
La executia programului se va afisa:
a[-3]= -3 a[2] = 2 a[12] = 12
zona neinitializata a[-4] = 65224
1..5.4. Legatura dintre tablouri si pointeri. Numele unui tablou este un pointer constant spre primul sau element.
Un tablou cu mai multe dimensiuni se defineşte prin:
tip nume[d1][d2]…[dn];
Referirile la elemente unui tablou cu mai multe dimensiuni se fac folosind variabile indexate de forma: nume[i1][i2]
…[in], în care 0<=ik<=dk-1
Un tablou cu n dimensiuni poate fi considerat ca un tablou cu o dimensiune având ca elemente tablouri cu n-1
dimensiuni.
Elementele tabloului ocupă o zonă continuă de memorie de:
d1 x d2 x…x dn x sizeof(T) octeţi.
Adresa în memorie a unui element a[i1][i2]…[in] este dată de funcţia de alocare:
&a[i1][i2]…[in]=a+sizeof(T)*[i1*d2*…*dn+i2*d3*…*dn+…+in]
În cazul vectorilor: &a[i]=a+sizeof(T)*i
aceasta ne permite ca la declararea unui parametru vector să nu specificăm dimensiunea tabloului.
În cazul matricilor, compilatorul le transformă în vectori:
&a[i][j]=a+sizeof(T)*(i*C+j)
Şi în cazul matricelor, ca şi la vectori putem înlocui indexarea prin operaţii cu indici şi avem:
a[i][j] = (*(a+i)[j]=*(*(a+i)+j)
La transmiterea unui tablou multidimensional ca parametru al unei funcţii vom omite numai prima dimensiune,
celelalte trebuind să fie specificate.
Prin urmare, prototipul unei funcţii de afişare a unei matrici având l linii şi c coloane nu poate fi scris ca:
void matprint(int a[][], int l, int c);
ci: void matprint(int a[][DMAX], int l, int c);
în care DMAX este numărul de coloane al matricii din apel, ceeace ne limitează utilizarea funcţiei numai pentru matrici cu
DMAX coloane!
Vom reda generalitate funcţiei de afişare a matricilor, declarând matricea parametru ca un vector de pointeri:
void matprint(int (*a)[], int l, int c)
{ int i,j;
for(i=0;i<l;i++) { for (j=0;j<c;j++) printf(“%d”,((int*)a)[i*n+j]; printf(“\n”); } }
Problema transmiterii matricilor ca parametri poate fi evitată, dacă le linearizăm, transformându-le în vectori. În acest
caz, în locul folosirii a doi indici i şi j vom folosi un singur indice: k=i*C+j
Expresiile de mai jos sunt deci echivalente:
        nume_tablou        &nume_tablou        &nume_tablou[0]     *nume_tablou      
nume_tablou[0]
La declaraţiile: int a[3][3]={ { 11,12,13 }, { 21,22,23 }, { 31,32,33 } }; int *pa[3]={ a,a[1],a[2] }; int *p=a[0]; se
obţine schema următoare:

Conform schemei de mai sus se obţin următoarele echivalenţe: a[0][0], *a, **a[0], *p, **pa, *p[0].
В[i][j][k] poate avea următoarele construcţii: *(*(*(B + i ) + j ) + k ) sau *(B[i][j] + k ).
Exemplul 13. Organizaţi programul cu instrucţiunile de mai jos. Da-ţi la execuţie şi analizaţi echivalenţa lor a, afişând
toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
a[ i ] /= k+m şi a[ i ] = a[ i ]/k+m
a[ i ] /= k+m şi a[ i ] = a[ i ]/(k+m)
a[ i++]+=3 şi a[i++] = a[ i++]+3
a[ i++]+=3 şi a[ i ] = a[ i++]+3
a[ i++]+=3 şi a[ i++ ] = a[ i ]+3
a[ i++]+=3 şi a[ i ] = a[ i ]+3; i++;
Exemplu 14. Rulaţi, afişaţi toate valorile intermediare şi finale, apoi analizaţi
main () { long ***a; int n,m,l,i,j;
scanf("%d %d %d",&n,&m,&l); /* ---- */
a=(long ***)calloc(m,sizeof(long **));
for (i=0; i<=m; i++) { a[i]="(long" **)calloc(n,sizeof(long *)); for (j="0;" i<="l;" j++) a[i][j]="(long"
*)calloc(l,sizeof(long)); }
for (i="0;" i<="m;" i++) { for (j="0;" j<="l;" j++) free (a[i][j]); free (a[i]); } free (a); }
Exemplu 15.: Programul de mai jos citeste si afiseaza elementele a doua tablouri, la primul accesul se face
indexat, la al doilea prin pointeri.   Rulaţi, afişaţi şi analizaţi
#include <stdio.h>
#include <conio.h>
#define N 5
int tab1[N],tab2[N];
void citire1(void){ /* citeste elementele lui tab1 prin accesarea indexata   a elementelor */
  int i;   puts("Introduceti elementele lui tab1:");
  for(i=0;i<N;i++){    putchar(':');scanf("%d",&tab1[i]);   } /*for i*/ } /* citire1 */
void tiparire1(void){ /* tipareste elementele lui tab1 prin accesarea indexata   a elementelor */
  int i;   puts("Elementele lui tab1:");
  for(i=0;i<N;i++)     printf("%d ",tab1[i]);   putchar('\n'); } /* tiparire1 */
void citire2(void){ /* citeste elementele lui tab2 - accesarea fiecarui   element se face printr-un
pointer la el */
  int *pi;   puts("Introduceti elementele lui tab2:");
  for(pi=tab2;pi<tab2+N;pi++){ /* initializari echivalente sunt pi=&tab2 
    sau pi=&tab[0]; conditie echivalenta pi<=&tab2[N-1] */
  putchar(':');scanf("%d",pi);   } /*for pi*/ } /* citire2 */
void tiparire2(void){ /* tipareste elementele lui tab2 prin accesare la
pointeri */
  int *pi;   puts("Elementele lui tab2:");
  for(pi=tab2;pi<tab2+N;pi++)   printf("%d ", *pi);   putchar('\n');
} /* tiparire2 */
void main(void){   clrscr();   citire1();   tiparire1();  
citire2();   tiparire2();   getche(); }
Programul acesta demonstrează două posibilităţi de prelucrare a elementelelor tabloului, unde accesul prin pointeri este
mai eficient.  
Stilul adecvat de prelucrare a elementelelor tabloului prin pointeri constă în declararea pointerilor suplimentari pentru a
stabili legăturile cu tablourile prin pointeri. şi parcurgerea, accesul, elementelelor tabloului se face numai prin pointeri, care
se mai numeşte “stilul prelucrării cu pointeri”.
Exemplu 16.: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
void main() { clrscr(); int i,j; int t[2][3], int t[2][3],*ptr[2] ,**pp;
for(i=0;i<2;i++) ptr[i]=&t[i][0];
for(i=0;i<2;i++) for(j=0;j<3;j++) t[i][j]=i+j;
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", *(*(ptr + i)+j) ); printf("\n\n");
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", *(ptr[i]+j) ); printf("\n\n");
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ",ptr[i][j]); getch();
for(i=0;i<2;i++) ptr[i]=&t[i][0]; //pp[i]=&t[i][0];
pp=ptr;
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", *(*(pp + i)+j) ); printf("\n\n");
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ",*(pp[i]+j) ); printf("\n\n");
for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", pp[i][j]); printf("\n\n"); }
Exemplu 17 cu pointeri la tablouri bidimensionale: Programul de mai jos determină numărul de zerouri pe liniile
matricei. Întrucât pointerii la primul element este pointerul la şir şi e mai bine să prelucrăm matricea pe rânduri ca linie
aparte, de aceea, se prvede elaborarea funcţiei cnt(int x[], int *n, int m), căreia i se transmite ca pointeri la primele elemente
a rândurilor matricei. Rulaţi, afişaţi şi analizaţi
#include <stdio.h>
#include <conio.h>
void cnt(int*, int*, int);
#define LIN 2
#define COL 3
void main() { int a[LIN][COL], b[LIN], *p, *q, *t, k;
clrscr(); q = a[0]; t = b;
for (q = a[0]; q < a[0] + LIN * COL;) { puts("citeste elementele liniei %d", i + 1);
for (p = q; p < q + COL; p++) scanf("%d",p); cnt(q,&k,COL); *(t++) = k; q = p; }
printf("Numărul de zerouri pe liniile matricei \n");
for (t = b; t < b + LIN; t++) printf(" %4d\n",*t); }
void cnt(int x[], int *n, int m) { int *p; *n = 0;
for(p = x; p <x + m; p++) if (*p == 0) ++(*n); }
1.6. POINTERI CA ARGUMENTE LA FUNCTII. APEL PRIN REFERINTA Am precizat in capitolul precedent ca
la apelul prin valoare functia apelata prelucreaza o copie a valorilor parametrilor, valori care au fost puse pe stiva. Inseamna ca
functia apelata are efect asupra functiei apelante doar printr-o singura valoare returnata. Dar ce posibilitati are programatorul
cand doreste ca functia apelata sa modifice mai multe variabile din functie apelanta? In aceste situatii programatorul va
transmite functiei apelate adresele variabilelor ce urmeaza sa fie modificate. Avand la dispozitie aceste adrese, functia apelata
va putea lucra direct asupra valorilor aflate la aceste adrese. Deci programul apelant va trebui sa transmita functiei apelate
pointeri catre variabilele ce vor fi modificate.
În limbajul C se pot transmite parametri si returna prin adresă, intr-un mod indirect, folosind pointeri care ne permit să
utilizăm funcţiile ca “proceduri”, adică să returnăm mai multe rezultate şi, chiar, structuri complexe (tablourilor uni- şi multi-
dimensionale etc.).
În cazul pointerilor, funcţia apelantă trebuie să furnizeze adresa variabilei de modificat (tehnic printr-un pointer la această
variabilă), iar funcţia apelată trebuie să declare argumentul corespunzător ca fiind un pointer. Referirea la variabila de
modificat se face prin adresare indirectă.
Printre argumentele funcţiei pot apărea şi nume de tablouri. În acest caz valoarea transmisă funcţiei este în realitate adresa
de început a masivului (elementele masivului nu sînt copiate). Prin indexarea acestei valori funcţia poate avea acces şi poate
modifica orice element din tablou.
Urmeaza ca parametrii formali ai functiei apelate vor fi declarati ca pointeri.
Acest tip de apel de functie se numeste apel prin referinta sau prin adresa (in engleza: call by adress).
Exemplu 18. Mai jos sunt doua variante pentru o functie care interschimba doua numere intregi.
/* Varianta 1 Gresită */ /* Varianta 2 Corectă */
void swap (int x, int y) void swap (int *px, int *py)
{ {
int temp; int temp;
temp = x; temp = *px ;
x=y; *px = *py;
y = temp; *py = temp;
} }
/* Gresit */ /* Corect */
Functia din varianta 1 (apel prin valoare) este gresita, deoarece se interschimba intre ele variabilele x si y locale functiei
swap si nu parametrii efectivi de la apel, asa cum ar dori programatorul.
Functia din varinta 2 lucreaza direct cu variabilele de interschimbat din functia apelanta,intrucat are ca parametrii formali
pointeri la acestea. O modalitate de apel ar fi: swap (&x, &y ); ceea ce constituie de fapt apelul prin referinta . Doar in
acest fel x si y sunt efectiv interschimbate intre ele.
Exemplul 19. Calculul lungimii unui sir de caractere terminat cu ’ \ O ‘ in varianta cu pointeri (vezi si : Exemplul
tradiţional) este :
unsigned strlen (char * a)
{
char * b = a ; /* Pointerul b se initializeaza cu adresa de inceput a sirului */
while ( * b ! = ‘ \ O ‘) /* Cat timp nu s-a ajuns la sf. */
b++; /* sirului incrementeaza l la b (adica treci la caracterul urmator) */
return n – a; /* Diferenta b – a ne furnizeaza numarul de caractere peste */
/* care s-a trecut, de fapt lungimea sirului. */
}
OBSERVATIE Instructiunea while ( * b! = ‘ 0 ‘) se putea scrie mai concis: while (*b ), deoarece expresiile : *b ! =
‘ \0 ‘ , respectiv * b sunt sau ambele adevarate, sau ambele false.
Exemplul 20. In exemplul tradiţional s-a prezentat o functie care copie sirul s ( sursa) in sirul d (destinatie). Iata
varianta cu pointeri .
void strcpy (char * d, char * s)
{ while ( ( * d = * s)! = ‘ \ O ‘ ) { / * Copie caracterul */
s++; /* curent din s in d pana cand s-a copiat si ‘ O ‘. */
d++; /* Avansarea la urmatorul caracter se face cu s++; d++; */
} }
Expresiile cu pointeri ne permit o scriere mult mai compacta :
void strcpy (char * d, char * s)
{ while ( * d++ =*s++) ; /* */
}
OBSERVATIE:
Instructiunea: while ( * d++ = * s++); cuprinde urmatoarele operatii:
-la adresa curenta data de pointerul d atribuie caracterul curent aflat la adresa data de pointerul s;
-incrementeaza l atat la d, cat si la s ( se trece la caracterul urmator);
-daca ultimul caracter atribuit este = ‘ \ O ‘ atunci repeta observatiile de mai sus; altfel iese din ciclul while.
In concluzie , cele trei operatii de la prima varianta :
1. * d = * s;
2. s++; si d++;
3. *d!=‘\O‘?
se executa intr-a doua varianta cu o singura instructiune.
Exemplul 21 In exemplul tradiţional se compara lexicografic doua siruri de caractere a cu b. Comparati varianta
de acolo cu varianta cu pointeri de mai jos :
int strcmp (char *a, char * b )
{
for ( ; * a = = * b ; a + +, b+ + ) /* cat timp caracterul */
if (* a = = ‘\ 0’ ) /* curent din a este egal */
return 0 ; /* cu cel din b si nu s-a */
return *a - *b; /* ajuns la sfarsitul lui a */
} /* trece la caracterul urmator. Daca s-a ajuns la sfarsitul lui a si cum *a = = *b */
/* inseamna ca sirurile sunt egale si returneaza 0 */
/* Daca *a ! = *b atunci returneaza *a - *b (dif. carecterelor curente). */
/* Daca diferenta este negativa, atunci a < b, altfel a > b */
Exemplu 22. Functia urmatoare utilizeaza pointerii pentru a sorta crescator un sir de numere prin metoda bulelor (vezi cel
tradiţional). Rulaţi, afişaţi şi analizaţi
void sortbule ( float *a, unsigned n )
{
unsigned i, m = n – 1; float temp;
int ind = 1;
while ( ind >= 0) { ind = - 1;
for ( i = 0 ; i < m ; i++)
if ( *(a + i) > * ( a +i +1)) { temp = *( a + i ) ; *( a + i ) = *( a + i + 1) ; *( a +i +1) = temp ; ind = i ; }
m = ind ; }
}
Prin aceasta metoda sirul a [ 0 ] , a [ 1 ] , …, a [ n – 1 ] se parcurge de la inceput spre dreapta, astfel incat daca a [ i ]
> a [ i + 1 ] atunci se face interschimbarea lui a [ i] cu a [ i + 1] . Indicele i pentru care s-a facut interschimbarea se retine in
ind care il va schimba pe m. Intrucat de la ultimul indice i retinut in ind si pana la sfarsitul sirului este ordonat crescator la
urmatoarea parcurgere se analizeaza sirul doar pana la ultimul indice ind minus l . Se continua astfel pana nu se mai fac
interschimbari ( ind = -l ) si deci sirul este ordonat.
Exemplul 23. Functia urmatoare sorteaza crescator un sir de numere utilizand metoda insertiei directe. In ce
consta metoda ? Se presupune ca subsirul:
a[0], a[1],…,a[i-1] este deja ordonat crescator si se cauta locul lui a[i] in acest sir. Cautarea se face spre stanga, adica
de la i - 1 spre 0. Daca j este indicele pentru care (j = -1, i; a[-1] = -);
a[ j ]  a[ i ] < a [ j + 1] atunci toate elementele a [ j + 1], a [j + 2],…,a[ i - 1] se deplaseaza la dreapta cu o pozitie, iar
in pozitia j + 1 devenita libera se insereaza elementul a [ i ]. Se aplica procedeul de mai sus pentru i de la 1 la n - 1 si astfel
sirul este ordonat crescator.
void sortinsdir (float * a, unsigned n)
{ int i, j ; float temp ;
for (i = 1 ; i < n ; i ++) { temp = * (a + i);
for ( j = i - 1 ; j > =0 ; j --) /* cauta inapoi locul */
if (* (a + j) > temp) /* elementului a j */
*(a+j+1) = *(a+j); /* deplasare spre dreapta */
else { *(a+j+1) = temp; go to sf; /* In locul lui */
} /* a [ j + 1 ] vine a [ i ] */
*a = temp; /* Locul lui a [ i ] este cel mai */
/* din stanga */
sf : ; /* Instructiune vida etichetata */
}
}
Exemplu 24. Rulaţi, afişaţi şi analizaţi
O alta metoda de sortare este metoda Shell cea prezentata in functia de mai jos. Principiul metodei :
- se fixeaza un pas ; la inceput n/2 ;
- se sorteaza subsirurile avand elementele aflate la distanta pas intre ele:
a [ 0] cu a [pas] , a [1] cu a [pas+1],…
- se injumatateste pasul ; pas=pas/2 si se sorteaza subsirurile :
a [0] , a [pas], a [2 * pas],…
a [1] , a [pas + 1], a [2 * pas + 1],… s.a.m.d.
- cand pas = 0 algoritmul se termina : sirul este ordonat crescator.
Subsirurile se ordoneaza prin metoda insertiei directe prezentata anterior.
Metoda are avantajul ca aranjeaza inca de la inceput elementele aflate departe de locul lor final. Astfel, in etapele
urmatoare (cu pasul injumatatit) ramane mai putin de facut.
void sort Shell (float *a, unsigned n)
{ int i , j , pas ; float temp ;
pas = n/2 ; /* Pasul initial */
while (pas >0) {
for (i = pas ; i < n ; i++) { /* sortarea subsirurilor */
j = i - pas ;
while ( j > =0 && a [ j ] > a [ j + pas ] ) {
temp = a [ j ] ;
a [ j ] = a [ j + pas] ;
a [ j + pas] = temp ;
j = j - pas ;
}
}
pas = pas/2 ; /* Injumatatirea pasului. Cand */
} /* acesta devine 0 programul */
} /* se termina */
Observatie Daca ar fi sa facem o comparatie intre cele trei metode de sortare prezentate anterior, cea mai rapida
este metoda Shell, urmeaza metoda insertiei directe si ultima, mai putin rapida, este metoda bulelor.
1.7 FUNCTII CARE RETURNEAZA POINTERI Sunt situatii cand nu este suficient ca functia apelanta
sa intoarca o singura valoare catre apelanta. De multe ori apelanta ar trebui sa returneze un sir de valori, o structura sau o
uniune, un sir de caractere, o matrice etc.
Toate aceste alternative sunt rezolvate in limbajul C prin intermediul functiilor care returneaza pointeri. Anume
pointeri la siruri, la structuri, la uniuni, la matrice etc.
O functie care returneaza pointer se declara astfel :
[tip] nume-functie (lista-declaratori-parametri) ;
In aceasta constructie caracterul asterisc “ * “ este cel care indica faptul ca va returna un pointer. Tip-ul din fata este
de fapt tipul pointerului returnat de functie.
Exemplu 25 Urmatoarea functie concateneaza sirul S la sfarsitul lui D si returneaza pointer catre noul sir D. Se
presupune ca in D este suficient loc pentru ambele siruri.
char *strcat (char *D, char *S)
{ char *M = D ;
while ( *D) /* cat timp caracterul curent din D  0 */
++D ; /* avanseaza la urmatorul caracter */
while ( *D++ = *S++) /* Copie caracterul curent */
; /* din S in caracterul curent de la sf. */
/* lui D si trece la caracterul urmator */
return M ; /* Returneaza adresa de inceput a sirului rezultat */
Exemplu 26 Functia ce urmeaza determina daca S 1 este subsir al sirului S 2 si returneaza pointer la elementul
din S 2 de unde incepe subsirul S 1, respectiv NULL daca S 1 nu este subsir al sirului S 2.
# include “stdio.h”
char *strstr (char *S 1, char *S 2)
{ unsigned i ;
char *SA = S 1, *SB = S 2, *SC = S 2 ;
while ( *SB ! = ‘\0’ ) {
i=1;
while ( *SA ! = ‘\0’) /* Verifica daca S 1 este */
if ( *SA == *SB) { /* subsir incepand din */
SA ++; SB++;} /* pozitia SB */
else {
i = 0 ; break ; }
if (i == 1) /* Daca este subsir atunci */
return SC ; /* returneaza SC */
SA = S 1 ; SC++ ; SB = SC ; /* Trece la urmatoarea */
} /* pozitie in S 2 */
return NULL ; /* Nu este subsir */
}
1.8. POINTERI LA FUNCTII Daca intr-o functie este nevoie sa se apeleze alta functie, atunci metoda comoda de
lucru este ca aceasta din urma sa fie declarata ca parametru pentru prima functie. In limbajul C, o functie poate apare ca
parametru numai ca pointer la acea functie.
Un pointer la o functie se declara prin :
tip ( * nume-functie) (lista-declaratii-parametri) ;
unde “tip” este tipul valorii returnate de functie (poate fi si void), iar “lista-declaratii-parametri” este o lista de declaratori
pentru parametri separati prin virgule. Specificarea acestei liste nu este obligatorie.
Deşi o funcţie nu este o variabila, aceasta are o adresă fizică precisă de lansare în execuţie, numită şi punct de intrare în
funcţie. Această adresă poate, deci, fi atribuită unei variabile de tip pointer, ceea ce poate duce la posibilitatea apelării funcţiei
folosind un pointer către funcţie. In orice limbaj de programare, deci şi în LC, apelarea pentru execuţie a unei funcţii presupune
lansarea execuţiei programului în cod maşină, obţinut după compilare şi link-editare, de la adresa punctului de intrare în
funcţie. Acest lucru permite ca apelarea şi lansarea în execuţie a unei funcţii să se facă prin intermediul unui pointer către
funcţia respectivă. Ca şi în cazul obţinerii adreselor tablourilor (prin folosirea numelor acestora fără utilizarea indicilor)
adresele funcţiilor se obţin prin folosirea numelor acestora fără paranteze si argumente.
Exemplu float ( *func) (unsigned, char) ;
Mai sus se declara func ca fiind un pointer spre o functie care are doi parametri, primul unsigned si al doilea char si
returneaza o valoare de tip float.
Un pointer catre o functie are ca valoare adresa de memorie unde incepe partea de instructiuni executabile a functiei
in cauza. In ceea ce priveste obtinerea facem precizarea ca numele unei functii neurmat de paranteze rotunde este pointer catre
functie.
Daca de exemplu avem functie cu prototipul:
long test (int, float) ;
atunci “test” este pointer la aceasta functie.
Observatie Parantezele rotunde de la declaratia de pointer la functie : ( *nume-functie) sunt necesare, deoarece
fara ele : tip *nume-functie ( ) ; obtinem o declaratie corecta, dar de functie ce returneaza pointer (vezi
paragraful precedent) si nu de pointer la functie. Aceasta pentru ca “( )” sunt mai prioritare decat “*”.
int (*arr[])() = {f,g,h};
int n = 3;
main () { int i;
for(i=0; i<n; i++) printf("%d\n",*arr[i]);
getch(); return 0;
}
Este posibil să creem un tablou de pointeri la funcţii; apelarea funcţiilor, în acest caz, se face prin referirea la componentele
tabloului.
De exemplu, iniţializarea unui tablou cu pointeri cu funcţiile matematice uzuale se face prin:
double (*tabfun[])(double) = {sin, cos, tan, exp, log};
Pentru a calcula rădăcina de ordinul 5 din e este suficientă atribuirea:

y = (*tabfun[3])(0.2);
Numele unei funcţii fiind un pointer către funcţie, poate fi folosit ca parametru în apeluri de funcţii.
În acest mod putem transmite în lista de parametri a unei funcţii – numele altei funcţii.
b

De exemplu, o funcţie care calculează integrala definită: I   f(x) dx


a
va avea prototipul:

double integrala(double, double, double(*)(double));

b
 f(a)  f(b) n1
 ba
a f(x)dx  h  2
  f(a  ih) cu h 
n
 i1 
Exemplu 27. Rulaţi, afişaţi şi analizaţi. Definiţi o funcţie pentru calculul unei integrale definite prin metoda
trapezelor,cu un număr fixat n de puncte de diviziune:
Folosiţi apoi această funcţie pentru calculul unei integrale definite cu o precizie dată . Această precizie este atinsă
în momentul în care diferenţa între două integrale, calculate cu n, respectiv 2n puncte de diviziune este inferioară lui 
#include <math.h>
double sinxp();
double trapez(double,double,int,double(*)());
double a=0.0, b=1.0, eps=1E-6; int N=10;
void main(void) { int n=N; double In,I2n,vabs; In=trapez(a,b,n,sinxp);
do { n*=2; I2n=trapez(a,b,n,sinxp); if((vabs=In-I2n)<0) vabs=-vabs; In=I2n; } while(vabs > eps);
printf(“%6.2lf\n”, I2n); }
double trapez(double a,double b,int n,double(*f)())
{ double h,s; int i; h=(b-a)/n;
for(s=0.0,i=1;i<n;i++) s+=(*f)(a+i*h); s+=((*f)(a)+(*f)(b))/2.0; s*=h; return s; }
Exemplu 28. Fie de calculat valoarea aproximativa a integralei definite dintr-o functie f. O metoda suficient de
rapid convergenta este metoda Simpson (vezi [ ] pag. ). Formula lui Simpson este:
I  h/3 * [f(a)+2(f(a+2h)+f(a+4h)+…+f(a+(2n-2)h) )+4 *(f(a+h)+f(a+3h)+…+f(a+(2n-1)h) ) ]
unde h = (b-a)/2n. Rulaţi, afişaţi şi analizaţi
Vom scrie o functie Simpson care calculeaza expresia de mai sus si in care va fi apelata functia f transmisa primeia ca
un pointer la functie.
# include “stdio.h”
# include “math.h.”
double f (double) ;
double Simpson (double a, double b, unsigned n, double ( *f) ( ) ) ;
/* Ultimul parametru al functiei Simpson este pointer la functia f */
main ( )
{
int n ;
double i 1, i 2, eps, a, b ;
printf (“Calculul integralei prin metoda Simpson \ n”) ;
do {
printf (“Introduceti a, b, eps : “) ;
scanf (“%1f, %1f, %1f”, &a, &b, &eps) ;
}
while (a > = b || eps < = 0) ;
n=4;
i 2 = Simpson (a, b, n, f) ;
/* Ultimul parametru este pointer la fc. f */
do {
i1 = i2 ;
n = n*2 ;
i2 = Simpson (a, b, n, f) ;
}
while (fabs (i1 - i2) / 15 > = eps) ;
/* Criteriul de oprire este i1 - i2 / 15 eps unde */
/* i2 este urmatoarea aprox. obtinuta dubland nr. de intervale */
printf (“Valoarea integralei = %f \ n”, i2) ;
}
double Simpson (double a, double b, unsigned n, double ( *pf) ( ) )
{ unsigned i ;
double S, h ;
h = (b - a) / n ;
S = (*pf) (a) + ( *pf) (h) ; /* Calculul sumelor */
for (i = 1 ; i < n ; i = i + 2) /* ce intervin */
S = S + ( *pf) (a + i*h) * 4 ; /* in formula lui Simpson */
for ( i=2 ; i < n ; i = i+2)
S = S+( *pf) (a+i*h) *2 ;
return h/3 *S ; /* Returneaza urmatoarea aproximatie */
}
double f (double x) { return exp (-x *x) ; /* Functia de integrat */ }
Exemplu 29 Da-ţi la execuţie pentru a înţelege principiile principale. Analizaţi definiţiile funcţiilor, modul de
organizare a apelurilor şi obţinere a rezultatelor prin antetul funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <math.h>
double sinxp();
double trapez(double,double,int,double(*)());
double a=0.0, b=1.0, eps=1E-6; int N=10;
void main(void) { int n=N; double In,I2n,vabs; In=trapez(a,b,n,sinxp);
do { n*=2; I2n=trapez(a,b,n,sinxp); if((vabs=In-I2n)<0) vabs=-vabs; In=I2n; } while(vabs > eps);
printf(“%6.2lfn”, I2n); }
double trapez(double a,double b,int n,double(*f)())
{ double h,s; int i; h=(b-a)/n;
for(s=0.0,i=1;i<n;i++) s+=(*f)(a+i*h); s+=((*f)(a)+(*f)(b))/2.0; s*=h; return s; }

1.9. TABLOURI DE POINTERI. INITIALIZAREA TABLOURILOR DE POINTERI Sa presupunem ca


avem de scris o functie care sa returneze un sir de caractere cu numele zilei a n-a din saptamana. O functie nu poate intoarce un
sir de caractere asa ca este clar ca trebuie sa revina cu un pointer la numele zilei a n-a. Deci, in cadrul functiei trebuie sa se
creeze un sir de nume de zile si un sir de pointeri spre aceste nume. Clasa de memorie pentru acest sir de nume trebuie sa fie
static interna, asa fel incat sa nu se distruga la fiecare revenire din functie.
Exemplu 30
char *nume-zi (int n)
{
static char *zi [ ] = { /* Declaratie de sir de */
“zi ilegala”, /* pointeri si */
“Luni”, /* initializarea */
“Marti”, /* acestuia */
“Miercuri”
“Joi”
“Vineri”
“Sambata”
“Duminica” } ;
if (n > = 1 && n < = 7) return zi [ n ] ;
else return zi [ 0 ] ;
}
Exemplu 31. Un alt exemplu clasic de utilizare de siruri de pointeri este transmiterea matricilor ca parametri ai unei
functii. Deoarece accesul la un element al matricei se face prin dubla referinta la linie si coloana, modalitatea de transmitere a
matricelor la functii difera de cea de la siruri. In acest caz trebuie transmis un pointer la un sir de pointeri, fiecare element al
sirului pointand la liniile matricei.
Mai jos se prezinta programul care inmulteste doua matrice. Catre functia care efectueaza inmultirea se transmit
pointeri la siruri de pointeri pentru matricele A [ M ] [ N ] , B [N] [P] respectiv spre matricea rezultat C [ M ] [ P ], de fapt
pointeri la pointeri. Rulaţi, afişaţi şi analizaţi
# include “stdio.h”
# define M 4
# define N 2
# define P 3
void inm-matr (float **, float **, float **, unsigned, unsigned, unsigned) ;
main ( )
{ unsigned i, j ;
float *pa [M], *pb [N], *pc [M], **ppa, **ppb, **ppc ;
float a [M] [N] = h
{ 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }
};
float b [N] [P] = h
{1, 2, 3}, {4, 5, 6} } ;
float c [M] [P] ;
for (i = 0 ; i < M ; i++ ) pa[ i ] = a [ i ] ; /* pa [ i ] = adresa liniei i in matricea a */
for ( i = 0 ; i < N ; i++ ) pb [ i ] = b [ i ] ;/* pb [ i ] = adresa liniei i in matricea b */
for ( i = 0 ; i < M; i++) pc [ i ] = c [ i ] ; /* pc [ i ] = adresa liniei i in matricea c */
ppa = pa ; ppb = pb ; ppc = pc ;
inm-matr (ppa, ppb, ppc, M, N, P) ;
printf (“Matricea rezultat = \ n”) ;
for ( i = 0 ; i < M ; i++) { for ( j = o ; j < P ; j++) printf (“?”) ; } }
O situatie în care pointerii sunt mai utili este declararea structurilor recursive. În C nu se permite ca o structura sa
contina câmpuri de tipul aceleiasi structuri. Aceasta restrictie poate fi evitata folosind pointeri la structuri, dupa cum se arata în
exemplul urmator:
typedef struct pers { char nume[20]; int varsta; struct pers * urmator; } persoana;
persoana *p;
Pentru a referi un câmp folosind un pointer spre o structura, se foloseste operatorul de dereferentiere: (*p).nume.
Echivalent cu acest operator exista în C un alt operator pentru accesarea câmpurilor din structurile indicate de pointeri: p-
>nume.
Folosirea neadecvata a operatiilor cu pointeri. O greseala des întâlnita este referirea pointerilor spre alte locatii de
memorie:
int obiect=5; int *obiect_ptr;
obiect_ptr = obiect;
Lipsa operatorului de adresa & face ca obiect_ptr sa indice spre locatia de memorie aflata la adresa 5.
Urmatorul exemplu ilustreaza un stil de programare "daunator", care strica lizibilitatea codului. Limbajul C este foarte
flexibil, fapt ce poate duce, în special în cazul pointerilor, la rezultate imprevizibile:
void copy_string(char * p, char *q) { /* copiaza sirul q in p */
while (*p++ = *q++); }
Chiar daca aceasta implementare este mult mai compacta, urmarirea codului este foarte dificila, iar folosirea combinata a
operatorilor de incrementare si pointerilor poate duce la efecte necontrolate. Urmatoarea varianta de implementare a aceleiasi
functii copy_string prezinta un stil adecvat programarii cu pointeri:
void copy_string(char * dest, char * sursa)
{ *dest = *sursa;
while (*dest!='\0') { ++dest; ++sursa; *dest = *sursa; } }
1.10. Alocarea dinamică a memoriei. Alocarea dinamica a memoriei este deosebit de utila atâta timp cât
programatorul foloseste structuri de date pentru care nu ştie dimensiunea la scrierea programului. In cursul executiei programul
poate aloca memorie in limitele impuse de sistem. În C, deoarece limbajul ofera o mare libertate de exprimare, este foarte usor
pentru un programator neatent sa faca erori care nu sunt semnalate la compilare, dar care duc la comportari neasteptate ale
programului, adică, iarăşi, folosirea neadecvata a operatiilor cu pointeri O eroare tipica este atribuirea de valori pointerilor
neinitializati:
int *un_pointer; *un_pointer=5;
În aceasta situatie, deoarece un_pointer este neinitializat, valoarea 5 este scrisa în memorie la o locatie aleatoare, poate
chiar rezervata altei variabile.
Alocarea dinamică este un mijloc folosit, în programarea în LC, pentru alocarea de memorie pentru diversele variabile pe
parcursul executării programului. Alocarea dinamica de memorie se face cu ajutorul unor funcţii speciale numite funcţii de
alocare dinamică. Prin aceste funcţii pot fi alocate zone de memorie dinamice în zona speciala de memorie heap care se află
întotdeauna intercalata între programul utilizatorului împreuna cu zona de memorare permanenta şi memoria stiva.
Dimensiunea zonei heap este necunoscută utilizatorului dar dispune de o cantitate relativ mare de memorie.
Pentru alocarea dinamica în LC se folosesc, în mod uzual, funcţiile malloc() si free(), prima pentru alocarea dinamică
propriu-zisă şi cea de-a doua pentru eliberarea zonei de memorie ocupată prin funcţia malloc(). Aceste funcţii se găsesc în
fişierul antet (header) stdlib.h.
Alocarea dinamica de memorie se face cu sintaxa:
*malloc (size_t numar_de_bytes)
unde:
- size_t este un tip definit în fişierul stdlib.h ca un intreg fară semn;
- numar_de_octeti reprezintă numărul de bytes (octeţi) pe care programatorul doreşte să-i aloce prin funcţia malloc.
După o alocare reuşita functia malloc returneaza un pointer de tip void, (ceea ce precizează ca poate fi atribuit oricarui tip
de pointer), al primului octet liber din zona de memorie heap, iar dacă alocarea eşueaza ca urmare a inexistenţei lungimii de
memorie cerută din zona heap atunci funcţia malloc returnează valoarea zero.
În cazul în care un pointer nu arata spre o zona de memorie rezervata, trebuie alocata o zona de memorie în mod explicit,
folosind functia malloc:
un_pointer=(int *)malloc(sizeof(int));
Functia malloc rezerva spatiu de memorie si returneaza adresa spatiului rezervat; zona de memorie referita de un pointer
poate fi eliberata folosind functia free. Ambele functii sunt definite în stdlib.h.
În C nu exista un mecanism de garbage collection, astfel ca programatorul trebuie sa aiba grija ca zonele de memorie
alocate dinamic si care nu mai sunt utile sa fie dealocate (folosind functia free). Faptul ca unui pointer i se atribuie o noua
valoare nu înseamna ca zona de memorie spre care arata s-a eliberat si în acest mod programul poate ajunge sa tina alocata
toata memoria disponibila.
Astfel utilizatorul poate solicita în timpul execuţiei programului alocarea unei zone de memorie.
Această zonă de memorie poate fi eliberată, în momentul în care nu mai este necesară.
Alocarea şi eliberarea de memorie la execuţie permite gestionarea optimă a memoriei.
int **a, n, m, i; scanf("%d%d", &n, &m); a = (int **) malloc(n *sizeof(int *));
for (i = 0 ; i < n; i++) a [i] = (int *) malloc(m *sizeof(int));
eliberarea de memorie se efectuează în ordinea inversă creării:
for (i = 0 ; i < n; i++) free(a[i]); free(a);
Biblioteca standard oferă 4 funcţii, având prototipurile în <alloc.h> şi <stdlib.h>. Acestea sunt:
/1/ void *malloc(unsigned n);
Funcţia alocă un bloc de memorie de n octeţi. Funcţia întoarce un pointer la începutul zonei alocate. În caz că cererea
de alocare nu poate fi satisfăcută, funcţia returnează NULL.
Exemple a)- secventa de program:
char *ptrsir;
ptrsir = malloc(200); /* se aloca 200 octeti */
aloca 200 de octeti , iar adresa primului octet se va gasi in pointerul ptrsir
b)- secventa de program:
int *ptrint;
ptrint = malloc(75*sizeof(int); /* se aloca memorie pentru 75 nr. intregi */
aloca memorie pentru 75 de numere intregi (75*ziseof(int)=75*2 octeti), iar adresa primului numar se va gasi in pointerul
ptrint.
/2/ void *calloc(unsigned nelem, unsigned dim);
Alocă nelem*dim octeţi de memorie (nelem blocuri formate din dim octeţi fiecare). Întoarce un pointer la începutul
zonei alocate sau NULL.Memoria alocată este iniţializată cu zerouri.
void free(void *p);
/3/ Funcţia realloc eliberează o zonă de memorie indicată de p, alocată în prealabil prin malloc() sau calloc().
void *realloc(void *p, unsigned dim);

Modifică dimensiunea spaţiului alocat prin pointerul p, la dim. Funcţia întoarce adresa zonei de memorie realocate,
iar pointerul p va adresa zona realocată.

 dacă dimensiunea blocului realocat este mai mică decât a blocului iniţial, p nu se modifică, iar funcţia va întoarce valoarea
lui p.

 dacă dim==0 zona adresată de p va fi eliberată şi funcţia întoarce NULL.

 dacă p==NULL, funcţia alocă o zonă de dim octeţi (echivalent cu malloc()).


Eliberarea zonei memorie alocată printr-o funcţie malloc se face cu funcţia free() având sintaxa următoare:
/4/ free(nume_pointer);
unde nume_pointer este variabilă de tip pointer care conţine adresa de memorie a zonei alocate şi utilizate anterior şi care
urmează sa fie disponibilizată pentru alte alocări dinamice viitoare.
Tehnica alocării dinamice în LC este folosită alături de pointeri pentru crearea, actualizarea şi exploatarea unor structuri de
date importante cum ar fi: listele simplu si dublu înlănţuite, arborii binari şi tablourile alocate dinamic.
Tablouri alocate dinamic Deoarece oricărui pointer i se poate aplica un indice, asemănător unui tablou unidimensional,
atunci se poate lucra cu zona de memorie indicata de un pointer ca şi cum ar fi un tablou unidimensional.
Funcţiile de alocare întorc pointeri generici (void*) la zone de memorie, în timp ce utilizatorul alocă memorie ce
păstrează informaţii de un anumit tip. Pentru a putea accesa memoria alocată, indirect, prin intermediul pointerului, acesta va
trebui să fie un pointer cu tip, ceea ce impune conversia explicită (prin cast) a pointerului întors de funcţia de alocare într-un
pointer cu tip. De exemplu, pentru a aloca un vector de întregi, având n elemente vom folosi:
int *p:
if (p=(int*)malloc(n*sizeof(int))==NULL) {
printf(“Memorie insuficienta\n”); exit(1); }
Funcţiile care întorc pointeri sunt utile în alocarea de spaţiu pentru variabile dinamice.
Deoarece zona heap de memorie nu este infinită (ci limitată), la fiecare alocare dinamică de memorie, trebuie să se verifice
valoarea returnată de funcţia malloc pentru a fi siguri că alocarea s-a făcut cu succes, ca în exemplul de mai jos:
int *ptrint;
ptrint = malloc(75*sizeof(int); /* se aloca memorie pentru 75 nr. intregi */
if(ptrint){ printf(“\n memorie insuficienta, eroare de alocare”);
exit(1); }
sau if (!(ptrint =malloc(100))) { printf(“\n memorie insuficienta, eroare de alocare”); exit(1); }
   Functia exit() este cea care se foloseste la iesirea din rutina in momentul cand nu este suficienta memorie.
Variabilele dinamice sunt alocate în momentul execuţiei, nu au nume şi sunt accesate prin pointeri.
Un constructor este o funcţie care alocă spaţiu în mod dinamic pentru o variabilă şi întoarce un pointer la spaţiul
rezervat.
Un destructor este o funcţie care primeşte un pointer la o zonă alocată dinamic şi eliberează această zonă.
O funcţie utilă, strdup() – salvează un şir de caractere într-o zonă alocată dinamic şi întoarce un pointer la acea
zonă sau NULL.
char *strdup(char *s) { char *p;
p=(char*) malloc(strlen(s)+1);
if (p!=NULL) strcpy(p,s);
return p;
}
int main() { int n,i; int * a; // adresa vector alocat dinamic
printf ("n="); scanf ("%d", &n); // dimensiune vector
a=(int *) calloc (n,sizeof(int)); // a=(int*) malloc (n*sizeof(int));
printf ("componente vector: \n");
for (i=0;i<n;i++) scanf ("%d", &a[i]); // sau scanf (“%d”, a+i);
for (i=0;i<n;i++) // afisare vector printf ("%d ",a[i]); return 0; }
Exista si cazuri in care datele memorate intr-un vector rezulta din anumite prelucrari, iar numarul lor nu poate fi cunoscut
de la inceputul executiei. Un exemplu poate fi un vector cu toate numerele prime mai mici ca o valoare data. In acest caz se
poate recurge la o realocare dinamica a memoriei. In exemplul urmator se citeste un numar necunoscut de valori intregi intr-un
vector extensibil:
#define INCR 100 // cu cat creste vectorul la fiecare realocare
int main() {
int n,i,m ;
float x, * v; // v = adresa vector
n=INCR;
i=0;
v = (float *)malloc (n*sizeof(float)); // dimensiune initiala vector
while ( scanf("%f",&x) != EOF) {
if (++i == n) { // daca este necesar
n= n+ INCR; // creste dimensiune vector
v=(float *) realloc (vector,n*sizeof(float)); }
v[i]=x; // memorare in vector numar citit
}
for (i=0;i<n;i++) // afisare vector
printf ("%f ",v[i]); return 0;
}
Din exemplele anterioare lipseste eliberarea memoriei alocate pentru vectori, dar fiind vorba de un singur vector alocat in
functia “main” si necesar pe toata durata de executie, o eliberare finala este inutila. Inainte de executia unui nou program
memoria “heap” este reinitializata (se anuleaza automat toate alocarile facute de programul anterior). Eliberarea explicita poate
fi necesara pentru vectori de lucru, folositi numai in anumite secvente de program (sau functii). Realocarea repetata de
memorie poate conduce la fragmentarea memoriei “heap”, adica la crearea unor blocuri de memorie libere dar neadiacente si
prea mici pentru a mai fi reutilizate ulterior. De aceea, politica de realocare pentru un vector este uneori dublarea capacitatiii
sale anterioare.

Matrice alocate dinamic Alocarea dinamica pentru o matrice este importanta deoarece:
-Foloseste economic memoria si evita alocari acoperitoare, estimative.
-Permite matrice cu linii de lungimi diferite.
-Reprezinta o solutie buna la problema argumentelor de functii de tip matrice.
Daca programul poate afla numarul efectiv de linii si de coloane al unei matrice (cu dimensiuni diferite de la o executie la
alta), atunci se va aloca memorie pentru un vector de pointeri (functie de numarul liniilor) si apoi se va aloca memorie pentru
fiecare linie (functie de numarul coloanelor) cu memorarea adreselor liniilor in vectorul de pointeri. O astfel de matrice se
poate folosi la fel ca o matrice declarata cu dimensiuni constante. Exemplu:
int main () {
int ** a; int i,j,nl,nc;
printf (“nr. linii=“); scanf (“%d”,&nl); printf (“nr. col. =“); scanf (“%d”,&nc);
a = (int**) malloc (nl*sizeof(int*)); // alocare ptr vector de pointeri
for (i=0; i<n;i++) a[i] = (int*) calloc (nc, sizeof(int)); // alocare pentru o linie si initializare
// completare diagonala matrice unitate
for (i=0;i<nl;i++) a[i][i]=1; // a[i][j]=0 pentru i != j
// afisare matrice<
printmat (a,nl,nc); return 0; }
Functia de afisare a matricei se poate defini astfel :
void printmat (int ** a, int nl,int nc) {
for (i=0;i<nl;i++) { for (j=0;j<nc;j++) printf (“%2d”, a[i][j]); printf(“\n”); }
}
Notatia a[i][j] este interpretata astfel pentru o matrice alocata dinamic:
-a[i] contine un pointer (o adresa b)
-b[j] sau b+j contine intregul din pozitia “j” a vectorului cu adresa “b”.
Functia “printmat” data anterior nu poate fi apelata dintr-un program care declara argumentul efectiv ca o matrice cu
dimensiuni constante. Exemplul urmator este corect sintactic dar nu se executa corect:
int main () {
int x [2][2]={{1,2},{3,4}}; // o matrice patratica cu 2 linii si 2 coloane
printmat ( (int**)x, 2, 2); return 0;}
Explicatia este interpretarea diferita a continutului zonei de la adresa aflata in primul argument: functia
“printmat” considera ca este adresa unui vector de pointeri ( int * a[]). iar programul principal considera
ca este adresa unui vector de vectori (.int x[][2]). Se poate defini si o functie pentru alocarea de memorie
la executie pentru o matrice. Exemplu:
int **newmat(int nl, int nc) { // rezultat adresa matrice
int i; int ** p=(int **) malloc (nl*sizeof (int*));
for (i=0;i<n;i++) p[i] =(int*) calloc (nc,sizeof (int)); return p; }
Exemplul 32.: Rulaţi, afişaţi şi analizaţi. Citiţi de la intrarea standard un şir de caractere de lungime necunoscută
într-un vector alocat dinamic. Alocarea de memorie se va face progresiv, în incremente de lungime INC, după citirea a INC
caractere se face o realocare.
char *citire()
{ char *p, *q;
int n; unsigned dim=INC; p=q=(char*)malloc(dim);
for(n=1; (*p=getchar())!=’\n’ &&*p!=EOF; n++) {
if(n%INC==0) { dim+=INC; p=q=realloc(q,dim); p+=n; continue; }
p++; } *p=’\0’; return realloc(q,n); }
Notă. În C++, alocarea dinamică se face mai simplu şi mai sigur, folosind operatorii new şi delete. Operatorul new
permite alocarea de memorie în heap. El se foloseşte într-una din formele:
tip *p, *q, *r;
p=new tip; //rezerva in heap o zona de sizeof(tip) octeti
q=new tip(expresie);//acelasi efect cu initializare cu val.expresiei
r=new tip[expint]; //rezerva o zona neinitializata de expint*sizeof(tip) octeti
Eliberarea memoriei alocate se face prin: delete p: delete [] r; //elibereaza memoria alocata pentru tablou
Exemplul 33: Rulaţi, afişaţi şi analizaţi. Definiţi o funcţie care alocă dinamic memorie pentru o matrice având l linii şi c
coloane conform organigramei următoare:

a plin pelem
plin[0] pelem[0][0]
plin[1] pelem[0][1]
plin[2] pelem[1][0]

pelem[1][1]
pelem[2][1] pelem[2][0]

double **alocmat(int lin, int col)


{ double **plin; double *pelem; int i;
pelem=(double*)calloc(lin*col, sizeof(double));
if(pelem==(double*)NULL){ printf(“spatiu insuficient\n”); exit(1);}
plin=(double**)calloc(lin, sizeof(double*);
if(plin==(double**)NULL){ printf(“spatiu insuficient\n”); exit(1); }
for (i=0; i< lin; i++) { plin[i]=pelem; pelem+=col; } return plin; }
Pointeri la pointeri (indirectare multipla). Limbajul C permite realizarea indirectarii multiple prin faptul ca un pointer
poate fi pointat de un alt pointer. Indirectarea multipla este limitata la pointer catre pointer si se utilizeaza cu precautie.

Figura 11.1. Pointerul catre pointer retine adresa pointerului la o variabila

Daca pointer1 pointeaza catre pointer2 atunci acesta contine adresa lui pointer2. Pointer2 contine adresa variabilei.
Declararea unui pointer la pointer se face cu un * suplimentar fata de declararea pointerului. De exemplu:
int k; /* declararea variabilei */
int *p; /* declarare pointer */
int **plap; /* declarare pointer la pointer */
float v,*p,**pp; p=&v; pp=&p; **pp=1.2 /* v=1,2 */
Exemplul 34: Rulaţi, afişaţi şi analizaţi
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main() {  int n, i;  char (*cuvinte_u)[20] = NULL, (*cuvinte)[20]; // un pointer la un sir de 20 caractere
        int *aparitii;    scanf("%d\n", &n);   cuvinte = (char (*)[20]) malloc(n * sizeof(char[20]));
    i = 0;        while (i < n) {   scanf("%s", cuvinte[i]);    i++;   }
        int dim = 4, unice = 0, k;      cuvinte_u = (char (*)[20]) malloc(dim * sizeof(char[20]));
        aparitii = (int *) malloc(dim * sizeof(int));
   for (i = 0; i < n; i++) {  int found = 0;  // variabila care va indica daca am gasit cuvantul in vectorul de aparitii unice
  for (k = 0; k < unice; k++) {  if (strcmp(cuvinte_u[k], cuvinte[i]) == 0) {  aparitii[k]++; found = 1;}  }
   if (found != 1) {       //in cazul in care vucantul nu a fost gasit in vectorul de valori unice
        if (unice >= dim) {     //in cazul in care dimensiunea vectorului unic nu a ajuns la limita
     dim *= 2;   aparitii = (int *) realloc(aparitii, dim * sizeof(int));
     cuvinte_u = (char (*)[20]) realloc(cuvinte_u, dim * sizeof(char[20]));       }
      strcpy(cuvinte_u[unice], cuvinte[i]);   aparitii[unice] = 1;    unice++;    }   }
    for (i = 0; i < unice; i++)     { //afisarea rezultatelor
   printf("%s %d\n", cuvinte_u[i], aparitii[i]);        }        //dealocare de memorie
        free(aparitii);        free(cuvinte_u);        free(cuvinte);        return 0; }
Rulaţi, afişaţi şi analizaţi /* Citirea unui sir prin indirectare multipla */
#include<stdio.h>
void main (void)
{ char sir[80],*p_sir,**p_p_sir;
p=sir; p_p_sir=&p; printf("\n Introduceti numele Dvs.: "); gets(*p_p_sir); printf("\n Salut %s ", *p_p_sir); }
Pentru a declara un pointer către un alt pointer, trebuie plasat un asterix suplimentar în faţa numelui pointerului.
De exemplu:
char ** pp; pp este un pointer către un alt pointer de tip char, şi nu un pointer către un caracter.
Accesarea valorii printr-un pointer către un alt pointer, cere ca operatorul asterix să fie aplicat de două ori. De
exemplu:
p = &ch; // asignează lui p adresa lui ch
pp = &p; //asignează lui pp adresa lui p
**pp = ‘c’; // asignează lui ch valoarea ‘c’ folosind indirectare multiplă
Observaţie. Pointeri către pointeri pot produce confuzii. De aceea, indirectarea excesivă este derutantă şi sursă de erori
conceptuale.
#include <stdio.h>
void main(void)
{float a,s=0,*pa,**ppa; int i; i=1;
while(i<=10) {printf(“\n a=”); scanf(“%f”,&a);
pa=&a; ppa=&pa; s=s + **ppa; i++; }
printf(“\n media=%f”,s/(i-1)); }
Exemplul .35: Da-ţi la execuţie pentru a înţelege principiile principale. Analizaţi definiţiile funcţiilor, modul de organizare a
apelurilor şi obţinere a rezultatelor prin antetul funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <stdio.h>
#include <string.h>
#include <conio.h>
void SimetricPare(char*);
void SimetricImpare(char*);
void Transpune(char*);
char* Trim(char*);
void EnterProp(char*);
void SimetricImpare(char*);
void Calculate(char*);
int countPare=-1, countImpare=50, index, probels=0, len;
char *cMaiLung, *cMaiScurt, *cuvintele[10];
bool bPar=false, bImpar=false, entered=false;
int main(void)
{ char cPropozitia[125]; int i=0, j, mi;
printf("Menu:\n----------\n");
printf("1. Introducerea propozitiei\n");
printf("2. Calcule\n");
printf("3. Simetric lung pe poz.pare\n");
printf("4. Simetric scurt pe poz.impare\n");
printf("5. Transpusa\n");
printf("6. Iesire\n");
printf("\n-=Alegeti un punct al meniului=-:\n");
mi = getch();
while(mi!=(int)'6')
{ switch(mi)
{
case (int)'1': EnterProp(cPropozitia); break;
case (int)'2': if(!entered){printf("Introd. propozitia!"); break;}Calculate(cPropozitia); break;
case (int)'3': if(!entered){printf("Introd. propozitia!"); break;}if(bPar)printf("\nSimetric Lung de pe
pare: %s\n", cMaiLung);else printf("\n\nSimetric Lung pe pare nu a fost gasit\n"); break;
case (int)'4': if(!entered){printf("Introd. propozitia!"); break;}if(bImpar)printf("\nSimetric Scurt de
pe impare: %s\n", cMaiScurt);else printf("\n\nSimetric Scurt pe impare nu a fost gasit\n"); break;
case (int)'5':
{ if(!entered){printf("Introd. propozitia!"); break;}
for(i=0; i<probels+1; i+=2)
{ Transpune(cuvintele[i]); }
if(!bPar || !bImpar)
{
puts("Propozitia transpusa: ");
for(int i=0; i<len; i++) printf("%c", cPropozitia[i]);
}
else printf("Cuvinte transpuse nu sunt\n");
}
break;
default: printf("Comanda necunoscuta\n"); break;
}
mi = getch();
}
return 0;
}
void EnterProp(char* c)
{ printf("Introduceti propozitia: ");
gets(c);
c = Trim(c);
entered = true;
return;
}

void Calculate(char* c)
{ int i, j;
len = strlen(c);
for(i=0; i<strlen(c); i++)
if(c[i]==' ')probels++;
/* SPLIT */
cuvintele[0] = strtok(c, " ");
for(i=0; i<probels; i++)
cuvintele[i+1] = strtok(NULL, " ");
/* *** */
for(i=0, j=1; i<probels+1; i+=2, j+=2) //j-pare, i-impare
{
SimetricImpare(cuvintele[i]);
if(j<probels+1)SimetricPare(cuvintele[j]);
}
printf("\nCalcule si operatii cu propozitia au fost efectuate\n");
return;
}
void SimetricPare(char* c)
{ int j=strlen(c)-1, i;
for(i=0; i<strlen(c)/2; i++, j--)
if(c[i]!=c[j])return;
bPar = true;
if(countPare<(int)strlen(c))
{ cMaiLung = c;
countPare = strlen(c);
}
return;
}
void SimetricImpare(char* c)
{ int j=strlen(c)-1, i;
for(i=0; i<strlen(c)/2; i++, j--)
if(c[i]!=c[j])return;
bImpar = true;
if(countImpare>(int)strlen(c))
{
cMaiScurt = c;
countImpare = strlen(c);
}
return;
}
void Transpune(char* cuvintul)
{ int i, j=strlen(cuvintul)-1;
char c;
for(i=0; i<strlen(cuvintul)/2; i++, j--)
{
c = cuvintul[i];
cuvintul[i] = cuvintul[j];
cuvintul[j] = c;
}
return;
}
char* Trim(char* c)
{ int index, i, j;
char* str2trim;
str2trim = c;
index = strlen(str2trim);
for(i=0, j=0; i<strlen(str2trim); i++)
{
if(str2trim[i]==' ' && str2trim[i+1]==' '){ index--; continue; }
str2trim[j] = str2trim[i];
j++;
}
str2trim[index] = '\0';
while(*str2trim==' ')*str2trim++;
index = strlen(str2trim);
while(str2trim[index-1]==' ') index--;
str2trim[index] = '\0';
return str2trim; }
Exemplul 36: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul
funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <stdio.h>;
int cmp_cresc ( const int x, const int y )
{     if ( x == y )    return 0;     return x > y ? 1 : -1; }
int cmp_desc ( const int x, const int y ) {    if ( x == y )   return 0;   return x > y ? -1 : 1; }
void swap ( int *x, int *y) {    int aux;    aux = *x;  *x = *y;  *y = aux; }
void sort(int v[], int n, int (*cmp) ( const int x, const int y) ) {  int i, j;
  for ( i = 0; i < n - 1; i++ )  for ( j = i + 1; j < n; j++ )    if ( cmp ( v[i], v[j] ) == 1 )   swap( &v[i], &v[j] ); }
int main() {   int v[100], n, ord;
   int (*cmps[2]) ( const int, const int ) = { cmp_cresc, cmp_desc }; // citire n si vector ...
  printf("Alegeti tipul de sortare: 0 - crescator; 1 - descrescator.\n");    scanf("%d", &ord);
   sort(v, n, cmps[ord]);    // afisare vector sortat ...
    return 0; }
Exemplul 37: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul
funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
int a; int *pa; int **ppa; int ***pppa; int ****ppppa; int *****pppppa;
int (*v[3])[3][3]; int (*(*ppp)[3])[3][3]; int(*(*(*ph))[3])[3][3];
int s[3],m[3][3]={{1,2,8},{4,6,7},{6,7,5}};
void main(){clrscr();
a=33; pa=&a; ppa=&pa; pppa=&ppa; ppppa=&pppa; pppppa=&ppppa;
printf("\n%d %d %d ",*pa , ***pppa, *****pppppa); getch();
clrscr(); for(int i=0;i<3;i++) v[i]=&m; ppp=&v; ph=&ppp;
for(i=0;i<3;i++){s[i]=0; for(int j=0;j<3;j++) s[i]+=(*(*(*ph))[i])[i][j]; }for(i=0;i<3;i++) printf(" %d ",s[i]); getch(); }
Exemplul 38: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul funcţiilor.
Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
int ***ppp_int; int **pp_int; int *p_int; int intreg=44;
void main(){ clrscr(); p_int=(int *)malloc(sizeof(int)); pp_int=(int **)malloc(sizeof(int*));
ppp_int=(int ***)malloc(sizeof(int**)); p_int=&intreg; pp_int=&p_int; ppp_int=&pp_int;
printf(" %d %d ", *p_int ,***ppp_int); getch(); }
Exemplul 39: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul
funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<conio.h>
#include<stdio.h>
int (*v[3])[3][3]; int (*(*ppp)[3])[3][3]; int(*(*(*ph))[3])[3][3];
int s[3],m[3][3]={{1,2,8},{4,6,7},{6,7,5}};
void main(){ clrscr(); for(int i=0;i<3;i++) v[i]=&m; ppp=&v; ph=&ppp;
for(i=0;i<3;i++){s[i]=0; for(int j=0;j<3;j++) s[i]+=(*(*(*ph))[i])[i][j]; }
for(i=0;i<3;i++) printf(" %d ",s[i]); getch();}
Exemplul40: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul funcţiilor.
Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
float y[100]; int f[100],n; float xmed[3]; float (*functii[3])(float x[100],int f[100],int n);
float med1(float x[100],int f[100],int n){ float a=0,b;
for(int i=0;i<n;i++){ f[i]=1; a+=x[i]*f[i]; } return a/n; }
float med2(float x[100],int f[100],int n){ float a,b; a=b=0;
for(int i=0;i<n;i++){ b+=f[i]; a+=x[i]*f[i]; } return a/b; }
float med3(float x[100],int f[100],int n){ float a,b; a=b=0;
for(int i=0;i<n;i++){ b+=f[i]; a+=f[i]/x[i]; } return b/a; }
void main(){ functii[0]=med1; functii[1]=med2; functii[2]=med3;
printf("\nNr de elemente: ");fflush(stdin); scanf("%d",&n);
for(int i=0;i<n;i++){ printf("\n y[%d]= ",i+1); scanf("%f",&y[i]); }
for(i=0;i<3;i++){ xmed[i]=functii[i](y,f,n); printf("\n%12.10f",xmed[i]); } getch(); }
Exemplul 41. Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul funcţiilor.
Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
typedef char* f();
f *a[2][2]; //o matrice de pointeri spre functii
char *s;
char *f1(){ return "functia1"; } char* f2(){ return "functia2"; }
char* f3(){ return "functia3"; } char* f4(){ return "functia4"; }
void main(){ clrscr(); a[0][0]=f1; a[0][1]=f2; a[1][0]=f3; a[1][1]=f4;
for(unsigned char i=0;i<2;i++) for(unsigned char j=0;j<2;j++){ s=a[i][j](); printf("\n %s",s); } getch(); }
Exemplul 42: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
int main() { float *q, **b; int i, j, k, n, m;
scanf("%d %d",&n,&m); q=(float *)calloc(m,sizeof(float));
q[0]=22.3; q-=5;
q[5]=1.5; /* num. elem.?*/
q[6]=2.5; /* num. elem.?- */
q[7]=3.5; /* num. elem.? - */
q+=5; /* */
q+=2; /* */
q[-2]=8.2; q[-1]=4.5; q-=2; /* */
q--; /* */
free(++q); /* */
b=(float **)calloc(m,sizeof(float *));
for (i=0; i < m; i++) b[i]=(float *)calloc(n,sizeof(float));
for (i=0; i < m ; i++) --b[i]; b--;
for (i=1; i<=m; i++) for (j="1;" j<="n;" j++) b[i][j]="(float)(i+j);"
for (i="1;" i<="m;" i++) ++b[i]; b++; /* */
for (i="0;" i < m; i++) free(b[i]); free(b); ... ... return 0; }
Exemplul 43: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
main() { int vvod(double ***, long **);
double **a; /* a[n][m] */
long *b; /* b[n] */
vvod (&a,&b); /* */ }
int vvod(double ***a, long **b) { int n,m,i,j;
scanf (" %d %d ",&n,&m); *a=(double **)calloc(n,sizeof(double *));
*b=(long *)calloc(n,sizeof(long));
for (i=0; i<=n; i++) *a[i]="(double" *)calloc(m,sizeof(double)); }
Exemplul 44: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include …. #include …..
double cos(double); double sin(double); double tan(double);
int main() { double (*(*masfun))(double);
double x=0.5, y; int i;
masfun=(double(*(*))(double)) calloc(3,sizeof(double(*(*))(double)));
masfun[0]=cos; masfun[1]=sin; masfun[2]=tan;
for (i=0; i<3; i++); { y="masfun[i](x);" printf("\n x="%g" y="%g",x,y);" }
return 0; }
Exemplul 45: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#define n 3
int a[n][n],b[n][n];
void linii(int **a,int i,int j) // cerinţa a)
{ int *p,*q,aux; for(p=*(a+i),q=*(a+j);p<=*(a+i)+n-1;p++,q++)
{ aux=*p;*p=*q; *q=aux; } }
void coloane(int **a,int i,int j) // cerinţa b)
{ int *p,*q,aux; for(p=*a+i,q=*a+j;p<=*(a+n-1)+i;p+=n,q+=n)
{ aux=*p; *p=*q; *q=aux; } }
int suma(int **a) // cerinţa c)
{ int *p,s=0; for(p=*a;p<=*(a+n-1)+n-1;p+=n+1)
s+=*p; return s; }
int produs(int **a) // cerinţa d)
{ int *p,pr=1; for(p=*a+n-1;p<=*(a+n-1);p+=n-1)
pr*=*p; return pr; }
void transpusa(int **a,int **b) // cerinţa e)
{ int *p,*q,*r; for(int i=0;i<n;i++)
for(p=*(a+i),q=p+n-1,r=*b+i;p<=q;p++,r+=n)
*r=*p;
}
Exemplul 46: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <alloc.h>
main() {
#define NUMINTS 3
int *list,i; list = (int *) calloc(NUMINTS,sizeof(int));
*list = 421; *(list+1) = 53; *(list+2) = 1806; printf(":");
for (i=0; i<NUMINTS; i++) printf("%4p ",(list+i)); printf("\ :");
for (i=0; i<NUMINTS; i++) printf("%4p ",*(list+i)); printf("\n"); }
Exemplul47: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#define n 3
int a[n][n],b[n][n];
void linii(int **a,int i,int j) // cerin]a a)
{ int *p,*q,aux;
for(p=*(a+i),q=*(a+j);p<=*(a+i)+n-1;p++,q++)
{ aux=*p;
*p=*q; *q=aux; } }
//***********************
void coloane(int **a,int i,int j) // cerin]a b)
{ int *p,*q,aux;
for(p=*a+i,q=*a+j;p<=*(a+n-1)+i;p+=n,q+=n)
{ aux=*p;
*p=*q; *q=aux; } }
//***********************
int suma(int **a) // cerin]a c)
{ int *p,s=0;
for(p=*a;p<=*(a+n-1)+n-1;p+=n+1)
s+=*p; return s; }
//***********************
int produs(int **a) // cerin]a d)
{ int *p,pr=1;
for(p=*a+n-1;p<=*(a+n-1);p+=n-1)
pr*=*p; return pr; }
//***********************
void transpusa(int **a,int **b) // cerin]a e)
{ int *p,*q,*r;
for(int i=0;i<n;i++)
for(p=*(a+i),q=p+n-1,r=*b+i;p<=q;p++,r+=n)
*r=*p; }
Exemplul 48: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <alloc.h>
main()
{ #define NUMINTS 3
int *list,i; list = (int *) calloc(NUMINTS,sizeof(int));
*list = 421; *(list+1) = 53; *(list+2) = 1806; printf("Lista adreselor:");
for (i=0; i<NUMINTS; i++) printf("%4p ",(list+i)); printf("\n Lista valorilor:");
for (i=0; i<NUMINTS; i++) printf("%4p ",*(list+i)); printf("\n"); }
Exemplul 49: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
main()
{ int a[3][4] = {{ 5, 3, -21, 42},
{44, 15, 0, 6},
{97 , 6, 81, 2}};
int b[4][2] = {{22, 7},
{97, -53},
{45, 0},
{72, 1} };
int c[3][2],i,j,k;
for (i=0; i<3; i++) {
for (j=0; j<2; j++) {
c[i][j] = 0;
for (k=0; k<4; k++)
c[i][j] += a[i][k] * b[k][j];
}
}
for (i=0; i<3; i++) {
for (j=0; j<2; j++) printf("c[%d][%d] = %d ",i,j,c[i][j]); printf("\n");
}
}
Exemplul 50: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#include<string.h>
char *mesaj, alegere;
main(){ while (1) { puts ("\nIntroducetsi 1,2 say 3; esc pentru a iesi din program"); alegere= getch();
switch(alegere) {
case 49: { if(mesaj = malloc(14)!=NULL) { strcpy(mesaj, "ati apasat 1"); break; }
else { puts("Insuficienta memorie, nu se poate de continuat"); exit(1); } }
case 50: { if (mesaj=malloc(26)!=NULL) { strcpy(mesaj, "Ati apasat tasta 2"); break; }
else { puts("Insuficienta memorie, nu se poate de continuat"); exit(1); } }
case 51: { if (mesaj = malloc(24)!=NULL) { strcpy(mesaj, "De data asta atsi tastat 3"); break; }
else { puts("Insuficienta de memorie, nu se poate de continuat"); exit(1); } }
case 27: { exit (0); } default: { ; } } puts (mesaj); } }
Exemplul 51.: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
a). ...
int i, p, j, q;
p = &i; q = &p;
j = p = 1; q = p-1; p += 1;
i = ++q + p; q -= 1; i = q ++ + q;
printf("i=%d, j=%d, p=%d, q=%d \n", i, j, p, q);
b) ...
int x = 1, y; char c = ‘a’;
int pi, qi; char pc;
pi = &x;pi = 3; y = pi; pi = c; qi = pi;
pc = qi; qi+=1; pi++; (- - pi) = 5; y = qi+1;
pc = &c; ++pc; (pc)++; pc++; pc+=1;
x = (int)pi; pi=(int)pc; pi=(int)x; x = 1+ pi; pc=(char)pi;
c = pc; pc = &y; x = qi – pi; qi = 0; qi+=pi;
y = &pi;y = (int)&pi; pi = pi +5; (pi+1)=0; pi=&(x+0);
Exemplul 52: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
a) int i = 2; const int j = 5;
int pi;
const int pci;
int const cpi;
const int  const cpci;
pi = &i; pci = &j; cpi = &i; cpci = &j; pci = &i;
pi = (int)&j; i = pci + pi; pci = 3;
pi = 3; i=pci++; (cpi++)=5; cpi++;
b) int f(const int i, int j) { j++; return i+j; }
main()
{ int a, b; const int c = 5;
scanf("%d", &a);
b = f(c,a); printf("a=%d, b=%d, c=%d \n", a, b, c);
b = f(c,c); printf("a=%d, b=%d, c=%d \n", a, b, c);
b = f(a,a); printf("a=%d, b=%d, c=%d \n", a, b, c);
b = f(a,c); printf("a=%d, b=%d, c=%d \n", a, b, c);
}
Exemplul53: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
a) int a[100], sum, i;
sum = 0;
for ( i = 0; i < 100; ++i ) sum += a[i];
b) int a[100], p, sum;
sum = 0;
for ( p = a; p < &a[100]; ++p ) sum = sum + p;
c) int a[100], p, sum;
sum = 0;
for ( p = &a[0]; p < &a[100]; p++ ) sum += p;
d) int a[100], sum, i;
sum = 0;
for ( i = 0; i < 100; ++i ) sum += (a+i);
e) int a[100], sum, i;
sum = 0;
for ( i = 0; i < 100; ++a, ++i ) sum += a;
f) int a[100], p, sum, i;
sum = 0;
for ( i = 0, p = a; i < 100; ++i ) sum += p[i];
g) int a[100], p, sum, i;
sum = 0;
for ( i = 0, p = a; i < 100; ++i ) sum += (p+i);
Exemplul54: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
a) . . .
int a[5] = { 1, 2, 3, 4, 5 };
int p, x, q , i;
p = a + 2;  (p+2) = 7;
a += 3; q=&p-1;
x = ++ p - q ++; x += ++ p; x=p-- + p++;
for (i = 0; i < 5; i++) printf("a [%d]=%d", i, a[ i ] ); printf("\n");
printf("x=%d, p=%d, q=%d \n", x, p, q);
b) . . .
char str = "abcdef";
char p, q, r; int k;
p = str; q = 0; p++;
k = p - str; r = p+k;
if ( k && p || q ) q = str + 6;
p = q ? r : q; (p-1) = ‘a’; r = ‘x’;
printf("str: %s\n", str);
c) ...
char s[ ] = "0123456";
int pi; char pc1, pc2;
pc2 = s;
pc1 = s + (s+strlen(s) - 1) - ‘0’;
pi = ( int ) pc2; pc1-- = ‘8’;
if ( pc1 - pc2 < 3 ) pc1 = pc2 = pi; else pc1 = ( pc1+pc2 )/2;
if ( s == pc2 ) pc1 = pc2 + 1; else pc1 = ‘9’;
printf("s: %s\n", s);
d) . . .
int i; char c; int pi;
i = ‘a’;
pi = &i; c = (char)pi + 3; printf("c1=%c", c);
i <<= 8; c--; printf("c2=%c\n", c);
e) ...
char c1, c2; short i;
char pc; short ps;
c1 = ‘1’;c2 = ‘2’;ps =&i;
pc = (char)ps; pc = c1; pc++; pc = c2;
printf("i = %hd\n", i);
Exemplul55: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
char str[ ] = "ABCDIREWIEWRHOWEHOWL5";
main()
{ int i, c;
for ( i = 2; ( c = str [ i ] ) != ‘\0’; i++) {
switch (c) {
case ‘a’: putchar(‘i’); continue;
case ‘1’: break;
case 1: while ( ( c = str [++ i ] ) != ‘\1’ && c != ‘\0’);
case 9: putchar(‘S’);
case ‘E’: case ‘L’: continue;
default: putchar(c); continue; }
putchar(‘ ’); } putchar(‘\n’); }
Exemplul56: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
int a[ ] = { 0, 1, 2, 3, 4 };
main()
{ int i, p;
for ( i = 0; i <= 4; i++ ) printf("a[ i ]=%d ", a[ i ]); printf("\n");
for ( p = &a[0]; p <= &a[4]; p++ ) printf("p=%d ", p); printf("\n");
for ( p = &a[0], i = 0 ; i <= 4; i++ ) printf("p[ i ]=%d ", p[ i ]); printf("\n");
for ( p = a, i = 0; p+i <= a+4; i++ ) printf(" (p+i)=%d ",  (p+i));
printf("\n"); for ( p = a+4; p >= a; p-- ) printf("p=%d ", p ); printf("\n");
for ( p = a+4, i=0; i <= 4; i++ ) printf("p[ -i ]=%d ", p[ -i ]);
printf("\n"); for ( p = a+4; p >= a; p -- ) printf("a[ p - a ]=%d ", a[ p - a ]); printf("\n"); }
Exemplul 57: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
int a[ ] = { 8, 7, 6, 5, 4 };
int p[ ] = { a, a+1, a+2, a+3, a+4 };
int pp = p;
main()
{ printf("a=%d p=%d pp=%d\n", a, p, pp );
pp++;
printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp );
++pp;
printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp );
pp = p;
++pp;
printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp ); }
Exemplul58: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
int a[ 3 ][ 3 ] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int pa[ 3 ] = { a[ 0 ], a[ 1 ], a[ 2 ] };
int p = a[ 0 ];
main()
{ int i;
for ( i = 0; i < 3; i ++ )
printf(" a[ i ][ 2 – i ]=%d a[ i ]=%d ((a+i)+i)=%d\n",
a[ i ][ 2 – i ], a[ i ], ((a+i)+i));
for ( i = 0; i < 3; i ++ )
printf("pa[ i ]=%d p[ i ]=%d \n", pa[ i ], p[ i ] ); }
Exemplul 59: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
char c[ ] = { "FIRST", "FOR", "WHILE", "DO" };
char  cp[ ] = { c+3, c+2, c+1, c };
char cpp=cp;
main()
{ printf("%s", ++cpp );
printf("%s ",  -- ++cpp+3 );
printf("%s", cpp[ -2 ]+3 );
printf("%s\n", cpp[ -1 ][ -1 ]+1 ); }
Exemplul 60: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Se introduc de la tastatură două şiruri de caractere oarecare. Utilizându-se o funcţie (de
comparare a celor două şiruri) apelată prin intermediul unui pointer la această functie să se afişeze dacă cele două şiruri sunt
sau nu identice oricare ar fi cele doua siruri.
#include <stdio.h>
#include <string.h>
//--------------------------------------------------------------
// definirea functiei de verificare a doua siruri oarecare
void comparare_siruri (char *x, char *y, int ( *comp) (const char *,const char *))
{printf(“\n verificarea egalitatii sirurilor”);
if((*comp)(x,y)) /* apelarea functiei comp prin pointer catre aceasta */
printf(“\n sirul %s nu este egal cu sirul %s”,a,b);
else printf(“\n sirul %s este egal cu sirul %s”,a,b);
}
//--------------------------------------------------------------
// functia principala
void main(void) {char sir1[50],sir2[50],raspuns=’d’;
int (*ptrfunctie) ();
// memorarea in ptrfunctie a adresei punctului de intrare in functia de comparare a doua siruri strcmp
ptrfunctie = strcmp;
while(raspuns == ‘d’) {gets(sir1); gets(sir2);
// apelarea functiei de comparare siruri
compara_siruri(sir1,sir2,ptrfunctie);
printf(“\n continuati ?(d/n):”); raspuns = getchar(); } }
//--------------------------------------------------------------
Exemplul 61: Evaluaţi în programul dat expresia : **(*(*x)[i]).y[j]) Explicaţi modul de rezolvare şi rezultatul obţinut:
#include<stdio.h>
#include<conio.h>
typedef int a; typedef a *pa; typedef pa *ppa; typedef ppa v1[2];
struct r1 { v1 y; }; typedef struct r1 *pr1; typedef pr1 v2[2]; typedef v2 *pv2; a n1,n2,m1,m2;
pa pn1,pn2,pm1,pm2; ppa ppn1,ppn2,ppm1,ppm2; struct r1 art1,art2; v2 vec; pv2 x;
void main() { clrscr(); n1=100; n2=101; pn1=&n1; pn2=&n2; ppn1=&pn1; ppn2=&pn2; art1.y[0]=ppn1;
art1.y[1]=ppn2; m1=200; m2=201; pm1=&m1; pm2=&m2; ppm1=&pm1; ppm2=&pm2;
art2.y[0]=ppm1; art2.y[1]=ppm2; vec[0]=&art1; vec[1]=&art2; x=&vec;
for(int i=0;i<2;i++) for(int j=0;j<2;j++) printf("\n **(*(*x)[i]).y[j]=== %d ",i,j,**(*(*x)[i]).y[j]); getch(); }
Exemplul 62: Evaluaţi programul dat şi explicaţi modul de rezolvare şi rezultatul obţinut:
#include<dos.h>
#include<stdio.h>
unsigned int aaa, bbb; int a[3][3][3]; int *vp[3]; int *pp; int j,k; char rasp=’1’;
void main(){ clrscr();
for(int i=0;i<3;i++) for(j=1;j<3;j++) for(k=0;k<3;k++) a[i][k][j]=0;
for(i=0;i<3;i++) vp[i]=&a[i][0][0];
while(rasp!=’ ’){ printf("\nintroduceti coordonatele:\n "); printf("\ncoordonata i : ");scanf("%d",&i);i--;
printf("coordonata j : ");scanf("%d",&j);j--; printf("coordonata k : ");scanf("%d",&k);k--;
aaa=FP_SEG(vp[i]); bbb=FP_OFF(vp[i]); aaa+=(j*3+k)*2; bbb+=(j*3+k)*2;
pp=(int *)MK_FP(aaa,bbb); printf("\ndati elementul: "); scanf("%d",pp);
printf("\nContinuati introducerea? [pentru nu,tastati spatiu]: "); fflush(stdin);scanf("%c",&rasp); }
for(i=0;i<3;i++){ for(j=0;j<3;j++){ for(k=0;k<3;k++) printf(" %d ",a[i][j][k]); printf("\n"); }
printf("\n*******\n"); } getch(); }
Exemplul 63: Rulaţi, afişaţi şi analizaţi
#include <stdio.h>
#include <string.h>
// definirea functiei de verificare a doua siruri oarecare
void comparare_siruri (char *x, char *y, int ( *comp) (const char *,const char *))
{printf(“\n verificarea egalitatii sirurilor”);
if((*comp)(x,y)) /* apelarea functiei comp prin pointer catre aceasta */
printf(“\n sirul %s nu este egal cu sirul %s”,a,b);
else printf(“\n sirul %s este egal cu sirul %s”,a,b); }
// functia principala
void main(void) {char sir1[50],sir2[50],raspuns=’d’;
int (*ptrfunctie) (); // memorarea in ptrfunctie a adresei punctului de intrare in functia de comparare a doua siruri strcmp
ptrfunctie = strcmp;
while(raspuns == ‘d’) {gets(sir1); gets(sir2); // apelarea functiei de comparare siruri
compara_siruri(sir1,sir2,ptrfunctie); printf(“\n continuati ?(d/n):”); raspuns = getchar(); } }
Exemplul 64: Rulaţi, afişaţi şi analizaţi. Matrice alocata dinamic (cu dimensiuni cunoscute la executie)
#include <stdio.h>
#include <stdlib.h>
int main () {int n,i,j; int ** mat; // adresa matrice
// citire dimensiuni matrice<
printf("n="); scanf("%d",&n);
// alocare memorie ptr matrice
mat=(int **) malloc (n*sizeof (int*));
for (i=0;i<n;i++) mat[i] =(int*) calloc (n,sizeof (int));
// completare matrice
for (i=0;i<n;i++)
for (j=0;j<n;j++) mat[i][j]= n*i+j+1;
// afisare matrice
for (i=0;i<n;i++) {
for (j=0;j<n;j++) printf ("%6d ",mat[i][j]); printf ("\n"); } return 0; }
Exemplul 65: Rulaţi, afişaţi şi analizaţi. Pointeri la pointeri (indirectare multipla). Limbajul C permite realizarea indirectarii
multiple prin faptul ca un pointer poate fi pointat de un alt pointer.
#include<stdio.h>
char *m[]= {"ianurie", "februarie", "martie",
"aprilie","mai","iunie","iulie","august","septembrie","octombrie","noiembrie","decembrie"}; // initializare poiterilor la masiv
char **mp[]= {m+11,m+10,m+9,m+8,m+7,m+6,m+5,m+4,m+3,m+2,m+1,m};/*initializare poiterilor catre m*/
char ***mpp[]= {mp, mp+9,mp+6,mp+3};//initializarea poiterilor catre mp
char ****mppp=mpp; //atribuirea pointerului mppp la masivul mpp
main() { int i; printf("%s \n\n", ***++mppp); // va afisa « martie »
printf("%s \n\n", ***--mppp);// va afisa « decemrie »
for(i=0;i<12;i++) printf("%s %c",mppp[0][0][i-11],(i==6)?'\n':' '); //va afisa lunile in ordine normala
printf("\n\n");
for(i=0;i<12;i++) printf("%s %c",mppp[1][2][11-i],(i==5)?'\n':' '); va afisa lunile in ordine inversa
printf("\n\n"); printf(" %s \n",*mppp[3][1]);// va afisa cuvintul « august »
printf(" %s \n",**mppp[1]+5);// va afisa literea  « e » din cuvintul « martie »
printf("%s \n",mppp[2][-1][3]+2);//va afisa o parte « tombire » din « octomrie »
for(i=0;i<9;i++) printf("%s \t",mppp[3][-2][-9]+i); /*stergerea pe rind a primei litere din cuvintul februarie pina la ultima litera */
}
Exemplul 66: Modificaţi programul prin parcurgeri de pointeri şi cu pointeri la pointeri (indirectare multipla). Apoi Rulaţi,
afişaţi şi analizaţi
#include <conio.h>;
#include <stdio.h>;
#include <stdlib.h>;
void main() { int *p,*a,i,j,n,k,s,l; char ch; clrscr(); printf("Indicati dimensiunea matricei:"); scanf("%d",&n);
a=(int *) malloc(n*n); //Alocarea dinamica a memoriei
p=a; // pointu p se initializeaza cu pointu a
for(i=0;i<n;i++)
for(j=0;j<n;j++) { printf("a[%d,%d]=",i+1,j+1);
scanf("%d",p++); } //Se introduce valori in pointu p si pointu p intorudce valorile in pointu a
while(1) { clrscr();
printf("1 - afisarea elementelor prime\n");
printf("2 - suma elementelor impare\n");
printf("3 - frecventa elementului indicat\n");
printf("4 - iesire\n");
ch=getche();
switch (ch) {
case '1' : clrscr(); printf("Elementele matricei sunt:\n"); p=a; //initializarea pointurului p cu a
for(i=0;i<n;i++) {
for(j=0;j<n;j++)
printf(" %d",*p++); printf("\n"); } // afisharea valorilor din point
printf("Elementele prime ale liniilor impare sunt:\n");
p=a; // Initializam pointu p cu pointu a
for(i=0;i<n;i+=2) {
for(j=0;j<n;j++) {
k=1;s=0;
while(k<=*p && *p!=0)
{ if(*p%k==0) s++; //daca valoarea pointului p la impartire cu k =0
k++; }
if(s==2 || *p==1) //daca cointu p =1
printf(" %d",*p); p++; } // afisharea valoarea pointului p
p+=n; } getch();break;
case '2' : clrscr(); printf("Elementele maricei sunt:\n");
p=a; //initializam pointu p cu pointu a
for(i=0;i<n;i++) {
for(j=0;j<n;j++)
printf(" %d",*p++); printf("\n"); } // afisham valorile pointului p
printf("Suma elementele impare ale coloanelor pare sunt:\n");
s=0; p=a; //Initializam pointu cu poinru a
for(i=0;i<n;i++)
for(j=1;j<n;j+=2)
if(*(p+i*n+j)%2==1) //daca valoarea p[i][j]/2==1
s+=*(p+i*n+j); //la s se adauga valoarea p[i][j]

printf("s=%d",s); getch(); break;

case '3' : clrscr(); printf("Elementele maricei sunt:\n");


p=a;
for(i=0;i<n;i++) {
for(j=0;j<n;j++)
printf(" %d",*p++); printf("\n");}//afisez valoarea pointului
printf("Indicati numarul la care doriti sa aflati frecventa: ");
scanf("%d",&k);
s=0; p=a; // initializarea pointului p cu poitu a
if(n%2==0)
l=n/2-1;
else l=n/2;

for(i=0;i<l;i++)
for(j=i+1;j<n-i-1;j++)
if(*(p+i*n+j)==k) s++; //daca pointu p[i][j]=k
printf("\n Frecventa in cadranul 1 este: %d",s);
s=0;
for(i=n/2+1;i<n;i++)
for(j=i-1;j>=n-i;j--)
if(*(p+i*n+j)==k) s++; //daca pointu p[i][j]=k
printf("\n Frecventa in cadranul 3 este: %d",s); getch(); break;
case '4' : getch(); exit(1); break;
default: printf("\nTastati intre 1 si 3 !\n");
getch();
}
}
}
Exemplul 67: Analizati efectele utilizarii pointerilor la pointeri de caractere prin precizarea valorilor care se afiseaza pe
ecran la executia programului:
#include<stdio.h>
void main(void) {
char *psir[]={"unu","doi","trei","patru"};
char **ppsir[]={psir+3,psir+2,psir+1,psir};
char ***pppsir=ppsir;
printf("\n%s",**++pppsir); printf("\n%s",*--*++pppsir+3); printf("\n%s",*pppsir[-2]+3); printf("\n%s",pppsir[-1][-1]+1);
int a[10]={1,10,20,30,40,50,60,70,80,90} int b[10]={100,200,300,400,500,600,700,800,900,1000}; int *pa=a,*pb=b;
printf("\n *pa+*pb=%d",*pa+*pb); printf("\n *pa++ +*pa++=%d",*pa++ + *pb++);
printf("\n ++*pa + *pb++=%d",++*pa + *pb++); printf("\n *pa++ + ++*pb=%d",*pa++ + ++*pb);
printf("\n *pa++ + *pb++=%d",*pa++ + *pb++); }
Exemplul 68: Analizati efectele utilizarii funcţiilor de caractere prin precizarea valorilor care se afiseaza pe ecran la executia
programului:
#include <stdio.h>
int a[5][5; int b[25]; int i,j;
void f1(void) { int q=0,w=0; printf("introduceti vectorul");
while(i<25){scanf("%d",&b[i]);i++;}
for(i=0;i<25;i++) { a[q][w]=b[i];
if(w<4){w++;} else if(q<4){q++;w=0;} }
printf("matricea obţinută din vector este:");
for(i=0;i<5;i++){printf("\n"); for(j=0;j<5;j++) printf(" %d ",a[i][j]);} exit(); }
void f2(void) { int k=0; char t=0;
for(i=0;i<5;i++) for(j=0;j<5;j++) { scanf("%d",&a[i][j]);}
for(i=0;i<5;i++) for(j=0;j<5;j++) { b[k]=a[i][j]; k++; }
printf("vectorul primit din matrice e:");
for(i=0;i<k;i++) printf(" %d \n",b[i]); }
main() {printf("1:matrice->vector\t2:vector->matrice\n");
if(getchar()=='2') {f1();} else f2(); }
Exemplul 69: Rulaţi, afişaţi şi analizaţi.
void main()
{ int *pi, i=10; float *pf, f=12.5; double *pd, d=0.001; char *pc, c=’a’;
*pi=i; *pf=f; *pd=d; *pc=c;
printf(“pi=%p, pf=%p, pd=%p, pc=%p”, pi, pf, pd, pc);
printf(“*pi=%i, *pf=%f, *pd=%e, *pc=%c”, *pi, *pf, *pd, *pc);
printf(“pi++ =%p, pf++ =%p, pd++ =%p, pc++=%p”, pi++, pf++, pd++, pc++);
printf(“(*pi)++ =%p, (*pf)++ =%p, (*pd)++ =%p, (*pc)++ = %p”, (*pi)++ , (*pf)++, (*pd)++, (*pc)++);
}
Exemplul 70: Da-ţi la execuţie şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor prin pointeri şi
indecşi, afişând toate valorile. Dacă sunt construcţii inadmesibile, face-ţi corectări cu explicaţii şi rulaţi din nou. Comparaţi.
#include <stdio.h>
int a[ ] = { 8, 7, 6, 5, 4 };
int p[ ] = { a, a+1, a+2, a+3, a+4 };
int pp = p;
main() { printf("a=%d p=%d pp=%d\n", a, p, pp );
pp++; printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp );
++pp; printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp );
pp = p; ++pp; printf("pp-p=%d pp-a=%d pp=%d\n", pp-p, pp-a, pp ); }
Exemplul 71: Da-ţi la execuţie şi analizaţi modul de organizare a apelurilor şi obţinere a rezultatelor prin antetul
funcţiilor. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
int intreg=22; int *p_int; //pointer spre intreg
int **pp_int; //pointer spre pointer
void main(){ clrscr(); p_int=(int *)malloc(sizeof(int)); p_int=&intreg; pp_int=(int **)malloc(sizeof(int*));
pp_int=&p_int; printf(" %d %d ",*p_int ,**pp_int); getch(); }
NTREBĂRI ŞI EXERCIŢII

Chestiuni teoretice
1. În ce constă operaţia de incrementare a pointerilor?
2. Tablouri de pointeri.
3. Ce sunt pointerii generici?
4. Ce operaţii se pot realiza asupra variabilelor pointer?
5. De ce numele unui pointer este rvalue?
6. Ce fel de variabile pot constitui operandul operatorului de deferenţiere?
7. Operatorul de referenţiere.
8. Unui pointer generic i se poate atribui valoarea unui pointer cu tip?
9. Care este legătura între tablouri şi pointeri?
10. De ce numele unui tablou este lvalue?

1. Chestiuni practice
11. Să se implementeze programele cu exemplele prezentate.
12. Să se scrie programele pentru exerciţiile rezolvate care au fost prezentate.
13. Analizaţi următoarele secvenţe de instrucţiuni. Identificaţi secvenţele incorecte (acolo unde este
cazul) şi sursele erorilor:
int a,b,*c; a=7; b=90; c=a;
double y, z, *x=&z; z=&y;
char x, **p, *q; x = 'A'; q = &x; p = &q; cout<<”x=”<<x<<’\n’;
cout<<”**p=”<<**p<<’\n’; cout<<”*q=”<<*q<<’\n’;
cout<<”p=”<<p<<” q=”<<q<<”*p=”<<*p<<’\n’;
char *p, x[3] = {'a', 'b', 'c'}; int i, *q, y[3] = {10, 20, 30};
p = &x[0];
for (i = 0; i < 3; i++)
{
cout<<”*p=”<<*p<<” p=”<<p<<’\n’;
p++;
}
q = &y[0];
for (i = 0; i < 3; i++)
{
cout<<”*q=”<<*q<<”q=”<<q<<’\n’;
q++;
}
const char *sirul=”să programăm”; *(sirul)++;
double a, *s; s=&(a+89); cout<<”s=”s<<’\n’;
double a1, *a2, *a3; a2=&a1; a2+=7.8; a3=a2; a3++;
int m[10], *p;p=m;
for (int i=0; i<10; i++)
cout<<*m++;
void *p1; int *p2; int x; p2=&x; p2=p1;
char c=’A’; char *cc=&c; cout<<(*cc)++<<’\n’;

14. Rescrieţi programele pentru problemele din , utilizând aritmetica pointerilor.????????????

Bibliografie:
1. ."Limbajul de programare C". Brian W.Kernighan. Dennis M.Ritchie.
2. Liviu Negrescu. ”Limbajul de programare C şi C++” V.1-4. Buc. 1999
3. Knuth, D. E. - "Arta programarii calculatoarelor, vol. 1: Algoritmi fundamentali", Ed. Teora, 1999.
4. Knuth, D. E. - "Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici", Ed. Teora, 2000.
5. Knuth, D. E. - "Arta programarii calculatoarelor, vol. 3: Sortare si cautare", Ed. Teora, 2001.
6. Bacivarov, A.; Nastac, I. - "Limbajul C. Indrumar de laborator", Tipografia UPB, Bucuresti, 1997.
7. Bates, J; Tompkins, T. - "Utilizare C++", Ed. Teora 2001.
8. Ionescu Texe, C.; Zsako, I. - "Structuri arborescente si aplicatiile lor", Ed. Tehnica, 1990.
9. Andonie, R.; Gabarcea, I. - "Algoritmi fundamentali. O perspectiva C++", Ed. Libris, 1995.
10. Help din Turbo C ++IDE ,versiunea 3.0. 1999
11. CORMEN, T. LEISERSON, CH. RIVEST, R.: Introducere in algoritmi, Ed. Comp Libris. Agora, Cluj, 2000.
12. D. Lucanu: Bazele proiectarii programelor si algoritmilor, Universitatea “A.I.Cuza” Iasi, 1996
13. L. Livovschi, H. Georgescu: Sinteza si analiza algoritmilor, Ed. Stiintifica si enciclopedica, 1986
14. Cristea Valentin. Tehnici de programare. Ed.: Bucur., Teora, 1993. /681.3; T29/
15. Odagescu Ioan, Copos Cristina s.a. Metode si Tehnici de programare./enunţuri, solutii, probleme propuse/ Ed.:Bucur.:
INTACT, 1994 /681.3; O23/
16. Tudor Bălănescu. Corectudinea agoritmilor.Bucur.:Ed. Tehn.1995
17. Conspectele la PC 2015 şi SDA-2016

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

  • Lab 7 SDA
    Lab 7 SDA
    Document6 pagini
    Lab 7 SDA
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 3 SDA UTM
    Lab 3 SDA UTM
    Document5 pagini
    Lab 3 SDA UTM
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 5 SDA
    Lab 5 SDA
    Document5 pagini
    Lab 5 SDA
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 6 SDA
    Lab 6 SDA
    Document6 pagini
    Lab 6 SDA
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 1+2 SDA
    Lab 1+2 SDA
    Document6 pagini
    Lab 1+2 SDA
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 6 Mecanica
    Lab 6 Mecanica
    Document4 pagini
    Lab 6 Mecanica
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 7 Mecanica
    Lab 7 Mecanica
    Document5 pagini
    Lab 7 Mecanica
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 5 PC
    Lab 5 PC
    Document3 pagini
    Lab 5 PC
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 5 Mecanica
    Lab 5 Mecanica
    Document5 pagini
    Lab 5 Mecanica
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 3 Mecanica
    Lab 3 Mecanica
    Document5 pagini
    Lab 3 Mecanica
    ПарасийАлекс
    Încă nu există evaluări
  • Lucrare Nr.1 Mecanica UTM
    Lucrare Nr.1 Mecanica UTM
    Document3 pagini
    Lucrare Nr.1 Mecanica UTM
    ПарасийАлекс
    Încă nu există evaluări
  • Lab 2 Mecanica
    Lab 2 Mecanica
    Document4 pagini
    Lab 2 Mecanica
    ПарасийАлекс
    Încă nu există evaluări