Capitolul 4.5. Reguli de domeniu

Vezi subiectul anterior Vezi subiectul urmator In jos

Capitolul 4.5. Reguli de domeniu

Mesaj  zooky la data de Mier Mar 18, 2009 12:19 pm

Functiile si variabilele externe care compun un program C nu trebuie sa fie compilate toate in acelasi timp; textul sursa al programului poate fi pastrat in mai multe fisiere iar rutinele compilate anterior pot fi incarcate din biblioteci. Cele doua intrebari care prezinta interes aici sint:

Cum sint scrise declaratiile astfel incit variabilele sa fie declarate cum se cuvine in timpul compilarii ?

Cum sint fixate declaratiile astfel incit toate piesele sa fie conectate cum se cuvine atunci cind programul este incarcat ?


Domeniul unui nume este acea parte de program in care numele este definit. Pentru o variabila automata declarata la inceputul unei functii, domeniul este functia in care numele este declarat si variabilele cu acelasi nume in functii diferite sint fara legatura unele cu altele. La fel se intimpla si cu argumentele functiilor .

Domeniul unei variabile externe dureaza din punctul in care ea este decalrata intr-un fisier sursa pina la sfirsitul acelui fisier. De exemplu, daca val,sp,push,pop,clear sint definite intru-un fisier in ordinea de mai sus, adica:

int sp = 0;
double val[MAXVAL];
double push(f) {...}
double pop() {...}
clear() {...}

atunci variabilele val si sp pot fi folosite in push ,pop si clear pur si simplu numindu-le; nu sint necesare declaratii suplimentare . Pe de alta parte, daca o variabila externa trebuie sa fie referita inainte de a fi definita sau este definita intr-un alt fisier sursa decit cel in care este folosita, arunci este necesara o declaratie"extern".

Este important sa distingem intre declaratia unei variabile externe si definitia sa. O declaratie anunta proprietatile unei variabile (tipul marimea, etc); o definitie provoaca in plus o alocare de memorie. Daca liniile:

int sp;
double val[MAXVAL];

apar in afara oricarei functii, ele definesc variabilele externe sp si val, provoaca o alocare de memorie pentru ele si servesc in plus ,ca declaratie pentru restul fisierului sursa. Pe de alta parte liniile

extern int sp;
extern double val[];

declara pentru restul fisierului sursa ca sp este un int si ca val este un tablou double (a carei dimensiune este determinata altundeva ),dar ele nu creaza variabilele si nici nu aloca memorie pentru ele .
Trebuie sa existe o singura definitie pentru o variabila externa in toate fisierele care compun programul sursa; alte fisiere pot contine declaratii extern pentru a o accede. (Poate exista o declaratie extern si in fisierul ce contine definitia).
Orice initializare a unei variabile externe se face numai in definitie. Dimensiunile de tablouri trebuie specificate cu definitia dar sint optionale cu o declaratie externa. Cu toate ca nu este o organizare adecvata pentru acest program ,val si sp pot fi definite si initializate intr-un fisier iar functiile push, pop si clear definite intr-altul. Aceste definitii si declaratii ar trebui legate impreuna astfel:

In fisierul 1:

int sp=0; /* pointerul de stiva */
double val[MAXVAL]; /* valoarea stivei */

In fisierul 2:

extern int sp;
extern double val[];
double push(f) {...}
double pop() {...}
clear () {...}

Deoarece declaratiile extern din fisierul 2 se gasesc in fata si in afara celor trei functii, ele se aplica tuturora; un set de declaratii este suficient pentru tot fisierul 2.
Pentru programe mai mari, facilitatea de includere in fisier "#include" care va fi discutata mai tirziu in acest capitol, permite unui utilizator sa pastreze o singura copie a declaratiilor "extern" pentru programul dat si sa o insereze in fiecare fisier sursa care trebuie compilat.
Ne vom intoarce acum la implementarea lui getop, functia care aduce urmatorul operator sau operand. Lucrarea de baza este usoara: se sar blancurile, taburile si liniile noi. Daca urmatorul caracter nu este o cifra sau punctul zecimal, returneaza-l.Astfel, colecteaza un sir de cifre (care poate include si punctul zecimal) si returneaza NUMBER, care semnaleaza faptul ca s-a colectat un numar.
Rutina este complicata substantial de incercarea de a minui in mod potrivit situatia in care numarul de intrare
este prea lung getop citeste cifrele (probabil si un punct zecimal) atita timp cit le gaseste dar le memoreaza numai pe acelea care incap. Daca numarul nu a fost prea lung (nu s-a produs o depasire ) functia returneaza NUMBER si sirul de cifre. Daca numarul a fost prea lung totusi getop elimina restul liniei de intrare asa ca utilizatorul poate retipari simplu linia din punctul de eroare; functia returneaza TOOBIG drept semnal pentru depasire:

getop(s, lim) /* obtine urmatorul operand sau operator */
char s[];
int lim;
{
int i, c;
while ((c = getch()) == ' ' || c == '\t' || c == '\n')
;
if(c != '.' && (c < '0' || c>'9'))
return(c);
s[0] = c;
for (i = 1; c = getchar()) >= '0' && c <= '9'; i++)
if (i < lim)
s[i] = c;
if (c == '.') { /* fractia */
if (i < lim)
s[i] = c;
for (i++; (c = getchar()) >= '0' && c <= '9'; i++)
if (i < lim)
s[i] = c;
}
if (i < lim) { /* numarul este ok */
ungetch(c);
s[i] = '\0';
return(NUMBER);
} else { /* numar prea mare, se sare restul liniei */
while (c != '\n' && c != EOF)
c = getchar();
s[lim-1] = '\0';
return(TOOBIG)
}
}

Ce sint getch si ungetch ? Se intimpla adesea cazul ca un program care citeste date de intrare nu poate determina daca a citit destul pina cind a ajuns sa citeasca prea mult. Un exemplu este colectarea de caractere ce alcatuiesc un numar: pina cind nu se intilneste un caracter necifra, numarul nu este complet. Dar atunci programul a citit un caracter mult mai necesar, caracter ce nu a fost pregatit pentru aceasta.
Problema ar putea fi rezolvata daca ar fi fost posibil sa "nu citim" caracterul nedorit. Apoi, de fiecare data cind programul citeste un caracter prea mult, el il poate pune inapoi in intrare, asa ca restul codului se va comporta ca si cind nu a fost citit niciodata. Din fericire este usor de simulat necitirea unui caracter, scriind o pereche de functii de cooperare.
getch descopera urmatorul caracter de intrare ce trebuie considerat; ungetch pune caracterul inapoi in intrare, asa ca urmatorul apel al lui getch il va returna din nou. Modul in care lucreaza aceste functii impreuna este simplu. ungetch pune caracterul intr-un buffer partajabil un tablou de caractere ,getch citeste din buffer pentru a vedea daca exista vreun caracter si apeleaza pe getchar daca bufferul este vid. Trebuie deasemenea sa existe o variabila index care inregistrwaza pozitia caracterului curent din buffer.
Deoarece bufferul si indexul sint partajate de getch si ungetch si trebuie sa-si retina valorile lor intre apeluri, ele trebuie sa fie externe ambelor rutine. Deci putem scrie getch si ungetch precum si variablelelor partajate astfel:

#define BUFSIZE 100
char buf[BUFSIZE]; /* bufferul pentru ungetch */
int bufp = 0 /* urmatoarea pozitie libera din buffer */
getch() /* ia un posibil caracter din buffer */
{
return((bufp > 0) ? buf[--bufp] : getchar());
}
ungetch(c) /* pune caracterul la loc in intrare */
int c;
{
if (bufp > BUFSIZE)
printf("ungetch: prea multe caractere\n");
else
buf[bufp++] = c;
}

Am folosit un tablou pentru buffer si nu un singur caracter deoarece generalitatea programului se va observa mai tirziu.

Exercitiul 4.4. Scrieti o rutina ungets(s) care va depune inapoi in intrare un sir intreg de caractere. Cum credeti ca ar fi mai bine , folosind ungetch sau folosind buf si bufp ?

Exercitiul 4.5. Presupunem ca in buffer nunva fi niciodata mai mult de un caracter. Modificati in consecinta getch si ungetch.

Exercitiul 4.6. Functiile noastre getch si ungetch nu minuiesc EOF-ul intr-un mod portabil. Decideti ce proprietati ar trebui sa aibe acestea pentru a minui un EOF apoi implementati-le.
avatar
zooky
Moderator
Moderator

Numarul mesajelor : 147
Data de inscriere : 15/03/2009
Varsta : 24
Localizare : Cernatesti City

Vezi profilul utilizatorului http://e-learning.forumhit.ro

Sus In jos

Vezi subiectul anterior Vezi subiectul urmator Sus


 
Permisiunile acestui forum:
Nu puteti raspunde la subiectele acestui forum