Sunteți pe pagina 1din 46

VII. Compilarea independent. Date abstracte 1. Crearea unui executabil din mai multe fiiere surs 2.

Fiiere antet 3. Variabile externe 4. Variabile i funcii statice 5. Tipuri de date abstracte

Compilarea independent
Principii
Codul surs al unui program nu este ntotdeauna coninut n acelai fiier. De exemplu, definiiile funciilor standard, care sunt apelate din majoritatea programelor, nu sunt scrise n fiierele din care se realizeaz apelul. Se vor prezenta modalitile n care se poate face mprirea codului surs al unui program n mai multe fiiere, n special pentru a obine o mai bun organizare a programului. Se vor discuta mecanismele prin care se poate realiza acest lucru, crearea unei aplicaii executabile din mai multe fiiere surs, precum i principiile de proiectare care trebuie avute n vedere la mprirea unei aplicaii complexe n module diferite.

Compilarea independent
Exemplu de program
#include <stdio.h> int calcul(int a, int b) { return a + b; } void main(void) { int x=3, y=4, z; z=calcul( x, y ); printf("rezultatul calculelor este %d \n", z); }

Compilarea independent
Exemplu de program
Programul realizeaz un set bine definit de calcule sau prelucrri asupra unor date particulare. Algoritmul de calcul (funcia calcul) poate fi definit i implementat independent de aplicaie pentru a fi incorporat n mai multe aplicaii diferite. Aplicaia n ansamblu se poate implementa prin dou module (fiiere) diferite: un fiier calc.c care s conin implementarea funciei calcul i un fiier prog.c care s implementeze funcia main.

Compilarea independent
Fiier executabil din mai multe fiiere surs
Problemele care apar n cazul unui program mprit n mai multe fiiere surs sunt: cum se asigur faptul c o variabil global (extern) definit ntr-un fiier poate fi cunoscut, la compilare, n alte fiiere ? cum se asigur faptul c acestei variabile i este asociat aceeai zon unic de memorie ? cum se asigur faptul c o funcie definit ntr-un fiier poate fi apelat n alte fiiere ? Aceste probleme sunt rezolvate n diferite faze ale procesrii codului: preprocesarea, compilarea i editarea de legturi.

Compilarea independent
Fiier executabil din mai multe fiiere surs
Preprocesarea are rolul de a completa codul surs, n special prin includerea altor fiiere. Compilarea se face la nivel de fiier: este verificat corectitudinea sintactic a codului dintr-un fiier i faptul c toi identificatorii (nume de funcii i de variabile) care apar n acel fiier sunt cunoscui (vizibili). Un identificator se consider cunoscut n aceast faz dac a fost declarat sau definit.

Compilarea independent
Fiier executabil din mai multe fiiere surs
n urma compilrii unui fiier surs rezult un fiier obiect. Fiierul obiect are de obicei acelai nume ca i fiierul surs, dar primete extensia .o sau .obj (n funcie de sistemul sub care se lucreaz). Editarea legturilor (linkeditarea) realizeaz legarea tuturor apelurilor de funcii de codul efectiv apelat. Aceast faz necesit deci accesul la definiiile tuturor funciilor, din toate fiierele obiect care vor fi parte a aplicaiei. n urm operaiei de editare a legturilor rezult fiierul executabil. n sistemele Windows aceste fiiere au extensia .exe. n Linux nu au, de obicei, aceast extensie.

Compilarea independent
Fiier executabil din mai multe fiiere surs
Pentru a putea compila independent fiierul prog.c se asigur faptul c funcia calcul va fi cunoscut n acest fiier prin completarea lui cu o declaraie a funciei: int calcul(int a, int b); Pentru faza de editare a legturilor trebuie specificate toate fiierele care sunt implicate n obinerea executabilului. Modalitile practice de compilare i linkeditare a unui program C compus din mai multe fiiere surs depind de sistemul utilizat. n medii ca TurboC sau VisualC se creaz un proiect la care se adaug toate fiierele surs ale aplicaiei. Acestea vor fi n mod automat linkeditate mpreun.

