y8e8eo
Un program C consta dintr-o secventa de definitii externe. O definitie externa declara un identificator ca avind clasa de memorare extern (in lipsa specificatorului sau static, si un tip specificatn tip specificat. Specificatorul de tip
(vezi 8. 2) poate fi vid, in care caz tipul va fi luat ca si int.
Intinderea unei definitii externe persista pina la sfirsitul fisierului in care a fost declarata asa cum efectul unei declaratii persista pina la sfirsitul unui bloc. Sintaxa defintiilor extern este aceeasi ca a tuturor declaratorilor, cu exceptia ca numai la acest nivel poate fi dat codul pentru functii.
10. 1. definitii defunctie externe
Definitiile de functii au forma: function-definition: decl-specifiersopt function-declarator function-body
Singurii specificatori de clasa de memorare permisa in cadrul specificatorilor de declaratii sint extern sau static; a se vedea &11. 2 pentru distinctia dintre ele. Un declarator de functie este similar cu un declarator pentru "functie care returneaza..." cu exceptia ca el listeaza parametrii formali ai functiei de definit. function-declarator: declarator(parameter-listopt) parameter-list: identifier identifier, parameter-list
Corpul functiei are forma: function-body: declaration-list compound-statement
Identificatorii din lista de parametrii, si numai acesti identificatori, pot fi declarati in lista de declaratii. Orice identificator al carui tip nu este dat se considera a fi de tip int. Singurul tip de clasa de memorare care poate fi specificata este register; daca aceasta e specificata, parametrul actual corespunzator va fi copiat, daca este posibil, intr- un registru in codul codului functiei. Un exemplu simplu de definitie completa de functie este: int max(a, b, c) int a, b, c;
A int m; m=(a>b)?a:b; return((m>p)?m:c);
S
Aici int este specificator de tip;max(a, b, c) este declaratorul de functie int a, b, c; este lista de declaratii pentru parametrii formali; A...S este blocul care da codul pentru instructie.
"C"converteste parametrii actuali de tip float in double, astfel ca parametrii formali declarati float isi vor avea declaratiile ajustate pentru a se citi double. Astfel, daca o referinta la un tablou in orice context (in particular cu un parametru actual)este cnsiderata a avea sensul unui pointer la primul element al unui tablou, declaratiileparametrilor formali de genul "tablou de..." sint ajustate in "pointeri catre...".
In final, intrucit structurile, uniunile si functiile nu pot fi trecute unei functii, este fara utilizare declararea ca parametri formali a unei structuri, uniuni sau functii (pointerii la astfel de obiecte sint permisi).
10. 2. Definitii de date externe
O definitie de data externa are forma: data definition: declaration
Clasa de memorare a unei astfel de date poate fi extern (in lipsa) sau static dar nu auto sau register.
11. Reguli despre domenii
Un program in C poate sa nu fie compilat tot deodata: textul sursa al progarmului poate fi pastrat in mai multe fisiere si rutine precompilate pot fi incaracte din biblioteci.
Comunicatiile intre functiile unui program pot fi apeluri explicite sau utilitare de date externe.
Exista 2 feluri: primul, ce ar putea fi numit obiectivul lexical al unui identificator, care este in esenta regiunea unui program in timpul caruia el poate fi folosit fara a apare eroarea de "identificator nedefinit"; si al 2-lea obiectivul asociat cu identificatori externi care se caracterizeaza prin regula ca referinta la acelasi identificator extern sint referinte la acelasi obiect.
11. 1 Domeniu lexical
Obiectivul lexical al identificatorilor declarati in definitiile externe se intinde de la definitie pina la sfirsitul fisierului sursa in care apare. Intinderea lexicala a indentificatorilor care sint parametri formali persista in functia cu care sint asociati. Scopul lexical al identificatorilor declarati la inceput de bloc tine pina la sfirsitul blocului. Intentia lexicala a etichetelor este in functia in care apar.
Intrucit toate referintele la acelasi identificator extern se refera la acelasi obiect(vezi 11. 2)compilatorul verifica toate declaratiile aceluiasi identificator extern pt compatibilitate; de fapt puterea lor se intinde asupra intregului fisier pe care apar.
In toate cazurile, daca un identificator este explicit declarat la inceputul unui bloc, incluzind blocul care constitue o functie, orice declaratie al acelui identificator in afara blocului este suspendata pina la sfirsitul bocului.
De reamintit (vezi 8. 5) ca identificatorii asociati cu varaibile ordinare pe de o parte si acei asociati cu membrii ai unor structuri sau reuniuni pe de alta parte, frmeaza doua clase disjuncte fara conflict intre ele. Membrii (de reuniuni sau structuri n. t. ) si etichetele urmeaza aceleasi reguli obiectuale ca si identificatorii; numele declarate cu typedef sint de aceasi clasa cu identificatorii ordinari. Ei pot fi redeclarati in blocurile interne, dar un tip explicit se va da in declaratia interioara. typedef float distance;
...
A auto int distance;
Declaratia int trebuie sa fie prezenta in a doua declaratie; in caz contrar va fi considerata fara declaratori si de tipul distance.
11. 2. Domeniul externilor
Daca o functie se refera la un identificator ca fiind extern atunci undeva intre fisierele si bibliotecile din care se constituie programul complet trebuie sa fie o declaratie externa pt acel identificator ca fiind externe, atunci undeva intre fisierele bibliotecilor din care se constituie programul complet trebuie sa fie o declaratie externa pt acel identificator. Toate functiile dintr-un program care se refera la acelasi identificator extern se refera la acelasi obiect, deci trebuie grija ca tipul si dimensiunea specificate in definitie sa fie compatibile cu acelea specificate de fiecare functie care face referire la data respectiva.
Aparitia cuvintului cheie extern intr-o definitie externa indica ca memoria pt identificatorii care se declara va fi alocata in alt fisier. DEci intr-un program multifisier, o definitie la date externe fara specificatorul extern trebuie sa apara exact intr-unul din fisiere. Oricare alte fisiere care doresc sa dea o definitie externa pt identificator trebuie sa includa extern in definitie. Identificatorul poate fi initializat numai in declaratia unde memoria este alocata.
Identificatorii declarati static la nivelul superior din definitiile externe nu sint vizibile in alte fisiere. Functiile trebuie declarate static.
12. Linii de comanda pt compilator
Compilatorul C contine un preprocesor capabil sa faca macrosubstitutii compilari conditionale si includeri de fisiere numite. Liniile care incep cu # comunica cu acest preprocesor.
Aceste linii au sintaxa independenta de restul limbajului; ele pot apare oriunde si au efecte cu remanenta pina la sfirsitul fisierului sursa.
12. 1. Inlocuire de atomi
(N. T. atom element sintactic independent. SEnsul nu poate fi precizat la data traducerii )
O linie de comanda de forma:
#define identifier token-string
(de notat : fara punct virgula final) face ca preprocesorul sa inlocuiasca identificatorul cu sirul dat de atomi. O linie de forma
#define identifier (identifier, ..., identifier)token-string unde nu exista spatiu intre primul identificator si (, este o macrodefinitie cu argumente. Exprimarile ce urmeaza primului identificator urmat de (sint inlocuite de elementele sirului specificat in definitie. Oricare aparitie a unui identificator mentionat in lista de parametri formali ai definitiei este inlocuit de sirul de elemente (atomi) de la apel. Argumentele din apel sint siruri de atomi separati prin virgule; dar virgulele din sirurile intre apostroafe, sau care sint protejate prin paranteze nu separa argumente. Numarul de argumente formale si actuale trebuie sa fie aceleasi. Textul dintr-un sir sau o constanta caracter nu se inlocuieste.
In ambele forme sirul cu care se face inlocuirea este riscant pentru identificatorii mai definiti. O definitie lunga poate fi continuata si pe o alta linie scriind \ la sfirsitul liniei de continuat.
Facilitatea este valoroasa pt definirea "constantelor manifeste "
#define TABSIZE 100 int tableaTABSIZEi; o linie de comanda de forma:
#undef identifier face ca defintia pt procesor a identificatorului sa fie notata.
12. 2. Includeri de fisiere
O linie de forma:
#include "filename" face ca sa inlocuiasca linia cu intregul continut al fisierului
"filename ". Numele este cauata mai intii in directorul fisierului sura original si apoi in alte locuri (biblioteci N. T) standard. Alternativa este
#include <filename> cauta numai in locurile standard nu in directorul din care face parte fisierul sursa. #include poate fi imbricat.
12. 3. Compilarea conditionala
O linie de comanda de forma:
#if constant-expression verifica daca constanta (vezi &15) este nonzero. O linie de forma:
#if def identifier verifica daca identificatorul este definit in procesor, deci daca a fost sau nu subiectul unei linii #define.
O linie de forma:
#ifndef identifier verifica daca identificatorul nu e definit in procesor. Toate cele
3 forme sint urmate de un numar de linii arbitrare si pot contine o linie de comanda.
#else si apoi o linie de comanda
#endif
Daca conditia este adevarata atunci liniile intre #else si
#end if sint ignorate. Daca conditia este falsa atunci liniile intre test si #else sau in lipsa lui #else, #end if sint ignorate.
12. 4. Controlul liniei
Pentru beneficiul altor preprocesoare care genereaza progarmul C
#line constnat identifier face ca numarul liniei curente sa devina egal cu valoarea constantei iar fisierul de intrare curent este denumit de identificator. Daca identificatorul este absent numele fisierului memorat nu se schimba.
13. Declaratii implicite
Nu este todeauna necesar sa se specifice atit clasa de memorare cit si tipul identificatorului in declaratie. Clasa de memoare se extrage din contextul definitiilor externe si din declaratiile parametrilor formali si ai membrilor de structuri.
Intr-o declaratie din interiorul unei functii daca nu se da clasa de memorare sau tipul, identificatorul se presupune de tipul int; daca se indica tipul dar nu se indica clasa de memorare identificatorul se presupune a fi auto. O exceptie a ultimei reguli se face pt functii, functiile de tip auto neavind sens (C nu e capabil sa compileze codul intr-o stiva ); daca tipul identificatorului este " functia care returneaza..." el este implicit declarat extern. Intr-o expresie un identificator urmat de ( si nedeclarat inca este contextual declarat ca fiind
"functie care returneaza un int ".
14. Revedere asupra tipurilor
Aceasta sectiune rezuma operatiile care pot fi realizate asupra obiectelor de diverse tipuri.
14. 1. Structuri si uniuni
Asupra unei structuri sau marimi se pot face doua lucruri: numirea (N. T. apelarea) unia din membrii sai ( cu ajutorul operatorului . ) sau obtinerea adresei sale (cu ajutorul operatorului &). Alte operatii, cum ar fi asigurarea valorii sale sau unei valori sau transmiterea lui ca si parametru conduc la un mesaj de eroare. In viitor se preconizeaza ca aceste operatii dar nenecesar altele sa fie permise.
&7. 1 arata ca intr- referinta directa sau indirecta la o structura (cu ajutorul lui -sau -> ) numele din dreapta trebuie sa fie membru al structurii numite sau sa fie indicat (pointat
)de expresia din stinga. Ca o iesire din regulile privind tipurile aceasta regula privind restrictia nu este strict restrictiva. De fapt inainte de. se permite o lvaloare si acea lvaloare se presupune a avea forma structurii din care face parte numele pus in dreapta (operatorului ). Deci expresia din fata lui -> poate fi pointer sau intreg. Daca este pointer se presupune ca indica structura din care face parte membrul drept.
Daca e un intreg el va fi o expresie de adresa absoluta in unitati de memorie ai masinii al structurii corespunzatoare.
Astfel de constructii nu sint portabile.
14. 2. Functii
Cu o functie se pot face doua lucruri: apelul ei sau obtinirea adresei ei. Daca numele unei functii apare intr-o expresie si nu in apelul de functie se genereaza un pointer la functia respectiva. Trecerea unei functii alteia: int f();
.
.
. g(f)
Definitia lui g va fi: g(functia) int (*funcp)();
A...
(*funcp)();
S
De notat ca f trebuie declarata explicit in rutina apelanta pt ca aparitia sa in g(f) nu este urmata de (.
14. 3. Tablouri, pointeri si indici
De fiecare data cind un identificator de tipul tablou apare intr o expresie el este convertit intr-un pointer catre primul element al tabloului. Din cauza acestei conversii tablourile nu sint lavlori. Prin definitie operatorul indice ai este interpretat de asa maniera incit E1aE2i e identic cu *((E1)+(E2)).
Din cauza regulilor de conversie care se aplica lui +, daca E1 este tablou si E2 intreg atunci E1(E2) se refera la al E2 lea membru al lui E1. Darin ciuda acestei aparente asimetrii, indicii formeaza o operatie comutativa.
O regula soloida se aplica in cazul tablourilor multidimensionale. Daca E este un tablou de ordinul "n" si indici ixjx...xk atunci cind E apare intr-o expresie el va fi convertit intr-un pointer la un tablou cu "n-1" dimensiuni cu indici jx...xk. Daca se aplica operatorul *, explicit sau implicit ca rezultat al utilizarii indicilor, rezultatul este un tablou pointat de n-1 dimensiuni, care este imediat convertit intr-un pointer. De exemplu: int xa3ia5i;
Unde x este un tablou de intregi cu 3X5 elemente. Cind x apare intr-o expresie, este convertit intr-un pointer catre(primul din cele 3) tabloul de 5 membrii intregi. In expresia xaii, care e echivalenta cu *(x+i), X este mai intii convertit intr un pointer asa cum s-a descris;i este apoi convertit in tipul lui x, care implica inmultirea lui i cu lungimea obiectului spre care pointeaza, adica 5 obiecte intregi.
Rezultatul se aduna si se aplica indirectarea producind un tablou(de intregi) care este la rindul sau transformat intr-un pointer la primul dintre intregi. Daca mai este un indice acelasi procedeu se aplica din nou; acum rezultatul va fi un intreg.
Rezulta ca in C tablourile sint stocate pe linii (ultimul indice variaza cel mai repede ) si ca primul indice din declaratie permite sa se de termine necesarul de memorie dar nu are alt rol in calculul indicilor.
14. 4. Conversii de pointeri explicite
Unele conversii de pointeri sint permise doar au aspecte dependente de implementare. Toate se specificaprin conversii explicite de tip ca in &7. 2 si &8. 7.
Un pointer se poate converti in oricare tip de intreg suficient de mare pentru a-l pastra. Dar a utiliza int sau long este dependent de masina. Functiile de mapare sint dependente de, dar nu vor surprinde pe aceea care cunosc structura de adresare a masinii. Detalii se dau mai jos.
Un obiect de tip intreg poate fi convertit explicit in pointeri.
Maparea face ca un intreg convertit din pointer sa dea acelasi pointer, depinzind de masina. Un pointer catre un tip poate fi convertit intr-un pointer la alt tip. Pointerul rezultat da exceptii de adresare la folosire daca pointerul subiect nu se refera la un obiect aliniat corespunzator in memorie. Se garanteaza ca un pointer la un obiect de o marime data poate fi convertit intr-un pointer catre un obiect mai mic in dimensiune si inapoi fara modificari.
De exemplu, rutina de alocare de memorie poate accepta o marime (in biti, a unui obiect de alocat si sa returneze un pointer char; acesta putind fi folosit conform scopului. extern char *alloc(); double *dp; dp=(double*)alloc(sizeof(double));
*dp=22. 0/7. 0; alloc trebuie sa asigure (de o maniera dependenta de masina) ca valoarea pe care o returneaza este corespunzatoare connversiei intr-un pointer catre double < atunci utilizarea functiei este portabila.
Reprezentarea pointerului la PDP-11 este un intreg de 16 biti si se masoara in bytes; chars nu necesita aliniere speciala; orice altceva trebuie sa aiba o adresa para.
Pe Honeywell un pointer are 36 de biti si e intreg; adresa de cuvint e pe cei 18 biti din stinga, iar bitii care selecteaza caracterul din cuvint in partea dreapta; deci pointerii char sint masurati in unitati de 2 la 16 bytes; orice altceva in unitati de 2 la 18 cuvinte; double si agregatele care le contin trebuie sa fie la adresa de cuvint para.
IBM 370 si Interdata 8/32 sint similare. La ambele adresele se masoara in bytes; obiectele elementare sint aliniate la limite egale cu lungimea lor; pointeri catre short sint0 mod 2, catre int sau float 0 mod 4 si la double 0 mod 8. Agregatele sint aliniate la limitele stricte necesitate de constituenti.
15. Expresii "constante"
In multe locuri C necesita expresii care dau o constanta: dupa case, ca limite de tablouri, la initializari. In primele 2, expresiile pot cuprinde doar constante intregi, constante caracter, expresii tip sizeof conectate la operatorii binari:
+ - * / % & \ ^ << >> == != <> <= >=
Sau prin operatorii unari:
- I
Sau prin operatorul ternar:
? :
Parantezele sint folosite pt grupare, nu pt apeluri de functii. O latitudine mai mare o permit initializarile; in afara de expresiile constante se poate aplica operatorul unar & la obiecte externe sau statice, sau la tablouri externe sau statice avind ca indici expresii constante.
Operatorul unar & poate fi aplicat implicit prin aaparitia de tablouri fara indici sau functii. Regula de baza este ca initializarile conduc la o constanta sau la o adresa a unui obiect extern sau static plus sau minus o constanta.
16. Consideratii de portabilitate
Unele parti din C sint inerent dependente de masina. Lista care urmeaza contine sursele de prorbleme cele mai importante:
Lungimea cuvintului de memorie si proprietatea aritmeticei in VF sau impartirea intregilor au dovedit in practica ca nu sint o problema. Alte fatete ale hardware-ului sint reflectate in diversele implementari. Unele din ele, ca extensia de semn ( convertirea unui caracter negativ intr-un inttreg) si ordinea bytelor in cuvint, sint neplaceri care trebuie observate atent.
Altele sint probleme minore.
Numarul de variabile register care pot fi plasate efectiv in registre depind de la masina la masina, ca si seturi de tipuri valide. Compilatoarele fac lucrurile corespunzator propriei masini; declaratiile register excesive sau invalide sint ignorate.
Unele dificultati cresc cind se folosesc practici de codificare dubioase. Nu este recomandat a se scrie programe care depind de aceasta proprietate.
Ordinea de evaluare a argumentelor functiilor nu este specifi cata de limbaj. La PDP-11 este de la dreapta la stinga, la altele de la stinga la dreapta. Ordinea de aparitie a efectelor secundare nu e specificata.
Daca constantele caracter sint obiecte de tipul int, constantele caracter multicaracter sin permise. Implementarea specifica depinde de masina pt ca ordinea in care caracterele sint plasate in cuvint variaza de la o masina la alta.
Cimpurile apar in cuvint si caracterele in intregi de la dreapta la stinga in PDP si de la stinga la dreapta in alte masini. Aceste diferente sint invizibile in programe izolate care nu fac apel la dependente de tip (de exemplu conversia din pointer int in pointer char si implementarea memoriei pointate) dar trebuie considerate daca se doreste conformarea cu modalitatile de memorare externa.
Limbajul acceptat de diversele compilatoare difera putin. Dar, compilatorul pt PDP=11 folosit curent nu initializeaza structuri de cimpuri de biti, nu accepta operatori de asignare in unele contexte in care se foloseste valoarea asignarii.
17. Anacronisme
C este un limbaj in evolutie, unele constructii invechite pot fi intilnite in programe mai vechi. Unele compilatoare suporta aceste anacronisme, ele fiind pe cale de disparitie, raminind doar problema portabilitatii.
Versiunile mai vechi de C permiteau forma =op in locul lui op= pt asignare. Acestea duceau la ambiguitati, cum ar fi x=-1 care acum inseamna scaderea unui 1 din x, = si - fiind adiacente, dar care poate insemna si asignarea lui -1 lui x.
Sintaxa initializarilor s-a schimbat; la inceput semnul = care anunta un initializator nu era prezent, adica in loc de int x = 1; se folosea int x 1; modificarea s-a facut pentru ca initializarea int f(1+2) semana cu o declaratie de functie atit de mult incit deranja compilatoarele.
18. Sumar al sintaxei
Acest numar al sintaxei C-ului are scopul de a ajuta intelegerea sa mai mult decit de a defini exact limbajul.
18. 1. Expresii
Expresii de baza sint: expression: primary
*expression
&expression
-expression
!expression
Iexpression
++lvalue
--lvalue lvalue++ lvalue- sizeof expression
(type-name)expression expression binop expression expression?expression:expression lvalue asgnop expression exprerssion, expression primary: identifier constant string
(expression) primary(expression-listopt) primaryaexpressioni lvalue. identifier primary->identifier lvalue: identifier primaryaexpressioni lvalue. identifier primary->identifier
*expression
(lvalue)
Operatorii expresiilor primare
() ai. -> au cea mai mare prioritate si grupeaza stinga la dreapta.
Operatorii unari:
* & - ! I ++ -- sizeof(type-name) au prioritatea sub cea a operatorilor primari dar mai mare decit a operatorilor binari si grupeaza de la dreapta la stinga.
Operatorii binari si operatorul conditional grupeaza stinga la dreapta si au priortatea descrescind asa cum e indicat binop:
* / %
+ >> <<
< > <= >=
== !=
&
^
|
&&
||
?!
Operatorii de asignare au aceeasi prioritate asgnop:
= += -= *= /=, %= >>= <<= &= ^= \=
Operatorul, au cea mai mica prioritate, grupeaza stinga spre dreapta
18. 2. Declaratii declaration: decl-specifiers init-declarator-listopt; decl-specifiers: type-specifier decl-specifiersopt
-specifier decl-specifiersopt sc-specifier: auto static extern register typedef type-specifier: char short int long unsigned float double struct-or-union-specifier typedef-name init-declaration-list: init-declarator init-declarator, init-declarator-list init-declarator: declarator initializeropt declarator: identifier
(declarator)
*declarator declarator() declaratoraconstant-expressionopti struct-or-union-specifier: structAstruct-decl-listS struct identifierAstruct-decl-listS struct identifier unionAstruct-decl-listS union identifierAstruct-decl-listS union identifier struct-decl-list: struct-declaration struct-declaration struct-decl-list struct-declaration: type-specifier struct-declarator-list; struct-declarato-list: struct-declarator struct-declarator, struct-declarator-list struct-declarator: declarator declarator:constant-expression
:constant-expression initializer:
=expression
=Ainitializer-listS
=Ainitializer-list, S initializer-list: expression initializer-list, initializer-list
Ainitializer-listS type-name: type-specifier abstract-declarator abstract-declarator: empty
(abstract-declarator)
*abstract-declarator abstract-declarator() abstract-declaratoraconstant-expressionopti typedef-name: identifier
18. 3. Enunturi compound-statement:
Adeclaration-listopt statement-listoptS declaration-list: declaration declaration declaration-list statement-list statement statement statement-list statement: compound-statement expression; if(expression)statement if(expression)statement else statement
while(expression)stament do statement while(expression); for(expression-1opt;expression-2opt;expression-3opt)statement switch(expression)statement case constant-expression:statement default:statement break; continue; return; return expression; goto identifier; identifier:statement
;
18. 4. Definitii externe program: external-definition external-definition program external-definition: function-definition data-definition function-definition type-specifieropt function-declarator function-body function-declarator declarator(parameter-listopt) parameter-list identifier identifier, parameter-list function-body: type-decl-list function-statement function-statement
Adeclaration-listopt statement-listS data-definition externopt type-specifieropt init-declarator-listopt staticopt type-specifieropt init-declarator-listopt
18. 5. Preprocesor
#define identifier token-string
#define identifier(identifier, ..., identifier)token-string
#undef identifier
#include "filname"
#include <filname>
#if constant-expression
#ifdef identifier
#ifndef identifier
#else
#endif
#line constant identifier