Sunteți pe pagina 1din 47

C_POINTERIL l_2 Sda2020 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ă si prin 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. In baza materialului teoretic la prima lucrare de laborator efectuati cate 2 exerciţii (unul de la inceput si
altul de la alt capat ) şi să se analizeze algoritmii şi si specificul organizarii programelor cu si fără pointeri
(declarări şi parcurgeri cu pointeri). Pentru aprofundarea înţelegerii implementarii pointeriilor in lucrarea de
laborator nr. 2 să se dezvolte algoritmii şi programele cu pointeri pentru condiţiile problemelor din
Anexa1a (Anexa2. De elaborat algoritmul și de scris programul cu pointeri si POINTERI LA FUNCTII, propuse
de profesor) ş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.
3. Î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 cate 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
cu srinshot-urile, 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ă.
1. Î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, elaborind algoritmii.

Consideraţii teoretice:
1. Noţiuni, exemple şi importanţa implementării pointelor
Orice program care realizează un produs are nevoie de eficacitate fără să mai vorbim de driverele la care se
lucrează continuu pentru câteva procente de viteză în plus. Poate că raportată la explozia calculatoarelor, a
programelor prietenoase, a bazelor de date, a paginilor Web, pare în scădere, însă de fapt nevoia de viteză este
din ce în ce mai mare şi mult timp această nevoie nu va dispărea.

1.1 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.
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 ) .
1.1.1 Pentru efectuarea eficienta se utilizeaza mecanismul de functionare a pointerilor,
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ă.
Pointerii au fost introdusi in limbajele de programare pentru a putea rezolva mai eficient anumite probleme.
O prima definitie poate fi urmatoarea: Pointerul este o variabila ce contine adresa unui obiect.
Obiectul a carei adresa este retinuta de pointer poate fi:
 variabila
 functie
Fie urmatorul exemplu:
int x; int *px;
Am definit o variabila de tip intreg x si o variabila pointer, care poate contine adresa unei variabile de tip intreg.
Simbolul * ce apare in stanga variabilei px arata ca px este o variabila pointer.
Prin atribuirea px=&x; (primul mod de initializare) pointerul va avea ca valoare adresa de memorie alocata
variabilei x (vezi laboratorul nr.1, definitia variabilei). Operatorul unar & este utilizat pentru a se obtine adresa
variabilei x (operator unar=are un singur operand)
Acum putem sa lucram cu continutul variabilei x (i.e cu valoarea acesteia) prin intermediul pointerului px, deci
indirect, fara sa mai folosim variabila x. La prima vedere, aceasta modalitate de lucru poate parea dificila si nu
tocmai utila. Necesitatea utilizarii pointerilor va apare cu mai multa claritate in sectiunea dedicata sirurilor de
caractere si functiilor.
1.1.2 Mecanismul de functionare a pointerilor. Deci un pointer este o variabila care are ca valoare
adresa unei zone de memorie.
Pointerii sunt variabile care pot contine 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).
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 mai 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 de declarare:
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; */
Exemple de incrementare:
*p++ operatorul ++ incrementeaza pointerul
(*p)++ operatorul ++ incrementeaza continutul pointerului
*++p operatorul ++ incrementeaza pointerul
++(*p) operatorul ++ incrementeaza continutul pointerului.
Un pointer poate fi utilizat pentru referirea diferitelor date si structuri de date. Schimband adresa memorata in pointer,
pot fi manipulate informatii situate la diferite locatii de memorie.
Pointerii permit de asemenea crearea de noi variabile in timpul executiei programului, prin alocare dinamica.
In declaratia unei variabile pointer se foloseste operatorul de indirectare *:
tip_referit * var_pointer; //* este operator de indirectare
Numele unui tablou x:
 este un pointer, deoarece are ca valoare adresa de memorie a primului sau element, adica
x=&x0
 este un pointer constant, lui nu i se poate atribui o alta valoare (adresa).
Daca p este un pointer spre tip si t este un tablou având elemente de tipul tip, atunci
 o atribuire de forma p=t este corecta, p va pointa spre primul element al tabloului
 o atribuire de forma t=p este incorecta, deoarece se încearca modificarea unui pointer
constant
În consecinta, urmatoarele declaratii sunt echivalente
char a10 si char *s;
int i10 si int *i;
double d1010 si double **d;
Operatorul prefix & dă adresa operandului: &x e adresa variabilei x
Operandul: un lvalue = orice poate apare la stânga unei atribuiri (variabilă, element de tablou, funcţie; NU pentru
expresii oarecare)
Initializarea cu adresa unei variabile:
tip_referit var,* var_pointer; var_pointer=&var;
Valoarea de la adresa indicata de pointer:
*var_pointer// este de tip_referit
Spatiul ocupat de o variabila pointer: sizeof(var_pointer) // valoarea expresiei este 2 ( in modelul
small ), oricare ar fi tip_referit // expresile de mai jos conduc la aceeasi valoare 2
sizeof(&var) sizeof(tip_referit *)
Tiparirea valorii unui pointer: se foloseste prototipul %p, valoarea aparand sub forma a patru cifre hexa Poate fi
tipărită (în hexazecimal) cu formatul %p în printf Adresele sunt întregi, dar posibil 6= sizeof(int) (atenţie la conversie!)
Adresa unei variabile pointer este un pointer, la fel ca adresa unei variabile de orice alt tip: &var_pointer
Observatie: Un pointer poate fi utilizat doar dupa initializare: prin atribuirea adresei unei variabile
sau prin alocare dinamica.
Orice expresie în C are un tip la fel şi expresiile adresă.
OBS: Dacă variabila x are tipul tip, &x are tipul tip * int x; > &x are tipul int *, adică pointer la int (adresă de int)
char c; > &c are tipul char *, (pointer la char, adresă de char)
> există tipuri de adresă diferite pentru fiecare tip de date
> putem declara variabile de aceste tipuri (pointeri):
tip * nume var; nume var e pointer la (adresă pt.) o valoare de tip pointer = o variabilă care conţine adresa altei
variabile
Operatorul prefix * dă obiectul *p de la adresa dată de operandul p
Operand: pointer. Rezultat: referinţă la obiectul indicat de pointer > operator de indirectare
(dereferenţiere, referire indirectă prin adresă)
#include <stdio.h>
double d; int a[10]; // variabile globale, in zona de date
int main(void)
{ int k; // variabilă locală, pe stivă (altă zonă)
printf("Adresa lui d: %p\n", &d); /* de ex. 0x80496c0 */
printf("Adresa lui a[0]: %p\n", &a[0]); /* 0x80496e0 */
printf("Adresa lui a[5]: %p\n", &a[5]); /* 0x80496f4 */
printf("Adresa lui k: %p\n", &k); /* 0xbffff8e4 */ } // &a[5] - &a[0] == 5 * sizeof(int) (poziţii
consecutive)
Toţi pointerii au valori adrese > sizeof(int *) == sizeof(float *) etc.
> putem face conversie explicită ( ) între două tipuri pointer (dăm o altă interpretare octeţilor de memorie la acea
adresă)
int x; char *pc = (char *)&x; *pc conţine primii 8 biţi (inferiori) din x
char s[20]; int n = *(int *)s; n conţine un întreg cu biţii din primii sizeof(int) caractere din s
float f; long n; n = *(long *)&f; n are aceiaşi biţi ca si f, dacă sizeof(float) == sizeof(long)
Tipul void * e folosit ca tip de adresă generică (nu indică nimic)
– poate fi atribuit în ambele sensuri la orice alt pointer, fără ( )
– nu poate fi dereferenţiat fără conversie (nu ştim ce indică)
Trebuie indicat când se cere un pointer dar nu putem da o adresă validă NULL definit în stddef.h ca (void *)0
== adresă distinctivă invalidă (zona de memorie de la adresa 0 nu aaccesibilă programului).
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; */
}

