Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
CLASE DE MEMORIE C++
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 

Programatorul isi poate defini intr-un program variabile utilizabile in tot programul, globale, care sunt alocate pe toata durata unui program. De asemenea programatorul isi poate defini variabile cu utilizari locale, valabile intr-o anumita parte a programului, ele fiind alocate in momentul in care controlul trece prin blocul unde sunt ele definite. p8j20jb
Clasa de memorie a unei variabile determina daca o variabila este globala sau locala.
Limbajul C defineste:
-doua tipuri de clase de memorare:
-globale
-locale si 4 tipuri de specificatori de clase de memorare:
-auto
-register
-static
-extern.
Specificatorii de clase de memorare afecteaza vizibilitatea functiilor si variabilelor precum si clasa lor de memorare.
Vizibilitatea se refera la portiunea din programul sursa in care variabila sau functia poate fi referita.
Plasarea declaratiilor unei functii sau variabile intr-un fisier sursa afecteaza:
-clasa de memorare
-vizibilitatea.
Declararea in afara tuturor definitiilor de functii este numita declarare la nivel extern.
Declararea din interiorul definitiilor de functii poarta denumirea de declarare la nivel intern.

1. DECLARARE DE VARIABILE LA NIVEL EXTERN

Aceasta declarare la nivel extern foloseste specificatorii static sau extern, sau ei se omit cind declararile apar in afara functiilor, caz in care definim variabile globale.
Declararile de variabile la nivel extern sunt:
-de variabile globale
-de referinte la variabile declarate in alte module.
O variabila se defineste la nivel extern o singura data intr-un fisier sursa. Definirea ei implica:
-declararea
-initializarea (daca nu este implicita).
Daca variabila este definita cu specificatorul static, ea este vizibila numai in fisierul sursa in care apare din momentul definirii ei, in alt fisier sursa putind exista o alta variabila cu acelas nume (spunem ca ascundem variabila in cadrul modulului).
Daca variabila este declarata cu specificatorul extern, ea este folosita pentru a declara o referinta la o variabila definita in alta parte (alt modul), fiind folosita pentru:
-a face vizibila o definitie din alt fisier sursa (alt modul)
-a face vizibila o variabila inainte de a o defini (declara) in acelas fisier sursa.
Declararile care contin specificatorul extern nu trebuie sa contina initializari pentru ca ele se refera deja la variabile cu valori definite sau la alte definitii.




2. DECLARARI DE VARIABILE LA NIVEL INTERN

Pentru declararile la nivel intern se pot folosi ori care din cei 4 specificatori de clasa de memorare. Daca acest specificator este omis la declararea la nivel intern , atunci clasa este considerata implicit auto, variabila fiind vizibila doar in blocul in care este declarata.
Variabilele de tip auto, nu sunt initializate automat ele avind valori nedefinite.
Specificatorul de clasa de memorare register, aloca variabilei un registru al procesorului, daca este posibil, aceasta implicind:
-un acces foarte rapid la variabila
-generarea unui cod minimal.
Daca nu este disponibil un asemenea registru, variabila respectiva este de tip auto. De remarcat ca la variabilele de tip register nu este disponibil operatorul de adresare, &.
O variabila declarata la nivel intern cu specificatorul static, retine valoarea ei la parasirea blocului pe cind o variabila declarata la nivel intern cu specificatorul auto, nu retine aceasta valoare. Variabila declarata cu static, este initializata la compilare cu 0 (zero), deci se defineste implicit, nefiind insa reinitializata de fiecare data cind se intra in bloc (practic putind fi o sursa de erori).
O variabila declarata cu specificatorul de clasa de memorare extern, la nivel intern, este o referinta la o variabila cu acelas nume avind rolul de a face variabilele definite la nivel extern (deci in afara functiilor) vizibile in bloc (deci la nivel intern).

3. VARIABILE GLOBALE SI LOCALE, FUNCTII

Variabilele globale pot fi utilizate in tot programul si ele au:
-o definitie la nivel extern, adica in afara functiilor
-sau reprezinta o declaratie de variabile externe adica o declaratie de variabile precedata de cuvintul cheie extern.
Variabilele globale sunt alocate in momentul compilarii la intilnirea definitiei lor, intr-o zona de memorie prevazuta special pentru variabile globale.
Variabilele locale, au o valabilitate locala in functia in care sunt declarate. Ele se declara imediat la inceputul functiei inaintea oricarei instructiuni (In C++ pot fi declarate ori unde in cadrul functiei, vizibilitatea lor fiind din locul in care au fost declarate).
Variabilele locale pot fi alocate:
-pe stiva, cele automatice (au specificatorul auto sau implicit sunt auto), alocarea lor fiind facuta doar la executie si nu la compilare, avind valori initiale nedefinite.
-in memorie, variabilele declarate static, ele fiind implicit initializate cu zero. Aceste variabile nu pot fi precedate de specificatorul extern neputind fi utilizate in alte fisiere.
Un modul contine o parte din textul sursa al unui program. El contine functii inrudite si date pe care le prelucreaza in comun, nexistind o regula de asociere a lor.
In acest caz o variabila statica declarata in afara corpurilor functiiilor este locala modulului unde a fost declarata. Utilizarea ei presupune declararea ei in prealabil sau definirea ei daca o si initializam (implicit este zero).
Variabilele globale pot fi utilizate in ori ce modul daca sunt precedate de:
-definitia sau declararea lor
-declararea cu specificatorul extern.
Observatie:
Putem avea variabile locale si globale cu acelas nume. In cadrul functiilor care compun un modul, compilatorul mai intii cauta o variabila locala cu numele specificat si apoi daca nu gaseste cauta o variabila globala din afara functiei.
Exemple:
Fie urmatoarele module: a)fis1.c care contine: extern void other(); //functie definita in alt modul int l=3; void next(void); //prototip next

void main (void)
A l++; printf(“%d\n”,l); // l=4 next();
S

void next (void)
A l++; printf(“%d\n”,l); //l=5 other();
S

Modulul al doilea va fi denumit fis2.c si va contine: extern int l; //se refera la variabila l din modulul fis1.c facind vizibila si in acest modul variabila l

void other(void)
A l++; printf(“%d\n”,l);//l=6
S b)Modulul fis3.c contine: int zi,luna,an;//variabile globale in modulul fis3.c

void f(void)
A
//functie in care pot folosi variabilele zi,luna,an
S

void g(void)
A
//si in aceasta functie se poate lucra cu variabilele zi, luna, an
S
Modulul fis4.c contine: void main(void)
A extern int zi,luna,an;//referiri la variabilele din modulul fis3.c
//aici pot lucra cu variabilele zi,luna,an
S

void p(void)
A
//aici nu pot folosi variabilele zi,luna,an
S c)Pentru a exemplifica declararea la nivel intern avem acest modul, fisc: void other(void);//prototip int l=1;

