Documente Academic
Documente Profesional
Documente Cultură
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.
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
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
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
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:
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.
{ expression(opt) }
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".
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.
A6. Conversii
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 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.
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
A7. Expresii
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.
primary-expression:
identifier
constant
string
( expression )
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
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
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".
cast-expression:
unary-expression
( type-name ) cast-expression
multiplicative-expression:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
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.
equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
Sint realizate conversiile aritmetice uzuale; rezultatul este functia AND la nivel
de biti asupra operanzilor. Operatorul se aplica numai operanzilor integrali.
exclusive-OR-expression:
AND-expression
exclusive-OR-expression ^ AND-expression
inclusive-OR-expression:
exclusive-OR-expression
inclusive-OR-expression | exclusive-OR-expression
logical-AND-expression:
inclusive-OR-expression
logical-AND-expression && inclusive-OR-expression
logical-OR-expression:
logical-AND-expression
logical-OR-expression || logical-AND-expression
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.
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-
expression
assignment-operator: one of
= *= /= %= += -= <<= >>= &= ^= |=
expression:
assignment-expression
expression , assignment-expression
constant-expression:
conditional-expression
declaration:
declaration-specifiers init-declarator-list(opt) ;
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
storage-class-specifier:
auto
register
static
extern
typedef
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
struct-or-union-specifier:
struct-or-union identifier(opt){struct-declaration-
list }
struct-or-union identifier
struct-or-union:
struct
union
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
struct-declarator:
declarator
declarator(opt) : constant-expression
Un specificator de forma:
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.
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.
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.
struct tnode {
char tword[20];
int count;
struct tnode *left;
struct tnode *right;
};
sp->count
s.left
s.right->tword[0]
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
enum-specifier:
enum identifier(opt) { enumerator-list }
enum identifier
enumerator-list:
enumerator
enumerator-list , enumerator
enumerator:
identifier
identifier = constant-expression
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
(D1)
* type-qualifier-list(opt) D1
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:
D1[constant-expression(opt)]
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)
D1(identifer-list(opt))
identifier-list:
identifier
identifier-list , identifier
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.
A8.7 Initializare
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
initializer
initializer-list , initializer
int x[] = { 1, 3, 5 };
float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};
float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};
float y[4][3] = {
{ 1 }, { 2 }, { 3 }, { 4 }
};
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) )
int
int *
int *[3]
int (*)[]
int *()
int (*[])(void)
A8.9 Typedef
typedef-name:
identifier
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;
il redeclara.
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
statement:
labeled-statement
expression-statement
compound-statement
selection-statement
iteration-statement
jump-statement
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
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.
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
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.
iteration-statement:
while ( expression ) statement
do statement while ( expression ) ;
for( expression(opt);expression(opt) ;
expression(opt) ) statement
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.
jump-statement:
goto identifier ;
continue ;
break ;
return expression(opt) ;
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.
function-definition:
declaration-specifiers(opt) declarator-
declaration-list(opt) compound-statement
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list(opt) )
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.
A11.2 Legatura
A12. Preprocesare
# undef identifier
Definitia:
"/usr/tmp" "/%s"
#define cat(x, y) 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.
# include <filename>
# include "filename"
# include token-sequence
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
defined identifier
sau
defined ( identifier )
#ifdef identifier
#ifndef identifier
# if defined identifier
# if ! defined identifier
# error token-sequence(opt)
A12.8 Pragma
# pragma token-sequence(opt)
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
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