Vom spune ca un pointer “refera” indirect un obiect sau ca “pointeaza”(arata) la obiectul respectiv.
1.3. OPERATORII unari: & si * Inainte de toate, pointerul este o variabila, ca orice variabila, si 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.
Exemplu. 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.
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.
Transformarea pointerilor:
int a = б; int *pa = &а;
void *vpa = (void *)&a; // Transformarea int* in void*
void *vp = {void *)pa; // Transformarea int* in void*
Analogic se face si transformarea inversa :
int a = 6; int *pa; void *vpa = (void *)&a; // Transformarea int* in void*
pa = (int *)vpa; //Transformarea void* in int*
Transformarea pointerilor dintr-un tip in altul :
int a = 6; double *dp - (double *)&а;
int *ip= (int *)dp;
Modul de utilizare: 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;
ca tablou de caractere; exemple:
char sir1[30];
char sir2[10]="exemplu";
ca pointer la caractere; exemple:
char *sir3; // sir3 trebuie initializat cu adresa unui sir sau a unui spatiu alocat pe heap
sir3=sir1; // sir3 ia adresa unui sir static
// sir3=&sir1; sir3=&sir1[0]; sunt echivalente cu instr de atribuire de mai sus
sir3=(char *)malloc(100);// se aloca dinamic un spatiu pe heap
char *sir4="test";// sir2 este initializat cu adresa sirului constant