Compilarea independent
Fiier executabil din mai multe fiiere surs
n mediul gcc se pot parcurge toi sau numai o parte din paii descrii. De exemplu, pentru cele dou fiiere ale aplicaiei precedente, vom avea: PREPROCESAREA I COMPILAREA gcc c calc.c =>se genereaz pe disc fiierul obiect calc.o gcc c prog.c =>se genereaz pe disc fiierul obiect prog.o EDITAREA LEGTURILOR gcc o prog calc.o prog.o => se genereaz pe disc fiierul executabil prog Acelai efect se poate obine i printr-o singur rulare a lui gcc: gcc o prog calc.c prog.c

Compilarea independent
Fiier executabil din mai multe fiiere surs
Aplicaie format din mai multe module: calc.c
prog.c calc.o prog prog.o

calc.h

Preprocesare

Compilare

Linkeditare

Compilarea independent
Lucrul n modul project
n mediul Borland C, compilarea i linkeditarea mpreun a mai multor fiiere se face n modul project. Pentru aceasta, trebuie executai urmtorii pai: Selectarea opiunii Project, apoi Open project. Stabilirea numelui fiierului project (extensia .prj). Marcarea cu INS a fiierelor care trebuie compilate i apoi linkeditate. Fiierele antet nu se includ n project (Cu DEL se elimin un fiier selectat greit). Selectarea opiunii pentru construirea programului executabil (meniul Compile, opiunea Build all). Selectarea opiunii Project i apoi Close project pentru ncheierea lucrului n modul project.

Compilarea independent
Fiiere antet
Pe lng modulele program, care conin definiiile funciilor, se pot defini module antet (header) asociate modulelor program. Modulele antet au rol declarativ. Un modul antet asociat unui modul program conine toate informaiile necesare pentru a verifica corectitudinea utilizrii funciilor sau altor entiti definite n modulul program asociat, atunci cnd acestea sunt utilizate n alte module. Se obisnuiete ca numele fiierului antet asociat unui modul x sa fie x.h iar numele modulului program x.c. De exemplu, modulului calc.c i se poate ataa modulul antet calc.h, cu urmtorul coninut:

int calcul(int , int );

Compilarea independent
Fiiere antet
Un program care utilizeaz funcia calcul nu mai trebuie s scrie declaraia ei, ci doar s specifice includerea fiierului antet n care aceasta este declarat. Programul prog.c se rescrie astfel: Fiierul prog.c:

#include <stdio.h> #include "calc.h" void main(void) { int x=3, y=4, z; z=calcul(x,y); printf("rezultatul calculelor este %d \n", z); }
O directiv #include este nlocuit n timpul preprocesrii cu coninutul integral al fiierului specificat.

Compilarea independent
Fiiere antet
Forma directivei #include poate fi: #include "nume_de_fisier" #include <nume_de_fisier> Cum se face cutarea ? Dac numele fiierului de inclus este dat ntre ghilimele, preprocesorul caut fiierul pornind de la directorul curent, cel n care se afl programul. Dac fiierul nu e gsit, sau dac numele este dat ntre paranteze unghiulare, cutarea fiierului se face n continuare dup reguli definite de implementarea compilatorului de C utilizat: se pot specifica directoarele n care s se efectueze cutarea (prin opiuni de configurare).

Compilarea independent
Fiiere antet
Directiva #include ofer o modalitate de a utiliza aceleai declaraii n cazul programelor care sunt mprite pe mai multe fiiere. Dac se modific un fiier inclus, trebuie recompilate toate fiierele care l includ pe acesta direct sau indirect. Un fiier antet bine construit s poate conin: Definiii de tipuri Declaraii de funcii Declaraii de variabile Definiii de constante Macrodefiniii Directive de includere

Compilarea independent
Fiiere antet
Un fiier antet bine proiectat nu trebuie s conin definiii de funcii i definiii de date! n principiu, un fiier antet poate fi inclus de ctre mai multe module diferite ale aceleiai aplicaii. n cazul n care acest modul antet conine o definiie de funcie sau o definiie de variabil global, prin includerea multipl se realizeaz definirea multipl a acestor entiti, fapt care constituie o eroare.

Compilarea independent
Variabile externe
Variabilele globale (externe) exist i i pstreaz valorile de-a lungul execuiei ntregului program. Ele pot fi folosite pentru comunicarea ntre funcii. n cazul n care aceste funcii fac parte din fiiere diferite, se pune problema definirii variabilelor externe astfel nct s fie cunoscute tuturor modulelor, dar fr a genera erori din cauza definirii lor multiple. Dac se include definiia unei variabile globale ntr-un modul antet, i acest modul antet este inclus de mai multe ori n fiiere ale aceluiai proiect, se va obine o eroare de programare, din cauza variabilei definite de mai multe ori.