void main(void)
A static int a; register int b=0; int c=0; printf(“%d, %d, %d, %d \n”,l,a,b,c); //1, 0, 0, 0 other(); getch();
S void other(void)
A int l=16;//l e local aici si mai prioritar decit l ca si variabila globala static int a=2; a+=2; printf(“%d, %d \n”,l,a); //16, 4
S
Functiile sunt analoage cu variabilele globale, ele putind fi apelate in ori ce modul al programului daca apelul lor este precedat in acel modul de:
-declararea ei
-prototipul ei (care inlocuieste declararea extern).
O functie este vizibila din momentul:
-declararii ei
-prin precizarea prototipului ei
-prin informarea compilatorului (nu la toate compilatoarele) inainte de declararea ei cu o constructie de forma: tip nume_functie (); cu rol de prototip.
Daca functia este precedata de cuvintul static, functia este locala modulului respectiv, astfel ca putem avea in alte module functii cu acelas nume.
In cadrul functiilor parametrii formali se aloca pe stiva ca si variabile automatice, putind fi considerate variabile locale utilizabile in corpul functiei.
Alocarea are loc doar la apelul functiei, caz in care parametrilor formali alocati li se atribuie valorile parametrilor efectivi (argumente) care le corespund.
De asemenea daca in cadrul functiei mai sunt declarate variabile automatice, acestea sunt apoi alocate pe stiva.
La revenirea din functie are loc dealocarea intii a variabilelor automatice definite in cadrul functiei si apoi a parametrilor, stiva ajungind la starea dinaintea apelului.
Parametrii unei functii se considera a fi o interfata intre functii. Cu ajutorul parametrilor efectivi se realizeaza transferul datelor de la functia care face apelul la functia apelata, care are destinat un parametru formal pentru fiecare parametru efectiv.
Functia apelata nu poate modifica parametrii efectivi (daca sunt transmisi prin valoare, adica nu cu pointeri sau referinte). Implicit limbajul C foloseste apelul prin valoare care realizeaza copierea fiecarui argument in parametrul formal corespunzator al functiei.
In acest caz se poate folosi de la aceasta functie doar valoarea returnata, ea fiind disponibila la revenirea dupa apelul functiei.
Variabilele globale pot fi si ele folosite la interfata intre functii (nu este prea indicat acest mecanism de interfatare si de aceea P.O.O. rezolva acest lucru mult mai elegant si sigur cu ajutorul claselor). In cazul lucrului cu variabile globale, ori ce functie are acces la variabilele globale. Aceste functii trebuie insa:
-sa cunoasca numele variabilelor globale
-sensul variabilelor globale (doar ca intrare, daor ca iesire sau mixt).
Acest lucru impune un control extrem de riguros in program a variabilelor globale, nestapinite ele fiind o sursa mare de erori.
Totusi variabilele globale se folosesc mai ales in cazurile in care mai multe functii folosesc rezultatele memorate in cadrul variabilelor globale.

4. INITIALIZAREA VARIABILELOR SI A TABLOURILOR

Variabilele se initializeaza prin constructii de forma: tip nume = expresie; //pentru variabile automatice sau globale static tip nume = expresie; //pentru variabile statice
La compilare, variabilele statice si globale se aloca in memorie si se initializeaza cu expresie, unde expresie trebuie sa aiba valori constante. Daca aceste variabile nu se initializeaza explicit atunci ele implicit se initializeaza cu zero.
La executie, variabilele automatice se aloca pe stiva si daca reprezinta parametrii formali ai unei functii, la apelul ei cu parametrii efectivi se face initializarea lor cu aceste valori efective. In acest caz expresiile folosite pentru initializare pot sa nu fie constante, operanzii variabili ai unei asemenea expresii avind valori predefinite in prealabil.
Variabilele automatice neinitializate au valori nedefinite.
Daca tipul expresiilor de initializare nu coincide cu tipul variabilei pe care o initializeaza atunci se face conversia de tip.
Tablourile, sunt initializate functie de tipul lor astfel:
-a)tablourile globale si statice se initializeaza la compilare prin constructii de forma:
-pentru tablouri unidimensionale: tip nume aeci = Aec0, …, eciS ;sau static tip nume aeci = Aec0, …, eciS ; unde: eci sunt expresii constante ec, este dimensiunea tabloului, care daca lipseste duce la initializarea tuturor elementelor din tablou. Elementele care nu se initializeaza explicit sunt automat initializate cu 0.
-pentru tablouri bi(multi)dimensionale initializarea se face pe acelas principiu astfel:
astatici tip nume aniami = A
Aec11, ec12, …, ec1mS,
Aec21, ec22, …, ec2mS,
….
Aecn1, ecn2, …, ecnmSS; unde: n, m, ecij, i=1, …, n, si j=1, …, m sunt expresii constante. Acpladele nu sunt obligatorii dar ele specifica modul de initializare pe linii. Variabila n ne da numarul maxim de linii iar m numarul maxim de coloane.
-b)tablourile automatice in cadrul mediulul Borland se initializeaza la executie, la apelul functiilor unde sunt declarate ca si parametrii formali.
Limita superioara a primului indice la tablourile uni-(multi)dimensionale poate lipsi in urmatoarele cazuri:
-in cadrul unor declaratii care contin initializari
-in cadrul declaratiilor de parametri formali
-in cadrul declaratiilor de tablouri externe.
Exemplu: int tab ai = A0, 1, 2, 3, 4S; //initializeaza un tablou de 5 elemente double t aia3i = A
A0, 1S,
A-1S,
A1, 2, 3SS; //defineste un tablou double de 3 x 3 elemente care are valorile: ta0ia0i=0, ta0ia1i=1, ta1ia0i=-1, ta2ia0i=1, ta2ia1i=2, ta2ia2i=3, celelalte elemente fiind 0 daca tabloul este global sau nedefinite daca tabloul este automatic.

6. POINTERI

Un pointer este o variabila care are ca si valori adrese, deci un pointer (indicator) reprezinta adrese ale unor zone de memorie asociate unor variabile.
Variabilele pointer din punct de vedere al continutului de memorie pe care il adreseaza pot fi de mai multe categorii:
-un pointer de date, care contine adresa unei variabile sau a unei constante din memorie
-un pointer de functii, care contine adresa codului executabil al unei functii
-un pointer de obiecte care contine in principiu adresa unui obiect in memorie deci adrese de date si functii. Acesti pointeri generalizeaza primele doua categorii.
Un pointer generic (void) ce poate contine adresa unui obiect oarecare.

6.1. DECLARAREA VARIABILELOR POINTER

Un pointer este asociat unui tip de variabile, sintaxa declaratiei fiind: tip * id_ptr; care inseamna ca id_ptr este numele unei variabile pointer, deci va contine o adresa a unei zone de memorie care contine un obiect de tipul, tip. Asteriscul din fata variabilei id_ptr, informeaza compilatorul ca trebuie sa creeze o variabila de tip pointer.
Exemplu: int *p; //precizeaza ca p este o variabila pointer care va putea contine adrese de memorie unde vor fi memorate date de tip intreg.
Pornind de la declaratia clasica: tip nume; la tip * nume;, putem considera ca: tip *, reprezinta un nou tip de data, si anume tipul pointer spre tip.
Observatie:
Declaratia void *id_ptr; // permite declararea unui pointer generic (void) care nu este asociat unui tip de date precis. In acest caz:
-dimensiunea zonei de memorie adresate
-interpretarea datelor din acea zona nu sunt definite.
Exemplu: int *nptr; //nptr este pointer la intreg int *tabptra10i;//tabptr este un tablou de 10 elemente fiecare element fiind pointer la intreg float *a, rez;//a este pointer la flotant, rez este flotant void *point_ne;//avem prin point_ne un pointer generic
Notiunea de pointer se poate exprima si prin:
-localizator
-reper
-indicator de adresa, etc. Uneori se mai utilizeaza si notiunea de referinta dar ea practic are alta semnificatie in C++.

6.2. OPERATORI SPECIFICI PENTRU POINTERI

