Sunteți pe pagina 1din 51

APENDICELE A: MANUAL DE REFERINTA

A1. Introducere

Acest manual descrie limbajul C asa cum este specificat prin proiectul "Draft
Proposed American National Standard for Information Systems-Programming Language
C", document cu numarul X3J11/88-001, datat 11 ianuarie 1988. Acest proiect nu
este standardul final si este inca posibil ca sa mai apara unele modificari in
limbaj. De aceea acest manual nu poate fi considerat ca descrie ultima definitie a
limbajului. Mai mult decit atit, el este o interpretare a proiectului propus ca
standard si nu standardul insusi, desi s-au luat toate masurile ca el sa fie un
ghid de nadejde.
In cea mai mare parte, acest manual urmeaza principiile generale ale
proiectului de standard, care la rindul lui urmeaza pe acelea ale primei editii a
acestei carti, desi organizarea difera in detalii. Cu exceptia redenumirii citorva
produse si a neformalizarii definitiilor entitatilor lexicale (token) sau a
preprocesorului, gramatica data aici pentru limbajul propriu zis este echivalenta
cu aceea a proiectului curent.

Peste tot in acest manual, comentariile sint indentate


ca aici. Cele mai multe dintre aceste comentarii
lumineaza caile prin care "ANSI standard C" difera de
limbajul definit in prima editie a acestei carti sau de
rafinamentele ulterioare introduse de diferite
compilatoare.

A2. Conventii lexicale

Un program se compune din una sau mai multe unitati de traducere sau de
translatie (translation unit) depuse in fisiere. Traducerea se realizeaza in
citeva faze care sint descrise in A12. Prima faza realizeaza transformari
lexicale de nivel coborit, executa directivele introduse de liniile de inceput
prin caracterul # si realizeaza macro-definitia si expandarea. In momentul in care
s-a incheiat preprocesarea conform A12, programul a fost, de fapt, redus la o
secventa de token-uri.

A2.1 Token-uri

Exista sase clase de token-uri: identificatori, cuvinte cheie, constante,


literale sir, operatori si alti separatori. Blancurile, tab-urile orizontale si
verticale, newline-urile, formfeed-urile si comentariile, asa cum sint descrise
mai jos (cu un termen general "spatii albe"), sint ignorate cu exceptia cazurilor
in care ele separa token-uri. Unele spatii albe sint solicitate sa separe, intr-
un mod diferit, identificatori, cuvinte cheie si constante adiacente.
Daca stream-ul de intrare a fost divizat in token-uri pina la un caracter dat,
urmatoarul token este sirul cel mai lung de caractere care ar putea sa constituie
un token.

A2.2 Comentarii
Caracterele /* introduce un comentariu, care se termina cu caracterele */.
Comentariile nu se cuibaresc si ele nu apar in interiorul sirurilor caracter sau
literalelor caracter.

A2.3 Identificatori

Un identificator este o secventa de litere si cifre. Primul caracter trebuie


neaparat sa fie o litera; undescore-ul "_" conteaza ca litera. Literele mari si
mici sint interpretate in mod diferit. Identificatorii pot avea orice lungime iar
pentru identificatorii interni, cel putin primele 31 caractere sint semnificative;
unele implementari pot asigura mai multe caractere semnificative. Identificatorii
interni includ nume de macro-uri ale preprocesorului si toate celelalte nume care
nu au legaturi externe (A11.2). Identificatorii cu legaturi externe sint mai
restrictionati: implementarile pot face ca numai primele sase caractere sa fie
semnificative si pot ignora distinctia dintre o litera mare si o aceeasi litera
mica.

A2.4 Cuvinte cheie

Urmatorii identificatori sint rezervati pentru o utilizare in calitate de


cuvinte cheie si nu pot fi utilizati altfel:

auto double int struct


break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

Unele implementari rezerva cuvintele fortran si asm.

Cuvintele cheie const, signed si volatile sint noi, o


data cu standardul ANSI; enum si void sint noi fata de
prima editie, dar au fost utilizate in mod obisnuit;
entry, rezervat cindva, dar niciodata utilizat, nu mai
este, in continuare, rezervat.

A2.5 Constante

Exista citeva feluri de constante. Fiecare are un tip de baza; A4.2 discuta
tipurile de baza.

constant:
integer-constant
character-constant
floating-constant
enumeration-constant

A2.5.1 Constante intregi


O constanta intreaga, care consta dintr-o secventa de cifre, este considerata
octala, daca ea incepe cu cifra 0, altminteri este considerata zecimala.
Constantele octale nu contin cifrele 8 si 9. O secventa de cifre precedata de 0x
sau 0X (cifra zero) este interpretata ca un intreg hexazecimal. Cifrele
hexazecimale includ literele a sau A pina la f sau F pentru valorile 10 pina la
15.
O constanta intreaga poate sa se termine prin litera u sau U pentru a se
specifica ca ea este fara semn (unsigned). Ea poate, de asemenea, sa se termine
prin litera l sau L pentru a se specifica ca este lunga (long).
Tipul unei constante intregi depinde de forma sa, valoarea sa si de sufixul
ei. (Vezi A4 pentru o discutie a tipurilor). Daca ea nu are un sufix si este
zecimala, ea are primul, din tipurile care urmeaza, in care valoarea sa sa poata
fi reprezentata: int, unsigned int, long int, unsigned long int. Daca ea este
terminata prin u sau U atunci va fi unsigned int sau unsigned long int. Daca ea
este terminata prin l sau L atunci ea va fi long int sau unsigned long int.

Elaborarea acestor tipuri de constante intregi merge


considerabil dincolo de prima editie, care, pur si
simplu,facea ca constantele intregi mari sa fie long.
Sufixele U sint noi.

A2.5.2 Constante caracter