Exercitiul 1: Analizati efectele utilizarii pointerilor la inversarea ordinii de apariţie a elementelor unui şir
#include<stdio.h>
void main(void)
{ int sir[]={1,2,3,4,5,6,7,8,9}, aux;
int *p_init,*p_fin;
for(p_init=sir,p_fin=sir+sizeof(sir)/sizeof(sir[0])-1;
p_init<p_fin; p_init++,p_fin--){ aux=*p_init; *p_init=*p_fin; *p_fin=aux; }
for(p_init=sir; p_fin=sir+sizeof(sir)/sizeof(sir[0]);
p_init++) printf("\n %d",*p_init);
}
Exercitiul 2: 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);
}
Exercitiul3: Presupunem ca avem declaratiile: int i = 3, j = 5, *p = &i, *q = &j, *r; double
x; de alcatuit programul pentru verificarea echivalentei expresiilor din tabel (Tabelul de mai jos ilustreaza
modul de evaluare a expresiilor cu pointeri), afisind si explicind rezultatul:
----------------------------------------------------------------------
| 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.
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 .
Exercitiul 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 Exercitiul urmator.
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.
Tablouri multidimensionale. In limbajul C, exista o foarte stransa legatura intre pointeri si tablouri, astfel ca
pointerii si tablourile sunt tratate la fel. Orice program in care apar tablouri poate fi modificat astfel incat sa foloseasca
poiteri in locul tablourilor. In aceasta sectiune vom discuta despre legatura dintre pointeri si vectori (tablouri
unidimensionale).
Fie urmatoarele declaratii: int a[20]; int *pa;
Am declrat o variabila a, care este un vector cu maxim 20 elemente intregi si un pointer la o variabila de tip intreg. Dupa
cum se stie, o valoare int are nevoie de 16 biti pentru a fi memorata, adica 2 bytes ( o variabila int poate retine numere
intregi intre -32768 si 32767, vezi curs Bazele Informaticii). Pentru tabloul a vor fi alocati 2 20=40 bytes consecutivi in
memorie adica, pentru primul element a[0] sunt alocati primii 2 bytes, pentru a[1] urmatorii 2 bytes,…, pentru a[19] ultimii
2 bytes din cei 40 alocati.
Fie atribuirea: pa=&a[0];
Dupa aceasta atribuire, pointerul pa contine adresa primului element al vectorului, adica pa pointeaza la inceputul
vectorului a.
Daca scriem pa=&a[3]; atunci pa va referi elementul al 4-lea din vectorul a, iar *pa va contine valoarea sa.
Operatiile care se pot realiza cu pointeri sunt:
* comparatia
*0 adunarea unui pointer cu un intreg
*1 scaderea unui intreg dintr-un pointer
Doi pointeri pot fi comparati folosind operatori relationali. In comparatia:
if(p1==p2) cout<<”Adrese identice”; else cout<<”Adrese diferite”;
se verifica daca adresa memorata de p1 este aceeasi cu adresa retinuta de p2, unde p1 si p2 sunt pointeri de acelasi tip.
Se poate compara un pointer cu valoarea NULL (sau 0). Un pointer are valoarea NULL (valoare nedefinita) daca nu refera
nici un obiect.
Adunarea unui pointer cu un intreg este definita numai atunci cand pointerul refera un tablou (un element al tabloului).
Scaderea este definita in acelasi caz.
Deci constructia “ * pointer “ este o valoare- stanga.
Fie declaraţia tip a[DIM1][DIM2]; Atunci &a[i] == a+i = adresa liniei i = constantă de tipul tip (*)[DIM2] (adresă
de tablou de DIM2 elem. tip)
a[i] e un tablou de DIM2 tip, deci ca nume de tablou are tipul tip *
a[i][j] este al j-lea element din linia (tabloul de DIM2 elem.> a[i] şi are adresa &a[i][j] == (tip *>(a + i> + j == (tip *)a
+ DIM2*i + j Putem parcurge un tablou cu indici sau cu pointeri. Fie int m[5][8];
int i,j; int (*lp)[8], *ip; for (i=0; i<5; ++i) for (lp=m; lp<m+5; ++lp)
// lp e pointer la tablou de 8 int deci ++lp avanseaza cu 8 int m[i] aici e echivalent cu (*lp) aici = tablou de 8 int
for (j=0; j<8; ++j) for (ip=*lp; ip<*(lp+1); ++ip)
// m[i][j] aici echivalent cu (*lp)[j] echivalent cu *ip lp şi *lp au valori numerice egale (adresa unei linii) dar tipuri
diferite: lp: pointer la tot tabloul (8 int); *lp: tabloul = pointer la elem. int
> pentru a obţine un element, parcurgem de la *lp sau îl indexăm.
Exercitiul: Presupunem ca sunt date elementele vectorului "a". Functia de mai jos se poate folosi pentru suma
elementelor unui sir. Atentie ! Trebuie specificat numarul de coloane.
int suma(int a[][5])
{ int i, j, suma = 0;
for (i = 0; i < 3; ++i)
for (j = 0; j < 5; ++j) suma += a[i][j];
return suma; }
In antetul functiei, urmatoarele declaratii sunt echivalente:
int a[][5] int (*a)[5] int a[3][5]
Constanta 3 actioneaza ca o reminiscenta a omului, dar compilatorul nu tine cont de ea.
Nou venitii in C sunt uneori confuzi in legatura cu deosebirea dintre un tablou bidimensional si un tablou de pointeri
cum ar fi "a" din exemplul de mai sus. Fiind date declaratiile
int a[10][10];
int *b[10];
utilizarile lui "a" si "b" pot fi similare, in sensul ca a[5][5] si b[5][5] sunt ambele referinte legale ale aceluiasi "int".
Avantaje pentru utilizarea vectorilor (dezavantaje pentru pointeri):
- "a" este un tablou in toata regula: toate cele 100 celule de memorie trebuie alocate, iar pentru gasirea fiecarui
element se face calculul obisnuit al indicelui;
- pentru "b", oricum prin declararea sa se aloca 10 pointeri; fiecare trebuie facut sa pointeze un tablou de intregi.
Presupunind ca fiecare pointeaza cate 10 elemente din tablou, atunci vom obtine 100 celule de memorie rezervate,
plus cele 10 celule pentru pointeri. Astfel tabloul de pointeri utilizeaza sensibil mai mult spatiu si poate cere un procedeu
explicit de initializare.
Avantaje pentru utilizarea pointerilor (dezavantaje pentru vectori):
- accesarea unui element se face indirect prin intermediul unui pointer, in loc sa se faca prin inmultire si adunare;
- liniile tabloului pot fi de lungimi diferite. Aceasta inseamna ca nu orice element al lui b este constrins sa pointeze pe
un vector de 10 elemente, unii pot pointa pe cate 2 elemente, altii pe cate 20 si altii pe niciunul.
Vectori 3-dimensionali
-----------------------------
Vectorii de dimensiune mai mare decat 3 lucreaza intr-un mod similar. Daca avem declaratia
int a[7][9][2];
atunci compilatorul va aloca spatiu pentru 7*9*2 intregi. Adresa de baza a sirului este "&a[0][0][0]", iar functia de
corespondenta in memorie este specificata de
a[i][j][k] care este echivalent cu *(&a[0][0][0] + 9*2*i + 2*j + k)
Pentru a[i][j] avem expresiile, de exemplu, echivalente:
*(a[i] + j)
(*(a + i))[j]
*((*(a + i)) + j)
*(&a[0][0] + 5*i + j)
Putem gandi "a[i]" ca a "i"-a coloana a lui "a" (numarand de la 0), si "a[i][j]" ca elementul din linia "i", coloana "j" a
sirului (numarand de la 0). Numele sirului ("a") este tot una cu "&a[0]"; acesta este un pointer catre un sir de 5 intregi.
Adresa de baza este "&a[0][0]", si nu "a". Ultimul exemplu de mai sus reflecta functia de corespondenta in memorie dintre
valoarea pointerului si indicele sirului.
Cand un vector multidimensional este un parametru formal in definitia unei functii, toate dimensiunile, exceptand prima
trebuie specificate.
Exercitiu 4. Definiţi şi referiţi un masiv tridimensional, punând în corespondenţă adresa elementelor de pe prima
linie şi coloana masivelor bidimensionale în care se descompune, cu elementele unui vector de pointeri. Iniţializarea
elementelor masivului tridimensional se realizează prin date introduse de la terminal utilizând aceşti pointeri . Rezolvarea
de analizat si explicat fiecare componenta.
#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(); }
Exercitiu 5. Rulaţi, afişaţi şi analizaţi
#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(); }
Exercitiu 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);
}
Exercitiu 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,); }
Exercitiu 8. Rulaţi, afişaţi şi analizaţi In acest Exercitiu 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
Adesea se creează confuzii în ceea ce priveşte diferenţa dintre un masiv bidimensional şi un masiv de pointeri. Fie date
declaraţiile:
int a[10][10];
int *b[10];
În această declaraţie a este un masiv de întregi căruia i se alocă spaţiu pentru toate cele 100 de elemente, iar
calculul indicilor se face în mod obişnuit pentru a avea acces la oricare element al masivului.
Pentru masivul b, declaraţia alocă spaţiu numai pentru zece pointeri, fiecare trebuind să fie încărcat cu adresa unui
masiv de întregi.
Presupunînd că fiecare pointer indică un masiv de zece elemente înseamnă că ar trebui alocate încă o sută de
locaţii de memorie pentru elementele masivelor.
În această accepţiune, folosirea masivelor a şi b poate fi similară în sensul că a[5][5] şi b[5][5], de exemplu, se
referă ambele la unul şi acelaşi întreg (dacă fiecare element b[i] este iniţializat cu adresa masivului a[i]).
Astfel, masivul de pointeri utilizează mai mult spaţiu de memorie decît masivele bidimensionale şi pot cere un pas
de iniţializare explicit. Dar masivele de pointeri prezintă două avantaje, şi anume: accesul la un element se face cu adresare
indirectă, prin intermediul unui pointer, în loc de procedura obişnuită folosind înmulţirea şi apoi adunarea, iar al doilea
avantaj constă în aceea că dimensiunea masivelor pointate poate fi variabilă. Acest lucru înseamnă că un element al
masivului de pointeri b poate indica un masiv de zece elemente, altul un masiv de două elemente şi altul de exemplu poate
să nu indice nici un masiv.
Cu toate că problema prezentată în acest am descris-o în termenii întregilor, ea este cel mai frecvent utilizată în
memorarea şirurilor de caractere de lungimi diferite (ca în funcţia month_name prezentată mai sus).
Funcţii de prelucrare a şirurilor
Declarate în stdio.h:

char * gets(char * s);


- citeşte caracterele din intrare până la întâlnirea caracterului Enter, care nu se adaugă la şirul s; plasează '\0' la
sfârşitul lui s; returnează adresa primului caracter din şir; dacă se tastează CTRL/Z returnează NULL; codul lui Enter e scos
din buffer-ul de intrare

int puts(char * s);


- tipăreşte şirul s, trece apoi la rând nou

scanf("%s",s);
- citeşte caracterele din intrare până la întâlnirea primului blanc sau Enter, care nu se adaugă la şirul s; plasează '\0'
la sfârşitul lui s; dacă se tastează CTRL/Z returnează EOF; codul lui blanc sau Enter rămân în buffer-ul de intrare

printf("%s",s);
- tipăreşte şirul s