Daca o variabila este de tipul, tip, atunci operatorul de adresare (referentiere), &, aplicat acestei variabile ne returneaza adresa acestei variabile, adica returneaza noul tip de date, pointer, adica tip *.
Deci operatorul de adresare, &, asociat variabilei sau mai general obiectului nume, obtine adresa acelei variabile sau obiect.
&nume = adresa variabilei nume
Exemplu: char c, *pc; pc=&c;
Operatorul de indirectare, (dereferentiere), *, permite accesul la continutul variabilei (obiectului) asupra caruia se aplica.
Constructia:
*id_ptr, unde id_ptr este o variabila pointer declarata in prealabil si careia i s-a asociat adresa unei variabile de acelas tip se citeste “continutul de la adresa data de pointerul id_ptr”. Rezultatul acestei evaluari depinde de tipul variabilei pointer id_ptr, adica de tipul spre care pointeaza ea.
Exemplu: void main (void)
A int l=1, j=5, *p; p=&l;// asigneaza lui p adresa lui l
*p=2;// continutul de la adresa variabilei l din 1 se face 2
(*(p=&j))++;//asigneaza aceluiasi p adresa lui j incrementind continutul de aici la valoarea 6 printf(“%d %d\n”,l,j);//l=2, j=6
S
Observatie:Limbajul C permite (sintactic) ca ori ce tip de pointer sa pointeze oriunde in memorie, tipul pointerului determinind modul in care va fi tratat obiectul pointat.
Exemplu: int q; float *fp; fp=&q;// sintactic se poate ca unui pointer flotant sa i se atribuie o adresa de intreg (care se reprezinta pe 2 octeti)
*fp=20.5;//atribuim la aceasta locatie un flotant (ce se reprezinta pe 4 octeti) deci voi mai scrie inca in 2 octeti si deci cresc posibilitatile de eroare
Deci un pointer de un anumit tip nu va fi folosit sa pointeze o data de alt tip.
Acest lucru va fi exemplificat si in urmatorul exemplu: int *p; float q, temp; temp=20.5; p=&temp;//p e pointer spre intregi dar l-am asociat la un flotant, sintactic doar warning q=*p;//continutul preluat va fi incorect printf(“%f\n”,q);//va tipari o prostie
Observatie:Compilatorul permite folosirea pointerilor NULL rezultatele fiind insa nedefinite
Exemplu: int *p;//p pointer spre intregi dar are valoarea inca NULL pentru ca nu este asociat la nici o variabila
*p=10;//erore, pentru ca p nu pointeaza un obiect cunoscut.

Observatie: Daca un pointer vrem sa-l utilizam cu mai multe tipuri de date atunci utilizam pointeri generici.
Exemplu: int x; float y; char c; void *p;
… p=&x;//lui p i se atibuie adresa de memorie unde pot fi intregi
*(int*)p=10;//atribuim o valoare variabilei x folosind operatorul cast
… p=&y; ;//lui p i se atibuie adresa de memorie unde pot fi flotanti
*(float*)p=10.0;
… p=&c; ;//lui p i se atibuie adresa de memorie unde pot fi caractere
*(char *)p=’a’;
La folosirea pointerilor generici nu se pot folosi atribuiri simple ci trebuie precizate conversiile explicit prin operatori de tip cast.
Utilizarea tipului void *, pointer generic, asigura o mare flexibilitate, dar poate duce deseori la erori. De aceea in cazul unor aplicatii concrete programatorul trebuie sa stapineasca bine folosirea acestor tipuri.

6.3. APELUL PRIN REFERINTA UTILIZIND PARAMETRII DE TIP POINTERI

In cadrul limbajului C transferul parametrilor este implicit efectuat prin valoare.
Daca vrem ca o functie sa modifice o variabila parametru formal, atunci trebuie sa transmitem functiei adresa variabilei, deci folosim operatorul de adresare & in cadrul apelului functiei cu parametrii efectivi, iar in interiorul functiei vom accesa variabila cu ajutorul operatorului de indirectare *. In acest caz ca si parametrii formali ai functiei pe care o realizam vom folosi variabile de tip pointer, variabile ce pot avea ca si valoare adresa zonelor de memorie precizate ca si parametrii efectivi la apelul functiei.
Deci cind ca si argumente se folosesc adrese, ca si parametrii formali se folosesc pointeri unde se vor copia aceste adrese.
Exemplu: Fie doua functii cu care vrem sa interschimbam continutul a doua variabile: void schimba_1(int a, int b)
A int temp; temp=a; a=b; b=temp;
S
Daca avem: int x=1, y=2;
…. schimba_1(x, y); // vom remarca ca aceasta schimbare nu va fi efectuata schimbarea efectuindu-se doar intern cu variabilele locale automatice de pe stiva a si b, rezultatul nereflectindu-se asupra parametrilor actuali x si y. De aceea se foloseste functia: void schimba_2(int *a, int *b)
A int temp; temp=*a;
*a=*b;
*b=temp;
S
Apelul in acest caz va fi: schimba_2(&x, &y);// care va efectua schimbarea continutului celor doua variabile x si y.
Observatie:In limbajul C++ se introduce notiunea de referinta la care vom reveni, si in acest caz shimbarea continutului celor doa variabile se poate face astfel: void schimba_3(int &a, int &b)
A int temp; temp=a; a=b; b=temp;
S iar apelul se face prin: schimba_3(x, y);
In acest caz:
-a este referinta la variabila x
-b este referinta la variabila y.
Constructia de forma: int &a=x;//spune ca variabila a este o referinta la variabila x si numai la x (un alias al lui x).
Accesul la o variabila prin intermediul referintei se face fara utilizarea operatorului de adresare asa cum s-a vazut din exemplul de mai sus.
Daca la apelul unei functii se foloseste ca si argument un tablou, functia va primi doar adresa acelui tablou, adica adresa primului element al acelui tablou.
La declararea functiei, parametrii formali care sunt declarati trebuie sa fie compatibili cu tipul tablourilor pe care le pointeaza.
Exista 3 metode care se utilizeaza pentru a declara un parametru formal ca si parametru care sa primeasca un pointer catre un tablou. Aceste sunt:
-a)parametrul formal trebuie sa fie declarat ca un tablou de acelasi tip si dimensiune cu tabloul folosit ca si argument la apelul functiei
-b)parametrul formal poate fi declarat ca un tablou fara dimensiune
-c)parametrul formal este declarat ca un pointer catre tablou, argumentul folosit ca si parametru efectiv fiind adresa tabloului asupra caruia se fac operatii in functie (cea mai folosita metoda).
Exemplu:
#include <stdio.h> void f1 (int numa5i); void f2 (int numai); void f3 (int *num); //cele 3 prototipuri

void main (void)
A int numerea5i=A1,2,3,4,5S; f1(numere); printf(“\n”); f2(numere); printf(“\n”); f3(numere); printf(“\n”);//cele 3 apeluri echivalente
S

void f1 (int numa5i)
A int l; for(l=0;l < 5; printf(“%d”, numali), l++);
S//f1

void f2 (int numai)
A int l; for(l=0;l < 5; printf(“%d”, numali), l++);
S//f2

void f3 (int *num)
A int l; for(l=0;l < 5; printf(“%d”, *(num+l)), l++);
S//f3

6.4. OPERATII ARITMETICE CU POINTERI

Cu ajutorul pointerilor se pot efectua operatii de:
-atribuire
-comparare
-adunare
-scadere
-incrementare, decrementare.
Deci pe linga operatorii * si & se mai folosesc alti 4 operatori: +, ++, -, --.
Nu pot fi adaugate sau scazute decit cantitati intregi. Operatiile se efectueaza relativ la tipul pointerului (int, float, char, etc.)
Conditia care se cere la efectuarea operatiilor aritmetice cu ajutorul pointerilor este ca pointerii sa fie de acelas tip.
Consideram ca adresele care reprezinta pointeri au valori numerice intregi fara semn, operatiile cu pointeri fiind supuse unor reguli si restrictii specifice.

6.4.1. Compararea a doi pointeri

Compararea a 2 pointeri se face cu operatorii relationali, in cazul in care pointerii sunt inruditi, adica pointeaza pe obiecte de acelas tip.
Exemplu: int *p1, *p2, l, j; p1=&l; p2=&j; if (p1 < p2) printf (“p1= %n p2 = %n\n”,p1,p2);
Operatorii de egalitate == si != pot fi folositi pentru a compara pointeri cu o constanta speciala NULL definita de obicei in stdio.h prin:
#define NULL 0
Daca p este un pointer generic, void *p; atunci se pot face comparatiile: p == NULL si p != NULL dar este recomandabil a folosi: p == 0 si p != 0 pentru ca in C++ nu se recomanda folosirea constantei simbolice NULL sau:
!p si p cu care sunt echivalente.
Deci urmatoarele constructii sunt echivalente: if (p == 0) = if (!p) if (p != 0) = if (p).

6.4.2. Operatii de adunare/scadere si incrementare/decrementare

