Sunteți pe pagina 1din 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 f2.h

void f1(void); void f2(void);

f1.cpp f2.cpp

#include “f1.h” #include “f2.h”


extern int i; extern double x;
void f1(void) 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)
{
….
}

S-ar putea să vă placă și