CLASE DE VARIABILE (DE MEMORIE)
Compilatorul C aloca memorie variabilelor din program de dimensiune corespunzatoare tipului fiecareia.
Memoria se aloca în 2 moduri:
static, repartizata într-o zona speciala asociata programului;
dinamic, repartizata într-o zona speciala numita stiva (se comporta ca o listă 919c27j ; LIFO).
În functie de modul cum se aloca memorie, vom distinge mai multe clase de variabile.
O prima clasa de variabile este aceea a variabilelor globale carora li se aloca memorie pe toata durata executiei programului si ele pot fi utilizate în orice functie a programului. Alta clasa de variabile este clasa variabilelor locale, aceste variabile au o utilizare locala la nivelul unei functii.
2.1. VARIABILE GLOBALE
O variabila globala are o definitie si atâtea declaratii de variabila externa câte sunt necesare.
Definitia unei variabile globale coincide sintactic cu o declaratie obisnuita, dar care este scrisa în afara oricarei functii a programului (fisierului sursa). Implicit, definitia unei variabile globale determina ca variabila respectiva sa fie definita începând din punctul scrierii ei si pâna la sfârsitul fisierului sursa respectiv. De aceea se recomanda ca definitiile variabilelor globale sa fie scrise la începutul fisierului sursa, pentru a fi accesate în orice functie a fisierului.
Pentru a utiliza variabilele globale si în alte functii situate în alte fisiere sursa decât în cel în care sunt definite, ele trebuie declarate ca externe în functiile respective.
O declaratie de variabila externa coincide cu o declaratie obisnuita doar ca începe cu cuvântul cheie extern.
Exemplu:
fisierul în care sunt declarate ca variabile globale fisierul în care sunt folosite ca variabile externe
int i;
float f; void functie(. . .)
void main(void) }
Variabilele i si f sunt declarate în afara functiei main si în afara oricarei functii, deci sunt variabile globale. Ele pot fi folosite în toate functiile din fisierul sursa care contine definitiile lor. Pentru a le utiliza în functii situate în alte fisiere sursa decât în cel în care sunt definite ele sunt declarate ca externe. Deci variabilele i si f sunt declarate ca externe în functia functie din al doilea fisier sursa. Cele doua fisiere sursa care pot fi scrise în momente si de persoane diferite se pot asambla într-un singur program cu ajutorul directivei de preprocesare include.
2.2. VARIABILE LOCALE
Variabilele locale nu sunt valabile în tot programul. Ele au o utilizare locala în doua feluri:
ca si variabile automatice (alocate pe stiva) la nivelul unei functii;
ca si variabile statice (alocate în zona programului) la nivel de fisier (eventual si la nivelul unei functii).
Variabilele declarate în interiorul unei functii si care nu sunt declarate ca externe sunt variabile locale. Lor li se aloca memorie pe stiva la intrarea în functia în care sunt declarate. La iesirea din functie, stiva se reface la continutul dinaintea apelului si variabilele locale pierd alocarea. Deci ori de câte ori se apeleaza o functie, variabilele locale acesteia (denumite si variabile automatice) se aloca (primesc memorie) pe stiva si la iesirea din functie variabilele locale sunt sterse din stiva.
Variabilele locale pot sa nu fie alocate pe stiva, ci într-o zona de memorie destinata acestui scop. O astfel de variabila locala se numeste variabila statica. O variabila locala statica se declara printr-o declaratie obisnuita, precedata de cuvântul cheie static. O variabila statica poate fi declarata atât în corpul unei functii cât si în afara oricarei functii. În cazul unei variabile statice declarata în interiorul unei functii alocarea nu se face pe stiva ci într-o zona de memorie destinata acestui scop, aceasta fiind deosebirea esentiala fata de variabilele automatice. În cazul în care o variabila statica este declarata în afara functiilor, ea este definita din punctul în care a fost declarata si pâna la sfârsitul fisierului sursa care contine declaratia ei. Spre deosebire de variabilele globale, o variabila statica nu poate fi declarata ca externa si deci nu poate fi utilizata în functiile din alte fisiere sursa diferite de cel în care a fost declarata.
Se recomanda ca tablourile mari sa fie declarate statice, deoarece daca sunt automatice pot depasi capacitatea stivei (care are implicit 4K octeti).
Exemple:
Fisierul fisier1.c este un fisier sursa care contine 2 variabile globale i si d , o variabila statica x si doua functii f si main. Functia main contine variabila statica a iar functia f contine variabila statica b.
int i; // definitia variabilei globale i
double d; // definitia variabilei globale d
static int x; // definitia variabilei statice x, locala fisierului fisier1.c
void main (void)
Variabilele a si c fiind locale functiei main nu pot fi utilizate în functia f. Analog, variabilele p si b sunt locale în functia f, nu pot fi utilizate în functia main.
Fisierul fisier2.c contine functiile f1 si f2 care intra în componenta aceluiasi program ca si functiile main si f din fisierul fisier1.c
static unsigned t; // definitia variabilei statice t, locala fisierului fisier2.c
void f1(...)
void f2(...)
Variabila statica x definita în fisierul fisier1.c nu poate fi utilizata în fisierul fisier2.c. De asemenea, variabila statica t nu poate fi utilizata în fisierul fisier1.c. Variabila globala d nu poate fi utilizata în functia f2, ea nefiind declarata ca si externa.
Observatii:
1o. Variabilele globale constituie un mijloc simplu de interfata între functiile unui program. Se recomanda a fi folosite când dorim transferuri de valori între doua sau mai multe functii în asa fel încât modificarile realizate de o functie sa fie accesibile pentru toate functiile programului. Nu trebuie sa se faca abuz în utilizarea variabilelor globale deoarece constituie si o sursa potentiala de erori, pentru ca accesul unui mare numar de functii la anumite date globale conduce deseori la modificari nedorite si greu evidentiabile.
2o. Functiile sunt considerate cu atributul implicit extern. Daca într-un program exista mai multe fisiere sursa atunci o functie poate fi apelata oriunde, bine înteles respectând conventia definirii ei sau a prototipului ei înainte de a fi folosita. Putem sa limitam definind functiile cu atributul static precedând antetul functiei prin cuvântul cheie static. Astfel functia respectiva devine locala si deci apelabila doar în fisierul în care este definita.
2.3. VARIABILE REGISTRU
Limbajul C ofera posibilitatea de a aloca anumite variabile în registri microprocesorului. Deoarece registri constituie un tip de memorie ultrarapida în anumite cazuri se poate economisi atât timp de executie cât si memorie. Se pot aloca în registri numai parametrii functiilor si variabilele automatice de tip int, char si de tip pointer. O variabila registru se declara în mod obisnuit, precedând declaratia ei prin cuvântul rezervat register.
Alocarea într-un registru a unei variabile se face numai daca exista un registru disponibil. În caz contrar, variabila registru se aloca pe stiva exact ca o variabila automatica. Alocarea se face în ordinea declararii variabilelor registru.
Exemplu:
void f (register int i)
Mai întâi se aloca parametrul i într-un registru, apoi se aloca c si j în alti doi registri.
Se recomanda alocarea în registri a variabilelor care au o utilizare mare, de exemplu a indicilor de tablouri.
2.4. INIŢIALIZARE
Variabilelor li se pot atribui valori initiale. Pentru variabilele globale valorile initiale se atribuie prin definitiile lor, iar în cazul celorlalte variabile se pot atribui valori prin declaratiile lor. Pentru ca de multe ori am folosit cuvintele definitia variabilelor sau declaratiile varibilelor precizam ca ele au înteles distinct în anumite contexte. O variabila globala se defineste o singura data si se poate declara ori de câte ori e necesara utilizarea ei în alte fisiere (evident declarata externa). În cazul celorlalte tipuri de variabile definitiile si declaratiile coincid. Totodata definitia si declaratia (prototipul) unei functii nu coincid.
O variabila simpla se poate initializa printr-o declaratie de forma:
tip nume=expresie;
Variabilelor globale si statice li se atribuie valori initiale la lansarea programului. Expresia utilizata în cazul acestor variabile trebuie sa fie o expresie constanta care sa poata fi evaluata de compilator la întâlnirea ei. Aceasta, deoarece variabilele globale si statice se initializeaza prin valori definite la compilare.
Variabilele automatice se initializeaza la executie, de fiecare data când se activeaza functia în care sunt declarate. Din aceasta cauza, nu mai este necesar ca expresia sa fie o expresie constanta. Totusi, la întâlnirea ei, trebuie sa fie definiti operanzii expresiei de initializare.
Exemplu:
void f(int n)
La întâlnirea expresiei i+n sunt deja definiti ambii operanzi:
i a fost în prealabil initializat cu valoarea 10;
n are o valoare care e transferata la apel.
Variabilele globale si statice neinitializate au implicit valoarea egala cu zero, iar varibilele automatice neinitializate au o valoare initiala imprevizibila.
Tablourile se pot initializa printr-o lista de expresii incluse între acolade. Astfel un tablou unidimensional se poate initializa folosind urmatorul format:
tip nume[n] =
La initializarea tablourilor se pot utiliza numai expresii constante. Numarul expresiilor poate fi mai mic decât numarul elementelor tabloului. Elementele neinitializate au valoarea zero în cazul tablourilor globale si statice. Daca se initializeaza fiecare element al tabloului atunci numarul n al elementelor acestuia nu mai este obligatoriu în declaratia tabloului respectiv. Deci se poate scrie astfel:
tip nume[ ] = ;
Numarul elementelor tabloului se considera ca este egal cu cel al expresiilor care realizeaza initializarea lor.
Pentru un tablou bidimensional vom folosi urmatoarea structura:
tip nume [n][m] = , // pentru linia întâi
, // pentru linia a doua
. . .
, // pentru ultima linie
};
Numarul expresiilor poate fi mai mic decât m în oricare din acoladele corespunzatoare celor n linii ale tabloului bidimensional. În acest caz se poate omite doar numarul n (al liniilor tablourilor), m fiind obligatoriu. Formatul de initializare a tablourilor bidimensionale se extinde imediat pentru tablouri cu mai multe dimensiuni.
Tablourile de tip caracter pot fi initializate folosind un format mai simplu decât cel indicat anterior. Astfel. un tablou de tip caracter se poate initializa printr-o declaratie de forma:
char sir[n] = sir_de_caractere;
Evident, marginea superioara n poate fi omisa si în acest caz. Totodata compilatorul pune automat caracterul NUL dupa ultimul caracter al sirului utilizat în initializare.
Exemple:
1) int itab[] = // tabloul de tip intreg are 5 elemente itab[0] = 1,. . . itab[4] = 5
2) int m[3][3] = ,,}; // tabloul are 3 linii si 3 coloane.
Elementele primei linii sunt initializate astfel:
m[0][0] = -1;
m[0][1] = 0;
m[0][2] = 1;
În linia a doua se initializeaza numai m[1][0] = -1; Daca tabloul ar fi declarat global sau static atunci m[1][1] si m[1][2] ar avea valoarea zero. Altfel ele au o valoare imprevizibila.
Elementele ultimei linii se initializeaza astfel:
m[2][0] = 0;
m[2][1] = 1;
declaratiile de mai jos sunt identice:
char sir [ ] = ;
char sir [ ] = ;
|