Operatia de adunare sau scadere intre un pointer de obiecte si un intreg este conforma urmatoarelor reguli specifice:
Fie id_ptr un pointer declarat prin: tip *id_ptr;
Atunci operatiile: id_ptr + n respectiv id_ptr - n, corespund adaugarii/scaderii la adresa obtinuta in cadrul variabilei id_ptr a valorii: n * sizeof(tip).
Analog se efectueaza si operatiile de incrementare/decrementare doar ca n = 1.
Exemplu: float *fp1, *fp2, f; //sizeof (float) =4 double *dp, d; //sizeof (double) =8, fp1 si dp ar trebui sa se refere la tablouri fp1=&f; dp=&d;
… fp2=fp1+5; //fp2 va contine adr. lui f + 5*4 dp=dp-2; //dp va contine adresa lui dp - 2*8 fp1++; //fp1 va contine adresa lui fp1 + 1*4 dp--; //dp va contine adresa lui dp - 1*8
Aceste operatii aditive cu pointeri si intregi dau rezultate utilizabile doar atunci cind pointerul adreseaza un tablou si prin operatiile respective se produce o deplasare in limitele tabloului respectiv.
6.4.3. Scaderea a doi pointeri

Scaderea a doi pointeri de obiecte de acelas tip este permisa, rezultatul fiind o valoare care reprezinta diferenta de adrese divizata la dimensiunea tipului de baza.
Adunarea acestor pointeri nu este permisa.
Exemplu: int l,ja10i; float *p1=&ja4i, *p2=&ja2i; l=p2-p1; // l= (adresa p2- adresa p1)/4=2, cite elemente sunt intre cele doua adrese
Datorita rolului tipului la adunare si scadere, operanzii nu pot fi pointeri void sau pointeri spre functii.
Observatie:
1. Operatorii de tip cast pot modifica semnificatia pointerilor: int *pi; float *pf;

*((char*)pf) ne va da primul octet din reprezentarea unei variabile de tip float
*((char*)pf+1) ne va da al doilea octet din reprezentarea unei variabile de tip float
(char*) este folosita pentru acces pe octet la zone de marimi diferite.
2. Operatia de incrementare/decrementare se poate aplica:
-asupra pointerului insusi
-asupra obiectului pe care il pointeaza.
Exemplu: int *p, q; p=&q; q=1; printf(“Adresa lui q este %n”,p);
*p++;//asociativitate de la D la S, intai aplic ++ si apoi * printf(“\n Operatie asupra pointerului q= %d Adresa = %n”,q,p);//q=1, Adresa va fi incrementata adica mai mare cu 2
Daca ultimele doua instructiuni le inlocuiesc cu:
(*p)++;// obtin intai continutul de la adresa p pe care apoi il incrementez cu 1 printf(“\n Operatie asupra obiectului q= %d Adresa = %n”,q,p);//q=2, Adr lui p nu se va modifica

6. TABLOURI SI POINTERI

In cadrul limbajului C exista o legatura intre tablouri si variabile pointeri. Un nume de tablou fara index este un pointer constant de tipul elementelor tabloului si are ca si valoare adresa primului element din tablou. Aceasta valoare poate fi asignata unui alt pointer fiind posibil accesul la elementele tabloului folosind pointeri aritmetici.
Deosebirea intre variabile de tip pointeri si nume de tablouri este:
-unei variabile pointer i se atribuie valori in timpul executiei, valorile putind diferi la momente diferite de timp diferite
-numele unui tablou are tot timpul ca si valoare adresa primului sau element si de aceea se zice ca el este un pointer de tip constant.
Exemplu: a) float ftaba20i, *fptr, ftemp; int l; fptr=ftab;//fptr va contine adresa primului element din ftab ftemp=ftaba0i;//este echiv. cu ftemp=*fptr
De asemenea mai avem:
&ftaba0i ? ftab ftaba0i ? *ftab
&ftabali ? ftab+l ftabali ? *(ftab+l) ftaba4i ? *(ftab+4) iar expresia:
(&ftabali-ftab) == l este totdeauna True. b)
#include <stdio.h> int aa10i=A1,2,3,4,5,6,7,8,9,10S

void main(void)
A int *p; p=a;//p=&aa0i printf(“%d %d %d \n”,*p, *(p+1), *(p+2));//afis. primele 3 elemente ale tabloului printf(“%d %d %d \n”,aa0i, aa1i, aa2i); ));//afis. primele 3 elemente ale tabloului
S
Observatii: a)Un pointer se poate indexa ca si cind ar fi un tablou, daca refera un tablou:
Exemplu:
#include <stdio.h> char strai =”Pointeri in C”; void main(void)
A char *p; int l; p=str; // p= Adr. tablou de caractere, sirul str for(l=0;pali;l++) printf(“%c”,pali);//pointerul p a fost folosit indexat pentr ca pointeaza pe str care este un tablou
S b)Daca un pointer nu pointeaza pe un tablou el nu poate fi indexat:
Exemplu: char *p, ch; int l; p=&ch; for(l=0; l < 10;l++) pali=’A’+l;//Sintactic nu este eroare dar rezultatul este neprevazut c) Daca p pointeaza pe un tablou ca la a) compilatorul C genereaza cod executabil mai scurt pentru:
*(p+3) decit pentru pa3i sau mai general *(p+l) respectiv pali; d) Numele unui tablou este un pointer de tip constant deci nu se poate modifica dar poate fi folosit ca si pointer aritmetic in locul indexarii: char strai;
*(str+1)=’C’; printf(“%c “, *(str+1));// dar nu e permis str++

6.1. Tablouri de pointeri

Tablourile de pointeri se definesc functie de tipul de date la care se refera astfel:
Exemplu: int *paa20i; //tablou de 20 de pointeri la intregi int adrpoint; paa7i=&adrpoint; //al 8-lea element din tablou va contine adresa lui adrpoint
*paa7i=127; //continutul de la aceasta adresa va fi initializat cu 127
Tablourile de pointeri se folosesc mai ales la crearea de tabele de siruri care pot fi selectate functe de anumite valori.
Exemplu: char *pai = A
“Out of range”,
“Disk full”
“Paper out”

S si fie functia: void Err(int nr_err) //nr_err <=dim_max tablou de pointeri la apelul efectiv
A printf(panr_erri);//tipareste mesajul specific erori nr_err, luat din tabloul de pointeri la sirurile de caratere
S

6.2. Pointeri catre siruri constante

Prin sir constant intelegem un sir cuprins intre ghilimele.
Exemplu: a) b)
#include <stdio.h> #include <stdio.h> char *p=”unu doi trei”; void main(void) void main(void)
A ? A char *p;//pointer de tip char printf(p);//p initializat pe sir de char p=”unu doi trei” S printf(p);
S
Cele doua constructii sunt echivalente, si am putea utiliza si o a treia varianta: c) char strai =”unu doi trei”; si in functia main am putea folosi: p=str; printf(p);
Pointerii catre siruri de caractere se folosesc atunci cind se doreste tiparirea unor constante sir lungi ele introducindu-se in general ca si in varianta b), astfel incit daca mesajul trebuie modificat el se va shimba o singura data la initializare. Acest mesaj se poate tiparii astfel in mai multe locuri din cadrul programului intr-un mod foarte simplu.

6.6. INDIRECTAREA MULTIPLA

Cind un pointer pointeaza alt pointer avem un proces numit indirectare multipla. El arata asfel:

Cind un pointer pointeaza alt pointer, primul pointer contine adresa celui de-al doilea pointer, pointer care pointeaza locatia care va contine obiectul.
Declararea se face utilizind un asterisc suplimentar in fata numelui pointerului.
Exemplu: char **mp, *p, ch; p=&ch; mp=&p;
**mp=’A’; //lui ch i s-a asignat valoarea A prin 2 pointeri

Indirectarea multipla este utilizata in aplicatiile avansate de C putind fi continuata la un grad de adincime mai mare, lucru care in practica nu este prea indicat fiind greu de urmarit.

6.7. ALTE CONSIDERATII REFERITOARE LA POINTERI