Compilarea independent
Variabile externe
Soluia este declararea de variabile globale, utiliznd cuvntul cheie extern. O declaraie precizeaz doar natura variabilei, dar fr s i rezerve spaiu de memorie. Prin opoziie, o definiie creaz variabila, i rezerv spaiu de memorie. O anumit variabil poate avea oricte declaraii, dar poate fi definit o singur dat. Pentru identificatorii din clasa extern, trebuie s existe o definiie ntr-unul din fiiere; n celelalte fiiere apare doar declaraia acestora, precedat de specificatorul extern.

Compilarea independent
Variabile externe-exemplu de utilizare
Exemplul urmtor([19], pag. 231) prezint funciile calculeaza(), prel() i main() aflate n fiiere diferite i compilate separat, care folosesc n comun o variabil extern: valoare_importanta. Memoria corespunztoare lui valoare_importanta este alocat prin definiia din fiierul calcule.c. Declaraia variabilei valoare_importanta din fiierul antet calcule.h precizeaz (prin modificatorul extern) c memoria pentru valoare_importanta este alocat n alt parte. Acest fiier antet este inclus n prelucrari.c i prog.c. Fiierul calcule.h: extern int valoare_importanta; void calculeaza(void);

Compilarea independent
Variabile externe-exemplu de utilizare
Fiierul calcule.c: int valoare_importanta=1; void calculeaza(void) { valoare_importanta = 3; } Fiierul prelucrari.h: void prel (void); Fiierul prelucrari.c: #include "calcule.h" void prel (void) { valoare_importanta=4; }

Compilarea independent
Variabile externe-exemplu de utilizare
Fiierul prog.c: #include "calcule.h" #include "prelucrari.h" void main(void) { valoare_importanta=5; calculeaza(); prel(); }

Compilarea independent
Variabile externe-concluzii
n general, dac o funcie se refer la un nume declarat extern, atunci trebuie s existe o definiie a acelui identificator ntr-unul din fiierele sau bibliotecile programului. Toate funciile unui program care se refer la acelai nume extern indic aceeai entitate. Entitilor declarate cu extern nu li se mai aloc memorie la compilare, dar n faza de linkeditare sunt asociate cu definiiile lor.

Compilarea independent
Variabile externe-concluzii
Tipul specificat n declaraiile externe trebuie s fie acelai ca i tipul din definiie. ntr-un program compus din mai multe fiiere, o definiie de dat extern, fr specificatorul extern, are voie s apar o singur dat, ntr-un singur fiier. Orice alt fiier care mai declar acel identificator trebuie s foloseasc cuvntul cheie extern n declaraie (omiterea va fi semnalat ca eroare la faza de editare a legturilor ).

Compilarea independent
Variabile i funcii statice
Anumite variabile i funcii definite i utilizate ntr-un modul pot s aib un rol limitat la realizarea unor operaii ajuttoare, subordonate operaiilor exportate de modul. Asemenea variabile i funcii nu trebuie s fie direct accesate din afara modulului. Declararea ca static a unei variabile globale sau funcii, i limiteaz vizibilitatea la fiierul curent. O asemenea variabil sau funcie nu poate fi apelat din alt fiier surs. O variabil sau o funcie static nu este vizibil din alte fiiere. Declaraia de clas static se aplic i variabilelor interne (locale unei funcii), cu alt semnificaie(opus lui automatic). O asemenea variabil va exista n permanen, n loc s fie creat i s dispar la fiecare apel al funciei.

Tipuri de date abstracte


Caracteristici
Conceptul de dat abstract reprezint gruparea ntr-o construcie de program unitar a unor date, mpreun cu operaiile prin care se prelucreaz aceste date, organizate ca funcii. Acest concept generalizeaz noiunea de tip: reprezint att mulimea valorilor tipului ct i operaiile care pot fi executate asupra lor. Un tip de date abstract (TDA) este o entitate manipulat doar prin operaiile ce definesc acel tip. Utilizatorul nu trebuie s aib acces direct la reprezentarea intern a obiectului, ci numai prin intermediul acestor operaii. Limbajul C nu suport direct datele abstracte. Tipurile abstracte pot fi totui simulate folosind fiiere independente. Acest mod are propriile sale limitri: nu se pot defini tablouri de tipuri abstracte i nu se pot transmite parametri avnd ca i tip un tip abstract.

