Declaratiile se folosesc pentru a specifica interpretarea pe care compilatorul
trebuie sa o dea fiecarui identificator.
Declaratie: specificator-declaratie lista-declarator?opt?; i7o12oq
Specificator-declaratie: specificator-tip specificator-declaratie?opt? specificator-clasa-memorie specificator-declaratie?opt?
Specificator-tip: char short int long unsigned float double void specificator-structura-sau-reuniune typedef-nume
Specificator-clasa-memorie: auto static extern register const typedef
Lista-declarator: declarator-cu-initializare declarator-cu-initializare, lista-declarator
Declarator-cu-initializare: declarator initializator?opt?
Declarator: identificator
(declarator)
* declarator declarator () declarator aexpresie-constanta?opt?i
Declaratiile listeaza toate variabilele care urmeaza a fi folosite intr-un
program. O declaratie specifica tipul, clasa de memorie si eventual valorile
initiale pentru una sau mai multe variabile de acelasi tip. Declaratiile se
fac sau in afara oricarei functii sau la inceputul unei functii
inaintea oricarei instructiuni.
Nu orice declaratie rezerva si memorie pentru un anumit identificator, de aceea
deosebim:
? declaratia de definitie a unei variabile, care se refera la locul unde este
creata variabila si unde i se aloca memorie;
? declaratia de utilizare a unei variabile, care se refera la locul unde numele
variabilei este declarat pentru a anunta proprietatile variabilei care urmeaza
a fi folosita.
5.1. Specificatori de clasa de memorie
Specificatorii de clasa de memorie sint: auto static extern register const typedef
Specificatorul typedef nu rezerva memorie si este denumit „specificator
de clasa de memorie” numai din motive sintactice; el va fi discutat in
capitolul 10.
Semnificatia diferitelor clase de memorie a fost discutata deja in paragraful
3.1.
Declaratiile cu specificatorii auto, static si register determina si rezervarea
unei zone de memorie corespunzatoare. Declaratia cu specificatorul extern presupune
o definitie externa pentru identificatorii dati, undeva in afara functiei
sau fisierului in care ei sint declarati.
Intr-o declaratie poate sa apara cel mult un specificator de clasa de
memorie. Daca specificatorul de clasa lipseste din declaratie, el se considera
implicit auto in interiorul unei functii si definitie extern in
afara functiei. Exceptie fac functiile care nu sint niciodata automatice.
De exemplu liniile: int sp; double valaMAXVALi; care apar intr-un program in afara oricarei functii, definesc variabilele
externe sp de tip int si val de tip masiv de double. Ele determina alocarea
memoriei si servesc de asemenea ca declaratii ale acestor variabile in
tot restul fisierului sursa. Pe de alta parte liniile: extern int sp; extern double valai; declara pentru restul fisierului sursa ca variabilele sp si val sint externe,
sp este de tip int si val este un masiv de double si ca ele au fost definite
in alta parte, unde li s-a alocat si memorie. Deci aceste declaratii nu
creeaza aceste variabile si nici nu le aloca memorie.
5.2. Specificatori de tip
Specificatorii de tip sint: char short int long unsigned float double void specificator-structura-sau-reuniune typedef-nume
Cuvintele long, short si unsigned pot fi considerate si ca adjective; urmatoarele
combinatii sint acceptate:
short int long int unsigned int unsigned long int long double
Intr-o declaratie se admite cel mult un specificator de tip, cu exceptia
combinatiilor amintite mai sus. Daca specificatorul de tip lipseste din declaratie,
el se considera implicit int.
Specificatorii de structuri si reuniuni sint prezentati in sectiunea
10.9, iar declaratiile cu typedef in sectiunea 10.10.
5.3. Declaratori
Lista-declarator care apare intr-o declaratie este o succesiune de declaratori
separati prin virgule, fiecare dintre ei putind avea un initializator.
Declaratorii din lista-declarator sint identificatorii care trebuie declarati.
Fiecare declarator este considerat ca o afirmatie care, atunci cind apare
o constructie de aceeasi forma cu declaratorul, produce un obiect de tipul si
de clasa de memorie indicata. Fiecare declarator contine un singur identificator.
Gruparea declaratorilor este la fel ca si la expresii.
Daca declaratorul este un identificator simplu, atunci el are tipul indicat
de specificatorul din declaratie.
Un declarator intre paranteze este tot un declarator, dar legatura declaratorilor
complecsi poate fi alterata de paranteze.
Sa consideram acum o declaratie de forma:
T D1 unde T este un specificator de tip (ca de exemplu int) si D1 un declarator.
Sa presupunem ca aceasta declaratie face ca identificatorul sa aiba tipul „...T”
unde „...” este vid daca D1 este un identificator simplu (asa cum
tipul lui x in int x este int). Daca D1 are forma:
*D atunci tipul identificatorului pe care-l contine acest declarator este „pointer
la T”.
Daca D1 are forma:
D() atunci identificatorul pe care-l contine are tipul „functie care returneaza
T”.
Daca D1 are forma:
Daexpresie-constantai sau Dai atunci identificatorul pe care-l contine are tipul „masiv de T”.
In primul caz expresia constanta este o expresie a carei valoare este
determinabila la compilare si al carei tip este int. Cind mai multi identificatori
„masiv de T” sint adiacenti, se creeaza un masiv multidimensional;
expresiile constante care specifica marginile masivelor pot lipsi numai pentru
primul membru din secventa. Aceasta omisiune este utila cind masivul este
extern si definitia reala care aloca memoria este in alta parte (vezi
sectiunea 5.1). Prima expresie constanta poate lipsi de asemenea cind
declaratorul este urmat de initializare. In acest caz dimensiunea este
calculata la compilare din numarul elementelor initiale furnizate.
Un masiv poate fi construit din obiecte de unul dintre tipurile de baza, din
pointeri, din reuniuni sau structuri, sau din alte masive (pentru a genera un
masiv multidimensional).
Nu toate posibilitatile admise de sintaxa de mai sus sint permise. Restrictiile
sint urmatoarele: functiile nu pot returna masive, structuri, reuniuni
sau functii, desi ele pot returna pointeri la astfel de obiecte; nu exista masive
de functii, dar pot fi masive de pointeri la functii. De asemenea, o structura
sau reuniune nu poate contine o functie, dar ea poate contine un pointer la
functie. De exemplu, declaratia int i, *ip, f(), *fip(), (*pfi)(); declara un intreg i, un pointer ip la un intreg, o functie f care
returneaza un intreg, o functie fip care returneaza un pointer la un intreg,
un pointer pfi la o functie care returneaza un intreg. Prezinta interes
compararea ultimilor doi declaratori.
Constructia *fip() este *(fip()), astfel ca declaratia sugereaza apelul functiei
fip si apoi utilizind indirectarea prin intermediul pointerului se obtine
un intreg.
In declaratorul (*pfi)(), parantezele externe sint necesare pentru
arata ca indirectarea printr-un pointer la o functie furnizeaza o functie, care
este apoi apelata; ea returneaza un intreg.
Declaratiile de variabile pot fi explicite sau implicite prin context. De exemplu
declaratiile: int a,b,c; char d, ma100i; specifica un tip si o lista de variabile. Aici clasa de memorie nu este declarata
explicit, ea se deduce din context. Daca declaratia este facuta in afara
oricarei functii atunci clasa de memorie este extern; daca declaratia este facuta
in interiorul unei functii atunci implicit clasa de memorie este auto.
Variabilele pot fi distribuite in declaratii in orice mod; astfel
listele le mai sus pot fi scrise si sub forma: int a; int b; int c; char d; char ma100i;
Aceasta ultima forma ocupa mai mult spatiu dar este mai convenabila pentru adaugarea
unui comentariu pentru fiecare declaratie sau pentru modificari ulterioare.
5.4. Modificatorul const
Valoarea unei variabile declarate cu acest modificator nu poate fi modificata.
Sintaxa: const nume-variabila = valoare ; nume-functie (..., const tip *nume-variabila, ...)
In prima varianta, modificatorul atribuie o valoare initiala unei variabile
care nu mai poate fi ulterior modificata de program. De exemplu, const int virsta = 39;
Orice atribuire pentru variabila virsta va genera o eroare de compilare.
Atentie! O variabila declarata cu const poate fi indirect modificata prin intermediul
unui pointer: int *p = &virsta;
*p = 35;
In a doua varianta modificatorul const este folosit impreuna cu
un parametru pointer intr-o lista de parametri ai unei functii. Functia
nu poate modifica variabila pe care o indica pointerul: int printf (const char *format, ...);
5.5. Initializare
Un declarator poate specifica o valoare initiala pentru identificatorul care
se declara. Initializatorul este precedat de semnul = si consta dintr-o expresie
sau o lista de valori incluse in acolade.
Initializator: expresie
Alista-initializareS
Lista-initializare: expresie lista-initializare, lista-initializare
Alista-initializareS
Toate expresiile dintr-un initializator pentru variabile statice sau externe
trebuie sa fie expresii constante (vezi sectiunea 3.4) sau expresii care se
reduc la adresa unei variabile declarate anterior, posibil offset-ul unei expresii
constante. Variabilele de clasa auto sau register pot fi initializate cu expresii
oarecare, nu neaparat expresii constante, care implica constante sau variabile
declarate anterior sau chiar functii.
In absenta initializarii explicite, variabilele statice si externe sint
initializate implicit cu valoarea 0. Variabilele auto si register au valori
initiale nedefinite (reziduale).
Pentru variabilele statice si externe, initializarea se face o singura data,
in principiu inainte ca programul sa inceapa sa se execute.
Pentru variabilele auto si register, initializarea este facuta la fiecare intrare
in functie sau bloc.
Daca un initializator se aplica unui „scalar” (un pointer sau un
obiect de tip aritmetic) el consta dintr-o singura expresie, eventual in
acolade. Valoarea initiala a obiectului este luata din expresie; se efectueaza
aceleasi operatii ca in cazul atribuirii.
Pentru initializarea masivelor si masivelor de pointeri vezi sectiunea 9.8.
Pentru initializarea structurilor vezi sectiunea 10.3.
Daca masivul sau structura contine sub-masive sau sub-structuri regula de initializare
se aplica recursiv la membrii masivului sau structuri.
5.6. Nume-tip
In cele expuse mai sus furnizarea unui nume-tip a fost necesar in
doua contexte:
? pentru a specifica conversii explicite de tip prin intermediul unui cast (vezi
sectiunea 3.4);
? ca argument al lui sizeof (vezi sectiunea 4.2).
Un nume-tip este in esenta o declaratie pentru un obiect de acest tip,
dar care omite numele obiectului.
Nume-tip: specificator-tip declarator-abstract
Declarator-abstract: vid
(declarator-abstract)
*declarator-abstract declarator-abstract() declarator-abstractaexpresie-constanta?opt?i
Pentru a evita ambiguitatea, in constructia:
(declarator-abstract) declaratorul abstract se presupune a nu fi vid. Cu aceasta restrictie, este
posibil sa identificam in mod unic locul intr-un declarator-abstract,
unde ar putea aparea un identificator, daca aceasta constructie a fost un declarator
intr-o declaratie. Atunci tipul denumit este acelasi ca si tipul identificatorului
ipotetic. De exemplu: int int* int *a3i int(*)a3i int *( ) int(*)() denumeste respectiv tipurile int, „pointer la intreg”, „masiv
de 3 pointeri la intregi”, „pointer la un masiv de 3 intregi”,
„functie care returneaza pointer la intreg” si „pointer
la o functie care returneaza intreg”.