Declarate în string.h
int strcmp(char *s1,char *s2);
returnează <0, dacă s1 < s2
0, dacă s1 = s2
>0, dacă s1 > s2
int strncmp(char *s1,char *s2,int n);
comparare a două şiruri pe lungimea n
char* strcpy(char *d,char *s);
copiază şirul sursă s în şirul destinaţie d; returnează adresa şirului destinaţie
char* strncpy(char *d,char *s,int n);
copiază maxim n caractere de la sursă la destinaţie; returnează adresa şirului destinaţie. etc. (veziindicatiile metodice Ll.7.
In urmatorul exemplu de program se defineste un sir de caractere de dimensiune maxima 8 elemente initializate cu : abc).
Rulaţi, afişaţi şi analizaţi, apoi de modificat pentru alte moduri de introducere si afisare a sirului, utilizind functii
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); }
size_t strlen(const char *s) { // lungimea şirului s, până la \0
// size_t (stddef.h>: tip pt. dimensiuni pozitive (unsigned sau long unsigned)
char *p = s; // adică char *p; p = s;
while (*p> p++; // avansează până întâlneşte ’\0’
return p - s; // nr. de caract. între s şi p; ’\0’ nu e numărat
}
char *strcpy(char *dest, const char *src) { // copiază src în dest
char *p = dest;
while (*p++ = *src++); // copiază până la ’\0’; trebuie să avem loc !!!
return dest; // returnează dest prin convenţie
}
char *strcat(char *dest, const char *src) { // concatenează src la dest
char *d = dest; // trebuie sa avem loc in coada lui dest !!!
while (*d); ++d; while (*d++ = *src++); return dest; }
char *strchr(const char *s, int c> { // caută primul caracter c în s
do if (*s == c) return s; while (*s++); // returnează pointer la car. găsit
return NULL; // sau NULL dacă nu a fost găsit
}
int strcmp (const char *s1, const char *s2> { // compară 2 siruri
while (*s1 == *s2 && *s1) { s1++; s2++; } // egale dar nu ’\0’
return *s1 - *s2; // < 0 pt. s1<s2, > 0 pt. s1>s2, 0 daca egale
}
La executie se va afisa : abc ) adica elementele sirului pana la caracterul NULL (’ \0 ‘).
In Exercitiulul 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.
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; 
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 Exercitiu.
Exercitiul: 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.
Numele unui tablou este un pointer constant spre primul sau element. Expresiile de mai jos sunt deci
echivalente:
nume_tablou &nume_tablou &nume_tablou[0] *nume_tablou nume_tablou[0]
Observatie Operatorul de indirectare (*) tine locul unui indice, deci pentru a referi un element din tabloul
bidimensional d, avem nevoie fie de doi indici (d[0[[0[), fie de o indirectare si un indice (*d[0[), fie de doua
indirectari (**d). Exemple Fie urmatoarele declaratii
int x[10[; int a[10[[10[; Avem urmatoarele corespondente:
x+i &x[i[
*(x+i) x[i[
*(a+i)+j &a[i[[j[
*(*(a+i)+j) a[i[[j[
a+i &a[i[
*(a+i) a[i[
a[i[ &a[i[[0[
a[i[+j &a[i[[j[;

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 ).
Exercitiul 9. 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++;
Exercitiu 10. 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 11.: 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”.
Exercitiu 12.: 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 dupa fiecare ciclu. 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 13 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).
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 */ }

Calculul lungimii unui sir de caractere terminat cu ’ \O ‘ in varianta cu pointeri (vezi si : Exercitiul 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.
In Exercitiul 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.
In Exercitiul 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 */
Exercitiul: 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.
Exercitiul 14. Rulaţi, afişaţi şi analizaţi. Functia urmatoare sorteaza crescator un sir de numere utilizand metoda
insertiei directe. In ce consta metoda ? Avind in vedere presupunerea 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 */
}
}
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.Argumentele unei linii de comandă. În sistemul de calcul care admite limbajul C trebuie să existe posibilitatea ca
în momentul execuţiei unui program scris în acest limbaj să i se transmită acestuia argumente sau parametri prin linia de
comandă. Cînd un program este lansat în execuţie şi funcţia main este apelată, apelul va conţine două argumente. Primul
argument (numit convenţional argc) reprezintă numărul de argumente din linia de comandă care a lansat programul. Al
doilea argument (argv) este un pointer la un masiv de pointeri la şiruri de caractere care conţin argumentele, cîte unul pe şir.
Să ilustrăm acest mod dinamic de comunicare între utilizator şi programul său printr-un exemplu.
Fie programul numit pri care dorim să imprime la terminal argumentele lui luate din linia de comandă, imprimarea
făcîndu-se pe o linie, iar argumentele imprimate să fie separate prin spaţii.
Comanda:
pri succes colegi
va avea ca rezultat imprimarea la terminal a textului
succes colegi
Prin convenţie, argv[0] este un pointer la numele pri al programului apelat, astfel că argc, care specifică numărul
de argumente din linia de comandă este cel puţin 1.
În exemplul nostru, argc este 3, iar argv[0], argv[1] şi argv[2] sînt pointeri la "pri", "succes" şi respectiv "colegi".
Primul argument real este argv[1] iar ultimul este argv[argc-1]. Dacă argc este 1, înseamnă că linia de comandă nu are
nici un argument după numele programului.
Atunci programul pri are următorul cod:

main(int argc, char *argv[]) { /* tipăreşte argumentele */


int i;
for (i=1; i<argc; i++) printf("%s%%c",argv[i],(i<argc-1)? ' ':'\n');
}
Deoarece argv este un pointer la un masiv de pointeri, există mai multe posibilităţi de a scrie acest program. Să
mai scriem două versiuni ale acestui program.
main(int argc, char *argv[]) { /* versiunea a doua */
while (--argc>0) printf("%s%c",*++argv,(argv>1)? ' ':'\n'); }
Deoarece argv este un pointer la un masiv de pointeri, incrementîndu-l, (++argv), el va pointa la argv[1] în loc de
argv[0]. Fiecare incrementare succesivă poziţionează pe argv la următorul argument, iar *argv este pointerul la argumentul
şirului respectiv. În acelaşi timp argc este decrementat pînă devine zero, moment în care nu mai sînt argumente de
imprimat.
Alternativ:
main(int argc, char *argv[ ]) { /* versiunea a treia */
while (--argc>0) printf((argc>1)? "%s ":"%s\n",*++argv); }
Această versiune arată că argumentul funcţiei printf poate fi o expresie ca oricare alta, cu toate că acest mod de
utilizare nu este foarte frecvent.
Ca un al doilea exemplu, să reconsiderăm programul din secţiunea 7.5, care imprimă fiecare linie a unui text care
conţine un şir specificat de caractere (schemă).
Dorim acum ca această schemă să poată fi modificată dinamic, de la execuţie la execuţie. Pentru aceasta o
specificăm printr-un argument în linia de comandă.
Exercitiul 15: Şi atunci programul care caută schema dată de primul argument al liniei de comandă este:
#define MAXLINE 1000
main(int argc, char *argv[ ]) { /* găseşte schema din primul argument */
char line[MAXLINE];
if (argc!=2) printf("Linia de comanda eronata\n");
else while (getline(line,MAXLINE)>0)
if (index(line,argv[1])>=0) printf("%s",line); }

unde linia de comandă este de exemplu: "find limbaj" în care "find" este numele programului, iar "limbaj" este schema
căutată. Rezultatul va fi imprimarea tuturor liniilor textului de intrare care conţin cuvîntul "limbaj".
Să elaborăm acum modelul de bază, legat de linia de comandă şi argumentele ei.
Să presupunem că dorim să introducem în linia de comandă două argumente opţionale: unul care să tipărească
toate liniile cu excepţia acelora care conţin schema, şi al doilea care să preceadă fiecare linie tipărită cu numărul ei de linie.
O convenţie pentru programele scrise în limbajul C este ca argumentele dintr-o linie de comandă care încep cu un
semn '-' să introducă un parametru opţional. Dacă alegem, de exemplu, -x pentru a indica „cu excepţia” şi -n pentru a cere
„numărarea liniilor”, atunci comanda:
find -x -n la
avînd intrarea:
la miezul stinselor lumini
s-ajung victorios,
la temelii, la rădăcini,
la măduvă, la os.
va produce tipărirea liniei a doua, precedată de numărul ei, deoarece această linie nu conţine schema "la".
Argumentele opţionale sînt permise în orice ordine în linia de comandă. Analizarea şi prelucrarea argumentelor
unei linii de comandă trebuie efectuată în funcţia principală main, iniţializînd în mod corespunzător anumite variabile.
Celelalte funcţii ale programului nu vor mai ţine evidenţa acestor argumente.
Este mai comod pentru utilizator dacă argumentele opţionale sînt concatenate, ca în comanda:
find -xn la
Caracterele 'x' respectiv 'n' indică doar absenţa sau prezenţa acestor opţiuni (switch) şi nu sînt tratate din punct de
vedere al valorii lor.
Fie programul care caută schema "la" în liniile de la intrare şi le tipăreşte pe acelea, care nu conţin schema,
precedate de numărul lor de linie. Programul tratează corect, atît prima formă a liniei de comandă cît şi a doua.
#define MAXLINE 1000
main(int argc, char *argv[]) {/* caută schema */
char line[MAXLINE], *s;
long line0;
int except, number; line0 = 0; number = 0;
while (--argc>0 && (*++argv)[0]=='-')
for (s=argv[0]+1; *s!='\0'; s++)
switch(*s) {
case 'x': except = 1; break;
case 'n': number = 1; break;
default: printf ("find: optiune ilegala %c\n", *s);
argc = 0; break; }
if (argc!=1) printf ("Nu exista argumente sau schema\n");
else while (getline(line,MAXLINE)>0) { line0++;
if ((index(line,*argv)>=0)!=except) { if (number) printf("%d:",line0);
printf("%s",line);
}
}
}

Dacă nu există erori în linia de comandă, atunci la sfîrşitul primului ciclu while argc trebuie să fie 1, iar *argv
conţine adresa schemei. *++argv este un pointer la un şir argument, iar (*++argv)[0] este primul caracter al şirului. În
această ultimă expresie parantezele sînt necesare deoarece fără ele expresia înseamnă *++(argv[0]) ceea ce este cu totul
altceva (şi greşit): al doilea caracter din numele programului. O alternativă corectă pentru (*++argv[0]) este **++argv.
1.8. Pointeri la funcţii. În limbajul C o funcţie nu este o variabilă, dar putem defini un pointer la o
funcţie, care apoi poate fi prelucrat, transmis unor alte funcţii, introdus într-un masiv şi aşa mai departe. Relativ la o funcţie
se pot face doar două operaţii: apelul ei şi considerarea adresei ei. Dacă numele unei funcţii apare într-o expresie, fără a fi
urmat imediat de o paranteză stîngă, deci nu pe poziţia unui apel la ea, atunci se generează un pointer la această funcţie.
Pentru a transmite o funcţie unei alte funcţii, ca argument, se poate proceda în felul următor:
int f();
g(f);
unde funcţia f este un argument pentru funcţia g. Definiţia funcţiei g va fi:
g(int(*funcpt) ()) {
(*funcpt)();
}
Funcţia f trebuie declarată explicit în rutina apelantă (int f();), deoarece apariţia ei în g(f) nu a fost urmată
de paranteză stîngă ’(’. În expresia g(f) f nu apare pe poziţia de apel de funcţie. În acest caz, pentru argumentul funcţiei g se
generează un pointer la funcţia f. Deci g apelează funcţia f printr-un pointer la ea.
Declaraţiile din funcţia g trebuie studiate cu grijă.
int (*funcpt)();
spune că funcpt este un pointer la o funcţie care returnează un întreg. Primul set de paranteze este necesar, deoarece fără el
int *funcpt();
înseamnă că funcpt este o funcţie care returnează un pointer la un întreg, ceea ce este cu totul diferit faţă de sensul primei
expresii. Folosirea lui funcpt în expresia:
(*funcpt)();
indică faptul că funcpt este un pointer la o funcţie, *funcpt este funcţia, iar (*funcpt)() este apelul funcţiei.
O formă echivalentă simplificată de apel este următoarea:
funcpt();
Ca un exemplu, să considerăm procedura de sortare a liniilor de la intrare, descrisă în secţiunea 9.7, dar modificată
în sensul ca dacă argumentul opţional -n apare în linia de comandă, atunci liniile se vor sorta nu lexicografic ci numeric,
liniile conţinînd grupe de numere.
O sortare constă adesea din trei părţi: o comparare care determină ordinea oricărei perechi de elemente, un schimb
care inversează ordinea elementelor implicate şi un algoritm de sortare care face comparările şi inversările pînă cînd
elementele sînt aduse în ordinea cerută. Algoritmul de sortare este independent de operaţiile de comparare şi inversare,
astfel încît transmiţînd diferite funcţii de comparare şi inversare funcţiei de sortare, elementele de intrare se pot aranja după
diferite criterii.
Compararea lexicografică a două linii se realizează prin funcţiile strcmp şi swap. Mai avem nevoie de o rutină
numcmp care să compare două linii pe baza valorilor numerice şi care să returneze aceiaşi indicatori ca şi rutina strcmp.
Declarăm aceste trei funcţii în funcţia principală main, iar pointerii la aceste funcţii îi transmitem ca argumente
funcţiei sort, care la rîndul ei va apela aceste funcţii prin intermediul pointerilor respectivi.
Exercitiul 16: Funcţia principală main va avea atunci următorul cod:

#define LINES 100 /* nr maxim de linii de sortat */

main (int argc, char *argv[]) {


char *lineptr[LINES]; /* pointeri la linii text */
int nlines; /* număr de linii citite */
int strcmp(), numcmp(); /* funcţii de comparare */
int swap (); /* funcţia de inversare */
int numeric;
numeric = 0; /* 1 dacă sort numeric */
if (argc>1 && argv[1][0]=='-' &&
argv[1][1]=='n')
numeric = 1;
if ((nlines=readlines(lineptr,LINES))>=0)
{
if (numeric)
sort(lineptr,nlines,numcmp,swap);
else
sort(lineptr,nlines,strcmp,swap);
writelines (lineptr,nlines);
}
else
printf
("Nr de linii de intrare prea mare\n");
}

În apelul funcţiei sort, argumentele strcmp, numcmp şi swap sînt adresele funcţiilor respective. Deoarece ele au
fost declarate funcţii care returnează un întreg, operatorul ’&’ nu este necesar să preceadă numele funcţiilor, compilatorul
fiind cel care gestionează transmiterea adreselor funcţiilor.
Funcţia sort care aranjează liniile în ordinea crescătoare se va modifica astfel:

sort(char *v[], int n, int (*comp)(),


int (*exch)()) { /* sortează v0, v1, ... , vn1 */
int gap,i,j;
for (gap=n/2; gap>0; gap/=2)
for (i=gap; i<n; i++)
for (j=i-gap; j>=0; j-=gap) {
if (comp(v[j],v[j+gap])<=0)
break;
exch(v+j,v+j+gap);
}
}

Să studiem declaraţiile din această funcţie.


int(*comp)(), (*exch)();
indică faptul că comp şi exch sînt pointeri la funcţii care returnează un întreg (primul set de paranteze este necesar).
if (comp(v[j],v[j+gap])<=0)
înseamnă apelul funcţiei comp (adică strcmp sau numcmp), deoarece comp este un pointer la funcţie, *comp este funcţia,
iar
comp(v[j],v[j+gap])
este apelul funcţiei.
exch(v+j,v+j+gap)
este apelul funcţiei swap, de inversare a două linii, inversare care realizează interschimbarea adreselor liniilor implicate
(vezi secţiunea 9.2). Funcţia numcmp este următoarea:
numcmp(char *s1, char *s2) {
/* compară s1 şi s2 numeric */
double atof(),v1,v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1<v2)
return -1;
else
if (v1>v2)
return 1;
else
return 0;
}

Pentru ca programul nostru să fie complet să mai prezentăm şi codul funcţiei swap, care schimbă între ei pointerii
a două linii.

swap(char *px[], char *py[]) {


char *temp;
temp = *px;
*px = *py;
*py = temp;
}

1.8. 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-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.
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.
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 */
Exemplul 17 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 */
}
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.
În limbajul C o funcţie nu este o variabilă, dar putem defini un pointer la o funcţie, care apoi poate fi prelucrat, transmis
unor alte funcţii, introdus într-un masiv şi aşa mai departe. Relativ la o funcţie se pot face doar două operaţii: apelul ei şi
considerarea adresei ei. Dacă numele unei funcţii apare într-o expresie, fără a fi urmat imediat de o paranteză stîngă, deci
nu pe poziţia unui apel la ea, atunci se generează un pointer la această funcţie. Pentru a transmite o funcţie unei alte funcţii,
ca argument, se poate proceda în felul următor:
int f();
g(f);
unde funcţia f este un argument pentru funcţia g. Definiţia funcţiei g va fi:
g(int(*funcpt) ()) {
(*funcpt)();
}
Funcţia f trebuie declarată explicit în rutina apelantă (int f();), deoarece apariţia ei în g(f) nu a fost urmată de
paranteză stîngă ’(’. În expresia g(f) f nu apare pe poziţia de apel de funcţie. În acest caz, pentru argumentul funcţiei g se
generează un pointer la funcţia f. Deci g apelează funcţia f printr-un pointer la ea.
Declaraţiile din funcţia g trebuie studiate cu grijă.
int (*funcpt)();
spune că funcpt este un pointer la o funcţie care returnează un întreg. Primul set de paranteze este necesar, deoarece fără el
int *funcpt();
înseamnă că funcpt este o funcţie care returnează un pointer la un întreg, ceea ce este cu totul diferit faţă de sensul primei
expresii. Folosirea lui funcpt în expresia:
(*funcpt)();
indică faptul că funcpt este un pointer la o funcţie, *funcpt este funcţia, iar (*funcpt)() este apelul funcţiei.
O formă echivalentă simplificată de apel este următoarea:
funcpt();
Un pointer la o functie se declara prin :
Exercitiu 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 
Exercitiu 18. 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; }
Exercitiu 19. 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 */ }
Exercitiu 20 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; }
Exercitiu 21. 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.
void g(x){ printf("%d: here\n", x); }
main(){ void (*f)() = g;
(*f)(1);
f (2); }
Ce va afisa urmatoarele fragmente?
/*a*/ typedef void (*(*FUN))();
FUN g(FUN f){ return f; }
void main(){ FUN y = g(g(g(g(g))));
if(y == g) printf("OK %d y =\n", y); }
/*b*/
char *f(){return "Hello, user!";}
g(func)char * (*func)();
{puts((*func)()); }
main(){g(f);}
Exercitiu 22. O sortare constă adesea din trei părţi: o comparare care determină ordinea oricărei perechi de elemente, un
schimb care inversează ordinea elementelor implicate şi un algoritm de sortare care face comparările şi inversările pînă cînd
elementele sînt aduse în ordinea cerută. Algoritmul de sortare este independent de operaţiile de comparare şi inversare,
astfel încît transmiţînd diferite funcţii de comparare şi inversare funcţiei de sortare, elementele de intrare se pot aranja după
diferite criterii.
Compararea lexicografică a două linii se realizează prin funcţiile strcmp şi swap. Mai avem nevoie de o rutină
numcmp care să compare două linii pe baza valorilor numerice şi care să returneze aceiaşi indicatori ca şi rutina strcmp.
Declarăm aceste trei funcţii în funcţia principală main, iar pointerii la aceste funcţii îi transmitem ca argumente
funcţiei sort, care la rîndul ei va apela aceste funcţii prin intermediul pointerilor respectivi.
Funcţia principală main va avea atunci următorul cod:

#define LINES 100 /* nr maxim de linii de sortat */

main (int argc, char *argv[]) {


char *lineptr[LINES]; /* pointeri la linii text */
int nlines; /* număr de linii citite */
int strcmp(), numcmp(); /* funcţii de comparare */
int swap (); /* funcţia de inversare */
int numeric;
numeric = 0; /* 1 dacă sort numeric */
if (argc>1 && argv[1][0]=='-' &&
argv[1][1]=='n')
numeric = 1;
if ((nlines=readlines(lineptr,LINES))>=0)
{
if (numeric)
sort(lineptr,nlines,numcmp,swap);
else
sort(lineptr,nlines,strcmp,swap);
writelines (lineptr,nlines);
}
else
printf
("Nr de linii de intrare prea mare\n");
}

În apelul funcţiei sort, argumentele strcmp, numcmp şi swap sînt adresele funcţiilor respective. Deoarece ele au
fost declarate funcţii care returnează un întreg, operatorul ’&’ nu este necesar să preceadă numele funcţiilor, compilatorul
fiind cel care gestionează transmiterea adreselor funcţiilor.
Funcţia sort care aranjează liniile în ordinea crescătoare se va modifica astfel:

sort(char *v[], int n, int (*comp)(),


int (*exch)()) { /* sortează v0, v1, ... , vn1 */
int gap,i,j;
for (gap=n/2; gap>0; gap/=2)
for (i=gap; i<n; i++)
for (j=i-gap; j>=0; j-=gap) {
if (comp(v[j],v[j+gap])<=0)
break;
exch(v+j,v+j+gap);
}
}

Să studiem declaraţiile din această funcţie.


int(*comp)(), (*exch)();
indică faptul că comp şi exch sînt pointeri la funcţii care returnează un întreg (primul set de paranteze este necesar).
if (comp(v[j],v[j+gap])<=0)
înseamnă apelul funcţiei comp (adică strcmp sau numcmp), deoarece comp este un pointer la funcţie, *comp este funcţia,
iar
comp(v[j],v[j+gap])
este apelul funcţiei.
exch(v+j,v+j+gap)
este apelul funcţiei swap, de inversare a două linii, inversare care realizează interschimbarea adreselor liniilor implicate
(vezi secţiunea 9.2). Funcţia numcmp este următoarea:

numcmp(char *s1, char *s2) {


/* compară s1 şi s2 numeric */
double atof(),v1,v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1<v2)
return -1;
else
if (v1>v2)
return 1;
else
return 0;
}
swap(char *px[], char *py[]) {
char *temp;
temp = *px;
*px = *py;
*py = temp;
}

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.
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 ] ;
}
Exercitiu 23. 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, calloc, malloc, realloc - alocă memoria în mod dinamic
free - eliberează memoria alocată în mod dinamic
Declaraţie
#include <stdlib.h>
void *calloc(unsigned nel, unsigned size);
void *malloc(unsigned size);
void *realloc(void *ptr, unsigned size);
void free(void *ptr);
Descriere
Funcţia calloc alocă memorie pentru un tablou de nel elemente, fiecare de mărime size octeţi şi
returnează un pointer la memoria alocată. Conţinutul memoriei este pus la zero.
Funcţia malloc alocă size octeţi şi returnează un pointer la memoria alocată. Conţinutul memoriei nu
este şters.
Funcţia free eliberează spaţiul de memorie indicat de ptr, care trebuie să fi fost returnat de un apel
anterior malloc, calloc sau realloc. În caz contrar, sau dacă a existat deja un apel anterior free(ptr),
comportamentul programului este imprevizibil.
Funcţia realloc schimbă mărimea blocului de memorie indicat de ptr la size octeţi. Conţinutul rămîne
neschimbat la mărimea minimă dintre mărimea veche şi cea nouă; noul spaţiu de memorie care este eventual
alocat este neiniţializat. Dacă ptr este NULL apelul este echivalent cu malloc(size); dacă size este egal cu zero
apelul este echivalent cu free(ptr). Cu excepţia cazului cînd ptr este NULL, acesta trebuie să fi fost returnat de
un apel precedent malloc, calloc sau realloc.
Valori returnate
Pentru calloc şi malloc valoarea returnată este un pointer la memoria alocată, care este aliniată în mod
corespunzător pentru orice tip de variabile, sau NULL dacă nu există suficientă memorie continuă.
Funcţia free nu returnează nimic.
Funcţia realloc returnează un pointer la noua zonă de memorie alocată, care este aliniată în mod
corespunzător pentru orice tip de variabile, şi poate fi diferită de ptr, sau poate fi NULL dacă nu există suficientă
memorie continuă sau dacă valoarea size este egală cu 0. Dacă realloc eşuează, blocul original rămîne neatins -
nu este nici eliberat nici mutat.

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.
aloca memorie pentru tablou bidimensional:
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 memoriei pentru tablou bidimensional:
for (i = 0 ; i < n; i++) free(a[i]);
free(a);

/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:
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.
Exercitiul 24: 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; }
Exercitiul 25: 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.
Exercitiul 26: 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; }
Exercitiu 27.: 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
Exercitiul 28: 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 Exercitiu:
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 */
Exercitiul 29: 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)); }
Exercitiul .30: 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; }
Exercitiul 31: 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; }
Exercitiul 32: 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();
}
Exercitiul 33: 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(); }
Exercitiul 34: 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();}
Exercitiul 35: 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(); }
Exercitiul 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>
#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(); }
Exercitiul 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; }
Exercitiul 37: 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)); }
Exercitiul 38: 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; }
Exercitiul 39: 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;
}
Exercitiul 40: 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"); }
Exercitiul41: 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; }
Exercitiul 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.
#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"); }
Exercitiul 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 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");
}
}
Exercitiul 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<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); } }
Exercitiul 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. 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);
Exercitiul 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);
}
Exercitiul46: 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);
Exercitiul47: 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);
Exercitiul48: 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’); }
Exercitiul49: 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"); }
Exercitiul 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. 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 ); }
Exercitiul51: 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 ] ); }
Exercitiul 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.
#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 ); }
Exercitiul 53: 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(); } }
//--------------------------------------------------------------
Exercitiul 54: 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(); }
Exercitiul 55: 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(); }
Exercitiul 56: 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(); } }
Exercitiul 57: 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; }
Exercitiul 58: 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 */
}
Exercitiul 59: 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();
}
}
}
Exercitiul 60: 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++); }
Exercitiul 61: 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(); }
Exercitiul 62: 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)++);
}
Exercitiul63: 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 ); }
Exercitiul 64: 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(); }
Exercitiul 65: Da-ţi la execuţie şi analizaţi modul de organizare şi obţinere a rezultatelor , afisind fiecare pas
(linie)
#include<stdio.h>
#include<conio.h>
int a;
int *pa;
int **ppa;
int ***pppa;
int ****ppppa;
int *****pppppa;
void main(){
clrscr();
a=33;
pa=&a;
ppa=&pa;
pppa=&ppa;
ppppa=&pppa;
pppppa=&ppppa;
printf("\n%d",*****pppppa);
getch();
}
Exercitiul 66: Da-ţi la execuţie şi analizaţi modul de organizare şi obţinere a rezultatelor .
#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",**pp_int);
getch();
}
Exercitiul 67: Da-ţi la execuţie şi analizaţi modul de organizare şi obţinere a rezultatelor .
Definiţi un masiv bidimensional de pointeri spre funcţii îl iniţializaţi, efectuaţi, traversaţi acset masiv pe linii şi
coloane. Funcţiile vor afişa cuvinte diferite
#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();
}
Operaţii cu blocuri de memorie. Pentru majoritatea funcţiilor din această categorie compilatorul expandează codul
acestora folosind instrucţiuni pe şiruri de caractere. Declaraţiile acestor funcţii se obţin cu
#include <string.h>
Nume
memcpy - copiază o zonă de memorie
Declaraţie
void *memcpy(void *dest, const void *src, unsigned n);
void *memmove(void *dest, const void *src, unsigned n);
Descriere
Funcţia memcpy copiază n octeţi din zona de memorie src în zona de memorie dest. Zonele de memorie nu trebuie
să se suprapună. Dacă există acest risc se utilizează memmove.
Valoare returnată
Funcţiile returnează un pointer la dest.
Nume
memcmp - compară două zone de memorie
Declaraţie
int memcmp(const void *s1, const void *s2, unsigned n);
Descriere
Funcţia memcmp compară primii n octeţi ai zonelor de memorie s1 şi s2.
Valoare returnată
Returnează un întreg mai mic decît, egal cu, sau mai mare decît zero dacă s1 este mai mic decît, coincide,
respectiv este mai mare decît s2.
Nume
memset - umple o zonă de memorie cu o constantă pe un octet
Declaraţie
void *memset(void *s, int c, unsigned n);
Descriere
Funcţia memset umple primii n octeţi ai zonei de memorie indicată de s cu constanta c pe un octet.
Valoare returnată
Funcţia returnează un pointer la zona de memorie s.
Nume
memchr - caută în memorie un caracter
Declaraţie
void *memchr(const void *s, int c, unsigned n);
Descriere
Funcţia memchr caută caracterul c în primii n octeţi de memorie indicaţi de s. Căutarea se opreşte la primul octet
care are valoarea c (interpretată ca unsigned char).
Valoare returnată
Funcţia returnează un pointer la octetul găsit sau NULL dacă valoarea nu există în zona de memorie.