O constanta caracter este o secventa de unu sau mai multe caractere incadrata
de apostroafe, precum in 'x'. Valoarea unei constante caracter, cu un singur
caracter, este valoarea numerica a caracterului din multimea de caractere a
masinii la momentul executiei. Valoarea unei constante multicaracter este definita
la implementare.
Constantele caracter nu contin caracterul apostrof (') sau newline-uri; in
scopul de a le reprezenta pe acestea si anumite alte caractere, pot fi utilizate
urmatoarele secvente escape:

newline NL (LF) \n backslash \ \\


horizontal tab HT \t question mark ? \?
vertical tab VT \v single quote ' \'
backspace BS \b double quote " \"
carriage return CR \r octal number ooo \ooo
formfeed FF \f hex number hh \xhh
audible alert BEL \a

Secventa escape \ooo consta dintr-un backslash urmat de 1, 2 sau 3 cifre octale
care permit specificarea valorii caracterului dorit. Un exemplu obisnuit pentru
aceasta constructie este \0 (ne urmat de vreo cifra), care specifica caracterul
NULL. Secventa escape \xhh consta dintr-un backslash urmat de x, urmat de doua
cifre hexazecimale care permit specificarea valorii caracterului dorit. Nu exista
nici o limita in ce priveste numarul de cifre dar comportarea este nedefinita daca
valoarea rezultata a caracterului depaseste pe aceea a celui mai mare caracter.
Pentru oricare din caracterele escape, fie octale fie hexazecimale, daca
implementarea trateaza tipul char ca fiind signed, valoarea este cu extensie de
semn ca si cind a fost convertita prin cast la tipul char. Daca caracterul ce
urmeaza dupa backslash nu este dintre cele specificate, comportarea este
nedefinita.
In unele implementari, exista un set extins de caractere care nu pot fi
reprezentate prin tipul char. O constanta in acest set extins este scrisa cu un L
precedent, de exemplu L'x', si este denumita constanta caracter-larg (wide
character constant). O astfel de constanta are tipul wchar_t, un tip de intreg
definit in header- ul standard <stddef.h>. Ca si in cazul constantelor caracter
ordinare, pot fi utilizate secventele escape octale sau hexazecimale; efectul este
nedefinit daca valoarea specificata depaseste pe aceea reprezentabila prin
wchar_t.

Unele din aceste secvente escape sint noi, in particular


reprezentarea hexazecimala a caracterelor. Caracterele
extinse sint de asemenea noi. Seturile de caractere
folosite in mod obisnuit in America si Europa
occidentala pot fi codificate astfel incit sa se
potriveasca tipului char; intentia principala in
adaugarea tipului wchar_t a fost de a se acomoda
limbilor din Asia.

A2.5.3 Constante cu punct zecimal mobil

O constanta cu punct zecimal mobil consta dintr-o parte intreaga, un punct


zecimal, o parte fractionara, un e sau E, un exponent intreg optional cu semn si
un sufix optional de tip, adica una dintre literele f, F, l sau L. Partea
intreaga si partea fractionara consta, ambele din cite o secventa de cifre. Fie
partea intreaga, fie partea fractionara (dar nu amindoua) pot lipsi. Tipul este
determinat de sufix: F sau f inseamna float, L sau l inseamna long double;
altmiteri este double.

Sufixele la constantele cu punct mobil constituie noutate.

A2.5.4 Constante de enumeratie

Identificatorii declarati ca enumeratori (vezi A8.4) sint constante de tip


int.

A2.6 Literale sir

Un literal sir, de asemenea numit constanta sir, este o secventa de caractere


incadrate de ghilimele, precum in "...". Un sir are tipul "tabloul de caractere"
si clasa de memorie static (vezi A4) si este initializat cu caracterele date. Daca
literale sir identice sint distincte, acest lucru este definit la implementare
iar comportarea unui program care incearca sa modifice un literal sir este
nedefinita.
Literalele sir adiacente sint concatenate intr-un singur sir. Dupa fiecare
concatenare este adaugat un octet nul \0 sirului astfel ca programele care
exploreaza sa poata detecta sfirsitul sau. Literalele sir nu contin newline-uri si
nici ghilimele; in scopul de a le reprezenta sint valabile aceleasi secvente
escape.
Ca si cu constantele caracter, literalele sir intr-un set extins de caractere
sint scrise cu un L precedent, precum in L"...". Literalele sir caracter-larg au
tipul "tablou de wchar_t". Concate- narea literalelor sir ordinare si caracter-
larg este nedefinita.

Specificatia ca literalele sir nu e necesar sa fie


distincte si prohibitia modificarii lor sint noi in
standardulANSIasa cum este si concatenarealiteralelor
sir adiacente. Literalele sir caracter-larg sint de
asemenea, noi.

A3. Notatie de sintaxa

In notatia de sintaxa folosita in acest manual, categoriile sintactice sint


indicate prin caractere italice iar cuvintele din literale si caracterele sint
indicate prin forma "masina de scris". Categoriile alternative sint de obicei
listate pe linii separate; in putine cazuri, o multime mare de alternative strimte
este prezentata pe o linie, marcata cu sintagma "one of" ("una dintre"). Un
terminal optional sau un simbol neterminal optional poarta indicele (opt),
astfel incit, de exemplu:

{ expression(opt) }

inseamna o expresie optionala, incadrata de acolade. Sintaxa rezumata in A13.

Spre deosebire de gramatica prezentata in prima editie a


acestei carti, cea prezentata aici face explicita
ierarhiaprioritatilor si asociativitatea operatorilor
expresie.

A4. Semnificatia identificatorilor

Identificatorii sau numele se refera la o varietate de lucruri: functii, tag-


uri de structuri, uniuni, enumeratii, membri ai unei structuri sau uniuni,
constante de enumeratie, nume de typedef-uri si obiecte. Un obiect, uneori
denumit variabila, este o locatie in memorie iar interpretarea lui depinde de
doua atribute principale: clasa sa de memorie si tipul sau. Clasa de memorie
stabileste durata de asociere a memoriei cu obiectul identificat; tipul stabileste
semnificatia valorilor gasite in obiectul identificat. Un nume are, de asemenea,
un domeniu, care este regiunea din program in care el este cunoscut si o legatura
care stabileste daca acelasi nume, dintr-un alt domeniu, se refera la acelasi
obiect sau functie. Domeniul si legatura sint discutate in A11.

A4.1 Clase de memorie

Exista doua clase de memorie: automata si statica. Mai multe cuvinte cheie,
impreuna cu contextul unei declaratii de obiect, specifica clasa sa de memorie.
Obiectele automate sint locale unui bloc (A9.3), si dispar la iesirea din bloc.
Declaratiile din interiorul unui bloc creaza obiecte automate daca nu s-a
specificat nici o clasa de memorie sau daca s-a folosit specificatorul auto.
Obiectele declarate register sint automate si sint memorate (daca este posibil) in
registre rapide ale masinii.
Obiectele statice pot fi locale pentru un bloc sau externe pentru toate
blocurile, dar, in toate cazurile, ele pastreaza valorile lor pe toata durata
intervalului de timp scurs de la iesirea din functie sau bloc pina la reintrarea
in ele. In interiorul unui bloc, inclusiv blocul care asigura codul pentru o
functie, obiectele statice sint declarate cu cuvintul cheie static. Obiectele
declarate in afara tuturor blocurilor, la acelasi nivel cu definitiile functiilor,
sint intotdeauna statice. Ele pot fi facute locale unei unitati de traducere
(translation unit) particulare prin utilizarea cuvintului cheie static; acesta le
da lor "legatura interna". Ele devin globale pentru un intreg program prin
omiterea unei clase de memorie explicite sau prin utilizarea cuvintului cheie
extern; acesta le da lor "legatura externa".

A4.2 Tipuri de baza

Exista mai multe tipuri de baza. Header-ul standard <limits.h>, descris in


Apendicele B, defineste valorile cele mai mari si cele mai mici ale fiecarui tip
din implementarea locala. Numerele date in Apendicele B arata cele mai mici marimi
acceptabile.
Obiectele declarate a fi caractere (char) sint suficient de mari pentru a
memora orice membru al setului de caractere in executie. Daca un caracter
veritabil din acest set este memorat intr-un obiect char, valoarea sa este
echivalenta cu codul intreg al respectivului caracter si este nenegativ. Alte
marimi pot fi memorate in variabile char, dar gama valabila de valori si in
special daca valoarea este cu semn, depinde de implementare.
Caracterele fara semn declarate unsigned char consuma acceasi cantitate de
memorie ca si caracterele simple, dar intodeauna apar nenegative; caracterele, in
mod explicit cu semn, declarate signed char, consuma, la fel, aceeasi memorie ca
si caracterele simple.

Tipul unsigned char nu apare in prima editie a acestei


carti dar este in uzul curent. signed char este nou.

Alaturi de tipurile char, mai exista pina la trei marimi de intregi, declarate
short int, int si long int. Obiectele simplu-int au marimea naturala sugerata de
arhitectura masinii gazda; celelalte marimi sint furnizate pentru a raspunde
nevoilor speciale. Intregii mai lungi asigura cel putin tot atita memorie cit
asigura cei care sint mai scurti dar implementarea poate sa faca astfel incit
intregii simpli sa fie echivalenti cu oricare, short int sau long int. Tipurile
int toate reprezinta valori cu semn, in afara de cazurile cind se specifica
altfel.
Intregii fara semn, declarati folosind cuvintul cheie unsigned, asculta legile
aritmeticii modul 2 la puterea n unde n este numarul de biti din reprezentare si
astfel aritmetica marimilor fara semn nu conduce niciodata la depasire. Setul de
valori nenegative care pot fi memorate intr-un obiect cu semn este o submultime
a valorilor care pot fi memorate in obiectul corespunzator fara semn iar
reprezentarea pentru valorile care se propun este aceeasi.
Oricare dintre punct zecimal mobil cu simpla precizie (float), punct zecimal
mobil cu dubla precizie(double) si punct zecimal mobil cu precizie extinsa (long
double) poate fi sinonim, dar cele mai din urma in lista sint cel putin tot atit
de precise ca precedentele lor.

long double este nou. Prima editie face pe long double


echivalent cu double; locutiunea a fost retrasa.

Enumeratiile sint tipuri unice care au valori intregi; un set de constante cu


nume este asociat cu fiecare enumeratie (A8.4). Enumeratiile se comporta ca si
intregii dar este obisnuit pentru un compilator sa emita un avertisment cind unui
obiect de un tip particular de enumeratie ii este atribuit altceva decit una din
constantele sale sau o expresie de tipul sau.
Deoarece obiectele de aceste tipuri pot fi interpretate ca numere ele vor fi
referite ca tipuri aritmetice. Tipurile char si int de toate marimile, fiecare cu
sau fara semn si, de asemenea, tipurile enumeratie, vor fi denumite in asamblu,
tipuri integrale. Tipurile float, double si long double vor fi denumite tipuri
flotante (floating types).
Tipul void specifica o multime vida de valori. El este utilizat ca tip
returnat de catre functii care nu genereaza nici o valoare.

A4.3 Tipuri derivate

Alaturi de tipurile de baza, exista o clasa conceptual infinita de tipuri


derivate construite din tipuri de baza in urmatoarele feluri:
- tablouri de obiecte de un tip dat
- functii returnind obiecte de un tip dat
- pointere catre obiecte de un tip dat
- structuri continind o secventa de obiecte de diferite tipuri
- uniuni capabile sa contina una dintr-o serie de obiecte de
diferite tipuri.
In general aceste metode de constructie de obiecte pot fi aplicate recursiv.
A4.4 Calificatori de tipuri

Un tip de obiect poate avea calificatori aditionali. Declarind un obiect const


se anunta ca valoarea sa nu va fi schimbata; declarindu-l volatile se anunta ca el
are proprietati speciale, relevante pentru optimizare. Nici un calificator nu
afecteaza domeniul de valori sau proprietatile aritmetice ale obiectului.
Calificatorii sint discutati in A8.2.

A5. Obiecte si lvalori

Un obiect este o regiunea denumita a memoriei; o lvaloare este o expresie ce


refera un obiect. Un exemplu evident al unei expresii lvaloare este un
identificator cu tip si clasa de memorie corespunzatoare. Exista operatori care
produc lvalori: de exemplu, daca E este o expresie de tip pointer, atunci *E este
o expresie lvaloare care refera obiectul catre care a trimis E. Numele "lvaloare"
provine de la expresia de atribuire E1 = E2 in care operandul din stinga (left) E1
trebuie neaparat sa fie o expresie lvaloare. Discutia despre fiecare operator
specifica daca el asteapta operanzi lvaloare si daca el produce o lvaloare.

A6. Conversii

Unii operatori, in functie de operanzii lor, pot provoca conversia valorii


unui operand dintr-un tip in alt tip. Aceasta sectiune explica rezultatul ce se
poate astepta de la astfel de conversii. A6.5 trece in revista conversiile cerute
de catre majoritatea operatorilor ordinari; el va fi suplimentat, asa cum se cere,
prin discutarea fiecarui operator.

A6.1 Promovare integrala

Intr-o expresie, pretutindeni unde poate fi folosit un intreg, poate fi


utilizat un caracter, un intreg short, un intreg de genul cimp de biti, toate cu
semn sau nu, sau un obiect de tip enumeratie. Daca un int poate reprezenta toate
valorile cu tip original, atunci valoarea este convertita in int; altminteri
valoarea este convertita in unsigned int. Acest proces este denumit promovare
integrala.

A6.2 Conversii integrale

Orice intreg este convertit intr-un tip unsigned rezultat din obtinerea celei
mai mici valori nenegative care este congruenta acestui intreg, modulo cu unu mai
mult decit cea mai mare valoare ce poate fi reprezentata in tipul unsigned. Intr-o
reprezentare, complement al lui doi, acesta este echivalent trunchierii la stinga,
daca amprenta de biti (bit pattern) a tipului unsigned este mai ingusta, si
valorilor unsigned completate cu zero-uri si valorilor signed cu extensie de semn,
daca tipul unsigned este mai larg.
Cind orice intreg este convertit intr-un tip signed, valoarea ramine
nemodificata daca ea poate fi reprezentata in noul tip, altminteri rezultatul
depinde de implementare.
A6.3 Intreg si flotant

Cind o valoare de tip flotant este convertita in tip integral, partea


fractionara este pierduta; daca valoarea rezultata nu poate fi reprezentata in
tipul integral, comportarea este nedefinita. In particular, rezultatul convertirii
valorilor flotante negative in tipuri integrale unsigned nu este specificat.
Cind o valoare de tip integral este convertita in flotant si valoarea se
gaseste in domeniul reprezentabil dar nu reprezentabila exact, atunci rezultatul
poate fi fie valoarea reprezentabila imediat mai mare fie aceea imediat mai mica.
Daca rezultatul este in afara domeniului, comportarea este nedefinita.

A6.4 Tipuri flotante

Cind o valoare flotanta mai putin precisa este convertita intr- un tip flotant
de precizie mai mare sau egala valoarea este nemodificata. Cind o valoare flotanta
mai precisa este convertita intr-un tip flotant mai putin precis si valoarea este
in domeniul reprezentabil, rezultatul poate fi fie valoarea reprezentabila imediat
mai mare fie cea imediat mai mica. Daca rezultatul este in afara domeniului,
comportarea este nedefinita.

A6.5 Conversii aritmetice

Multi operatori provoaca conversii si produc tipuri intr-o maniera similara.


Efectul consta in aducerea operanzilor la un tip comun, care este, de asemenea,
tipul rezultatului. Acest model este denumit "conversie aritmetica usuala".
- Daca un operand este long double, celalalt este convertit
in long double.
- Altminteri, daca un operand este double, celalalt este
convertit in double.
- Alminteri, daca un operand este float, celalalt este
convertit in float.
- Alminteri, promovarile integrale sint realizate asupra
ambiloroperanzi; apoi daca un operand este unsigned long
int, celalalt este convertit in unsigned long int.
- Altminteri, daca un operand este long int si celalalt este
unsigned int, efectul depinde de faptul daca un long int
poate reprezenta toate valorile unui unsigned int; daca
poate, atunci operandul unsigned int este convertit in
long int; daca nu, ambii operanzi sint convertiti in
unsigned long int.
- Alminteri, daca un operand este long int, celalalt este
convertit long int.
- Alminteri, daca un operand este unsigned int, celalalt
este convertit in unsigned int.
- Altminteri, ambii operanzi au tipul int.

Aici exista doua modificari. Mai intii, aritmetica


operanzilor float poate fi facuta in simpla precizie si
nu in dubla; Prima editie specificase ca toata
aritmetica flotanta era in dubla precizie. In al doilea
rind, tipurile unsigned mai scurte, cind sint combinate
cu tipuri signed mai mari, proprietatea unsigned nu se
propaga la tipul rezultat; in prima editie, unsigned-ul
a dominat intodeauna. Noile reguli sint usor mai
complicate, dar reduc, intrucitva, surprizele ce pot
aparea cind este intilnita o marime unsigned. Rezultate
neasteptate pot inca aparea cind o expresie unsigned
este comparata cu o expresie signed de aceeasi marime.

A6.6 Pointere si intregi

O expresie de tip integral poate fi adaugata unui pointer sau scazuta din el;
In astfel de cazuri expresia integrala este convertita asa cum s-a specificat in
discutia operatorului de adunare (A7.7).
Doua pointere catre obiecte de acelasi tip, din acelasi tablou, pot fi scazute
unul din celalalt; rezultatul este convertit intr-un intreg asa cum s-a specificat
in discutia operatorului de scadere (A7.7).
O expresie constanta integrala cu valoare 0 sau o astfel de expresie,
convertita prin cast in tipul void *, poate fi convertita, prin cast, prin
atribuire sau comparatie intr-un pointer de orice tip. Acest lucru produce un
pointer nul, care este egal cu un alt pointer nul de acelas tip, dar diferit de
oricare pointer catre o functie sau catre un obiect.
Anumite alte conversii implicind pointere sint permise, dar au aspecte
dependente de implementare. Ele trebuie neaparat specificate printr-un operator
explicit, de conversie de tip, sau prin cast (A7.5 si A8.8).
Un pointer poate fi convertit intr-un tip integral suficient de mare pentru a-
l contine; marimea ceruta depinde de implementare. Functia de corespondenta
(mapping function) este, de asemenea, dependenta de implementare.
Un obiect de tip integral poate fi convertit, in mod explicit, intr-un
pointer. Corespondenta (the mapping) poarta intotdeauna un intreg suficient de
mare, convertit dintr-un pointer, inapoi catre acelasi pointer, dar, in alte
conditii, este dependent de implementare.
Un pointer catre un tip poate fi convertit intr-un pointer catre alt tip.
Pointerul rezultat poate provoca exceptii de adresare daca pointerul, supus
conversiei, nu trimite catre un obiect corespunzator aliniat in memorie. Se
garanteaza ca un pointer catre un obiect poate fi convertit intr-un pointer catre
alt obiect al carui tip reclama aliniere stricta de memorie mai mica sau egala si
din nou inapoi fara modificare; notiunea de "aliniere" depinde de implementare,
dar obiectele de tipul char au cele mai mici cerinte de aliniere stricta. Dupa cum
s-a descris in A6.8, un pointer poate fi convertit, de asemenea , in tipul void *
si din nou inapoi fara modificari.
In sfirsit, un pointer catre o functie poate fi convertit intr- un pointer
catre un alt tip de functie. Apelul functiei specificate prin pointerul convertit
este dependent de implementare; totusi, daca pointerul convertit este reconvertit
in tipul sau original, rezultatul este identic cu pointerul original.

A6.7 Void

Valoarea (inexistenta) a unui obiect void nu poate fi utilizata in nici un fel


si nici nu poate fi aplicata conversia implicita sau explicita in vreun tip non-
void. Deoarece o expresie void denota o valoare inexistenta, o astfel de expresie
poate fi utilizata numai acolo unde valoarea nu este ceruta, de exemplu ca o
instructiune- expresie (A9.2) sau ca operand sting al unui operator virgula
(A7.18).
O expresie poate fi convertita intr-un tip void cu ajutorul operatorului cast.
De exemplu, un cast void instrumenteaza pierderea valorii unui apel de functie
folosita ca instructiune-expresie.

void nu a aparut in prima editie a acestei carti, dar a


devenit obisnuit dupa aceea.

A6.8 Pointere catre void


Orice pointer poate fi convertit in tipul void * fara pierdere de informatie.
Daca rezultatul este convertit inapoi, catre tipul original de pointer, pointerul
original este recuperat. Spre deosebire de conversiile pointer in pointer
discutate in A6.6, care reclama un cast explicit, pointerele pot fi atribuite
unui tip void * si acesta din urma poate fi atribuit unui pointer; de asemenea,
pot fi comparate intre ele.

Aceasta interpretare a pointerelor void * este noua;


anterior pointerele char* jucau rolul de pointer
generic. Standardul ANSI binecuvinteaza, in mod clar,
intilnirea pointerelor void * cu pointere obiect in
atribuiri si relatii, intimpcesintcerutecast-uri
explicite pentru celelalte intilniri mixte de pointere.

A7. Expresii

Ordinea nivelelor ierarhice (de prioritate) a operatorilor de expresie este


aceeasi ca si ordinea majoritatii subsectiilor din aceasta sectiune, nivelul
ierarhic cel mai inalt se executa primul. Astfel de exemplu, expresiile oferite ca
operanzi al lui + (A7.7) sint acele expresii care sint definite in A7.1 - A7.6. In
interiorul fiecarei subsectii, operatorii au acelasi nivel ierarhic.
Asociativitatea de la stinga sau de la dreapta este specificata in fiecare
subsectie pentru operatorii discutati acolo. Gramatica incorporeaza nivelele
ierarhice si asociativitatile operatorilor de expresie; ea este recapitulata in
A13.
Nivelele ierarhice si asociativitatile operatorilor sint complet specificate
insa ordinea in ce priveste calculul expresiilor este, cu anumite exceptii,
nedefinita, chiar daca subexpresiile implica efecte laterale. Adica, in afara de
cazul in care definitia unui operator garanteaza ca operanzii sai sint calculati
intr-o anumita ordine, implementarea este libera sa calculeze operanzii in orice
ordine sau chiar sa intercaleze calculele lor. Totusi, fiecare operator combina
valorile produse de operanzii sai intr-o maniera compatibila cu descrierea
gramaticala a expresiei in care el apare.

Comitetul ANSI a decis tirziu, in lucrarile sale, sa


restrictioneze libertatea anterioara de reordonare a
expresiilor implicind operatori care, matematic, sint
comutativi si asociativi, dar pot sa nu fie asociativi
din punctul de vedere al calculelor. In practica,
modificarea afecteaza numai calculele in punct zecimal
mobil in apropierea limitelor preciziei lor si
situatiile unde depasirea este posibila.

Tratarea depasirilor, verificarea impartirii si alte exceptii in calculul


expresiilor nu sint definite de catre limbaj. Cele mai multe implementari
existente ale lui C ignora depasirea in calcul pentru expresiile integrale si in
cazul atribuirilor, dar aceasta comportare nu este garantata. Tratamentul
impartirii cu zero si toate exceptiile apartinind punctului zecimal mobil difera
de la o implementare la alta; uneori acest lucru este reglabil printr-o functie
nestandard de biblioteca.

A7.1 Generarea pointerului

Daca tipul unei expresii sau subexpresii este "tablou de T", pentru un tip
oarecare T, atunci valoarea expresiei este un pointer catre primul obiect din
tablou si tipul expresiei este modificat in "pointer catre T". Aceasta conversie
nu are loc daca expresia este operandul operatorilor unari &, ++, --, sizeof sau
ca operand sting al unui operator de atribuire sau operator punct (.). In mod
similar, o expresie de tipul "functie ce returneaza pe T", cu exceptia cazului
cind este utilizata ca operand al operatorului &, este convertita in "pointer
catre o functie ce returneaza pe T". O expresie, care a suferit una din aceste
conversii, nu este o lvaloare.

A7.2 Expresii primare

Expresii primare sint identificatorii, constantele, sirurile sau expresiile


incadrate de paranteze.

primary-expression:
identifier
constant
string
( expression )

Un identificator este o expresie primara, cu conditia ca el sa fie


corespunzator declarat, asa cum se va discuta in continuare. Tipul sau este
specificat prin declaratia sa. Un identificator este o lvaloare daca el refera un
obiect (A5) si daca tipul sau este aritmetic, structura, uniune sau pointer.
O constanta este o expresie primara. Tipul ei depinde de forma sa asa cum s-a
discutat in A2.5.
Un literal sir este o expresie primara. Tipul sau original este "tablou de
char-uri" (pentru sirurile de caractere largi, "tablou de wchar_t-uri"), dar
urmind regula data in A7.1, acesta este, de obicei, modificat in "pointer catre
char" (wchar_t) si rezultatul este un pointer catre primul caracter din sir.
Conversia, de asemenea, nu apare in anumiti initializatori; vezi A8.7.
O expresie intre paranteze este o expresie primara al carei tip si valoare
sint identici cu aceia ai expresiei neornate. Prezenta parantezelor nu afecteaza
expresia daca ea este o lvaloare.

A7.3 Expresii postfixe

Operatorii in expresiile postfixe se grupeaza de la stinga la dreapta.

postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression ( argument-expression-list(opt) )
postfix-expression . identifier
postfix-expression -> identifier
postfix-expression ++
postfix-expression --

argument-expression-list:
assignment-expression
argument-expression-list , assignment-expression

A7.3.1 Referinte la tablouri

O expresie postfixa, urmata de o expresie, incadrata de paranteze drepte, este


o expresie postfixa care denota o referinta la un tablou indexat. Una dintre cele
doua expresii trebuie neaparat sa aiba tipul "pointer catre T", unde T este un tip
oarecare iar cealalta trebuie neaparat sa aiba un tip integral; tipul expresiei
indice este T. Expresia E1[E2] este identica (prin definitie) cu *((E1) + (E2)).
Vezi A8.6.2 pentru discutii mai profunde.
A7.3.2 Apeluri de functii

Un apel de functie este o expresie postfixa, denumita desemnator de functie


(function designator) urmata de paranteze continind o lista, posibil vida, de
expresii de atribuire separate prin virgule (A7.17), care constituie argumentele
functiei. Daca expresia postfixa consta dintr-un identificator pentru care nu
exista nici o declaratie in domeniul curent, identificatorul este declarat
implicit, ca si cind declaratia:

extern int identifier();

ar fi fost facuta in blocul cel mai interior ce contine apelul de functie.


Expresia postfixa (dupa posibila declaratie implicita si generare de pointer,
A7.1) trebuie neaparat sa fie de tipul "pointer catre functie care returneaza T ",
pentru un tip oarecare T iar valoarea apelului functiei are tipul T.

In prima editie, tipul era restrictionat la "functie" si


eracerut un operator * explicit pentru a apela prin
pointere catre functii. Standardul ANSI binecuvinteaza
practica unor compilatoare existente, permitind aceeasi
sintaxa atit pentru apelurile de functii cit si pentru
functiile specificate prin pointere. Sintaxa mai veche
este inca in functiune.

Termenul argument este folosit pentru o expresie pasata printr- un apel de


functie; termenul parametru este folosit pentru un obiect de intrare (sau
identificatorul sau) primit cu ocazia definirii unei functii, sau descris intr-o
declaratie de functie. Uneori sint utilizati pentru aceleasi distinctii termenii
"argument (parametru) curent" si "argument (parametru) formal".
In pregatirea apelului unei functii, se realizeaza o copie a fiecarui
argument; toate pasarile de argumente se realizeaza strict prin valoare. O functie
poate modifica valorile obiectelor sale parametru, care sint copii ale expresiilor
argument, dar aceste modificari nu pot afecta valorile argumentelor. Cu toate
acestea, este posibil sa se paseze un pointer cu intelesul ca functia poate
modifica valoarea obiectului catre care trimite pointerul.
Exista doua stiluri prin care functiile pot fi declarate. In stilul nou,
tipurile parametrilor sint explicite si fac parte din tipul functiei; astfel o
declaratie este, de asemenea, denumita prototip de functie. In stilul vechi,
tipurile parametrilor nu erau specificate. Declaratia functiei este discutata in
A8.6.3 si A10.1. Daca declararea functiei din domeniu, pentru un apel, este in
stil vechi, atunci promovarea implicita a argumentului este aplicata fiecarui
argument dupa cum urmeaza: promovarea integrala (A6.1) este realizata pentru
fiecare argument de tip integral si fiecare argument float este convertit in
double. Efectul apelului este nedefinit daca numarul argumentelor nu concorda cu
numarul parametrilor din definitia functiei sau daca tipul unui argument, dupa
promovare, nu concorda cu tipul parametrului corespunzator. Concordanta de tip
depinde de modul de definire a functiei, in stilul nou sau in stilul vechi. Daca
este in stilul vechi, atunci compararea se face intre tipul promovat al
argumentului din apel si tipul promovat al parametrului; daca definitia este in
stilul nou, tipul promovat al argumentului trebuie neaparat sa fie identic cu
acela al parametrului insusi, fara promovare.
Daca declaratia functiei din domeniu, pentru un apel, este in stil nou, atunci
argumentele sint convertite, ca si la atribuire, in tipurile parametrilor
corespunzatori ai prototipului de functie. Numarul argumentelor trebuie neaparat
sa fie egal cu numarul parametrilor explicit descrisi, in afara de cazul ca lista
parametrilor din declaratie se incheie cu notatia elipsis (, ...). In acest caz,
numarul de argumente trebuie neaparat sa fie egal cu sau mai mare decit numarul
parametrilor; argumentele ce continua dincolo de parametrii cu tip explicit,
sufera promovarea implicita de argument asa cum s-a descris in paragraful
precedent. Daca definitia functiei este in stilul vechi, atunci tipul fiecarui
parametru, din prototipul vizibil la apel, trebuie neaparat sa concorde cu
parametrul corespunzator din definitie, dupa ce tipul parametrului din definitie a
impus promovarea argumentului.
Aceste reguli sint complicate, in special deoarece ele
trebuie neaparat sa serveasca un amestec de functii cu
stiluri vechi si noi. Pe cit este posibil trebuie
evitate astfel de amestecuri.

Ordinea de calcul a argumentelor nu este specificata; a se nota ca diferitele


compilatoare difera. Cu toate acestea, argumentele si desemnatorul de functie sint
complet calculate, inclusiv toate efectele laterale, inainte ca functia sa inceapa
executia. Apelurile recursive sint permise catre orice functie.

A7.3.3 Referinte la structuri

O expresie postfixa, urmata de un punct, urmat de un identificator este o


expresie postfixa. Prima expresie operand trebuie neaparat sa fie o structura sau
uniune, iar identificatorul trebuie neaparat sa numeasca un membru al structurii
sau uniunii. Valoarea este membrul numit al structurii sau uniunii si tipul ei
este tipul membrului. Expresia este o lvaloare daca prima expresie este o lvaloare
si daca tipul celei de a doua expresii nu este un tip tablou.
O expresie postfixa urmata de o sageata (construita din - si >) urmata de un
identificator este o expresie postfixa. Prima expresie operand trebuie neaparat sa
fie un pointer catre o structura sau uniune iar identificatorul trebuie neaparat
sa numeasca un membru al unei structuri sau uniuni. Rezultatul refera membrul
numit al structurii sau uniunii catre care trimite expresia pointer iar tipul este
tipul membrului; rezultatul este o lvaloare daca tipul nu este un tip tablou.
Astfel expresia E1->MOS este identica cu (*E1).MOS. Structurile si uniunile
sint discutate in A8.3.

In prima editie a acestei carti, era deja regula ca un


nume de membru,intr-o astfel de expresie, trebuie sa
apartinastructurii sau uniunii mentionate in expresia
postfixa; cutoate acestea, o nota admitea ca aceasta
regula nu era fortata in mod ferm. Compilatoarele
recente si ANSI o forteaza ferm.

A7.3.4 Incrementarea postfixa

O expresie postfixa urmata de un operator ++ sau -- este o expresie postfixa.


Valoarea expresiei este valoarea operandului. Dupa ce valoarea este notata,
operandul este incrementat (++) sau decrementat (--) cu o unitate. Operandul
trebuie neaparat sa fie o lvaloare; vezi discutia asupra operatorilor aditivi
(A7.7) si de atribuire (A7.17) pentru restrictii suplimentare asupra operandului
si detalii asupra operatiei. Rezultatul nu este o lvaloare.

A7.4 Operatori unari

Expresiile cu operatori unari se grupeaza de la dreapta la stinga.

unary-expression:
postfix-expression
++ unary-expression
-- unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof ( type-name )

unary-operator: one of
& * + - ~ !
A7.4.1 Operatori preficsi de incrementare

O expresie unara precedata de un operator ++ sau -- este o expresie unara.


Operandul este incrementat (++) sau decrementat (--) cu o unitate. Valoarea
expresiei este valoarea dupa incrementare (decrementare). Operandul trebuie
neaparat sa fie o lvaloare; vezi discutia asupra operatorilor aditivi (A7.7) si
de atribuire (A7.17) pentru restrictii suplimentare asupra operandului si detalii
asupra operatiei. Rezultatul nu este o lvaloare.

A7.4.2 Operator de adresa

Operatorul unar & ia adresa operandului sau. Operandul trebuie neaparat sa fie
o lvaloare care sa nu refere nici un cimp de biti si nici un obiect declarat ca
register sau trebuie neaparat sa fie de tip functie. Rezultatul este un pointer
catre obiectul sau functia la care s-a referit prin lvaloare. Daca tipul
operandului este T, tipul rezultatului este "pointer catre T".

A7.4.3 Operator de indirectare

Operatorul unar * denota indirectarea si returneaza obiectul sau functia catre


care trimite operandul sau. Rezultatul este o lvaloare daca operandul este un
pointer catre un obiect de tip aritmetic, structura, uniune sau pointer. Daca
tipul expresiei este "pointer catre T", tipul rezultatului este T.

A7.4.4 Operator unar plus

Operandul operatorului unar + trebuie neaparat sa aiba un tip aritmetic sau


pointer iar rezultatul este valoarea operandului. Un operand integral impune
promovarea integrala. Tipul rezultatului este tipul operandului promovat.

Operatorul unar + este nou in standardul ANSI. El a fost


adaugat pentru simetrie cu operatorul unar -.

A7.4.5 Operator unar minus

Operandul operatorului unar - trebuie neaparat sa aiba tipul aritmetic iar


rezultatul este negativul operandului sau. Un operand integral impune promovarea
integrala. Negativul unui unsigned este calculat prin scaderea valorii promovate
din cea mai mare valoare a tipului promovat adaugind apoi o unitate; dar negativul
lui zero este zero. Tipul rezultatului este tipul operandului promovat.

A7.4.6 Operator complement al lui unu

Operandul operatorului ~ trebuie neaparat sa aiba tipul integral iar


rezultatul este complement al lui unu al operandului sau. Promovarile integrale
sint realizate. Daca operandul este unsigned, rezultatul este calculat prin
scaderea valorii din cea mai mare valoare a tipului promovat. Daca operandul este
signed, rezultatul este calculat prin convertirea operandului promovat, in tipul
unsigned corespunzator, aplicind ~ si convertind inapoi la tipul signed. Tipul
rezultatului este tipul operandului promovat.

A7.4.7 Operator de negatie logica

Operandul operatorului ! trebuie neaparat sa aiba tipul aritmetic sau sa fie


un pointer iar rezultatul este 1 daca valoarea operandului sau este egala cu zero,
altminteri rezultatul este zero. Tipul rezultatului este int.

A7.4.8 Operator sizeof

Operatorul sizeof produce numarul octetilor necesari pentru memorarea unui


obiect de tipul operandului sau. Operandul este fie o expresie, care nu este
calculata, fie un nume de tip intre paranteze. Cind sizeof este aplicat unui char,
rezultatul este 1; cind este aplicat unui tablou, rezultatul este egal cu numarul
total de octeti din tablou. Cind este aplicat unei structuri sau uniuni,
rezultatul este egal cu numarul total de octeti din obiect, inclusiv orice
completari de octeti pentru a face din mozaicul obiectului, un tablou; marimea
unui tablou de n elemente este de n ori marimea unui element. Operatorul nu
poate fi aplicat unui operand de tip functie, sau de tip incomplet, sau unui cimp
de biti. Rezultatul este o constanta integrala fara semn; tipul anume este definit
la implementare. Header-ul standard <stddef.h> (vezi Apendicele B) defineste acest
tip ca fiind size_t.

A7.5 Operatori cast

O expresie unara precedata de un nume de tip, incadrat de paranteze, provoaca


conversia valorii expresiei in tipul numit.

cast-expression:
unary-expression
( type-name ) cast-expression

Aceasta constructie este denumita un cast. Numele de tip sint descrise in


A8.8. Efectele conversiilor sint descrise in A6. O expresie cu un cast nu este o
lvaloare.

A7.6 Operatori multiplicativi

Operatorii multiplicativi *, /, % se grupeaza de la stinga la dreapta.

multiplicative-expression:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression

Operanzii lui * si / trebuie neaparat sa aiba tipul aritmetic; operanzii lui %


trebuie neaparat sa aiba tipul integral. Conversiile aritmetice obisnuite sint
realizate asupra operanzilor si prezic tipul rezultatului.
Operatorul binar * denota multiplicarea.
Operatorul binar / produce citul iar operatorul % produce restul impartirii
primului operand la cel de al doilea; daca cel de al doilea operand este zero,
rezultatul este nedefinit. Altminteri este intotdeauna adevarat ca (a/b)*b+a%b
este egal cu a. Daca ambii operanzi sint nenegativi, atunci restul este nenegativ
si mai mic decit valoarea absoluta a divizorului.

A7.7 Operatori aditivi

Operatorii aditivi + si - se grupeaza de la stinga la dreapta. Daca operanzii


au tipul aritmetic, atunci se realizeaza conversiile aritmetice obisnuite. Exista
citeva posibilitati de tipuri suplimentare pentru fiecare operator.

additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression

Rezultatul operatorului + este suma operanzilor. Un pointer catre un obiect


este un tablou si i se poate adauga o valoare de orice tip integral. Cea din urma
este convertita intr-o deplasare de adresa prin inmultirea ei cu marimea
obiectului catre care trimite pointerul. Suma este un pointer de acelasi tip ca si
pointerul original si trimite catre un alt obiect din acelasi tablou, deplasat
adecvat fata de obiectul original. Astfel daca p este un pointer catre un obiect
dintr-un tablou, expresia p+1 este un pointer catre urmatorul obiect din tablou.
Daca pointerul suma trimite in afara limitelor tabloului, cu exceptia primei
locatii de dincolo de limita superioara, rezultatul este nedefinit.

Prevederea pointerelor care pot sa trimita imediat


dincolo de sfirsitul unui tablou este noua. Ea
legitimizeaza un idiom obisnuit pentru ciclarea
elementelor unui tablou.

Rezultatul operatorului - este diferenta operanzilor. O valoare de orice tip


integral poate fi scazuta dintr-un pointer si atunci se aplica aceleasi conversii
si conditii ca in cazul adunarii.
Daca doua pointere catre obiecte de acelasi tip se scad, rezultatul este o
valoare integrala cu semn reprezentind departarea (deplasarea) dintre cele doua
obiecte catre care s-a trimis; pointerele catre obiecte succesive difera prin o
unitate. Tipul rezultatului depinde de implementare, dar este definit sub numele
de ptrdiff_t in header-ul standard <stddef.h>. Valoarea este nedefinita in afara
de cazul in care pointerele trimit catre obiecte din interiorul aceluiasi tablou;
totusi, daca P trimite catre ultimul membru al unui tablou, atunci (P+1)-P are
valoarea 1.

A7.8 Operatori de deplasare

Operatorii de deplasare << si >> se grupeaza de la stinga la dreapta. Pentru


ambii operatori, fiecare operand trebuie neaparat sa fie integral si este supus
promovarii integrale. Tipul rezultatului este acela al operandului din stinga
promovat. Rezultatul este nedefinit daca operandul din dreapta este negativ sau
mai mare decit sau egal cu numarul de biti ai tipului expresiei din stinga.

shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression

Valoarea E1<<E2 este E1 (interpretat ca o configuratie de biti) deplasat spre


stinga cu E2 biti; in absenta depasirii, aceasta deplasare este echivalenta cu
inmultirea cu 2 la puterea E2. Valoarea E1>>E2 este E1 deplasat spre dreapta cu E2
pozitii de bit. Deplasarea spre dreapta este echivalenta cu impartirea prin 2 la
puterea E2 daca E1 este unsigned sau daca el are o valoare nenegativa; altminteri
rezultatul este definit la implementare.

A7.9 Operatori relationali

Operatorii relationali se grupeaza de la stinga la dreapta, dar acest fapt nu


este util; a<b<c este interpretat gramatical ca fiind (a<b)<c iar a<b, dupa
calcule, are valoarea 0 sau 1.

relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression

Operatorii < (mai mic decit), > (mai mare decit), <= (mai mic decit sau egal cu),
>= (mai mare decit sau egal cu) toti produc zero daca relatia specificata este
falsa sau 1 daca ea este adevarata. Tipul rezultatului este int. Conversiile
aritmetice uzuale sint efectuate asupra operanzilor aritmetici. Pointerele catre
obiecte de acelasi tip pot fi comparate; rezultatul depinde de locatiile relative
in spatiul de adrese al obiectelor catre care se trimite. Comparatia de pointere
este definita numai pentru parti ale aceluiasi obiect: daca doua pointere trimit
catre acelasi obiect simplu ele apar ca fiind egale; daca pointerele trimit catre
membri ai aceleiasi structuri, atunci pointerele catre obiecte declarate mai
tirziu, in structura, apar ca fiind mai mari; daca pointerele trimit catre membri
ai aceleasi uniuni, ele apar ca fiind egale; daca pointerele trimit catre membri
ai unui tablou, comparatia este echivalenta cu comparatia indicilor
corespunzatori. Daca p trimite catre ultimul membru al unui tablou, atunci p+1
apare ca fiind mai mare decit p, chiar daca p+1 trimite in afara tabloului.
Altminteri comparatia pointerelor este nedefinita.

Aceste reguli liberalizeaza usor restrictiile enuntate


in prima editie, prin permiterea comparatiei pointerelor
care trimit la diferiti membri ai unei structuri sau
uniuni. Ele legalizeaza, de asemenea, comparatia cu un
pointer ce trimite in afara imediata a limitelor unui
tablou.

A7.10 Operatori de egalitate

equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

Operatorii == (egal cu) si != (diferit de) sint analogi operatorilor relationali


cu exceptia nivelului lor ierarhic mai coborit. (Astfel a<b==c<d este 1 daca a<b
si c<d au aceeasi valoare de adevar).
Operatorii de egalitate urmeaza aceleasi reguli ca si operatorii relationali,
dar permit posibilitati suplimentare: un pointer poate fi comparat cu o expresie
integrala constanta cu valoarea zero sau cu un pointer catre void. Vezi A6.6.

A7.11 Operator AND la nivel de biti


AND-expression:
equality-expression
AND-expression & equality-expression

Sint realizate conversiile aritmetice uzuale; rezultatul este functia AND la nivel
de biti asupra operanzilor. Operatorul se aplica numai operanzilor integrali.

A7.12 Operator OR-exclusiv la nivel de biti

exclusive-OR-expression:
AND-expression
exclusive-OR-expression ^ AND-expression

Sint realizate conversiile aritmetice uzuale; rezultatul este functia OR-exclusiv


la nivel de biti, asupra operanzilor. Operatorul se aplica numai operanzilor
integrali.

A7.13 Operator OR-inclusiv la nivel de biti

inclusive-OR-expression:
exclusive-OR-expression
inclusive-OR-expression | exclusive-OR-expression

Sint realizate conversiile aritmetice uzuale; rezultatul este functia OR-inclusiv


la nivel de biti asupra operanzilor sai. Operatorul se aplica numai operanzilor
integrali.

A7.14 Operator AND logic

logical-AND-expression:
inclusive-OR-expression
logical-AND-expression && inclusive-OR-expression

Operatorii && se grupeaza de la stinga la dreapta. Ei returneaza 1, daca ambii


operanzi apar ca fiind diferiti de zero si zero in celelalte cazuri. Spre
deosebire de &, && garanteaza calculul de la stinga la dreapta: primul operand
este calculat, incluzind toate efectele laterale; daca el este egal cu zero,
valoarea expresiei este zero. Altminteri, este calculat operandul din dreapta si
daca el este egal cu zero, valoarea expresiei este zero; altminteri ea este egala
cu 1.
Operanzii pot sa nu fie de acelasi tip, dar fiecare trebuie neaparat sa aiba
un tip aritmetic sau sa fie un pointer. Rezultatul este un int.

A7.15 Operator OR logic

logical-OR-expression:
logical-AND-expression
logical-OR-expression || logical-AND-expression

Operatorii || se grupeaza de la stinga la dreapta. Ei returneaza 1 daca oricare


dintre operanzi apare ca fiind diferit de zero; altminteri returneaza zero. Spre
deosebire de |, || garanteaza calculul de la stinga la dreapta: este calculat
primul operand, incluzind toate efectele laterale; daca el este diferit de zero,
valoarea expresiei este 1. Altminteri, este calculat operandul din dreapta si daca
el este diferit de zero, valoarea expresiei este 1, altminteri este zero.
Operanzii pot sa nu fie de acelasi tip, dar fiecare trebuie neaparat sa aiba
tipul aritmetic sau sa fie pointer. Rezultatul este int.

A7.16 Operator conditional

conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-
expression

Este calculat primul operand, incluzind toate efectele laterale; daca el apare ca
fiind diferit de zero, rezultatul este valoarea celei de a doua expresii,
altminteri, valoarea celei de a treia expresii. Numai unul dintre operanzii doi si
trei este calculat. Daca operanzii doi si trei sint aritmetici, se realizeaza
conversii aritmetice obisnuite pentru a le aduce la un tip comun, iar acesta este
si tipul rezultatului. Daca ambii sint void sau structuri sau uniuni de acelasi
tip sau pointere catre obiecte de acelasi tip, rezultatul are tipul comun. Daca
unul este un pointer si altul o constanta zero, atunci zero-ul este convertit in
tip pointer si rezultatul are acest tip. Daca unul este un pointer catre void si
celalalt este un alt pointer, celalat pointer este convertit intr-un pointer catre
void si acesta este si tipul rezultatului.
In comparatia tipurilor pentru pointere, orice calificatori de tip (A8.2),
asupra tipului catre care trimite pointerul, sint fara semnificatie, dar tipul
rezultatului mosteneste calificatorii de la ambele ramuri ale conditionalului.

A7.17 Expresii de atribuire

Exista o serie de operatori de atribuire; toti se grupeaza de la stinga la


dreapta.

assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-
expression

assignment-operator: one of
= *= /= %= += -= <<= >>= &= ^= |=

Toti reclama o lvaloare ca operand sting si lvaloarea trebuie neaparat sa fie


modificabila: ea trebuie neaparat sa nu fie un tablou si trebuie neaparat sa nu
aiba un tip incomplet sau sa fie o functie. De asemenea, tipul sau trebuie
neaparat sa nu fie calificat cu const; daca el este o structura sau uniune, el
trebuie neaparat sa nu aiba vreun membru, sau, recursiv, submembru calificat cu
const. Tipul unei expresii de atribuire este acela al operandului sting iar
valoarea este valoarea memorata in operandul din stinga dupa ce atribuirea a avut
loc.
Intr-o simpla atribuire cu =, valoarea expresiei o inlocuieste pe aceea a
obiectului referit prin lvaloare. Una dintre urmatoarele situatii trebuie neaparat
sa fie adevarata; ambii operanzi au tip aritmetic, in care caz operandul din
dreapta este convertit la tipul celui din stinga prin atribuire; sau ambii
operanzi sint structuri sau uniuni de acelasi tip; sau unul dintre operanzi este
un pointer iar celalat este un pointer catre void; sau operandul din stinga este
un pointer iar cel din dreapta este o expresie constanta cu valoarea zero; sau
ambii operanzi sint pointere catre functii sau catre obiecte de acelasi tip cu
exceptia posibilei absente a lui const sau volatile la operandul din dreapta.
O expresie de forma E1 op= E2 este echivalenta cu E1=E1 op (E2) cu exceptia
ca E1 este calculat o singura data.
In conformitate cu restrictiile de mai sus, este ilegal
de a atribui pointere cind partea din dreapta trimite
catre un obiect de un tip oarecare iar partea din stinga
trimite catre un obiect cu o versiune calificata const a
acestui tip. O citire stricta a acestei reguli si a
uneia similare pentru cast-uri conduce la dificultati in
implementarea anumitor functii de biblioteca; exista o
forma buna (good case) pentru a relaxa acest lucru.

A7.18 Operator virgula

expression:
assignment-expression
expression , assignment-expression

O pereche de expresii separate printr-o virgula este calculata de la stinga la


dreapta iar valoarea expresiei din stinga este pierduta. Tipul si valoarea
rezultatului sint tipul si valoarea operandului din dreapta. Toate efectele
laterale rezultate din calculul operandului din stinga sint incheiate inaintea
inceperii calculului operandului din dreapta. In contexte in care virgula capata o
semnificatie speciala, de exemplu in listele de argumente de functii (A7.3.2) si
listele de initializatori, unitatea sintactica ceruta este o expresie de
atribuire, astfel ca operatorul virgula apare numai intr-o grupare incadrata de
paranteze; de exemplu:

f(a, (t=3, t+2), c)

are trei argumente, dintre care, cel de al doilea, are valoarea 5.

A7.19 Expresii constante

Sintactic, o expresie constanta este o expresie restrictionata la o submultime


a operatorilor:

constant-expression:
conditional-expression

Expresii, al caror calcul conduce la o constanta, sint cerute intr-o serie de


contexte: dupa case, ca limite de tablou si lungime de cimpuri de biti, ca
valoare a unei constante de enumeratie, in initializatori si in anumite expresii
de preprocesor.
Expresiile constante nu pot sa contina atribuiri, operatori de incrementare
sau decrementare, apeluri de functii sau operatori virgula cu exceptia unui
operand al lui sizeof. Daca o expresie constanta trebuie sa fie integrala,
operanzii sai trebuie neaparat sa fie de tip intreg, enumeratie, caracter si
constante cu punct zecimal mobil; cast-urile trebuie neaparat sa specifice un tip
integral si oricare constanta cu punct zecimal mobil trebuie neaparat convertita,
prin cast, intr-un intreg. Acest lucru exclude, in mod necesar, tablourile,
indirectarea, deplasarea de adresa (address-of) si operatiile cu membri de
structura. (Cu toate acestea, este permis orice operand pentru sizeof.)
O mai mare latitudine este permisa pentru expresiile constante ale
initializatorilor; operanzii pot fi orice tip de constanta iar operatorul unar &
poate fi aplicat obiectelor externe sau statice si tablourilor externe sau statice
indexate cu o expresie constanta.
Operatorul unar &, de asemenea, poate fi aplicat implicit, prin aparitia
tablourilor neindexate si a functiilor. Initializatorii trebuie neaparat ca, prin
calcul, sa ajunga fie la o constanta, fie la adresa unui obiect, anterior declarat
extern sau static, plus sau minus o constanta.
O mai mica latitudine este permisa pentru expresiile constante de dupa #if; nu
sint permise expresii sizeof, constante de enumeratie si cast-uri. Vezi A12.5.
A8. Declaratii

Declaratiile specifica interpretarea data fiecarui identificator; ele nu


rezerva, neaparat, memoria asociata cu identificatorul. Declaratiile care rezerva
memorie sint denumite definitii. Declaratiile au forma:

declaration:
declaration-specifiers init-declarator-list(opt) ;

Declaratorii din init-declarator-list contin identificatorii ce sint declarati;


declaration-specifiers consta dintr-o secventa de specificatori de tip si de
clasa de memorie.

declaration-specifiers:
storage-class-specifier declaration-specifiers(opt)
type-specifier declaration-specifiers(opt)
type-qualifier declaration-specifiers(opt)

init-declarator-list:
init-declarator
init-declarator-list , init-declarator

init-declarator:
declarator
declarator = initializer

Declaratorii vor fi discutati mai tirziu (A8.5); ei contin nume ce sint


declarate. O declaratie trebuie neaparat sa aiba cel putin un declarator, sau
specificatorul sau de tip trebuie neaparat sa declare un tag de structura, un tag
de uniune sau membri ai unei enumeratii; declaratiile vide nu sint permise.

A8.1 Specificatori de clasa de memorie

Specificatorii de clasa de memorie sint:

storage-class-specifier:
auto
register
static
extern
typedef

Semnificatiile claselor de memorie au fost discutate in A.4.


Specificatorii auto si register dau obiectelor declarate clasa de memorie
automata si pot fi utilizati numai in interiorul functiilor. Astfel de declaratii
servesc, de asemenea, ca definitii si provoaca rezervare de memorie. O declaratie
register este echivalenta cu o declaratie auto, dar sugereaza ca obiectele
declarate vor fi folosite frecvent. Numai putine obiecte sint plasate, de fapt, in
registre si numai putine tipuri sint eligibile; restrictiile sint dependente de
implementare. Totusi, daca un obiect este declarat register, operatorul unar & nu
i se poate aplica, explicit sau implicit.

Regula este ca este ilegal sa calculezi adresa unui


obiect declarat register,dar, de fapt, considerarea lui
de a fi auto, este noua.

Specificatorul static da obiectelor declarate clasa de memorie static si


poate fi utilizat fie in interiorul, fie in exteriorul functiilor. In interiorul
unei functii, acest specificator provoaca alocarea de memorie si serveste ca o
definitie; pentru efectele in afara unei functii, vezi A11.2.
O declaratie cu extern, folosita in interiorul unei functii, specifica ca
memoria pentru obiectele declarate este definita undeva; pentru efectele sale in
afara unei functii, vezi A11.2.
Specificatorul typedef nu rezerva memorie si este denumit specificator de
memorie numai pentru comoditate sintactica; el este discutat in A8.9.
Cel mult un specificator de clasa de memorie poate fi dat intr-o declaratie.
Daca nu este dat nici unul, atunci sint utilizate urmatoarele reguli: obiectele
declarate in interiorul unei functii sint considerate a fi auto; functiile
declarate in interiorul unei functii sint considerate a fi extern; obiectele si
functiile declarate in afara unei functii sint considerate a fi statice, cu
legatura externa. Vezi A10-A11.

A8.2 Specificatori de tip

Specificatorii de tip sint:

type-specifier:
void
char
short
int
long
float
double
signed
unsigned
struct-or-union-specifier
enum-specifier
typedef-name

Cel mult unul dintre cuvintele long sau short poate fi specificat impreuna cu int;
specificatia este aceeasi daca int nu este mentionat. Cuvintul long poate fi
specificat impreuna cu double. Cel mult unul dintre cuvintele signed sau unsigned
poate fi specificat impreuna cu int sau cu oricare dintre varietatile sale short,
long sau char. Oricare poate aparea singur, in care caz se subintelege si int.
Specificatorul signed este util pentru fortarea obiectelor char in a purta un
semn; este permisa dar redundanda folosirea lui cu alte tipuri integrale.
In celelalte cazuri, cel mult un specificator de tip poate fi mentionat intr-o
declaratie. Daca specificatorul de tip este absent intr-o declaratie, el este
considerat implicit int.
Tipurile pot fi, de asemenea, calificate, pentru a indica proprietati speciale
ale obiectelor declarate.

type-qualifier:
const
volatile

Calificatorii pot aparea cu oricare specificator de tip. Un obiect const poate


fi initializat, dar dupa aceea nu se poate atribui o alta valoare. Nu exista
semantici independente de implementare pentru obiectele volatile.

Proprietatile const si volatile sint noi, o data cu


standardul ANSI. Scopul lui const este de a anunta
obiectelecare pot fi plasate in memoria care poate fi
numai citita (read-onlymemory) si, poate, pentru a
creste ocaziile de optimizare.Scopul lui volatile este
de a forta o implementare sa suprime optimizarea care,
altminteri, ar putea sa apara. De exemplu, pentru o
masina cu intrare/iesire ce foloseste memorie-mapata, un
pointer catre un registru de dispozitiv ar putea fi
declarat ca un pointer catre volatile, in scopul de a
preveni compilatorul sa nu inlature referintele aparent
redundante prin pointer. Cu exceptia cazurilor cind
tentativa de modificare a obiectelor const se semnaleaza
explicit, un compilator ar putea sa ignore acesti
calificatori.Un al treilea calificator, noalias, ramine
in studiul comitetului de standardizare.

A8.3 Declaratii de structura si uniune

O structura este un obiect constind dintr-o secventa de membri numiti, de


diferite tipuri. O uniune este un obiect ce contine, in momente diferite, pe
oricare din membrii diferitelor tipuri, dar numai unul. Specificatorii de
structura si uniune au aceeasi forma.

struct-or-union-specifier:
struct-or-union identifier(opt){struct-declaration-
list }
struct-or-union identifier

struct-or-union:
struct
union

Un struct-declaration-list este o secventa de declaratii pentru membrii structurii


sau uniunii:

struct-declaration-list:
struct-declaration
struct-declaration-list struct declaration

struct-declaration:
specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list:
type-specifier specifier-qualifier-list(opt)
type-qualifier specifier-qualifier-list(opt)

struct-declarator-list:
struct-declarator
struct-declarator-list , struct-declarator

De obicei un struct-declarator este chiar un declarator pentru un membru al unei


structuri sau unei uniuni. Un membru de structura poate, de asemenea, sa conste
dintr-un numar specificat de biti. Un astfel de membru este, de asemenea, denumit
cimp de biti sau, mai simplu, cimp; lungimea sa este pusa in evidenta in
declaratorul numelui de cimp printr-un caracter : (doua puncte).

struct-declarator:
declarator
declarator(opt) : constant-expression

Un specificator de forma:

struct-or-union identifier { struct-declaration-list }


declara identificatorul a fi un tag al structurii sau uniunii specificate prin
lista. O declaratie ulterioara in acelasi domeniu sau intr-un domeniu interior
poate referi acelasi tip prin utilizarea tag-ului intr-un specificator fara lista:

struct-or-union identifier

Daca un specificator cu un tag, dar fara o lista, apare cind tag-ul este
nedeclarat, se considera ca s-a specificat un tip incomplet. Obiectele cu un tip
incomplet de structura sau uniune pot fi mentionate in contexte in care marimea
lor nu este necesara, de exemplu in declaratii (nu definitii), pentru specificarea
unui pointer sau pentru crearea unui typedef, dar nu in alte situatii. Tipul
devine complet la aparitia ulterioara a unui specificator cu acest tag care
contine o lista de declaratii. Chiar in specificatoarele cu o lista , tipul de
structura sau uniune ce se declara este incomplet in interiorul listei si devine
complet numai la aparitia terminatorului } al specificatorului.
O structura nu poate sa contina un membru de tip incomplet. De aceea este
imposibil sa se declare o structura sau uniune care sa contina un membru de insusi
tipul ei. Cu toate acestea, tag-urile, pe linga faptul ca dau un nume tipului de
structura sau uniune, permit definirea structurilor autoreferentiale; o structura
sau uniune poate contine un pointer catre un tip identic cu al sau, deoarece pot
fi declarate pointere catre tipuri incomplete.
O regula foarte speciala se aplica declaratiilor de forma:

struct-or-union identifier ;

care declara o structura sau uniune fara vreo lista de declaratii si fara
declaratori. Chiar daca identificatorul este un tag de structura sau uniune deja
declarat intr-un domeniu exterior domeniului in discutie ( A11.1), declaratia
aceasta face din identificator un tag al unei noi structuri sau uniuni, incomplete
ca tip, din domeniul curent.

Aceastaregula nerationalaeste noua inANSI-C. Ea este


destinata lucrului cu structuri mutual recursive
declarate intr-un domeniu interior, dar al caror tag-uri
ar putea fi deja declarate in domeniul exterior lui.

Un specificator de structura sau uniune cu o lista dar fara tag, creaza un tip
unic; el poate fi referit, in mod direct, numai in declaratia in care el
reprezinta o parte.
Numele membrilor si tag-urilor nu intra in conflict, unu cu celalalt, sau cu
variabile ordinare. Un nume de membru nu poate aparea de doua ori in aceeasi
structura sau uniune, dar acelasi nume de membru poate fi utilizat in diferite
alte structuri sau uniuni.

In prima editie a acestei carti, numele membrilor de


structuri si uniuni nu erau asociate cu parintii lor.
Totusi, aceasta asociere devenise obisnuita in
compilatoare cu mult inaintea standardului ANSI.

