Limbajul C

  • Uploaded by: Radu
  • 0
  • 0
  • June 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Limbajul C as PDF for free.

More details

  • Words: 1,788
  • Pages: 9
IDENTIFICATORI ŞI DOMENIILE ACESTORA PROGRAMAREA MODULARĂ ÎN LIMBAJUL C

Paradigme de programare: - programarea structurată – bazată pe Teorema de structură - programarea procedurală – bazată pe proiectarea de funcţii separate - programarea modulară – bazată pe alcătuirea de proiecte din mai multe fişiere - programarea orientată obiect – bazată pe moştenire şi polimorfism Un modul este constituit dintr-un set de funcţii înrudite împreună cu datele pe care acestea le prelucrează, stocate într-un fişier care se compilează separat. Aplicaţia se dezvoltă sub forma unui proiect prin intermediul căruia se ”leagă” (link-editează) împreună diversele module componente, creându-se un executabil unic. Programarea modulară ridică o serie de probleme legate de relaţia de interdependenţă dintre modulele unui proiect precum şi de modul de comunicare dintre acestea. Fiecare modul trebuie să aibă o interfaţă care să furnizeze compilatorului informaţii necesare despre funcţiile pe care le conţine. În limbajul C un identificator (nume) desemnează o funcţie, un tip de date, o variablă sau o etichetă. În general un identificator este introdus printr-o declaraţie (excepţie fac în C etichetele) care precizează implicit semnificaţia/modul de utilizare al acestuia . În privinţa locurilor în care poate fi folosit identificatorul respectiv, trebuie lămurite câteva aspecte : - un identificator poate fi folosit numai într-o anumită zonă a textului sursă al programului, numită domeniul de valabilitate al numelui. - semnificaţia unui identificator depinde de locul în care se foloseşte numele respectiv, semnificaţie precizată de aşa numitul domeniu de vizibilitate. - un nume folosit în mai multe module ale aceluiaşi program poate sau nu să refere acelaşi obiect, în funcţie de tipul de legătură (linkage) specificat în modulul care îl utilizează. - în ceea ce priveşte numele ce desemnează variabile, acestora li se asociază implicit o zonă de memorie. Zona de memorie în care se face alocarea este definită de clasa de alocare (memorie).

Diagrama ilustrează didactic o posibilă organizare a unui proiect main.cpp #include “f1.h” #include “f2.h” int i; double x; int main(void) { f1(); f2(); return 0; } f1.h void f1(void); f1.cpp #include “f1.h” extern int i; void f1(void) { …. …. }

f2.h void f2(void); f2.cpp #include “f2.h” extern double x; void f2(void) { …. …. }

Obs. Evident, fişierele componente pot conţine mai multe funcţii !