Vom prezenta pe puncte unele concluzii semnificative si unele precizari practice privind pointerii.
1. Daca un pointer p indica o variabila x, atunci expresia (*p) poate aparea in ori ce context in care este permisa aparitia lui x.
Exemplu:
#include <stdio.h>

int f (int *p)
A return (*p)++;
S//f

void main (void)
A int x=1,y=2; y=f(&x);printf(“x= %d y= %d\n”,x,y);//x=2, y=1 y=f(&x);printf(“x= %d y= %d\n”,x,y);//x=3, y=2
S
2. Daca avem definit un tablou de dimensiunea N atunci indicii variaza de la 0 la N-1, insa este permisa pozitionarea pointerului pe elementul imediat urmator ultimului element al tabloului, N. Acest lucru este permis datorita mecanismului de formare a adresei fizice in memorie.
3. In cadrul tablourilor unidimensionale (vectori), compilatorul C converteste expresia e1ae2i in *((e1)+(e2)) unde e1 si e2 sunt expresii valide si deci operatia de indexare este comutativa adica e1ae2i ? e2ae1i.
4. Pointerii dublii ofera posibilitatea unor indirectari duble. Tot ce este valabil la pointerii simpli este valabil si la cei dubli. Tablourile de pointeri pot fi accesate cu pointeri dubli.
Exemplu: a) char *aa5i;//tablou de 5 pointeri catre caractere char **p;//pointer (dublu) catre caracter p=a;//p va contine adresa primului element al tabloului a, unde voi avea o adresa spre un caracter, astfel incit p+1 va indica pe aa1i, … b)Fie 10 siruri de caractere ale caror adrese se afla intr-un tablou de pointeri, char *sira10i si construim o functie care afiseaza aceste siruri de caractere la un apel. Acest lucru il putem realiza in doua moduri:

-primul mod clasic: char *sira10i; void afis1_sir(char *tpai, int n)
A int l; for(l=0;l < n; l++) if (tpali != NULL) //if (tpali) printf(“%s\n”, tpali);
Safis1_sir iar apelul se efectueaza cu: afis1_sir(sir, 10);
-al doilea mod cu pointeri dubli doar functia de afisare se modifica astfel: void afis2_sir(char **tp, int n)
A char *p;
while (n--)
A p=*tp;
while (*p) putchar( *p++); putchar(‘\n’); tp++;
S//while
S//afis2_sir
Pointerii catre tablouri de tip T au o aritmetica in care unitatea este: dim_tab * sizeof(T), in timp ce pointerii spre tipul T au o aritmetica in care unitatea este sizeof (T).
Exemplu: int (*p)a10i;//p este un pointer spre un tablou de 10 intregi cu unitatea 10*sizeof(int) ce rezulta usor tinind cont de observatia 1 adica (*p) putem inlocui cu x, => int xa10i. int *pa10i;//p este un tablou de 10 pointeri spre intregi
6. In cazul in care avem tablouri cu mai multe dimensiuni care se declara prin:
T nume_tab ad1iad2i…adni; //unde T este tipul elementelor si d1, d2, …, dn sunt constante intregi si pozitive, atunci elementele tabloului nume_tab vor ocupa o zona contigua de memorie de dimensiunea: d1*d2*…*dn * sizeof(T), deci tabloul va fi alocat la adrese succesive de memorie.
Fie un tablou a de tip T si dimensiuni: d1, d2, …, dn:
T aad1iad2i…adni; si presupunem indicii: i1, i2, …, in astfel incit 0 <= ik <= dk-1, unde k ia valori intre 1 si n. functia f care realizeaza corespondenta intre indicii (i1, i2, …, in) si adresa lui aai1iai2i…aini se numeste functie de alocare a memoriei pentru tabloul a, functia de alocare fiind identica pentru toate tablourile cu aceiasi dimensiune si acelas tip T.
Notind cu d si i multiindicii: d = (d1, d2, …, dn) i = (i1, i2, …, in), si cu
T tipul de baza al tabloului atunci functia de alocare a memoriei pentru un tablou oarecare a se noteaza cu: fd, T (i,a) , adica adresa elementului i al tabloului a de tipul T si multiindici d este data in limbajul C prin: fd, T (i,a) = baza (a) + sizeof(T) *a i1d2d3…dn + i2d3d4…dn + i3d4d5…dn + in-1dn + in
i, deci tabloul este alocat la adrese succesive de memorie, ultimul indice variind cel mai repede. Baza(a) este &aa0ia0i…a0i.
Observatii:
-Functia de alocare nu depinde de prima dimensiune, d1, de aceea uneori putem sa o ignoram
-La tablourile unidimensionale functia de alocare devine: aaii = baza(a) + sizeof (T) * i
-Un tablou cu mai multe dimensiuni este tratat de catre compilator ca un tablou cu o dimensiune (si anume prima) care are ca si elemente un tablou cu restul de dimensiuni, deci nu exista limitari sintactice ale numarului de dimensiuni in C
Exemplu: int aa2ia3i; //este privit ca si un tablou unidimensional cu 2 elemente aua0i si aua1i, fiecare din elemente fiind la rindul lui un tablou unidimensional cu 3 elemente: aua0i => aa0ia0i, aa0ia1i, aa0ia2i aua1i => aa1ia0i, aa1ia1i, aa1ia2i sau daca avem:
T aad1iad2i atunci avem echivalenta cu pointeri: aaiiaji = (*(a+i))aji = *(*(a+i)+j) unde la (a+i) a este vazut ca un pointer catre un tablou de d2 obiecte de tip T adica se vor aduna d2 * sizeof (T).
7. In cazul transmiterii unui tablou cu mai multe dimensiuni la o functie, este de preferat a folosi ca si parametru formal un pointer catre tablou, adica adresa de inceput a acelui tablou. De asemenea se prefera folosirea pointerului de tip void, in cadrul functiei folosindu-se un pointer specific catre tipul T si cu conversia explicita catre acel tip.
Exemplu: Afisarea unei matrici:
#include <stdio.h>
#include <stddef.h>
#define FORMAT “%6d”

void afis_mat(void *a, int m, int n)
A int *v = (int *)a; int mn = m*n; ptrdiff_t d;//definire din stddef d ca si pointer diferenta
while ((d=v-(int*)a) < mn) printf ((d%n == n-1) ? FORMAT “\n” : FORMAT, *v++);
S
8. Cu modificatorul const putem declara:
-pointeri constanti, care nu pot fi modificati ca si la tablouri
-pointeri catre constante, care nu pot fi modificate
Exemplu: const int a = 10, *pc =&a;//a intreg constant, pc pointer catre un intreg constant deci pot modifica pointerul pc dar nu pot modifica continutul, *pc. int b, *const cp =&b; //cp este un pointer constant nu-l pot modifica dar *cp pot modifica
Putem avea si cazul pointeri constanti catre constante unde nu putem modifica nimic.
9. In limbajul C deseori se folosesc functii care returneaza pointeri. Una din aceste functii care copiaza un sir de caractere de la o sursa la o destinatie s-ar putea scrie astfel: char * strcpy(char *dest, const char *sursa)
A char *p = dest;
while (*p++ = *sursa++); return dest;
S//strcpy
Aceste functii care returneaza pointeri se folosesc mai ales la:
-alocarea dinamica a memoriei
-liste, arbori.
O functie care aloca in mod dinamic un spatiu pentru un obiect se numeste constructor.
O functie care primeste un pointer la o zona de memorie alocata dinamic si elibereaza aceasta zona se numeste destructor. (Vom reveni la alocarea dinamica a memoriei)

6.8. POINTERI SPRE FUNCTII