Exercitiul68 : Da-ţi la elaboraţi ş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.
Presupunem ca avem un vector 2-dimensional cu elemente intregi. int a[3][5];
Incepand cu adresa de baza, compilatorul va aloca spatiu contiguu pentru 15 intregi. Atunci putem
gandi acest vector ca o matrice, astfel:
col1 col2 col3 col4 col5
lin1 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]
lin2 a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]
lin3 a[2][0] a[2][1] a[2][2] a[2][3] a[2][4]
Pentru a[i][j] avem expresiile, de exemplu, echivalente:
*(a[i] + j) (*(a + i))[j] *((*(a + i)) + j) *(&a[0][0] + 5*i + j) (4)
Putem gândi "a[i]" ca a "i"-a coloana a lui "a" (numarand de la 0), si "a[i][j]" ca elementul din linia
"i", coloana "j" a tabloului (numarand de la 0). Numele tabloului ("a") este tot una cu "&a[0]"; acesta
(4) este un pointer catre un tablou de 5 intregi. Adresa de baza este "&a[0][0]", si nu "a". Ultimul
exemplu de mai sus reflecta funcţia de corespondenta in memorie dintre valoarea pointerului si indicele
tabloului.
Exercitiul69 : Da-ţi la elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând
toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include <stdio.h>
#include <math.h>
#define N 50
double f(double x);
double SumaPatrateFunctii (double (*f)(double), double x[], int n);
void main() { double x,tab[50], pas=2 * M_PI/N, s1,s2; int i;
for(i=x=0; i <N ; x+=pas) tab[i++]=x;
s1=SumaPatrateFunctii (sin, tab, N);
s2=SumaPatrateFunctii (f, tab, N);
printf("\nS1=%lf \nS2=%lf", s1,s2); }
//---------------------------------------------------------------
double SumaPatrateFunctii (double (*f)(double), double x[], int n)
{ double sumap=0;
for(n--; n>=0; n--) sumap+= f(x[n]) * (*f)(x[n]); return sumap; }
//----------------------------------------------------------------
double f(double x) { return 1./(x+1); }
Exercitiul70 : Da-ţi la elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând
toate valorile. 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}};
int i,j;
void main(){ for( i=0;i<3;i++) v[i]=&m; ppp=&v; ph=&ppp; printf(" v= %d ",v[i], "ppp=2 %d ",ppp, ph);
for(i=0;i<3;i++){s[i]=0; for(j=0;j<3;j++) s[i]+=(*(*(*ph))[i])[i][j]; printf("Primul s= %d ",s[i]); }
for(i=0;i<3;i++) printf(" v= %d ",v[i]); for(i=0;i<3;i++) printf(" s= %d ",s[i]); getch(); }
Exercitiul71 : Elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând toate
valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
int i;
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(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(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(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(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(); }
Exercitiul72 : Da-ţi la elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând
toate valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţ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); p=a;
for(i=0;i<n;i++) for(j=0;j<n;j++) { printf("a[%d,%d]=",i+1,j+1); scanf("%d",p++); }
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;
for(i=0;i<n;i++) {
for(j=0;j<n;j++) printf(" %d",*p++); printf("\n"); }
printf("Elementele prime ale liniilor impare sunt:\n"); p=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++; k++; }
if(s==2 || *p==1) printf(" %d",*p); p++; } p+=n; }getch();break;
case '2' : //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"); }
printf("Suma elementele impare ale coloanelor pare sunt:\n"); s=0;
p=a;
for(i=0;i<n;i++)
for(j=1;j<n;j+=2) if(*(p+i*n+j)%2==1) s+=*(p+i*n+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"); }
printf("Indicati numarul la care doriti sa aflati frecventa: "); scanf("%d",&k); s=0; p=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++; 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++;
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();
}
}
}
Exercitiul 73 : Elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând toate
valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
#include<stdio.h>
#include<conio.h>
//#include<alloc.h>
#include<mem.h>
#include<string.h>
#include<stdlib.h>