Tipuri de date abstracte


Avantaje
Avantajele utilizrii tipurilor de date abstracte sunt: 1. Programele devin independente de modul de reprezentare a datelor (de exemplu, o mulime poate fi implementat printr-un tablou sau printr-o list ordonat, dar partea de program ce definete operatorii tipului abstract poate s fie aceeai. 2. Se previne modificarea accidental a datelor. Utilizatorul tipului abstract este forat s manipuleze datele doar prin intermediul operatorilor ce compun tipul abstract (se reduce riscul unei distrugeri a datelor).

Tipuri de date abstracte


Stiva
Stiva este un tip special de list n care toate inseriile i suprimrile de noduri au loc la un singur capt (vrful stivei). Vom defini un tip abstract stiv cu urmtorii operatori: 1. Iniializarea stivei. 2. Verificarea faptului c stiva este plin. 3. Verificarea faptului c stiva este goal. 4. Introducerea unui element n vrful stivei. 5. Eliminarea unui element din vrful stivei. 6. Furnizarea elementului din vrful stivei fr a-l elimina. Stiva va fi materializat printr-un tablou de numere reale (stiva), iar vrful stivei este indicat de variabila ind_top.

Tipuri de date abstracte


Stiva
Toate declaraiile i definiiile trebuie s fie coninute ntr-un singur fiier. Att stiva ct i ind_top sunt definite cu clasa de memorare static pentru a ascunde de utilizator detaliile de implementare a stivei. static double stiva[MAX]; /* stiva */ static int ind_top; /* varful stivei */ Funciile au implicit clasa de memorare extern. Ele pot fi apelate din alte fiiere, iar toate operaiile asupra stivei sunt realizate doar prin intermediul lor.

Tipuri de date abstracte


Stiva
Iniializarea stivei: void init(void) { int i; for (i=0; i<MAX; i++) stiva[ i ]=0; /* toate elementele devin 0 */ ind_top=-1; /*varful stivei indica primul elem. ocupat */ } Verificarea faptului c stiva este plin: int plin(void) { return ind_top==MAX-1; }

Tipuri de date abstracte


Stiva
Verificarea faptului c stiva este goal: int gol(void) { return ind_top==-1; } Introducerea unui element n vrful stivei: void push(double nr) { if (plin()) /* daca stiva este plina */ { printf(Eroare: stiva este plina\n); exit(1); } stiva[++ind_top] = nr; /* noul element este introdus in varful stivei*/ }

Tipuri de date abstracte


Stiva
Eliminarea elementului din vrful stivei: void pop(void) { if (gol()) /* daca stiva este goala */ { printf(Eroare: stiva este goala\n); exit(1); } ind _top --; /* decrementeaza varful stivei*/ } Furnizarea elementului din vrful stivei (fr a-l elimina): double top(void) { if (gol()) /* daca stiva este goala */ { printf(Eroare: stiva este goala\n); exit(1); } return stiva[ind_top]; /* returneaza elementul din varf */}

Tipuri de date abstracte


Stiva-implementare cu tablou
Fiierul stack.h: typedef int tipelement; struct stivav; typedef struct stivav *stiva; int isEmpty(stiva s); int isFull(stiva s); stiva createStack(int elmaxim); void disposeStack(stiva s); void makeEmpty(stiva s); void push(tipelement x, stiva s); tipelement top(stiva s); void pop(stiva s); tipelement topandpop(stiva s);

Tipuri de date abstracte


Stiva-implementare cu tablou
#include "stack.h" #include <stdlib.h> #define vfsgol ( -1 ) #define stivamin ( 5 ) struct stivav { int marime; int vfs; tipelement *vectorel; }; int isEmpty(stiva s){ return s->vfs == vfsgol; } int isFull(stiva s){ return s->vfs==s->marime-1; }

Tipuri de date abstracte


Stiva-implementare cu tablou
stiva createStack(int elmaxim){ stiva s; if(elmaxim<stivamin) printf("Marimea stivei e prea mica\n"); s= (stiva) malloc(sizeof(struct stivav)); if(s==NULL){ printf("Nu mai este spatiu\n"); exit(1); } s->vectorel=(tipelement *) malloc(sizeof(tipelement)*elmaxim); if(s->vectorel==NULL){ printf("Nu mai este spatiu\n"); exit(1); } s->marime=elmaxim; makeEmpty(s); return s; }

Tipuri de date abstracte


Stiva-implementare cu tablou
void makeEmpty(stiva s){ s->vfs=vfsgol; }

void disposeStack(stiva s){ if(s!=NULL){ free(s->vectorel); free(s); }}

Tipuri de date abstracte


Stiva-implementare cu tablou
void push(tipelement x, stiva s){ if(isFull(s)) printf("Stiva plina\n"); else s->vectorel[++s->vfs]=x; } tipelement top(stiva s){ if(!isEmpty(s)) return s->vectorel[s->vfs]; printf("Stiva goala\n"); return 0; }

Tipuri de date abstracte


Stiva-implementare cu tablou
void pop(stiva s){ if(isEmpty(s)) printf("Stiva goala\n"); else s->vfs--; } tipelement topandpop(stiva s){ if(!isEmpty(s)) return s->vectorel[s->vfs--]; printf("Stiva goala\n"); return 0; }

Tipuri de date abstracte


Stiva-implementare cu tablou
#include <stdio.h> #include "stack.h" main( ){ stiva s; int i; s=createStack(12); for(i=0; i<10; i++) push(i,s); while(! isEmpty(s)){ printf( "%d\n", top(s)); pop(s); } disposeStack(s); return 0; }

Tipuri de date abstracte


Stiva-implementare cu list
Fiierul stack.h: typedef int tipelement; struct nod; typedef struct nod *pnod; typedef pnod stiva; int isEmpty(stiva s); stiva createStack(void); void disposeStack(stiva s); void makeEmpty(stiva s); void push(tipelement x, stiva s); tipelement top(stiva s); void pop(stiva s);

Tipuri de date abstracte


Stiva-implementare cu list
#include "stack.h" #include <stdlib.h> struct nod{ tipelement element; pnod urmator; };

int isEmpty(stiva s){ return s->urmator==NULL; }

Tipuri de date abstracte


Stiva-implementare cu list
stiva createStack(void){ stiva s; s = (stiva) malloc(sizeof(struct nod)); if(s==NULL){ printf("Eroare ! Nu s-a putut aloca stiva\n"); exit(1); } s->urmator=NULL; makeEmpty(s); return s; }

Tipuri de date abstracte


Stiva-implementare cu list
void makeEmpty(stiva s){ if(s==NULL) printf( "Trebuie folosit intai: createStack()\n" ); else while(! isEmpty(s)) Pop(s); } void disposeStack(stiva s){ makeEmpty(s); free(s); }

Tipuri de date abstracte


Stiva-implementare cu list
void push( tipelement x, stiva s){ pnod tmp; tmp= (pnod) malloc(sizeof(struct nod)); if(tmp==NULL) { printf( "Nu mai este spatiu de memorie disponibil\n" ); exit(1); } else { tmp->element=x; tmp->urmator=s->urmator; s->urmator=tmp; } }

Tipuri de date abstracte


Stiva-implementare cu list
tipelement top(stiva s){ if(! isEmpty(s)) return s->urmator->element; printf("Stiva goala\n"); return 0; } void pop(stiva s){ pnod prim; if(isEmpty(s)) printf("Stiva goala\n"); else { prim=s->urmator; s->urmator=s->urmator->urmator; free(prim); } }

Tipuri de date abstracte


Stiva-implementare cu list
#include <stdio.h> #include <stdlib.h> #include "stack.h" main( ){ stiva s; int i; s=createStack(); for(i=0; i<10; i++) push(i,s); while(! isEmpty(s)){ printf( "%d\n", top(s)); pop(s); } disposeStack(s); return 0; }

Tipuri de date abstracte


Concluzii
Modularizarea aplicaiile din programarea real (de mari dimensiuni) pot fi realizate din module(fiiere) care se proiecteaz, se codific, se compileaz, se testeaz i se ntrein separat, fiecare modul avnd dimensiuni rezonabile. Reutilizarea codului un modul realizat judicios are mari anse de a putea fi utilizat i n alte aplicaii. ncapsularea prin intermediul datelor abstracte, o parte din cod i date poate fi ascuns de restul aplicaiei; astfel acestea sunt protejate fa de modificarea accidental.

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