O functie ca si o structura sau un tablou are asociata o adresa fixa de memorie. Un pointer catre o functie va contine aceasta adresa.
Declararea unui pointer spre o functie trebuie sa precizeze:
-tipul functiei
-numarul si tipul parametrilor.
Forma generala a declaratiei este: tip (*pf) (lista_param_formali);
Este de remarcat precizarea parametrilor iar ca si regula practica declaratia se face ca si cum am scrie un prototip de functie, numele functiei fiind substituit de (*nume_pointer).
Exemplu: char * (*p) (char *, const char *); //este prototipul prin care p se defineste ca fiind un pointer la o functie care:
-are 2 parametri, unul char * altul const char *
-returneaza un pointer la caracter, char *.
De remarcat ca acest prototip poate fi asociat functiei strcpy, prezentata in precedentul subcapitol.
Numele unei functii fiind sinonim cu adresa de inceput a ei putem avea: p=strcpy; //sau p=&strcpy;
Apelul unei functii prin intermediul unui pointer se face cu constructia:
(*pf) (lista_param_actuali); iar daca functia returneaza o valoare care se poate atribui unei variabile var (functie cu tip) atunci apelul poate fi: var=(*pf) (lista_param_actuali);
Observatie:
Standardul ANSI permite a scrie apelul fara a folosi operatorul de indirectare * adica: pf (lista_param_actuali);sau var = pf (lista_param_actuali);
Pointerii la functii se folosesc la:
-construirea unor tablouri care sa contina adrese de functii care pot fi apelate indirect
-folosirea functiilor polimorfice care sunt proiectate pentru a se putea aplica ori caror tipuri de date

6.9. TRANSFERAREA DE ARGUMENTE CATRE FUNCTIA MAIN

La lansarea in executie multe aplicatii permit specificarea unor argumente in linia de comanda.
Exemplu:
WP nume_fisier sau
WIN nume_program sub Windows 3.x.
Programele scrise in limbajul C permit introducerea de argumente in linia de comanda, argumente care sunt transferate prin doi parametri ai functiei main, argc si argv:
-argc, contine numarul argumentelor si este definit ca si un intreg, int argc
-argv, este un tablou de pointeri catre siruri declarat prin, char * argvai sau char **argv, unde: argva0i pointeaza pe numele programului care se lanseaza in executie argva1i pointeaza pe primul argument, s.a.m.d.
Intre argumente se accepta spatiu, tabulator ca si separator, iar daca un argument va contine spatiu, acel argument va fi incadrat intre ghilimele.
Numele argc si argv nu sunt impuse, putem avea si alte doua nume.
Pentru ca aceste argumente se primesc sub forma de siruri de caractere, aceste siruri trebuie convertite spre un format intern de reprezentare in memorie si anume:
-pentru intregi, int atoi(char *str), returneaza intregul echivalent sirului
-pentru double, double atof(char *str), returneaza val. reala echivalenta sirului
-pentru long, long atol(char *str), returneaza val. long echivalenta sirului.
Aceste functii folosesc headerul stdlib.h.
Observatie:
In standardul ANSI se mai introduce un al 3-lea parametru optional ca si argument la functia main, tot ca si un tablou de pointeri catre siruri de caractere, care contine o descriere a mediului in care este rulat programul si depinde de implementarea. Acest parametru nu se prea foloseste, dar ori cum cei 3 parametrii ca sunt sau nu folositi sunt alocati pe stiva.
Exemplu: Prezentam un program poundk.exe, care transforma argumentul flotant care i se da ca un sir de caractere la apel si reprezentind numarul de kilograme in pounds.
#include <stdio.h>
#include <stdlib.h>

void main (int argc, char *argvai)
A double pounds; if (argc != 2) Aprintf(“Nu ati introdus arg. Kg. Reluati lansind din nou aplicatia”); exit(1);
S else A pounds = atof(argv a1i)/0.453; printf(“%lf Pounds”,pounds);
S
S//main

7. ALTE CONSIDERENTE PRIVIND STRUCTURILE DE DATE

Am prezentat citeva notiuni legate de structuri de date de tip struct pentru a introduce operatorii . (punct) si -> (sageata).
Am precizat ca o structura reprezinta o multime ordonata de elemente grupate in vederea utilizarii lor in comun.
Exista mai multe formate de descriere a structurilor, cel mai folosit fiind:

struct anume_structi
A lista_declaratii;
Sanume1,nume2,…,numeni unde:
-nume_struct, este un nou tip de date definit de catre utilizator, de tip struct cu constructia struct.
-lista_declaratii, este o lista prin care se declara componentele unei structuri, putind contine elemente de forma: tipi elementi, unde tipi este un tip admis de limbajul C inclusiv o noua structura,
-nume1,…,numen sunt o lista de variabile de tip nume_struct, putind lipsi la definirea structurii caz in care este obligatoriu sa fie precizat nume_struct.
Daca avem nume_struct si nu avem numei atunci putem defini ulterior alte structuri de tip nume_struct cu declaratia: struct nume_struct nume_nou; unde
-nume_struct reprezinta un nou tip de data un tip utilizator
-nume_nou este o variabila de tip nume_struct.
Elementele unei structuri se numesc cimpuri sau membri.
Exemplu: O structura de tip catalog de carti se poate declara prin: struct catalog
A char numea20i;//nume autor char titlua40i;//titlu carte char edituraa30i;//editura long data;//data aparitiei unsigned char editia;//editia
Scarte;
Deci catalog este tipul nou de date struct cu formatul precizat mai sus.
Variabila carte este o variabila de tip catalog si deci are o structura de forma: nume 20 octeti titlu 40 octeti editura 30 octeti data 4 octeti editia 2 octeti.
Accesul la elementele unei structuri, cimpuri, se face precizind numele variabilei de tip structura, carte, si cimpul care ne intereseaza separate prin operatorul . (punct).
Acest acces se numeste acces prin nume calificat.
Exemplu: carte.editia=2;//asigneaza valoarea 2 cimpului editia din structura cu numele carte care este de tip catalog
Observatii:
-1)In cazul in care se foloseste functia scanf pentru a introduce de la intrare un cimp intr-o structura, operatorul de adresare, & se pune in fata numelui structurii si nu in fata numelui cimpului: scanf(“%u”, &carte.editia);
-2)Un caracter dintr-un cimp ce este un tablou, de exemplu din titlu, se acceseaza prin: carte.titlua4i;//a cincea litera din titlu
-3)De obicei nume_struct, ca si tip nou de data este totdeauna folosit, dar cite o data cind se cunoaste exact cite variabile de acest nou tip se vor folosi el poate fi omis.
Exemplu: struct A int a; float b; char c;
Svar1,var2;
-4)Un tablou de structuri se declara considerind struct nume_struct ca fiind tipul tabloului.
Exemplu: struct catalog cartia100i;
… cartia16i.editia=2;//accesam cimpul editia din cea de-a 17-a structura
-5)Continutul unei variabile de structura poate fi asignat unei alte variabile de structura de acelasi tip.
Exemplu: struct valori
A int a; float b; char c;
Svar1,var2;
… var1.a=10; var1.b=12.4; var1.c=’A’; var2=var1;//cele doua structuri vor contine aceleasi valori
-6)Dimensiunea unei structuri se determina cu operatorul sizeof care asigura si portabilitatea programelor in cadrul diferitelor medii de programare. Lucrul acesta este necesar pentru ca unele compilatoare solicita alinierea unor tipuri de date la adrese de cuvint. Operatorul sizeof cind este folosit cu o structura trebuie precedat de cuvintul cheie struct.
Exemplu:
#include <stdio.h>

struct nume_struct
A int a; float b; char c; int *p;
S s;

void main(void)
A printf(“Structura nume_struct are lungimea =%d octeti\n”,sizeof(struct nume_struct));
S
-7)Numele elementelor unei structuri este separat de alte variabile care poarta acelas nume, neaparind conflict deci cu alte variabile cu acelas nume.
Exemplu:
#include <stdio.h>

void main(void)
A struct tip_s
A int l; int j;
Ss; int l; l=10; s.l=100; s.j=1000; printf(“l= %d s.l= %d s.j= %d”,l,s.l,s.j);
S
-8)Structurile pot fi transferate ca si parametri functiilor, iar o functie poate sa returneze o structura. Cind transferam o structura unei functii prin apelarea prin valori, atunci se transfera intreaga structura. Acest mod de transfer nu este valabil la toate compilatoarele, de aceea in general structurile nu se transfera prin valori ci prin pointeri spre structura.
Exemplu: a)Transfer structura catre o functie:
#include <stdio.h> struct tip_s
A int a; float b; char c;
Ss; void functie (struct tip_s);//prototip