Domeniul de valabilitate al unui identificator Domeniul de valabilitate al unui identificator reprezintă acea zonă de program în care este cunoscută (valabilă) declaraţia sau definiţia unui nume şi în care, în cazul variabilelor, există o zonă de memorie alocată acestuia . În C există trei tipuri de domenii de valabilitate : - local; - funcţie; - global (fişier). În C se numeşte bloc o zonă de program ce conţine instrucţiunile cuprinse în interiorul unei perechi de paranteze acolade. Domeniul local Un identificator definit în interiorul unui bloc se spune că are domeniul de valabilitate local (acelui bloc). El este cunoscut şi se poate folosi doar în interiorul blocului în cauză, mai precis, din punctul imediat ulterior declaraţiei sau definiţiei sale şi până la paranteza } care se împerechează cu cea mai apropiată (textual) paranteză { ce precede textul declaraţiei în cauză . Domeniul funcţie Domeniul funcţie se referă în special la cazul etichetelor pentru care domeniul de valabilitate este dat de punctul în care ele sunt folosite şi până la sfârşitul funcţiei respective. Domeniul global (fişier) Un identificator definit în afara tuturor blocurilor (practic în afara corpului oricărei funcţii) se spune că are domeniul de valabilitate fişier sau global. El se poate folosi în întreg fişierul în care a fost definit, din punctul imediat următor declaraţiei sau definiţiei sale. De exemplu pentru o variabilă, definiţia unei variabile globale coincide cu o declaraţie obişnuită, care este însă scrisă în afara corpului oricărei funcţii a programului. De obicei o astfel de definiţie se scrie la începutul fişierului, fiind valabilă până la sfârşitul fişierului sursă respectiv.

Domeniul de vizibilitate al unui identificator Semnificaţia unui nume (identificator) depinde de zona de program în care acesta se utilizează şi de tipul expresiei prin care de face accesul (în C++). Domeniul de vizibilitate defineşte acea zonă de program în care un nume îşi păstrează aceeaşi semnificaţie (aceleaşi atribute). Un nume se poate utiliza numai în interiorul domeniului său de vizibilitate. În general domeniul de vizibilitate (scope) coincide cu domeniul de valabilitate, cu excepţia situaţiilor în care, în blocuri imbricate, apar declaraţiile unor omonime. Semnificaţia unui nume într-un anumit punct al unui program este dată de cea mai apropiată declaraţie care precede punctul în cauză Domeniul de vizibilitate al unui nume este domeniul de valabilitate din care se exclud blocurile incluse ce conţin declaraţia unui omonim. Utilizarea parametrilor şi a variabilelor globale Atât variabilele globale, cât şi parametrii reprezintă interfeţe între funcţii, implementând o posibilitate de comunicare între acestea. Aşa cum s-a arătat, transferul parametrilor în C este unilateral, de la funcţia apelantă la funcţia apelată. Deşi ar părea ca avantajoasă utilizarea variabilelor globale ca modalitate de transfer bidirecţional a informaţiei între funcţii, acest mod de lucru nu este în general recomandat, fiind o potenţială sursă de erori. Există astfel posibilitatea ca o funcţie să modifice valoarea unei variabile globale, fapt ce poate avea consecinţe nefavorabile pentru restul funcţiilor ce folosesc variabila respectivă. Un astfel de efect se numeşte “efect lateral” (în limba engleză “side effect”).

Exemplul 1: #include <stdio.h> int i = 1;

/* i global */

int main(void) *----- { ¹ ¹ printf("%d\n", i); /* afiseaza 1 */ ¹ ¹ *--- { ¹ ² int i = 2, j = 3; /* i si j locale */ ¹ ² /* definitia globala a lui i e ascunsa */ ¹ ² printf("%d\n%d\n", i, j); /* afiseaza 2, 3 */ ¹ ² ¹ ² *-{ ¹ ²³ int i = 0; /* i este redefinit intr-un bloc indentat */ ¹ ²³ /* definitiile anterioare ale lui i sunt ascunse */ ¹ ²³ printf("%d\n%d\n", i, j); /* afiseaza 0, 3 */ ¹ ² *-} ¹ ² ¹ ² printf("%d\n", i); /* afiseaza 2 */ ¹ ² ¹ *--- } ¹ ¹ printf("%d\n", i); /* afiseaza 1 */ ¹ ¹ return 0; ¹ *------ }

Exemplul 2: #include <stdio.h> int x,y; void subpr(float x) { int z; x=1.0; y=1; z=1; printf(“Subpr: x=%g\ty=%d\tz=%d\n”,x,y,z); } int main(void) { x=0; y=0; printf(“Main I: x=%g\ty=%d\n”,x,y); subpr(x); printf(“Main II: x=%g\ty=%d\n”,x,y); return 0; }

Tipul de legătură al unui identificator În cazul în care un program se compune din mai multe module (mai multe fişiere sursă), o variabilă globală poate fi utilizată într-un fişier sursă în care nu este definită, doar dacă este declarată ca variabilă externă în acel fişier. O declaraţie de variabilă externă coincide cu o declaraţie de variabilă obişnuită, precedată de cuvântul rezervat extern. Restul identificatorilor se spune că au legătură internă. Un nume cu domeniul global prefixat de cuvântul rezervat static este local modulului, având legătură internă. Clase de alocare Clasele de alocare precizează locul şi momentul în care se rezervă memorie pentru o anumită variabilă. Există trei clase de alocare : - automatic; - registru; - static. Clasa de alocare automatic Clasa de alocare automatic este referită prin intermediul cuvântului rezervat automatic, menţiune implicită pentru variabilele locale (dacă nu este specificată nici o altă clasă de alocare, declaraţiile din interiorul unei funcţii crează variabilele din clasa automatic) . Alocarea automatică presupune rezervare de memorie, în momentul execuţiei, într-o zonă specială numită stivă (stack), o mulţime ordonată de celule de memorie la care se poate avea acces în acest caz conform principiului LIFO (Last In First Out) - ultimul intrat, primul servit. Ultimul element al stivei se numeşte vârful stivei. Accesul la stivă se face numai prin vârful acesteia. La adăugarea unui element în stivă dimensiunea acesteia creşte, iar la scoaterea unui element din stivă, dimensiunea acesteia se micşorează. Variabilele locale se alocă pe stivă, atunci când nu se precizează explicit un alt mod de alocare. La apelul unei funcţii, variabilelor automatice li se rezervă memorie pe stivă, iar când se revine din funcţie, la încheierea execuţiei funcţiei, variabilele respective se dezalocă şi stiva revine la statrea de dinaintea apelului. Aceasta înseamnă că variabilele automatice se “nasc” (apar) şi “mor” (dispar) odată cu funcţia, de aceea, la apeluri diferite ale funcţiei, ele pot fi alocate la adrese diferite.

Clasa de alocare registru Variabilele se pot aloca în registrele unităţii centrale, aparţinând în acest caz clasei de alocare registru, specificată prin cuvântul rezervat register. Se pot aloca în acest mod doar variabilele de tip întreg, caracter şi pointer. Parametrii formali pot fi declaraţi din clasa register, aceasta neafectând modul lor de transmitere către funcţie (care rămâne prin intermediul stivei), ci numai faptul că, pe durata execuţiei funcţiei vor fi plasaţi în registrele unităţii centrale. Există următoarele restricţii cu privire la clasa de alocare registru: - nu se pot folosi pointeri pentru a referi variabile alocate registru; - nu se poate folosi clasa de alocare registru pentru variabile globale; - unei variabile alocate registru nu i se poate aplica operatorul adresă (&). Clasa de alocare static Variabilele din clasa static sunt alocate într-o zonă de memorie special destinată acestui scop (nu pe stivă). Ele sunt alocate la compilare, la adrese fixe de memorie, neschimbate pe întreaga durată a execuţiei programului. Alocarea statică este specificată prin intermediul cuvântuilui rezervat static. De exemplu tablourile de dimensiuni mari se recomandă a fi alocate static pentru a nu încărca stiva. Variabilele statice se pot declara atât în corpul unei funcţii, cât şi în afara corpului oricărei funcţii, cu alte cuvinte pot avea domeniul de valabilitate local sau global (fişier). Variabilele globale alocate static nu pot avea însă legătură externă (ele nu pot fi declarate extern). Modul de alocare are repercursiuni şi asupra modului de iniţializare a valorilor variabilelor şi asupra posibilităţilor de păstrare a valorilor de la un apel la altul al funcţiei în care sunt definite. Variabilele din clasa automatic şi registru nu-şi păstrează valorile de la un apel la altul, fiind alocate ad-hoc. De aceea, la fiecare apel ele trebuie iniţializate. În cazul variabilelor statice, iniţializarea acestora se va face o singură dată, în momentul alocării. Ele îţi păstrază însă valorile de la un apel la altul al funcţiei. Pentru a extinde cele de mai sus şi pentru cazul funcţiilor, se poate spune că funcţiile sunt analoge variabilelor globale cu legătură externă. Ele pot fi utilizate (apelate) în orice modul al programului, dacă apelul este precedat în modulul respectiv fie de definiţia funcţiei, fie de prototipul ei. În acest caz, prototipul funcţiei înlocuieşte declaraţia de extern a variabilei globale.

Exemplul 3: …. int i for(i=1; i<4; i++) { static int j=0; j++; printf(“i=%d\tj=%d\n”,i,j); } Exemplul 4: main.cpp #include “f1.h” #include “f2.h” static int i; static double x; int main(void) { f1(); f2(); return 0; } f1.cpp #include “f1.h” extern int i ; -- EROARE la link - unresolved externals void f1(void) { …. }

Related Documents


More Documents from ""