Un membru, care nu este cimp, al unei structuri sau uniuni, poate avea orice
tip, specific unui obiect. Un membru cimp (care nu necesita un declarator si ca
urmare poate sa nu aiba nume) are tipul int, unsigned int sau signed int si este
interpretat ca un obiect de tip integral de lungime specificata in biti; daca un
cimp int este considerat a fi sau nu signed aceasta depinde de implementare.
Membrii cimp, adiacenti, ai structurilor sint implementati in unitati de memorie
dependente de implementare. Cind un cimp, care urmeaza unui alt cimp, nu incape
intr-o unitate de memorie, partial umpluta, el poate fi impartit intre unitati sau
unitatea poate fi completata cu "umplutura". Un cimp fara nume, cu lungime zero,
forteaza aceasta completare cu "umplutura" astfel incit urmatorul cimp va
incepe la inceputul urmatoarei unitati de alocare.

Standardul ANSI face aceste cimpuri si mai dependente de


implementare decit erau in prima editie. Este
recomandabil de a citi regulile limbajului pentru
memorarea cimpurilor de biti ca fiind "dependente de
implementare" fara calificare. Structurile cu cimpuri de
biti pot fi folosite ca o cale portabila a tentativei de
reducere a memoriei cerute pentru o structura (cu costul
probabil al cresterii spatiului pentru instructiuni si a
timpului necesar pentru accesul cimpurilor), sau ca o
cale non-portabila de descriere a dispunerii in memorie,
cunoscuta la nivel de biti. In cel de al doilea caz,
este necesar sa intelegem regulile implementarii locale.