void main(void)
A s.a=10; s.b=12.4; s.c=’A’; functie(s);
S//main void functie(struct tip_s temp)
A printf(“a=%d, b=%f, c=%c \n”,temp.a,temp.b,temp.c);
S//functie b)Functie care returneaza o structura:
#include <stdio.h> struct tip_s
A int a; float b; char c;
Ss; struct tip_s f(void);//prototip

void main (void)
A s=f(); printf(“a=%d, b=%f, c=%c \n”,s.a,s.b,s.c);
S//main

struct tip_s f(void)
A struct tip_s temp; temp.a=37; temp.b=17.21 temp.c=’O’; return temp;

S//f

7.1. DECLARAREA DE POINTERI LA STRUCTURI

Un pointer catre o structura se declara la fel ca si ori ce pointer catre ori ce alt tip de variabila.
Exemplu: struct tip_s
A int l; char stra20i;
Ss, *p;//s variabila de tip structura tip_s, p pointer catre structura tip_s
… p=&s;//pointerul p va contine adresa structurii s, de fapt va pointa pe adresa primei componente a structurii s adica variabila l
In acest caz accesul la elementele structurii s se va face apel prin:

s.l=17; sau
(*p).l=17; sau p -> l=17;
Observatii:
Cind este folosit apelul prin valori, limbajul C transfera functiilor structurile care sunt apelate in intregime. Daca dimensiunea este mare, transferul duce la reducerea vitezei de lucru a programului si pot sa apara probleme si cu dimensiunea stivei.
In aceste cazuri este recomandabil sa transferam functiei un pointer catre structuri si nu structura insasi.
De obicei, cind accesam un element dintr-o structura printr-o variabila de structura folosim operatorul . (punct). Acest acces se numeste acces prin nume calificat. Cind accesam un element dintr-o structura printr-un pointer folosim operatorul sageata, ->.
Exemplu: struct data_calend
A int zi; char lunaa11i; int an;
Sdc;
Structura dc poate fi prelucrata de functia f daca ea se apeleaza prin parametrul efectiv &dc, adica adresa de inceput a zonei de memorie unde este alocata structura dc prin: f(&dc);
Functia f va avea in acest caz antetul: void f(struct data_calend *p), deci p va avea ca si valoare o adresa de inceput a zonei de memorie in care se pastreaza date de tip data_calend cum este si &dc.
Accesul la componentele structurii in corpul functiei f nu se face cu nume calificate, pentru ca nu este cunoscut numele structurii dc ci doar pointerul p spre aceasta structura. Deci vom inlocui numele dc cu *p si vom avea:
(*p).zi sau p ->zi
(*p).luna sau p ->luna
(*p).an sau p ->an
Observatii:
-1)Structurile sunt date ca ori ce alte date variabile simple sau tablouri. Ele pot fi alocate in acelas mod, adica:
-global, daca sunt declarate in afara corpului ori carei functii
-local (automatic) daca sunt declarate in corpul unei functii
-static, daca declaratia incepe cu cuvintul static.
-2)Datele elementare care sunt componente ale unei structuri se pot initializa. Astfel dupa variabila nume de structura intre acolade si separate prin virgule se initializeaza componentele elementare ale structurii. Considerind ddc o structura de tip data_calend definita in prealabil, ea poate fi initializata astfel: struct data_calend ddc = A6,”septembrie”,1997S;

7.2. STRUCTURI INCLUSE UNA IN ALTA

Printre elementele unei structuri se pot gasi si alte structuri, numite structuri incluse (inclusiv pointeri catre structura insasi caz ce se intilneste mai ales in cadrul listelor si a arborilor definiti recursiv).
Structurile sunt referite din exterior spre interior deci se incepe cu structura cea mai exterioara si se incheie cu structura cea mai interioara.
Exemplu: struct data_calend
A int zi; char lunaa11i; int an;
S

struct date_pers
A char numea15i; char prenumea20i; long cod; struct data_calend data_nast; struct data_calend data_angaj;
Sangajat;
Pentru a defini data nasterii vom folosi o dubla calificare astfel: angajat.data_nast.zi=9; angajat.data_nast.luna=”noiembrie”; angajat.data_nast.an=1977;

7.3. REUNIUNI

In limbajul C exista posibilitatea de a pastra in aceiasi zona de memorie date de diferite tipuri.
Exemplu: long x;//variabilei x i se aloca o zona de memorie de 32 biti unde se pastreaza intregi in conventia C2.
Daca vrem ca aceasta zona sa o reutilizam si sa pastram aici date de alt tip si anume char, int sau float, pentru ca se integreaza in 32 de biti, vom face o grupare impreuna a datelor care vrem sa fie alocate in aceiasi zona de memorie. Pentru a realiza o astfel de grupare se foloseste o constructie similara cu struct numita union.
Union este un nou tip definit de utilizator si-l vom numi reuniune.
Exemplu: union a
A int x; long y; double r; char c;
Su;
Constructie prin care s-a definit:
-un nou tip de data a de tip reuniune
-o variabila de tip reuniune a, cu numele u.
Pentru aceasta variabila u, se aloca o zona de memorie suficient de mare pentru a putea pastra date care necesita numarul maxim de octeti, in cazul nostru 8 plus eventuale alinieri care pot duce la marirea zonei.
Toate aceste date sunt alocate in aceiasi zona de memorie (de 8 octeti) la un moment dat al executiei numai una din aceste date fiind definita.
Observatie: Daca in loc de union am scrie struct, atunci prin a vom defini o structura, u fiind o variabila de tip structura si la care toate componentele structurii sunt definite simultan care trebuie alocate in zone diferite de memorie in acest caz minim 15 octeti.
Accesul la elementele x, y, r, c ale reuniunii se face ca si la structuri folosind operatorul . (punct) sau -> (sageata).
Exemplu:
Consideram reuniunea a declarata ca mai inainte. Vom declara in continuare prin: union a w, *p; //w o variabila de tip a, p pointer la o reuniune de tip a p=&w; //atribuie lui p adresa primului element din reuniune. Accesul la componentele reuniuni se va face la momente diferite de timp cu:
w.x sau p ->x
w.y sau p ->y
w.r sau p ->r
w.c sau p ->c
Observatii:
-1)In cazul reuniunilor programatorul trebuie sa cunoasca in fiecare moment al executiei care componenta a reuniunii este prezenta in zona alocata ei.
Astfel daca avem atribuirea:
w.x=17; si imediat dupa aceea: if (w.y > 0) nu se va semnala eroare nici la compilare nici la executie, dar programatorul ar putea evita eroarea lucrind in mod corect.
-2)Reuniunile nu pot fi initializate ca si structurile
-3)Dimensiunea reuniunilor se poate determina ca si in cazul structurilor cu ajutorul operatorului sizeof.

7.4. CIMPURI DE BITI (bit fields)