#define NRLINII 20
#define NRCARACT 60
int i;

typedef int (*pointerfcmp)(const void *, const void *);


int getText(char **text, char linieText[]);
int compara(const char **s1, const char **s2);
void afisSortedText(char **text, int nrLinii);
void dezalocMemory(char **text, int nrLinii)

void main(void){

char *tab[NRLINII], linieText[NRCARACT];


//clrscr();
int n = getText(tab, linieText);
//sortare cu qsort
qsort(tab, n, sizeof(char*), (pointerfcmp)compara);
afisSortedText(tab,n);
//dezalocarea memoriei
dezalocMemory(tab, n);
getch();
}
void dezalocMemory(char **text, int nrLinii) {
for(i=0; i<nrLinii; i++)
free(text[i]);
}

int compara(const char *s1[], const char *s2[]) {


return strcmp(*s1, *s2);
}

void afisSortedText(char **text, int nrLinii) {


puts("\nTextul ordonat prin qsort este:");
for(i =0; i<nrLinii; i++)
printf("\n%s", text[i]);}
int getText(char **text, char linieText[]) {
printf("\nIntroduceti liniile (incheiere cu linie vida):\n");
int i = 0;
while(1) {
if(i==NRLINII){ //s-a atins maximul alocat
puts("\nS-a atins maximul de linii alocat pentru text");
break;}
gets(linieText);
//iesirea cu linie vida
if(strlen(linieText) == 0)
break;
//strlen nu contorizeaza '\0'
text[i] = (char*) malloc(strlen(linieText)+1);
if(text[i] == 0) {
puts("\nEroare la alocarea memoriei pentru text");
exit(1);}
strcpy(text[i++], linieText);}
return i; }
Exercitiul74 : Elaboraţi şi analizaţi modul de organizare a parcurgerii şi obţinere a rezultatelor, afişând toate
valorile. Face-ţi îmbunătăţiri şi rulaţi din nou. Comparaţi.
char *lines[NLINES];
void scrollUp(){ char *save = lines[0]; bcopy((char *) lines+1, /* from */ (char *) lines, /* to */
sizeof(char *) * (NLINES-1)); lines[NLINES-1] = save; }
void scrollDown(){ char *save = lines[NLINES-1]; bcopy((char *) &lines[0], /* from */
(char *) &lines[1], /* to */ sizeof(char *) * (NLINES-1)); lines[0] = save; }

INTREBĂRI ŞI EXERCIŢII

Chestiuni teoretice in mod deosebit


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’;

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 2019 şi SDA-2019

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