Membrii unei structuri au adrese crescatoare in ordinea declararii lor. Un


membru non-cimp al unei structuri este aliniat la o adresa multiplu de o marime
care depinde de tipul sau; de aceea pot exista "gauri fara nume" intr-o
structura . Daca un pointer catre o structura este convertit prin cast intr-un tip
de pointer catre primul sau membru, rezultatul va trimite catre primul membru.
O uniune poate fi gindita ca o structura in care toti membrii incep la
deplasarea zero si a carei marime este suficienta pentru a contine pe oricare
dintre membrii sai. Intr-o uniune, poate fi memorat cel mult un membru, la un
moment dat. Daca un pointer catre o uniune este convertit prin cast intr-un tip de
pointer catre un membru, rezultatul va trimite catre acel membru.
Un exemplu simplu al unei declaratii de structura este:

struct tnode {
char tword[20];
int count;
struct tnode *left;
struct tnode *right;
};

care contine un tablou de 20 caractere, un intreg si doua pointere catre structuri


similare. O data ce aceasta declaratie a fost facuta, declaratia:

struct tnode s, *sp;

declara pe s a fi o structura de felul mentionat iar pe sp a fi un pointer catre o


structura de acest fel. Cu aceste declaratii, expresia:

sp->count

refera cimpul count al structurii catre care a trimis pointerul sp;

s.left

refera pointerul catre subarborele sting, al structurii s; si

s.right->tword[0]

refera primul caracter al membrului tword al subarborelui drept al lui s.


In general, un membru al unei uniuni nu poate fi inspectat decit daca ultima
valoare a uniunii a fost obtinuta printr-o atribuire catre acest membru al
uniunii. Cu toate acestea o garantie speciala simplifica folosirea uniunilor: daca
o uniune contine citeva structuri care contin o parte initiala comuna si daca
uniunea contine, la un moment dat, una dintre aceste structuri, este permis sa
referim partea initiala comuna a oricareia dintre structurile continute. De
exemplu, urmatorul fragment este legal:

union {
struct {
int type;
} n;
struct {
int type;
int intnode;
} ni;
struct {
int type;
float floatnode;
} nf;
} u;
...
u.nf.type = FLOAT;
u.nf.floatnode = 3.14;
...
if (u.n.type == FLOAT)
...sin(u.nf.floatnode) ...

A8.4 Enumeratii

Enumeratiile sint tipuri unice cu valori ce se intind peste o multime de


constante cu nume, denumite enumeratori. Forma unui specificator de enumeratie
este imprumutata de la sttructuri si uniuni.

enum-specifier:
enum identifier(opt) { enumerator-list }
enum identifier

enumerator-list:
enumerator
enumerator-list , enumerator

enumerator:
identifier
identifier = constant-expression

Identificatorii dintr-o lista de enumeratori sint declarati drept constante de tip


int si pot sa apara oriunde aceste constante sint cerute. Daca nu apar enumeratori
cu = atunci valorile constantelor corespunzatoare incep cu zero si cresc cu o
unitate pe masura ce declaratia este citita de la stinga la dreapta. Un enumerator
cu = da identificatorului asociat valoarea specificata; identificatorii urmatori
continua progresia de la valoarea atribuita. Numele enumeratorilor din acelasi
domeniu trebuie neaparat sa fie distincte unul fata de celalalt si fata de numele
variabilelor ordinare, dar valorile pot sa nu fie distincte.
Rolul identificatorului in specificatorul enum este analog cu acela al tag-
ului de structura dintr-un specificator struct; el denumeste o enumeratie
particulara. Regulile pentru specificatorii enum cu si fara tag-uri si liste sint
aceleasi cu cele pentru specificatorii de structuri sau uniuni cu exceptia ca
tipuri incomplete de enumeratie nu exista; tag-ul unui specificator enum fara o
lista de enumeratori trebuie neaparat sa se refere la un specificator, din
domeniu, care sa aiba o lista.

Enumeratiile sint noi fata de prima editie a acestei


carti, dar au facut parte din limbaj de citiva ani.

A8.5 Declaratori

Declaratorii au sintaxa:

declarator:
pointer(opt) direct-declarator

direct-declarator:
identifier
( declarator )
direct-declarator [ constant-expression(opt) ]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list(opt )
pointer:
* type-qualifier-list(opt)
* type-qualifier-list(opt) pointer

type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier

Structura declaratorilor seamana cu aceea a indirectarii, a functiei si a


expresiilor tablou; gruparea se face la fel.

A8.6 Semnificatia declaratorilor

O lista de declaratori apare dupa o secventa de specificatori de clasa de


memorie si de tip. Fiecare declarator declara un identificator principal unic,
unul care apare ca prima alternativa a productiei pentru direct-declarator.
Specificatorii de clasa de memorie se aplica direct acestui identificator, dar
tipul sau depinde de forma acestui declarator. Un declarator este citit ca o
afirmatie ca acolo unde apare identificatorul sau, intr-o expresie de aceeasi
forma cu declaratorul, el produce un obiect de tipul specificat.
Considerind numai partile de tip ale specificatorilor de declaratie (A8.2) si
un declarator particular, o declaratie are forma "T D," unde T este un tip iar D
este un declarator. Tipul atribuit identificatorului in diferite forme ale
declaratorului este descris inductiv folosind aceasta notatie.
Intr-o declaratie T D, unde D este un identificator fara ornamente, tipul
identificatorului este T.
Intr-o declaratie T D, unde D are forma:

(D1)

tipul identificatorului D1 este acelasi ca si al lui D. Parantezele nu modifica


tipul, dar poate modifica modul de legare al declaratorilor complecsi.

A8.6.1 Declaratori de pointer

Intr-o declaratie T D, unde D are forma:

* type-qualifier-list(opt) D1

iar tipul identificatorului din declaratia T D1 este "type-modifier


T," tipul identificatorului lui D este "type-modifier type-
qualifier-list pointer to T". Calificatorii ce urmeaza lui * se aplica pointerului
insusi si nu obiectului catre care trimite pointerul.

De exemplu, sa luam in considerare declaratia:

int *ap[];

Aici ap[] joaca rolul lui D1; o declaratie "int ap[]" ar da lui ap tipul "tablou
de int-uri", lista cu calificatorii de tip fiind vida iar modificatorul de tip
este "tabllou de ". Deci declaratia curenta da lui ap tipul "tablou de pointere
catre int"
In calitate de alte exemple, declaratiile:

int i, *pi, *const cpi = &i;


const int ci = 3, *pci;

declara un intreg i si un pointer catre un intreg, pi. Valoarea pointerului


constant cpi nu poate fi modificata; el va trimite totdeauna catre aceeasi
locatie, desi valoarea catre care el trimite poate fi modificata. Intregul ci este
o constanta care nu poate fi modificata (desi ea poate fi initializata, ca in
cazul de fata). Tipul lui pci este "pointer catre un const int" iar pci-ul insusi
poate fi modificat pentru a trimite intr-un alt loc, dar valoarea catre care
trimite nu poate fi modificata prin atribuire folosind pci.

A8.6.2 Declaratori de tablou

Intr-o declaratie T D, unde D are forma:

D1[constant-expression(opt)]

iar tipul identificatorului din declaratia T D1 este "type-modifier T," tipul


identificatorului lui D este "type-modifier array of T". Daca constant-expression
este prezenta, ea trebuie neaparat sa aiba tipul integral iar valoarea mai mare
decit zero. Daca constant- expression, specificind limita, este absenta, atunci
tabloul are un tip incomplet.
Un tablou poate fi construit dintr-un tip aritmetic, dintr-un pointer, dintr-o
structura sau uniune sau dintr-un alt tablou (pentru a genera un tablou multi-
dimensional). Orice tip, din care se construieste un tablou, trebuie neaparat sa
fie complet; trebuie neaparat sa nu existe un tablou sau structura de tip
incomplet. Aceasta implica faptul ca pentru un tablou multi-dimensional, numai
prima dimensiune poate sa lipseasca. Tipul unui obiect de tip tablou incomplet
este completat printr-o alta declaratie, completa, pentru obiect (A10.2) sau prin
initializarea lui (A8.7). De exemplu:

float fa[17], *afp[17];

declara un tablou de membri float si un tablou de pointere catre membri float. De


asemenea:

static int x3d[3][5][7];

declara un tablou static, tridimensional, de intregi, cu rangul 3x5x7. In


detaliere completa x3d este un tablou de 3 articole; fiecare articol este un
tablou de cinci tablouri; fiecare dintre aceste ultime tablouri este un tablou de
sapte intregi. Oricare dintre expresiile x3d, x3d[i], x3d[i][j], x3d[i][j][k]
poate aparea, corect, intr-o expresie. Primele trei au tipul "tablou" iar ultimul
are tipul int. Si mai clar, x3d[i][j] este un tablou de sapte intregi iar x3d[i]
este un tablou de cinci tablouri de cite sapte intregi.
Operatia cu indici de tablou este definita astfel incit E1[E2] este identica
cu *(E1+E2). De aceea, in ciuda aparentei de asimetrie, indexarea este o
operatie comutativa. In virtutea regulilor de conversie care se aplica lui + si
tablourilor (A6.6, A7.1, A7.7), daca E1 este un tablou si E2 un intreg, atunci
E1[E2] refera pe cel de al E2-lea membru al lui E1.
In exemplul dat, x3d[i][j][k] este echivalent cu *(x3d[i][j]+k). Prima
subexpresie x3d[i][j] este convertita prin A7.1 in tipul "pointer catre tablou de
intregi"; prin A7.7, adunarea implica multiplicarea cu marimea unui int. Ea
rezulta din regulile ca tablourile sint memorate pe rinduri (ultimul indice
variaza cel mai rapid) si ca primul indice din declaratie ajuta la stabilirea
cantitatii de memorie consumata de catre un tablou, dar nu joaca niciun alt rol in
calculele de indici.

A8.6.3 Declaratori de functie

Intr-o declaratie, de stil nou, pentru o functie T D unde D are forma:

D1(parameter-type-list)
iar tipul identificatorului din declaratia T D1 este "type-modifier T," tipul
identificatorului lui D este "type-modifier function with arguments parameter-
type-list returning T".
Sintaxa parametrilor este:

parameter-type-list:
parameter-list
parameter-list , ...

parameter-list:
parameter-declaration
parameter-list , parameter-declaration

parameter-declaration:
declaration-specifiers declarator
declaration-specifiers abstract-declarator(opt)

In declaratia, de stil nou, lista parametrilor specifica tipurile parametrilor. Ca


un caz special, declaratorul pentru o functie de stil nou fara nici un parametru,
are o lista de tipuri de parametru ce se compune doar din cuvintul cheie void.
Daca lista de tipuri de parametru se incheie cu un elipsis "...", atunci functia
poate sa accepte mai multe argumente decit numarul de parametri descrisi in mod
explicit; vezi A7.3.2.
Tipurile parametrilor care sint tablouri sau functii sint modificate in
pointere cu respectarea regulilor pentru conversii de parametri; vezi A10.1.
Singurul specificator de clasa de memorie permis intr-un specificator de
declaratie de parametri este register iar acest specificator este ignorat in afara
de cazul in care declaratorul de functie este in fruntea unei definitii de
functie. In mod similar, daca declaratorii din declaratiile de parametri contin
identificatori iar declaratorul de functie nu se gaseste in fruntea unei definitii
de functie, identificatorii ies imediat in afara domeniului. Declaratorii
abstracti, care nu mentioneaza identificatorii, sint discutati in A8.8.
Intr-o declaratie, de stil vechi, pentru o functie T D, unde D are forma:

D1(identifer-list(opt))

iar tipul identificatorului din declaratia T D1 este "type-modifier T", tipul


identificatorului lui D este "type-modifier function of unspecified arguments
returning T". Parametrii (daca sint prezenti) au forma:

identifier-list:
identifier
identifier-list , identifier

Intr-un declarator, de stil vechi, lista de identificatori trebuie neaparat sa fie


absenta, in afara de cazul in care declaratorul este folosit in fruntea unei
definitii de functii (A10.1). Nici o informatie despre tipurile parametrilor nu
este furnizata de declaratie.
De exmplu, declaratia:

int f(), *fpi(), (*pfi)();

declara o functie f care returneaza un int, o functie fpi care returneaza un


pointer catre un int si un pointer pfi catre o functie ce returneaza un int. In
nici unul dintre aceste cazuri nu exista specificate tipuri de parametri; ele
sint de stil vechi.
In declaratia de stil nou:

int strcpy(char *dest, const char *source), rand(void);

strcpy este o functie care returneaza int, cu doua argumente, primul un pointer
catre char iar cel de al doilea un pointer catre const char. Numele parametrilor
sint, efectiv, comentarii. Cea de a doua functie, rand nu are argumente si
returneaza un int.

Declaratorii de functie cu prototipuri de parametri


sint, de departe, cea mai importanta modificare
introdusa de standardul ANSI. Acesti declaratori ofera
un avantaj fata de cei "de stil vechi" din prima editie
prin asigurarea descoperirii erorii si prin fortarea
argumentelor cu ocazia apelurilor de functie, dar cu un
cost: harababura si confuzia pe timpul introducerii lor
si necesitatea de a acomoda ambele forme. O oarecare
uritire sintactica a fost necesara, in scop de
compatibilitate, pe nume void, ca marcator explicit al
functiilor de stil nou fara parametri.
Notatia elipsis ", ..." pentru functii cu liste cu
lungimi variabile, de parametri, este, de asemenea, noua
si impreuna cu macro-urile din header-ul standard
<stdarg.h>, formalizeaza un mecanism care, oficial, a
fost interzis, dar neoficial a fost trecut cu vederea in
prima editie.
Aceste notatii au fost adoptate din limbajul C++.

A8.7 Initializare

Cind este declarat un obiect, init-declarator-ul sau poate sa specifice o


valoare initiala pentru identificatorul ce se declara. Initializatorul este
precedat de = si este fie o expresie, fie o lista de initializatori incadrata de
alocade. O lista se poate termina cu o virgula, ceea ce este o subtilitate pentru
o formatare bine executata.

initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }

initializer-list:
initializer
initializer-list , initializer

Toate expresiile din initializatorul pentru un obiect static sau tablou


trebuie sa fie expresii constante, asa cum au fost descrise in A7.19. Expresiile
dintr-un initializator pentru un obiect auto sau register sau pentru un tablou
trebuie neaparat sa fie, de asemenea, expresii constante daca initializatorul este
o lista incadrata de alocade. Cu toate acestea, daca initializatorul pentru un
obiect automat este o expresie singulara, ea nu trebuie sa fie o expresie
constanta, ci pur si simplu, trebuie neaparat sa aiba tipul adecvat pentru
atribuirea catre obiect.

Prima editie nu a aprobat initializarea structurilor,


uniunilor sau tablourilor automate. Standardul ANSI
permite acest lucru, dar numai prin constructii
constante, in afara de cazul in careinitializatorul
poate fi exprimat printr-o expresie simpla.

Un obiect static, neinitializat in mod explicit, este initializat ca si cum


lui (sau membrilor sai) i s-a atribuit constanta zero. Valoarea initiala a unui
obiect automat, neinitializat in mod explicit, este nedefinita.
Initializatorul pentru un pointer sau pentru un obiect de tip aritmetic este o
expresie singulara, eventual incadrata de acolade. Expresia este atribuita
obiectului.
Initializatorul pentru o structura este fie o expresie de acelasi tip, fie o
lista de initializatori, in ordine, pentru membrii sai, incadrata de acolade. Daca
exista mai putini initializatori in lista decit membri in structura, atunci
membrii neonorati sint initializati cu zero. Nu este permis sa fie mai multi
initializatori decit membri.
Initializatorul pentru un tablou este o lista de initializatori, incadrata de
acolade, pentru membrii sai. Daca tabloul are o dimensiune necunoscuta, numarul
initializatorilor determina dimensiunea tabloului si tipul sau devine complet.
Daca tabloul are dimensiune fixata, numarul initializatorilor nu poate sa
depaseasca numarul membrilor tabloului; daca ei sint mai putini, membrii neonorati
sint initializati cu zero.
Ca un caz special, un tablou de caractere poate fi initializat printr-un
literal sir; caracterele succesive ale sirului initializeaza membrii succesivi ai
tabloului. In mod similar, un literal de caractere largi (A2.6) poate initializa
un tablou de tipul wchar_t. Daca tabloul are dimensiune necunoscuta, numarul
caracterelor din sir, inclusiv caracterul nul de terminare, determina dimensiunea
sa; daca dimensiunea sa este fixata, numarul de caractere din sir, neincluzind
caracterul nul de terminare, trebuie neaparat sa nu depaseasca dimensiunea
tabloului.
Initializatorul pentru o uniune este fie o expresie singulara de acelasi tip,
fie un initializator, incadrat de acolade, pentru primul membru al uniunii.

Prima editie nu permite initializarea uniunilor. Regula


"primului membru" este stingace, dar este dificil de
generalizat fara o noua sintaxa. Pe linga permisiunea de
initializare explicita, intr-o maniera cel putin
primitiva, aceasta regula ANSI defineste semantica
uniunilor statice initializate neexplicit.

Un agregat este o structura sau un tablou. Daca un agregat contine membri de


tip agregat, regulile de initializare se aplica recursiv. Acoladele pot fi omise
in initializare, dupa cum urmeaza: daca initializatorul pentru un membru al
agregatului, care el insusi este un agregat, incepe cu o acolada stinga, apoi
lista initializatorilor, separati succesiv prin virgula, initializeaza membrii
subagregatului; este o eroare de a prezenta mai multi initializatori decit membri.
Daca, totusi, initializatorul pentru un subagregat nu incepe cu o acolada stinga,
atunci sint luate in considerare suficiente elemente din lista, pentru membrii
subagregatului; restul elementelor sint lasate sa initializeze urmatorul membru al
agregatului din care face parte subagregatul.
De exemplu:

int x[] = { 1, 3, 5 };

declara si initializeaza pe x ca un tablou, cu dimensiunea unu, cu trei membri,


deoarece nu a fost specificata o marime si exista trei initializatori.

float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};

este o initializare a unui tablou cu dimensiuni complet definite: 1, 3 si 5


initializeaza prima linie a tabloului y[0], adica y[0][0], y[0][1] si y[0][2]. In
mod asemanator, urmatoarele doua linii initializeaza pe y[1] si y[2].
Initializatorul se termina mai devreme si de aceea elementele lui y[3] sint
initializate cu zero. Exact acelasi efect ar fi fost obtinut prin:

float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};

Initializatorul pentru y incepe cu o acolada stinga, dar nu si cel pentru


y[0]; de aceea sint utilizate trei elemente din lista. In mod asemanator, sint
luate in considerare urmatoarele trei, succesiv, pentru y[1] si apoi pentru y[2].
De asemenea:

float y[4][3] = {
{ 1 }, { 2 }, { 3 }, { 4 }
};

initializeaza prima coloana a lui y (privit ca un tablou bidimensional), lasind


restul elementelor cu valoarea zero.
In sfirsit,

char msg[] = "Sintax error on line %s\n";

arata un tablou de caractere ai carui membri sint initializati cu un sir; marimea


sa include si caracterul nul de terminare.

A8.8 Nume de tip

Intr-o serie de contexte (pentru a specifica, in mod explicit, conversiile de


tip cu un operator cast, pentru a declara tipurile parametrilor din declaratorii
de functii si ca argument al lui sizeof) este necesar sa furnizam numele unui tip
de data. Acest lucru se realizeaza folosind un nume de tip, care, sintactic, este
o declaratie pentru un obiect de acel tip, omitind numele obiectului.

type-name:
specifier-qualifier-list abstract-declarator(opt)

abstract-declarator:
pointer
pointer(opt) direct-abstract-declarator

direct-abstract-declarator:
( abstract-declarator )
direct-abstract-declarator(opt)[constant-
expression(opt) ]
direct-abstract-declarator(opt) ( parameter-type-
list(opt) )

Este posibil sa identificam, in mod unic, locatia din abstract- declarator,


unde identificatorul ar aparea daca constructia ar fi un declarator dintr-o
declaratie. Tipul numit este atunci acelasi cu tipul identificatorului ipotetic.
De exemplu:

int
int *
int *[3]
int (*)[]
int *()
int (*[])(void)

denumesc tipuri de, respectiv, "intreg", "pointer catre intreg", "tablou de 3


pointere catre intreg", "pointer catre un tablou de un numar nespecificat de
intregi", "functie de parametri nespecificati returnind pointer catre intreg" si
"tablou, de marime nespecificata, de pointere catre functii, fara parametri, care
returneaza un intreg".

A8.9 Typedef

Declaratiile al caror specificator de clasa de memorie este typedef nu declara


obiecte; in schimb, ele definesc identificatori care denumesc tipuri. Acesti
identificatori sint denumiti nume typedef

typedef-name:
identifier

O declaratie typedef atribuie un tip la fiecare nume dintre declaratorii sai,


in felul obisnuit (vezi &8.6). Dupa aceea, fiecare astfel de nume este sintactic
echivalent cu cuvintul cheie specificator de tip pentru tipul asociat.
De exemplu, dupa
typedef long Blockno, *Blockptr;
typedef struct { double r, theta; } Complex;

constructiile

Blockno b;
extern Blockptr bp;
Complex z, *zp;

sint declaratii legale. Tipul lui b este long, cel al lui bp este "pointer catre
long" iar cel al lui z este structura specificata; zp este un pointer catre o
astfel de structura.
typedef nu introduce tipuri noi, ci numai sinonime pentru tipurile care s-ar
putea sa fie specificare intr-un alt mod. In exemplul de mai inainte, b are
acelasi tip ca oricare alt obiect long.
Numele typedef pot fi redeclarate intr-un domeniu interior, dar trebuie
neaparat sa fie data o multime nevida de specificatori de tip. De exemplu:
extern Blockno;

nu-l redeclara pe Blockno, dar

extern int Blockno;

il redeclara.

A8.10 Echivalenta de tip

Doua liste de specificatori de tip sint echivalente daca ele contin aceeasi
multime de specificatori de tip, tinind cont ca unii specificatori pot fi
implicati de catre altii (de exemplu, long singur implica long int). Structurile,
uniunile si enumeratiile cu tag-uri diferite sint distincte iar uniunile,
structurile sau enumeratiile fara tag-uri specifica un tip unic.
Doua tipuri sint identice daca declaratorii lor abstracti (A8.8), dupa
expandarea oricaror tipuri typedef si dupa stergerea oricaror identificatori de
parametru de functie, sint identici pina la echivalenta listelor de specificatori
de tip. Marimile tablourilor si tipurile parametrilor de functie sint
semnificative.

A9. Instructiuni

Cu exceptiile care au fost descrise, instructiunile sint executate in


secventa, adica in ordinea aparitiei lor. Instructiunile sint executate pentru
efectul lor iar ele nu au valori. Ele se incadreaza in citeva grupe:

statement:
labeled-statement
expression-statement
compound-statement
selection-statement
iteration-statement
jump-statement

A9.1 Instructiuni cu eticheta

Instructiunile pot avea, ca prefix, etichete.

labeled-statement:
identifier : statement
case constant-expression : statement
default : statement

O eticheta constind dintr-un identificator declara identificatorul. Singura


utilizare a identificatorului eticheta este ca tinta pentru goto. Domeniul
identificatorului este functia curenta. Deoarece etichetele au spatiul lor propriu
pentru nume, ele nu se interfe- reaza cu alti identificatori si nu pot fi
redeclarate. Vezi A11.1.
Etichetele case si etichetele default sint utilizate cu instructiunea switch
(A9.4). Expresia constanta a lui case trebuie neaparat sa aiba tipul integral.
Etichetele singure nu modifica secventa normala de executie.

A9.2 Instructiune expresie

Cele mai multe instructiuni sint instructiuni expresie, care au forma:

expression-statement:
expression(opt) ;

Cele mai multe instructiuni expresie sint atribuiri sau apeluri de functie. Toate
efectele laterale, provenite de la o expresie sint incheiate inainte ca urmatoarea
instructiune sa fie executata. Daca expresia este absenta, constructia se numeste
o instructiune nula; se obisnuieste adesea sa se furnizeze un corp vid unei
instructiuni de iteratie sau sa i se plaseze o eticheta.

A9.3 Instructiune compusa

Deoarece mai multe instructiuni pot fi utilizate, in bloc, acolo unde este
nevoie, s-a furnizat instructiunea compusa (denumita, de asemenea, "bloc"). Corpul
definitiei unei functii este o instructiune compusa.

compound-statement:
{ declaration-list(opt) statement-list(opt) }

declaration-list:
declaration
declaration-list declaration

statement-list:
statement
statement-list statement

Daca un identificator din declaration-list exista in domeniul exterior


blocului, atunci declaratia exterioara este suspendata in interiorul blocului
(vezi A11.1), dupa care ea isi recapata forta. Un identificator poate fi declarat
numai o singura data in acelasi bloc. Aceste reguli se aplica identificatorilor
din acelasi spatiu de nume (A11.1); identificatorii din spatii de nume, diferite,
sint tratati ca fiind distincti.
Initializarea obiectelor automate este realizata de fiecare data cind blocul
este abordat prin partea de sus (inceputul blocului) iar prelucrarea se face in
ordinea declaratiilor. Daca se executa un salt, direct in interiorul blocului,
atunci aceste initializari nu mai sint efectuate. Initializarea obiectelor static
este efectuata doar o data, inainte ca programul sa inceapa executia.

A9.4 Instructiuni de selectie


Instructiunile de selectie aleg unul dintr-o serie de fluxuri de comenzi.

selection-statement:
if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement

In ambele forme ale instructiunii if, expresia, care trebuie neaparat sa aiba
tipul aritmetic sau pointer, este calculata, inclusiv toate efectele laterale, si
daca ea apare ca fiind diferita de zero, se executa prima subinstructiune. In cea
de a doua forma, daca expresia este egala cu zero, se executa cea de a doua
subinstructiune. Ambiguitatea lui else este rezolvata prin legarea unui else cu
ultimul if fara else, intilnit la acelasi nivel de cuibarire de bloc.
Instructiunea switch provoaca transferul controlului (adica un salt) la una
dintr-o serie de instructiuni, in functie de valoarea unei expresii, care trebuie
sa aiba tipul integral. Subinstructiunea controlata printr-un switch este, in mod
obisnuit, compusa. Orice instructiune din interiorul subinstructiunii poate fi
etichetata cu una sau mai multe etichete case (A9.1). Expresia de control este
supusa promovarii integrale (A6.1) iar constantele case sint convertite in tipul
promovat. Oricare doua dintre constantele case, asociate cu acelasi switch, nu pot
avea aceeasi valoare dupa conversie. De asemenea, poate exista, cel mult, o
eticheta default asociata cu un switch. Switch-urile pot fi cuibarite; o eticheta
case sau default este asociata cu cel mai mic switch care o contine.
Cind este executata instructiunea switch, este calculata expresia sa,
incluzind toate efectele laterale, iar apoi este comparata cu fiecare constanta
case. Daca una dintre constantele case este egala cu valoarea expresiei, controlul
este transferat la instructiunea ce ii corespunde. Daca nici una dintre
constantele case nu este egala cu valoarea expresiei si daca exista o eticheta
default, controlul este transferat la instructiunea ce corespunde acesteia din
urma. Daca nici un case nu se potriveste si daca nu exista nici un default, atunci
nici una dintre subinstructiunile switch-ului nu se executa.

In prima editie a acestei carti, expresia de control a


switch-ului si constantele case trebuiau sa aibatipul
int.

A9.5 Instructiuni de iteratie

Instructiunile de iteratie specifica ciclare.

iteration-statement:
while ( expression ) statement
do statement while ( expression ) ;
for( expression(opt);expression(opt) ;
expression(opt) ) statement

In instructiunile while si do, subinstructiunea este executata in mod repetat


atit timp cit valoarea expresiei ramine diferita de zero; expresia trebuie
neaparat sa aiba tipul aritmetic sau tipul pointer. La while, testul, incluzind
toate efectele laterale decurgind din expresie, se efectueaza inaintea fiecarei
executii a instructiunii; la do, testul se efectueaza dupa fiecare iteratie.
In instructiunea for, prima expresie se calculeaza o data si, in acest fel, se
specifica initializarea pentru ciclare. Nu exista restrictie in privinta tipului
ei. Cea de a doua expresie trebuie neaparat sa aiba tipul aritmetic sau tipul
pointer; ea este calculata inaintea fiecarei iteratii si daca ea devine egala cu
zero, atunci instructiunea for si-a incheiat executia. Cea de a treia expresie
este calculata dupa fiecare iteratie si in acest fel se specifica o reinitializare
pentru ciclare. Nu exista restrictie in privinta tipului ei. Efectele secundare,
provenite de la fiecare expresie, sint terminate imediat dupa calculul ei. Daca
subinstructiunea nu contine continue, o instructiune

for ( expression1 ; expression2 ; expression3 ) statement


este echivalenta cu

expression1 ;
while ( expression2 ) {
statement
expression3 ;
}

Oricare dintre cele trei expresii poate fi omisa. Omiterea celei de a doua
expresii implica un test echivalent cu testul unei constante diferite de zero.

A9.6 Instructiuni de salt

Instructiunile de salt transfera controlul in mod neconditionat.

jump-statement:
goto identifier ;
continue ;
break ;
return expression(opt) ;

In instructiunea goto, identificatorul trebuie neaparat sa fie o eticheta


(A9.1) existenta in functia curenta. Controlul se transfera la instructiunea cu
respectiva eticheta.
O instructiune continue poate sa apara numai in interiorul unei instructiuni
de iteratie. Ea prevede transferul controlului catre portiunea de continuare a
ciclarii, respectiv catre cea mai mica bucla ce contine aceasta instructiune. Mai
precis, in interiorul fiecareia dintre instructiunile:

while (...) { do { for (...) {


... ... ...
contin: ; contin: ; contin: ;
} } while (...); }

un continue, necontinut intr-o instructiune de iteratie mai mica, este la fel cu


goto contin.
O instructiune break poate apare numai intr-o instructiune de iteratie sau
intr-o instructiune switch si termina executia celei mai mici instructiuni ce o
contine; controlul se transfera instructiunii ce urmeaza dupa instructiunea
terminata.
O functie se intoarce catre apelantul ei prin instructiunea return. Cind
return-ul este urmat de o expresie, valoarea acesteia este returnata apelantului
functiei. Expresia este convertita, ca in cazul atribuirii, in tipul returnat de
catre functia ce contine return-ul in cauza.
Incheind o functie, fara return, este echivalent cu un return fara expresie.
In orice caz, valoarea returnata este nedefinita.

A10. Declaratii externe

Unitatea de intrare furnizata compilatorului C este denumita unitate de


translatie; ea consta dintr-o secventa de declaratii externe, care sint fie
declaratii, fie definitii de functie.

translation-unit:
external-declaration
translation-unit external-declaration

external-declaration:
function-definition
declaration
Domeniul declaratiilor externe se intinde pina la sfirsitul unitatii de
translatie in care ele sint declarate, tot asa cum declaratiile din interiorul
unui bloc au un domeniu ce se intinde pina la sfirsitul blocului. Sintaxa
declaratiilor externe este aceeasi ca la toate declaratiile, cu exceptia ca codul
functiilor poate fi dat numai la acest nivel.

A10.1 Definitii de functie

Definitiile de functie au forma:

function-definition:
declaration-specifiers(opt) declarator-
declaration-list(opt) compound-statement

Singurii specificatori de clasa de memorie, permisi printre specificatorii de


declaratie, sint: extern sau static; vezi A11.2 pentru distinctia dintre ei.
O functie poate returna un tip aritmetic, o structura, o uniune, un pointer
sau void, dar nu o functie sau un tablou. Declaratorul dintr-o declaratie de
functie, trebuie neaparat sa specifice explicit ca identificatorul declarat are
tipul functie; adica, el trebuie neaparat sa contina una din formele (vezi
A8.6.3):

direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list(opt) )

unde direct-declarator este un identificator sau un identificator intre


paranteze. In particular, trebuie neaparat sa nu realizam tipul functiei cu
ajutorul unui typedef.
In prima forma, definitia este o functie in stil nou iar parametrii sai,
impreuna cu tipurile lor, sint declarati in lista sa parameter-type-list; lista de
declaratii ce urmeaza declaratorului functiei trebuie neaparat sa fie absenta. In
afara de cazul ca lista de parametri cu tipuri se compune numai din void, aratind
ca functia nu are parametri, fiecare declarator din lista de parametri cu tipuri
trebuie neaparat sa contina un identificator. Daca lista de parametri cu tipuri
se incheie cu ", ..." atunci functia poate fi apelata cu mai multe argumente
decit parametri; mecanismul macroului va_arg definit in header-ul standard
<stdarg.h> si descris in Apendicele B trebuie neaparat sa fie folosit pentru a
referi extraargumente. Astfel de functii, denumite si functii variadice, trebuie
neaparat sa aiba cel putin un parametru cu nume.
In cea de a doua forma, definitia este in stil vechi; lista de identificatori
numeste parametrii, in timp ce lista de declaratii le atribuie tipurile. Daca nici
o declaratie nu a fost data pentru un parametru, tipul sau este, implicit, int.
Lista de declaratii trebuie neaparat sa declare numai parametrii numiti in lista,
initializarea nu este permisa si singurul specificator de clasa de memorie posibil
este register.
In ambele stiluri de definire a unei functii, parametrii sint considerati a fi
fost declarati imediat dupa inceperea instructiunii compuse ce constituie corpul
functiei si astfel aceiasi identificatori trebuie neaparat sa nu mai fie
redeclarati acolo (desi, la fel cu alti identificatori, ei pot fi redeclarati in
blocuri interioare). Daca un parametru este declarat ca are tipul "tablou de tip",
declaratia este corectata pentru a citi "pointer catre tip"; in mod similar, daca
un parametru este declarat a avea tipul "functie returnind tip", declaratia este
corectata pentru a citi "pointer catre functie returnind tip". Pe timpul apelului
unei functii, argumentele sint convertite cum trebuie si sint atribuite
parametrilor; vezi A7.3.2.

Definitiile functiilor in stil nou sint noi in


standardul ANSI. De asemenea, exista o mica modificare
in detaliile de promovare; prima editie a specificat ca
declaratiile parametrilor float erau corectate pentru a
citi double. Diferenta devine notabila cind un pointer
catre parametru este generat in interiorul unei functii.
Un exemplu complet de definitie a unei functii in stil nou este:

int max(int a, int b, int c)


{
int m;

m = (a > b) ? a : b;
return (m > c) ? m : c;
}

Aici int este specificatorul de declaratie; max (int a, int b, int c) este
declaratorul functiei iar {...} este blocul care da codul pentru functie.
Definitia ce corespunde stilului vechi ar fi:

int max(a, b, c)
int a, b, c;
{
/*...*/
}

unde acum int max(a, b, c) este declaratorul iar int a, b, c; este lista de
declaratii pentru parametri.

A10.2 Declaratii externe

Declaratiile externe specifica caracteristicile obiectelor, functiilor si ale


altor identificatori. Termenul "extern" se refera la locatia lor in afara
functiilor si nu este direct legat cu cuvintul cheie extern; clasa de memorie
pentru un obiect declarat extern poate fi lasata goala sau ea poate fi specificata
ca extern sau static.
Poate exista o serie de declaratii externe pentru acelasi identificator in
interiorul aceleiasi unitati de translatie daca ele concorda in tip si legatura si
daca exista cel mult o definitie pentru identificator.
Doua declaratii pentru un obiect sau functie sint considerate ca concorda in
tip conform regulilor discutate in A8.10. In plus daca declaratiile difera
deoarece un tip este o incompleta structura, uniune sau enumeratie (A8.3) iar
celalalt este un tip corespunzator complet, cu acelasi tag, tipurile sint
considerate ca concorda. Mai mult, daca un tip este un tip tablou, incomplet
(A8.6.2) iar celalalt este un tip tablou, complet si daca altminteri sint
identice, ele se considera ca concorda. In sfirsit, daca un tip specifica o
functie in stil vechi iar celalalt o functie, altminteri identica, in stil nou, cu
declaratii de parametri, tipurile sint considerate ca concorda.
Daca prima declaratie externa pentru o functie sau obiect include
specificatorul static, identificatorul are legatura (linkage) interna; altminteri
el are legatura externa. Legatura este discutata in A11.2.
O declaratie externa pentru un obiect este o definitie daca ea are si un
initializator. O declaratie externa de obiect care nu are un initializator si nu
contine specificatorul extern este o definitie-tentativa. Daca o definitie pentru
un obiect apare intr-o unitate de translatie, orice definitie-tentativa este
tratata ca o declaratie redundanta. Daca pentru acel obiect nu apare nici o
definitie intr-o unitate de translatie, atunci toate definitiile- tentativa devin
o singura definitie cu initializator zero.
Fiecare obiect trebuie neaparat sa aiba o singura definitie. Pentru obiectele
cu legaturi interne, aceasta regula se aplica separat pentru fiecare unitate de
translatie, deoarece obiectele legate intern sint unice pentru o unitate de
translatie. Pentru obiectele cu legaturi externe, regula se aplica pentru intregul
program.

Desi regula unei singure definitii este formulata


oarecum diferit in prima editie a acestei carti ea este
in realitate identica cu cea enuntata aici. Unele
implementari o relaxeaza prin generalizarea notiunii de
definitie-tentativa. In formularea alternativa, care
este obisnuita in sistemele UNIX si recunoscuta ca o
extensie obisnuita de catre Standard, toate definitiile-
tentativa pentru un obiect cu legaturi externe, de-a
lungul tuturor unitatilor de translatie ale unui
program, sint considerate impreuna in loc de fiecare
unitate de translatie luata separat. Daca undeva in
program apare o definitie, atunci definitiile-tentativa
devin pur si simplu declaratii, dar daca nu apare nici o
definitie, atunci toate definitiile-tentativa devin o
definitie cu initializator zero.

A11. Domeniu si legatura

Un program nu necesita compilarea sa, in intregime, la un moment dat: textul


sursa poate fi depus intr-o serie de fisiere care sa contina unitati de translatie
si rutinele precompilate pot fi incarcate din biblioteci. Comunicatia dintre
functiile unui program poate fi realizata atit prin apeluri cit si prin
manipularea datelor externe.
De aceea exista doua feluri de domenii ce trebuie avute in vedere: primul,
domeniul lexical al unui identificator, care reprezinta regiunea textului de
program, in interiorul careia, sint intelese caracteristicile unui identificator;
al doilea, domeniu asociat cu obiecte si functii ce au legaturi externe, care
determina legaturile dintre identificatori din unitati de translatie compilate
separat.

A11.1 Domeniu lexical

Identificatorii sint plasati in citeva spatii de nume care nu interfera unul


cu altul ; acelasi identificator poate fi folosit pentru scopuri diferite, chiar
in acelasi domeniu, daca utilizarile sint in spatii de nume, diferite. Aceste
clase sint: obiecte, functii, nume typedef si constante enum; etichete; tag-uri de
structuri, uniuni si enumeratii; si membrii fiecarei structuri sau uniuni, luate
individual.

Aceste reguli difera, in citeva moduri, de cele descrise


in prima editie a acestui manual. Etichetele nu aveau,
dinainte, propriul lor spatiu de nume; tag-urile
structurilor si uniunilor aveau fiecare un spatiu
separat iar in unele implementari, tag-urile de
enumeratie aveau si ele tot la fel; O noua restrictie o
reprezinta depunerea diferitelor feluri de tag-uri in
acelasi spatiu de nume. Cea mai importanta departajare,
fata de prima editie, consta in aceea ca fiecare
structura sau uniune creaza un spatiu de nume, separat
pentru membrii sai, astfel incit acelasi nume poate sa
apara in citeva structuri diferite. Aceasta regula a
constituit o practica obisnuita timp de citiva ani.

Domeniul lexical al unui identificator de obiect sau functie dintr-o


declaratie externa incepe de la sfirsitul declaratorului sau si se intinde pina la
sfirsitul unitatii de translatie in care el apare. Domeniul unui parametru al unei
definitii de functie incepe la startul blocului ce defineste functia si se intinde
pina la sfirsitul acestui bloc; domeniul unui parametru dintr-o declaratie de
functie se sfirseste la sfirsitul declaratorului. Domeniul unui identificator
declarat la inceputul unui bloc, incepe la sfirsitul declaratorului sau si se
intinde pina la sfirsitul blocului. Domeniul unei etichete este intreaga functie
in care ea apare. Domeniul unui tag de structura, uniune sau enumeratie sau al
unei constante de enumeratie, incepe la aparitia sa intr-un specificator de tip si
se intinde pina la sfirsitul unitatii de translatie (pentru declaratiile la nivel
extern) sau pina la sfirsitul blocului (pentru declaratiile din interiorul unei
functii).
Daca un identificator este explicit declarat la inceputul unui bloc, inclusiv
blocul ce constituie o functie, orice declaratie, din afara blocului, a aceluiasi
identificator se suspenda pina la sfirsitul blocului.

A11.2 Legatura

In interiorul unei unitati de translatie, toate declaratiile unui acelasi


identificator de obiect sau functie, cu legatura interna, se refera la acelasi
lucru iar obiectul sau functia este unica pentru acea unitate de translatie. Toate
declaratiile pentru acelasi identificator de obiect sau functie, cu legatura
externa, se refera la acelasi lucru iar obiectul sau functia este partajata de
catre intregul program.
Dupa cum s-a discutat in A10.2, prima declaratie externa pentru un
identificator da identificatorului legatura interna daca s-a folosit
specificatorul static; altminteri legatura este externa. Daca o declaratie pentru
un identificator, facuta in interiorul unui bloc, nu include specificatorul
extern, identificatorul nu are nici o legatura si este unic pentru functie. Daca
declaratia include specificatorul extern si daca o declaratie externa pentru
identificator este activa in domeniul ce cuprinde blocul, atunci identificatorul
are aceeasi legatura ca si declaratia externa si se refera la acelasi obiect sau
functie; dar daca nici o declaratie externa nu este vizibila, atunci legatura sa
este externa.

A12. Preprocesare

Un preprocesor realizeaza macro substitutii, compilare conditionala si


includerea unor fisiere numite. Liniile ce incep cu #, eventual precedat de un
spatiu alb, comunica cu acest preprocesor. Sintaxa acestor linii este independenta
de restul limbajului; ele pot aparea oriunde si au un efect care se intinde
(independent de domeniu) pina la sfirsitul unitatii de translatie. Marginile de
linie au semnificatie; fiecare linie este analizata individual (dar vezi A12.2
pentru modul cum pot fi unite liniile). Pentru preprocesor, un token este orice
token al limbajului sau o secventa de caractere ce da un nume de fisier, precum in
directiva #include (A12.4); in plus, orice caracter, nedefinit altfel, este
considerat un token. Cu toate acestea, caracterele din categoria spatiilor albe,
altele decit blancul si horizontal-tab-ul, sint nedefinite in interiorul liniilor
destinate preprocesorului.
Preprocesarea insasi are loc in citeva faze, logic succesive, care pot fi
condensate intr-o implementare particulara.
1. Mai intii, sint inlocuite, prin echivalentele lor, secventele trigrafice
(vezi A12.1). Mediul sistemului de operare ar trebui sa introduca caractere
newline intre liniile fisierului sursa.
2. Fiecare aparitie a unui caracter \ (backslash) urmat un
newline sint, impreuna, sterse astfel unindu-se liniile (A12.2).
3. Programul este impartit in token-uri separate prin caractere din categoria
spatiilor albe; comentariile sint inlocuite printr-un singur blanc. Apoi sint
ascultate directivele de preprocesare iar macrou-rile (A12.3-A12.10) sint
expandate.
4. Secventele escape din constantele caracter si din literalele sir (A12.5,
A2.6) sint inlocuite prin echivalentele lor; apoi literalele sir adiacente sint
concatenate.
5. Rezultatul este translatat, apoi legat impreuna cu alte programe si
biblioteci, prin colectarea programelor si datelor necesare, precum si conectarea
referintelor de functii externe si de obiecte, de definitiile lor.
A12.1 Secvente trigrafice

Setul de caractere al programului sursa C este continut in standardul de


sapte biti ASCII, care este un superset al lui "ISO- 1983 in variant Code Set".
Pentru a permite programelor sa fie scrise in codul redus, toate aparitiile
urmatoarelor secvente trigrafice sint inlocuite prin caracterul singular
corespunzator. Aceasta inlocuire apare inaintea oricarei alte prelucrari.

??= # ??( [ ??< {


??/ \ ??) ] ??> }
??' ^ ??! | ??- ~

Nici un alt fel de astfel de inlocuiri nu mai apar.


Secventele trigrafice au aparut o data cu standardul
ANSI.

A12.2 Unirea liniilor

Linia care se termina cu caracterul \ (backslash) este unita cu linia


urmatoare prin stergerea backslash-ului si a caracterului newline care urmeaza.
Aceasta operatie se realizeaza inainte de impartirea in token-uri.

A12.3 Macro definitie si expandare

O linie de comanda de forma:

# define identifier token-sequence

face ca preprocesorul sa inlocuiasca toate aparitiile identificatorului din


fisierul sursa cu secventa data de token-uri; toate spatiile albe ce incadreaza
secventa de token-uri sint pierdute. Un al doilea #define pentru acelasi
identificator constituie o eroare, in afara de cazul in care cea de a doua
secventa de token-uri este identica cu prima, unde toate separatiile cu spatii
albe sint considerate a fi echivalente.
O linie de forma:

# define identifier( identifier-list ) token-sequence

unde nu exista nici un blanc intre primul identificator si (, este o macro


definitie cu parametri dati de lista de parametri. Ca si prima forma, spatiile
albe ce incadreaza secventa de token-uri sint pierdute iar macro-ul poate fi
redefinit numai cu o definitie in care numarul si ortografia parametrilor, precum
si secventa de token-uri sint identice.
O linie de comanda de forma:

# undef identifier

face ca definitia identificatorului pentru preprocesor sa fie anulata. Nu este o


eroare aplicarea unui #undef unui identificator necunoscut.
Cind un macro a fost definit in cea de a doua forma, textul format din
identificatorul macro-ului urmat, optional de un spatiu alb si apoi de (, o
secventa de token-uri separate prin virgule si un ), constituie un apel al macro-
ului. Argumentele apelului sint secvente de token-uri separate prin virgule;
virgulele incadrate de ghilimele sau protejate prin paranteze cuibarite, nu separa
argumente. Pe timpul colectarii, argumentele nu sint macro expandate. Numarul
elementelor din apel trebuie neaparat sa se potriveasca cu numarul parametrilor
din definitie. Dupa ce argumentele sint izolate, se indeparteaza spatiile albe
anterioare si ulterioare. Apoi secventa de token-uri rezultata de la fiecare
argument, inlocuieste fiecare aparitie neincadrata de ghilimele a
identificatorului parametrului corespunzator din secventa de token- uri de
inlocuire din macro. In afara de cazul ca parametrul din secventa de inlocuire
este precedat de # sau precedat sau urmat de ##, token-urile argument pentru
apelurile de macro sint examinate si expandate daca este necesar, chiar inainte de
insertie.
Doi operatori speciali influenteaza procesul de inlocuire. Mai intii, daca o
aparitie a unui parametru din secventa de token-uri de inlocuire este precedata
imediat de #, atunci sint plasate ghilimele (") in jurul parametrului
corespunzator si apoi sint inlocuite ambele, # si identificatorul de parametru, cu
argumentul incadrat intre ghilimele. In fata fiecarui caracter " sau \, ce apare
in preajma sau in interiorul unui literal sir sau constante caracter, este inserat
un caracter \.
In al doilea rind, daca definitia secventei de token-uri, pentru orice fel de
macro, contine un operator ##, atunci imediat dupa inlocuirea parametrilor,
fiecare ## este sters, impreuna cu orice spatiu alb, existent inainte sau dupa,
astfel ca sa se concateneze token-urile adiacente si sa se formeze un nou token.
Efectul este nedefinit daca au fost produse token-uri invalide sau daca rezultatul
depinde de ordinea de prelucrare a operatorilor ##. De asemenea, operatorii # nu
pot aparea la inceputul sau sfirsitul secventei de token-uri de inlocuire.
In ambele forme de macro, secventa de token-uri de inlocuire este reexplorata,
in mod repetat, pentru alti identificatori definiti. Cu toate acestea, o data ce
un identificator dat a fost inlocuit intr-o expandare, el nu mai este inlocuit
daca apare, din nou, pe timpul reexplorarii; in realitate el este lasat
nemodificat.
Chiar daca valoarea finala a unei macro expandari incepe cu #, el nu este
considerat a fi directiva de preprocesare.

Detaliile procesorului de macro expandare sint descrise


mai precis in standardul ANSI decit in prima editie. Cea
mai importanta modificare este adaugarea operatorilor #
si ##, care fac admisibile incadrarea cu ghilimele si
concatenarea. Unele din noile reguli, in special acelea
care implica concatenarea, sint bizare (vezi exemplul
care urmeaza).

De exemplu, aceasta facilitate poate fi folosita pentru "constante manifeste"


precum in:

#define TABSIZE 100


int table[TABSIZE];

Definitia:

#define ABSDIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))

defineste un macro pentru returnarea valorii absolute a diferentei dintre


argumentele sale. Spre deosebire de o functie care face acelasi lucru, argumentele
si valoarea returnata pot avea oricare tip aritmetic sau chiar pointere. De
asemenea, argumentele, care pot avea efecte laterale, sint calculate de doua ori ,
o data pentru test si o data pentru a produce valoarea.
Dindu-se definitia:

#define tempfile(dir) #dir "/%s"

apelul macro tempfile(/usr/tmp) produce

"/usr/tmp" "/%s"

care, in continuare, vor fi concatenate intr-un singur sir. Dupa

#define cat(x, y) x ## y

apelul cat(var,123) produce var123. Cu toate acestea apelul cat(cat(1,2),3) este


nedefinit: prezenta operatorului ## interzice argumentelor din apelul exterior sa
fie expandate. Astfel el produce sirul de token-uri:
cat ( 1 , 2 )3

iar )3 ( concatenarea ultimului token al primului argument cu primul token al


celui de al doilea) este un token nelegal. Daca se introduce un al doilea nivel de
definitie macro:

#define xcat(x,y) cat(x,y)

atunci se lucreaza bine; xcat(xcat(1, 2), 3) produce 123, deoarece expandarea lui
xcat insusi nu implica operatorul ##.
In mod asemanator, ABSDIFF(ABSDIFF(a,b),c) produce rezultatul asteptat, adica
complet expandat.

A12.4 Includere de fisier.

O linie de comanda de forma:

# include <filename>

provoaca inlocuirea acestei linii cu intregul continut al fisierului filename.


Caracterele din numele filename trebuie neaparat sa nu includa caracterul > sau
newline iar efectul este nedefinit daca el contine oricare dintre caracterele ",
', \, sau /*. Fisierul denumit este cautat intr-o serie de locuri in functie de
implementare.
In mod similar, o linie de comanda de forma:

# include "filename"

cauta mai intii in asociatie cu fisierul original sursa (o exprimare deliberat


dependenta de implementare) si daca cautarea nu reuseste se procedeaza ca in prima
forma. Efectul utilizarii caracterelor ', \, sau /* intr-un filename este
nedefinit, dar caracterul > este permis.
In sfirsit, o directiva de forma:

# include token-sequence

care nu se potriveste cu nici una dintre formele anterioare este interpretata,


prin expandarea secventei de token-uri, ca un text normal; trebuie neaparat sa
rezulte una dintre cele doua forme cu <...> sau "...", in care caz tratarea va fi
asa cum s-a descris mai inainte.
Fisierul inclus prin # include poate, la rindul lui, sa contina linii #
include.

A12.5 Compilare conditionala

Parti ale unui program pot fi compilate conditional, in conformitate cu


urmatoarea sintaxa schematica:

preprocessor-conditional:
if-line text elif-parts else-part(opt) #endif

if-line:
# if constant-expression
# ifdef identifier
# ifndef identifier

elif-parts:
elif-line text
elif-parts(opt)

elif-line:
# elif constant-expression

else-part:
else-line text

else-line:
# else

Fiecare dintre directivele if-line, elif-line, else-line si #endif apare singura


pe o linie. Expresiile constante din liniile #if si urmatoarea linie #elif sint
calculate in ordine pina ce este gasita o expresie cu valoare diferita de zero;
textul ce urmeaza unei linii cu valoare zero este pierdut. Textul ce urmeaza unei
linii directiva ce are succes este tratat normal. Aici "text" inseamna orice
material, inclusiv linii preprocesor, care nu constituie parte a structurii
conditionale; el poate fi vid, absent. O data ce a fost gasita o linie cu #if sau
#elif, ce are succes, iar textul sau este prelucrat, urmatoarele linii #elif si
#else, impreuna cu textul lor sint ignorate. Daca toate expresiile sint zero si
exista o linie #else, textul care ii urmeaza este tratat normal. Textul controlat
de ramurile inactive ale conditionalului este ignorat cu exceptia verificarii
cuibaririi conditionalelor.
Expresia constanta din #if si #elif este supusa unei inlocuiri macro ordinare.
Mai mult, orice expresii de forma:

defined identifier

sau

defined ( identifier )

sint inlocuite, inainte de explorarea pentru macro-uri, printr-un 1L daca


identificatorul este definit in preprocesor sau printr-un 0L daca nu este. Toti
identificatorii ramasi dupa macro expandare sint inlocuiti prin 0L. In sfirsit,
fiecare constanta intreaga se considera a fi urmata de L, astfel incit toata
aritmetica sa fie considerata long sau unsigned long.
Expresia constanta rezultata (A7.19) este restrictionata: ea trebuie neaparat
sa fie integrala si nu poate sa contina sizeof, un cast sau o constanta de
enumeratie.
Liniile de comanda:

#ifdef identifier
#ifndef identifier

sint echivalente, respectiv, cu:

# if defined identifier
# if ! defined identifier

#elif este nou fata de prima editie, desi el a fost


introdus in unele preprocesoare. Operandul defined al
preprocesorului este, de asemenea, nou.

A12.6 Comanda line

Pentru beneficiul altor preprocesoare care genereaza programe C, o linie,


intr-una din formele:

# line constant "filename"


# line constant
il face pe compilator sa creada, in scopuri de diagnosticare a erorilor, ca
numarul de linie, al urmatoarei linii sursa, este dat de constanta intreaga
zecimala iar fisierul de intrare curent este numit prin identificator. Daca
"filename" este absent, numele memorat nu se modifica. Macro-urile din linie sint
expandate inainte ca aceasta sa fie interpretata.

A12.7 Generare de eroare

O linie de preprocesor de forma:

# error token-sequence(opt)

face ca preprocesorul sa scrie un mesaj de diagnostic care include secventa de


token-uri.

A12.8 Pragma

O linie de comanda de forma:

# pragma token-sequence(opt)

face ca preprocesorul sa execute o actiune dependenta de implementare. Un program


nerecunoscut este ignorat.

A12.9 Directiva NULL

O linie de preprocesor de forma:

nu are nici un efect.

A12.10 Nume predefinite

O serie de identificatori sint predefiniti si expandati pentru a produce


informatie speciala. Ei si operatorul de expresie al preprocesorului, defined, nu
pot fi nedefiniti sau redefiniti

__LINE__ O constanta zecimala ce contine numarul curent al


liniei sursa.
__FILE__ Un literal sir cecontinenumele fisieruluice se
compileaza.
__DATE__ Un literal sir ce contine data compilarii, in
forma "Mmm dd yyyy".
__STDC__ Constanta 1. Se considera ca acest identificator
a fost definit ca fiind 1 numai in implementarile
conforme cu standardul ANSI.

#error si #pragma sint introduse o data cu standardul


ANSI; macro-urile predefinite ale preprocesorului sint
noi dar unele dintre ele au fost disponibile in unele
implementari.
A13. Gramatica

Mai jos este prezentata o recapitulare a gramaticii folosite pretutindeni in


partile anterioare ale acestui apendice. Ea are exact acelasi continut, dar intr-o
ordine diferita.
Gramatica nu a definit simbolurile terminale integer-constant, character-
constantt, floating-constant, identifier, string si enumeration-constant;
Cuvintele si simbolurile in stil subliniat (typewriter style) sint terminale, date
literal. Aceasta gramatica poate fi transformata intr-un generator parser,
automat, de intrare, acceptabil. (Prin parser se intelege un analizor-descriptor
de gramatica). Pe linga faptul ca adaugarea unei marcari sintactice oarecare
este folosita pentru a indica alternativele in productii, ea este necesara pentru
expandarea constructiilor "one of" si (in functie de regulile generatorului
parser) sa faca un duplicat fiecarei productii cu un simbol opt, o data cu simbol
si o data fara simbol. Cu o modificare ulterioara, si anume stergerea productiei
typedef-name: identifier si facind pe typedef-name un simbol terminal, aceasta
gramatica este acceptabila pentru generatorul parser YACC. Ea are doar un singur
conflict, generat prin ambiguitatea if-else.

translation-unit:
external-declaration
translation-unit external-declaration

external-declaration:
function-definition
declaration

function-definition:
declaration-specifiers(opt) declarator-
declaration-list(opt) compound-statement

declaration:
declaration-specifiers init-declarator-list(opt) ;

declaration-list:
declaration
declaration-list declaration

declaration-specifiers:
storage-class-specifier declaration-specifiers(opt)
type-specifier declaration-specifiers(opt)
type-qualifier declaration-specifiers(opt)

storage-class-specifier: one of
auto register static extern typedef

type-specifier: one of
void char short int long float double signed unsigned
struct-or-union-specifier enum-specifier
typedef-name

type-qualifier: one of
const volatile

struct-or-union-specifier:
struct-or-union identifier(opt)
{ struct-declaration-list }
struct-or-union identifier

struct-or-union: one of
struct union

struct-declaration-list:
struct-declaration
struct-declaration-list struct-declaration

init-declarator-list:
init-declarator
init-declarator-list , init-declarator

init-declarator:
declarator
declarator = initializer

struct-declaration:
specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list:
type-specifier specifier-qualifier-list(opt)
type-qualifier specifier-qualifier-list(opt)

struct-declarator-list:
struct-declarator
struct-declarator-list , struct-declarator

struct-declarator:
declarator
declarator(opt) : constant-expression

enum-specifier:
enum identifier(opt) { enumerator-list }
enum identifier

enumerator-list:
enumerator
enumerator-list , enumerator

enumerator:
identifier
identifier = constant-expression

declarator:
pointer(opt) direct-declarator

direct-declarator:
identifier
( declarator )
direct-declarator [ const-expression(opt) ]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list(opt) )

pointer:
* type-qualifier-list(opt)
* type-qualifier-list(opt) pointer

type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier

parameter-type-list:
parameter-list
parameter-list , ...

parameter-list:
parameter-declaration
parameter-list , parameter-declaration

parameter-declaration:
declaration-specifiers declarator
declaration-specifiers abstract-declarator(opt)

identifier-list:
identifier
identifier-list , identifier

initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }

initializer-list:
initializer
initializer-list , inializer

type-name:
specifier-qualifier-list abstract-declarator(opt)

abstract-declarator:
pointer
pointer(opt) direct-abstract-declarator

direct-abstract-declarator:
( abstract-declarator )
direct-abstract-declarator(opt)
[ constant-expression(opt) ]
direct-abstract-declarator(opt)
( parameter-type-list(opt) )

typedef-name:
identifier

statement:
labeled-statement
expression-statement
compound-statement
selection-statement
iteration-statement
jump-statement

labeled-statement:
identifier : statement
case const-expression : statement
default : statement

expression-statement:
expression(opt) ;

compound-statement:
{ declaration-list(opt) statement-list(opt) }

statement-list:
statement
statement-list statement

selection-statement:
if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement

iteration-statement:
while ( expression ) statement
do statement while ( expression ) ;
for ( expression(opt) ; expression(opt) ;
expression(opt) ) statement

jump-statement:
goto identifier ;
continue ;
break ;
return expression(opt) ;

expression:
assignment-expression
expression , asignment-expression

assignment-expression:
conditional-expression
unary-expressionassignment-operator
assignment-expression

assignment-operator: one of
= *= /= %= += -= <<= >>= &= ^= |=

conditional-expression:
logical-OR-expression
logical-OR-expression?expression :
conditional-expression

constant-expression:
conditional-expression

logical-OR-expression:
logical-AND-expression
logical-OR-expression || logical-AND-expression

logical-AND-expression:
inclusive-OR-expression
logical-AND-expression && inclusive-OR-expression

inclusive-OR-expression:
exclusive-OR-expression
inclusive-OR-expression | exclusive-OR-expression

exclusive-OR-expression:
AND-expression
exclusive-OR-expression ^ AND-expression

AND-expression:
equality-expression
AND-expression & equality-expression

equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression

shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression

additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression

multiplicative-expression:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression

cast-expression:
unary-expression
( type-name) cast-expression

unary-expression:
postfix-expression
++ unary-expression
-- unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof (type-name )

unary-operator: one of
& * + - ~ !

postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression ( argument-expression-list(opt) )
postfix-expression . identifier
postfix-expression -> identifier
postfix-expression ++
postfix-expression --

primary-expression:
identifier
constant
string
( expression )

argument-expression-list:
assignment-expression
argument-expression-list , assignment-expression

constant:
integer-constant
character-constant
floating-constant
enumeration-constant

Urmatoarea gramatica, pentru preprocesor, recapituleaza structura liniilor de


comanda, dar nu este potrivita pentru un parsing mecanic. Ea include simbolul
text, care inseamna text de program ordinar, linii de comanda preprocesor non-
conditionale sau constructii complete conditionale de preprocesor.

control-line:
# define identifier token-sequence
# define identifier( identifier , ... , identifier )
token-sequence
# undef identifier
# include <filename>
# include "filename"
# include token-sequence
# line constant "filename"
# line constant
# error token-sequence(opt)
# pragma token-sequence(opt)
#
preprocessor-conditional

preprocessor-conditional:
if-line text elif-parts else-part(opt) # endif

if-line:
# if constant-expression
# ifdef identifier
# ifndef identifier

elif-parts:
elif-line text
elif-parts(opt)

elif-line:
# elif constant-expression

else-part:
else-line text

else-line:
# else