Un cimp de biti este un element al unei structuri care cuprinde unul sau mai multi biti adiacenti.
Cu ajutorul cimpurilor de biti se pot accesa prin nume, unul sau mai multi biti dintr-un octet sau cuvint.
Un cimp de biti trebuie sa se poata aloca intr-un cuvint calculator. De asemenea mai multe cimpuri de biti daca incap pot fi pastrate in acelas cuvint calculator.
Cimpurile de biti se pot grupa formind o structura. O structura care contine mai multe cimpuri de biti se declara astfel: struct nume_struct
A cimp1; cimp2;
… cimpn;
Snume1, nume2, …, numem;
Un cimpi se declara astfel: tip cimpi: lungime_in_biti; sau prin: tip: lungime_in_biti; // cind definim o zona neutilizata. tip este un cuvint cheie, de obicei unsigned, care inseamna ca sirul de biti este interpretat ca un intreg fara semn. Pentru tip se mai pot folosi si:
-int
-unsigned char
-char.
Observatii:
-Cimpurile se aloca de la bitii de ordin inferior ai cuvintului spre cei de ordin superior.
-Cimpurile cu semn se utilizeaza pentru a pastra intregi de valori mici utilizind conventia C2. In acest caz bitul cel mai semnificativ al cimpului este bitul de semn.
-Daca un cimp nu se poate aloca in cuvintul curent, atunci el se va aloca in cuvintul urmator.
-Un cimp fara nume nu se poate referi. El defineste o zona neutilizata dintr-un cuvint.
-Lungimea in biti alocata pentru un cimp de biti poate fi egala cu zero. In acest caz data urmatoare din structura cu cimpuri de biti va fi alocata in cuvintul urmator.
-Cimpurile de biti se pot referi folosind aceleasi conventii ca si in cazul structurilor obisnuite.
Exemplu: struct cimp_biti
A unsigned a:2; int b:2; unsigned :3; unsigned c:2; unsigned :0//trec la urmatorul cuvint int d:5; char e:7;
Sx,y;
In acest caz pentru variabila x (si y) se aloca doua cuvinte astfel:

15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
|………………………|…c..|…………|…b..|…a..|

|……………|……….e……………|……d…….…|

Atribuiri de valori se pot face astfel: x.a=1 => cimpul a va avea valoarea 1, adica se va reprezenta prin 01 x.b=-1 => cimpul b va avea valoarea -1, adica se va reprezenta prin 11
Observatii:
-1)Unele compilatoare permit definirea de tablori de cimpuri de biti, altele nu. Daca se pot defini, am putea utiliza o declaratie de forma: struct cimp_biti tabl_cimpa10i;
Referirea se va face prin: tabl_cimpaii.a=1;
-2)Octetul fiind cea mai mica unitate de memorie adresabila la calculatoarele compatibile IBM PC, adresa de memorie a unui cuvint de biti nu se poate obtine cu ajutorul operatorului de adresare, & care nu se poate aplica la un cimp de biti.
-3)Datele pe biti duc la programe care de obicei nu sunt portabile sau cu portabilitate redusa. Ele necesita instructiuni suplimentare cum ar fi deplasari, setari, si/sau mascari de biti.
-4)Cimpurile de biti sunt folosite cind lucram cu date booleene permitind folosirea eficienta a memoriei, 8 date booleene pe octet.
-5)Prelucrarea datelor pe biti se poate face si cu ajutorul operatorilor logici pe biti care duc insa in general la un efort mai mare in programare.

7. ASIGNARI DE NUME PENTRU TIPURI DE DATE

Pina acum am constatat ca avem:
-tipuri predefinite de date, identificate prin cuvinte cheie: char, int, float, etc.
-tipuri utilizator definite cu constructii de forma: struct nume_struct
A

S sau union nume_reuniune
A

S.
In limbajul C se poate atribui un nume unui tip predefinit sau utilizator cu o constructie de forma: typedef tip nume_tip; unde:
-tip, este tipul predefinit sau utilizator
-nume_tip, este noul nume care se atribuie tipului definit de tip.
Noul tip, nume_tip, poate fi utilizat in continuare pentru a declara date de acelas tip (De obicei noul tip se scrie cu litere mari, dar nu este obligatoriu acest lucru).
Exemple:
1) typedef unsigned char BYTE;//BYTE este noul nume_tip
BYTE x, y;//definiri de variabile cu noul nume_tip
-2) typedef struct data_calend
A int zi; char lunaa11i; int an;
SDC;
DC data_nast, data_angaj, *p;
… p=&data_nast; p -> zi=8;

-3) typedef union
A char numea40i; int urmat; long cod;
SZC;
ZC sir, *p;
… p=&sir; p -> numea0i=’A’; p -> cod=17;
-4) typedef struct
A double real; double imag;
SCOMPLEX;
COMPLEX z, tza10i;
-5) typedef struct
A unsigned int x; unsigned int y; int val;
SPIXEL;
PIXEL a,b;//defineste 2 variabile a si b ce pot fi pixeli intr-o imagine
-6) typedef int * PINT;//defineste pointer la intreg
PINT p, ta100i;// este identica cu: int *p, *ta100i;

7.6. ENUMERARI (Enumeration, enum)

Tipul enumerare permite programatorului sa defineasca o lista de constante intregi cu nume in vederea folosirii de nume sugestive pentru valori numerice.
Tipul enumerare se declara printr-un format asemanator cu cel utilizat in cadrul structurilor si anume:

enum nume_enum
A nume0, nume1, …, numek
Sd1, d2, …, dn; unde:
-nume_enum, este numele noului tip de date utilizator introdus prin aceasta declaratie
-nume0, nume1, …, numek, sunt nume care se vor utiliza in locul valorilor numerice si anume numei va avea valoarea i (ca si intreg)
-d1, d2, …, dn, sunt variabile enum de noul tip nume_enum.
Exemple:-1) enum Boolean Afase,trueS bisect;// ce implica false=0 si true=1 si putem folosi expresii conditionale de forma bisect == false sau bisect == true
-2) enum Aileg, ian, feb, mar, apr, mai, iun, iul, aug, sep, oct, nov, decS luna;
Avind aceasta declaratie putem folosi in loc de: luna=3; luna=mar;//pentru ca mar=3
-3)La declararea tipului enumerare se poate folosi cuvintul cheie typedef ca si in cazul tipurilor utilizator struct si union astfel: typedef enum nume_enum Anume0, nume1, …, numekS nume_tip; in continuare putem folosi: nume_tip d1, d2, …, dn; typedef enum Afalse, trueSBOOL;
BOOL bisect;
-4)Daca avem o functie care returneaza doua valori 0 sau 1 putem defini noul tip BOOL la definirea antetului functiei astfel:
BOOL f(lista_param_formali)
-5)Tipul enumerare poate fi definit impunind si alte valori diferite de cele care rezulta implicit, adica de la 0 si tot crescind cu o unitate.
In acest caz in locul unde dorim sa impunem o noua valoare, vom folosi urmatoarea constructie:
…, numei = eci, numei+1,… astfel incit numei va lua valoarea eci, eci fiind o expresie constanta, numei+1 va lua valoarea eci+1, si tot asa mai departe valoarea de atribuire va creste cu o unitate la fiecare nume nou: typedef enum Aian=1, feb, mar, apr, mai, iun, iul, aug, sep, oct, nov, decS DL;
-6)Datele de tip enum nu sunt supuse controlului compilatorului C, ele fiind tratate ca si simple date de tip int. De asemenea aceste date nu sunt cunoscute de functiile din bibliotecile standard. In acest caz cu ele se pot scrie expresii care sa nu corespunda scopului pentru care s-au definit:
DL d1, d2, d3;//DL este tipul definit inainte d3 = d1+d2; d3 = d1*d2; nu sunt interpretate ca eronate datele fiind tratate ca si intregi

7.7. DATE DEFINITE RECURSIV

Declaratia de forma, struct nume_struct
A declaratii;
S; defineste componentele datei structurate de tip nume_struct.
Aceste declaratii pot defini date de diferite tipuri predefinite sau utilizator, diferite insa de tipul nume_struct.
Se pot insa defini pointeri spre datele de tip nume_struct chiar in cadrul declaratiei nume_struct cu o declaratie de forma: struct nume_struct
A declaratii; struct nume_struct *nume1; declaratii;
S;
Daca in loc de *nume1 avem nume1, declaratia este eronata.
Un tip utilizator este direct recursiv, daca are cel putin o componenta care este de tip pointer spre ea insasi.
Exemple:-1) struct lista
A char *token; int count; struct lista *next;
Slinie; //lista este un tip utilizator direct recursiv, next fiind un pointer catre lista
-2)

typedef struct tnod
A char cuvinta100i; int nr; struct tnod *urm;
STNOD; si putem defini in continuare date de tip TNOD prin:
TNOD nod, *p;
Pentru a defini tipul de data indirect recursiv, adica un tip t1 care contine un pointer spre tip


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta