Documente Academic
Documente Profesional
Documente Cultură
Programarea calculatoarelor
de la teorie la practic
Predarea disciplinelor de programare se bazeaz n foarte mare msur pe exemple de programe. Succesul unui curs de programare depinde, n final, i de modul n care au fost alese exemplele din acel curs. Majoritatea exemplelor prezint studenilor programul direct n forma lui final, trecnd peste detaliile de realizare ale acestuia.
Programarea calculatoarelor
de la teorie la practic
Activitatea de programare nu se restrnge la contemplarea unor programe gata fcute, ci presupune conceperea de noi programe. Este greit impresia c programarea necesit doar cunoaterea unui limbaj de programare, trecerea de la idei la program realizndu-se pe baz de intuiie. Un curs de programare adevrat trebuie s prezinte metode de proiectare i implementare, iar exemplele selectate trebuie s ilustreze un stil de programare i s scoat n eviden stadiile prin care trece un program n procesul dezvoltrii sale.
Stilul de programare
Elemente caracteristice
Unul dintre elementele care contribuie la realizarea eficient a programelor de calitate este stilul de programare. Stilul de programare se poate caracteriza prin : aspectul general al programului; claritatea i lizibilitatea programului; structurarea i modularizarea programului n conformitate cu funciile i operaiile care se implementeaz; robusteea programului : calitatea sa de a continua chiar i la apariia unor erori; mentenabilitatea programului : uurina cu care poate fi modificat i mbuntit ulterior.
Stilul de programare
Prghii de aciune
1. Organizarea programului: definirea subprogramelor (funciilor) n conformitate cu principalele activiti i operaii desprinse din problema care urmeaz a fi rezolvat. 2. Organizarea datelor: trebuie s concorde cu structura obiectelor din realitatea problemei care se rezolv. 3. Comunicarea ntre subprograme: se recomand s se realizeze n special prin mecanismul parametri-argumente i numai n mod excepional prin alte variante. 4. Testarea i tratarea prin program a unor categorii de erori, de exemplu cele de intrare/ieire. Unele limbaje, inclusiv C, permit astfel de operaii ceea ce determin mbuntirea robusteii programelor.
Stilul de programare
Prghii de aciune
5. Alegerea numelor simbolice (identificatorilor) din program astfel nct s se fac o legtur direct cu semnificaia entitii respective n problema de rezolvat. n acest fel se poate uura urmrirea i nelegerea programului. 6. Utilizarea comentariilor: reprezint cea mai simpl metod de documentare a programelor, util chiar i autorilor programului, dac l reanalizeaz dup un anumit timp. n practica programrii se utilizeaz linii de comentarii distincte prin care se descriu funciile programului, funcia fiecrui subprogram, datele de intrare i cele de ieire, indicaii de utilizare a programului etc. De asemenea, se obinuiete ca prelucrrile mai importante sau mai dificile din program s fie nsoite de comentarii explicative.
Stilul de programare
Prghii de aciune
7. Formatul liber de redactare al liniilor surs. Aceast facilitate, prezent n majoritatea limbajelor de programare moderne, permite punerea n concordan a textului programului cu organizarea i semnificaia sa. Utilizarea indentrii duce la creterea lizibilitati i clariti programului. Indentarea reprezint deplasarea spre dreapta a unui bloc de date sau de instruciuni, care aparine unei structuri, fa de marginea stng a elementelor structurii nconjurtoare.
Exemplu de program
Enunul problemei
S se scrie un program pentru realizarea unui top al melodiilor. Persoanele care particip la alctuirea topului se mpart n 4 categorii, dup sex si vrst (mai tineri de 20 de ani i peste 20 de ani). Fiecare persoan nominalizeaz, n ordine, 5 melodii preferate, identificate prin titlu. Datele privind persoanele participante la realizarea topului se citesc de pe mediul de intrare sau dintr-un fiier. Datele pentru o persoan vor fi de forma: nume prenume sex(m sau f) vrsta melodie1 melodie2 melodie3 melodie4 melodie5
Exemplu de program
Enunul problemei
S se afiseze: 1. Lista melodiilor n ordinea popularitii lor. 2. Listele cstigtorilor (cei care au ghicit melodiile cstigatoare), i anume: 4 liste separate cuprinznd numele si prenumele persoanelor care au menionat pe prima pozitie a preferinelor una dintre cele mai solicitate 3 melodii la categoria lor.
Program exemplu
Pasul 1
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMEL 5 typedef struct pers{ char * nume; char * prenume; char sex; int varsta; char * melodii[NMEL]; } persoana;
Program exemplu
Pasul 1
typedef struct m { char * titlu; int punctaj; } melodie; persoana * citire(int *np){ // citeste datele din intrare si le pune intr-o lista } void afisare_test(persoana *plista, int np){ // afisararile de test sunt foarte importante, pentru a descoperii // eventualele greseli in faza incipienta }
Program exemplu
Pasul 1
melodie * top_melodii(persoana *plista, int np){ // creaza o lista cu melodiile si voturile aferente fiecarei melodii // ordoneaza lista de melodii descrescator, functie de nr. voturilor // afiseaza lista de melodii } void castigatori(melodie *ptop, persoana *plista, int np){ // se apeleaza functia afiseaza_castigatori pentru cele 4 categorii // metoda are avantajul de a modifica foarte usor parametrii listelor // de exemplu vom putea imparti in 3 grupe de varsta lista // corespunzatoare fiecarui sex doar apeland de 6 ori functia // afiseaza_castigatori }
Program exemplu
Pasul 1
int main() { int npers; melodie * top; persoana * lista=NULL; /* lista persoanelor */ lista=citire(& npers); //citeste datele si le pune in lista afisare_test(lista, npers); top=top_melodii(lista, npers); //topul ordonat al melodiilor castigatori(top, lista, npers); //afiseaza cele 4 liste return 0; }
Program exemplu
Pasul 2
void afisare_test(persoana *plista, int n){ int i,j; for( i=0; i<n; i++ ){ printf("\n persoana %d", i); printf("\n Nume: %s Prenume: %s sex:%c v:%d \n", plista[i ].nume, plista[ i ].prenume, plista[ i ].sex, plista[i ].varsta); for( j=0; j<NMEL; j++ ) printf("%s ",plista[ i ].melodii[ j ]); } }
Program exemplu
Pasul 2
melodie *creaza_lista_melodii(persoana *plista,int n, int *nm){ // creaza o lista cu melodiile si voturile aferente fiecarei melodii } void sorteaza_lista_melodii(melodie *ptop, int nm){ // creaza o lista ordonata descrescator, functie de numarul voturilor, // din lista de melodii primita ca parametru } void afiseaza_top(melodie *ptop,int nm){ // afiseaza lista de melodii primita ca parametru }
Program exemplu
Pasul 2
melodie * top_melodii(persoana *plista, int np){ melodie *ptop; int nmelodii; // creaza o lista cu melodiile si voturile aferente fiecarei melodii ptop=creaza_lista_melodii(plista, np, & nmelodii); // ordoneaza lista de melodii descrescator, functie de nr.voturi sorteaza_lista_melodii(ptop, nmelodii); // afiseaza lista de melodii afiseaza_top(ptop, nmelodii); return ptop; }
Program exemplu
Pasul 2
void afiseaza_castigatori(persoana *plista,int np,melodie * ptop, char sx, int v1, int v2) { // se parcurge lista de persoane plista si se afiseaza // persoanele cu sexul sx si varsta cuprinsa intre v1 si v2, // adica plista[i].sex==sx si v1<=lista[i].varsta<=v2 (pseudocod) }
Program exemplu
Pasul 2
void castigatori(melodie *ptop, persoana *plista, int np) { afiseaza_castigatori(plista,np,ptop,'f',1,19); afiseaza_castigatori(plista,np,ptop,'f', 20,150); afiseaza_castigatori(plista,np,ptop,'m',1,19); afiseaza_castigatori(plista,np,ptop,'m', 20,150); /* o alta metoda consta in parcurgerea listei de persoane o singura data, si crearea in acest timp a celor 4 liste mai mici, pe care sa le afisam ulterior*/ }
Concluzii
1. Dezvoltarea programelor const ntr-o succesiune de pai de rafinare. La fiecare pas o operaie complex este descompus ntr-un numr de operaii mai simple. Fiecare rafinare la nivelul unei operaii poate fi nsoit de o rafinare a structurilor de date aferente, datele fiind cele care realizeaz interaciunea dintre (sub)operaii. Rafinarea descrierii operaiilor i a structurilor de date trebuie s se realizeze n paralel. 2. Gradul de modularitate obinut va determina uurina sau dificultatea cu care programul va putea fi adaptat la modificrile i extensiile ulterioare, inclusiv la schimbarea mediului de execuie (limbaj de programare, sistem de operare, platform hardware).
Concluzii
3. Pe parcursul rafinrilor succesive vor fi folosite mai multe notaii. Este recomandabil ca notaia iniial, care deriv din natura problemei de rezolvat, s fie meninut ct mai mult cu putin. Direcia n care va evolua notaia, pe parcursul rafinrilor, depinde, n ultim instan, de limbajul de programare n care va fi scris programul n variant final. Notaia final se identific cu limbajul de programare ales pentru implementarea programului. De aceea, limbajul de programare trebuie s permit exprimarea ct mai clar i natural a structurilor de date i de control care apar n procesul de proiectare.
Concluzii
4. Fiecare pas de rafinare implic luarea unui numr de decizii pe baza unor criterii de performan. Printre aceste criterii se numr : viteza de execuie, spaiul de memorie necesar rulrii, claritatea codului, uniformitatea structurilor etc. Programatorul trebuie s fie consecvent n ceea ce privete luarea acestor decizii, trebuie s cntreasc cu atenie toate aspectele i s fie pregtit s revin asupra unor decizii anterioare, chiar dac aceasta nseamn s reia proiectarea de la nceput.
Preprocesorul
Preprocesarea este o faz care precede compilarea. Preprocesorul limbajului C este relativ simplu i n principiu execut substituii de texte. Prin intermediul lui se realizeaz: - Includeri de fiiere surs (antet); - Macrodefiniii; - Compilare condiionat. Directivele de preprocesare au caracterul # la nceput de linie.
Preprocesorul
Includerea de fiiere antet
O directiv #include este nlocuit n timpul preprocesrii cu coninutul integral al fiierului specificat, care se insereaz n locul ei. Forma directivei include este #include "nume_de_fisier" sau #include <nume_de_fisier> Diferena ntre cele dou moduri de scriere este urmtoarea: dac numele fiierului de inclus este dat ntre ghilimele, preprocesorul caut fiierul pornind de la directorul curent. 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. Un fiier inclus poate conine la rndul su directive #include.
Preprocesorul
Includerea de fiiere antet
Utilizarea directivei #include e 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 poate conine: definiii de tipuri, declaraii de funcii, declaraii de variabile, definiii de constante, macrodefiniii, alte directive de includere. 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. Dac fiierul 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.
Preprocesorul
Macrodefiniii
Definirea constantelor simbolice prin directiva #define este un caz particular de macrodefiniie. n general, o macrodefiniie (un macro) are la baz tot operaia de substituie. Un macro are o definiie i poate fi apelat de un numr oarecare de ori. La definirea unui macro se specific de fapt textul care urmeaz a se substitui la fiecare apel al su. Acest text poate fi variabil, n funcie de anumii parametri.
Preprocesorul
Macrodefiniii
Forma general a unei macrodefiniii este: #define nume(p1, p2, ...,pn) text_de_substituit nume = numele macroului p1, p2,... , pn = parametrii macroului text_de_substituit = textul cu care este nlocuit macroul la fiecare apel O definiie poate folosi definiii anterioare. Textul de nlocuit este restul liniei surs; dac se dorete continuarea definiiei pe mai multe linii, la sfritul liniei care urmeaz s fie continuat, se scrie caracterul /. n cazul unei macrodefiniii cu parametri, ntre numele macroului i paranteza deschis nu trebuie s existe spaii .
Preprocesorul
Macrodefiniii
Exemplu: Un macro pentru calculul maximului a dou numere. #define MAX(x,y) ((x)>(y) ? (x) : (y)) Un exemplu simplu de apel al acestui macro este: float a,b,c; c=MAX(a,b); La preprocesare, linia c=MAX(a,b); se nlocuieste cu c=((a) > (b) ? (a) : (b)); Un alt exemplu de apel al macro-ului MAX: int i,j,k; k=MAX(i + j, i - j); La preprocesare, linia k=MAX(i + j, i - j); va fi nlocuit cu k=(i + j) > (i - j) ? (i + j) : (i - j))
Preprocesorul
Macrodefiniii
Exist i anumite pericole legate de utilizarea necorespunztoare a macrodefiniiilor, care pot avea efecte secundare ascunse. Exemple: MAX(i++, j++) va incrementa de dou ori valoarea variabilei care e mai mare, pentru c expandarea macro-ului se face n felul urmtor: (i++) > (j++) ? (i++) : (j++) Macroul de ridicare a unui numr la ptrat #define PATRAT(x) x*x d rezultate eronate n cazul n care este apelat de exemplu pentru x+1: PATRAT(x+1) este expandat n x+1*x+1, ceea ce, avnd n vedere precedena operatorilor, nu calculeaz ptratul expresiei (x+1). Pentru a fi corect un macro de ridicare la ptrat ar trebui scris utiliznd paranteze: #define PATRAT(x) (x)*(x)
Preprocesorul
Macrodefiniii. Exemple
Transformarea unui caracter din liter mic n liter mare: #define UPPER(c) ((c)-a+A) Definirea unui ciclu infinit: #define FOREVER for(;;) Interschimbarea a dou numere ntregi: #define SCHIMBA(X,Y) { int t; t=X; X=Y; Y=t; } Apelul SCHIMBA(a,b) este substituit cu secvena: { int t; t=a; a=b; b=t; } Variabila t exist numai n interiorul instruciunii compuse generat de preprocesor!
Preprocesorul
Macrodefiniii. Exemple
Macrodefiniiile permit parametrizarea unei operaii cu un nume de tip, ceea ce se poate folosi ca o facilitate de realizare a unor funcii generice primitive. Macro-ul de interschimbare a dou elemente poate fi rescris astfel nct s fie parametrizat i cu tipul elementelor: #define SWAP(TIP, X, Y) { TIP t; t=X; X=Y; Y=t; } Macrodefiniia SWAP se poate apela cu parametri de orice tip pe care este definit operaia =. int a, b; SWAP(int, a, b); float x, y; SWAP(float, x, y);
Preprocesorul
Macrodefiniii. Exemple
Macro pentru alocarea dinamic a unui bloc de memorie de n elemente de un anumit tip: #define ALOCA(tip, n) (tip *)malloc(sizeof(tip)*n) void main(void) { int *ip; float *fp; ip=ALOCA(int, 10); fp=ALOCA(float, 10); } Macroexpandarea poate fi oricnd suspendat cu directiva: #undef nume Numele respectiv nu mai este expandat n continuarea fiierului. Definirea unui macro nu se ncheie, de obicei, cu ;.
Preprocesorul
Compilare condiionat
Permite s se aleag dintr-un text general prile care se compileaz mpreun. Se realizeaz folosind construciile #if, #ifdef i #ifndef Se noteaz cu expr o expresie constant (valoarea poate fi evaluat de preprocesor la ntlnirea ei). #if expr text #endif Dac expr are valoarea adevrat atunci text se supune preprocesrii, altfel se continu cu ceea ce urmeaz dup #endif.
Preprocesorul
Compilare condiionat
#if poate avea i ramur de else: #if expr text1 #else text2 #endif Directivele de preprocesare (compilare condiionat) i textul care urmeaz s fie inclus sau nu n programul pentru compilare se dau pe linii de text separate.
Preprocesorul
Compilare condiionat
n interiorul unei directive #if, expresia defined(nume) are valoarea 1 dac nume a fost deja definit de o directiv define, sau 0 n caz contrar. Exemplu: includerea o singur dat a coninutul unui fiier antet antet1.h: #if !defined(ANTET1) #define ANTET1 /* continutul fisierului antet1.h */ #endif
Preprocesorul
Compilare condiionat
Prima includere a fiierului antet1.h definete numele ANTET1. n cazul n care ar mai exista includeri ulterioare, chiar i indirect, prin alte fiiere, preprocesorul vede c numele e deja definit i sare direct la #endif. Aceast facilitate este util n situaii n care un fiier antet ar fi altfel inclus de mai multe ori, direct sau indirect, ca n urmtoarea situaie de exemplu: Se consider un fiier antet a.h i un fiier antet b.h care conine o directiv de includere a lui a.h. Dac un alt fiier main.c conine directive de includere a ambelor fiiere a.h i b.h, rezult c fiierul a.h va fi inclus de dou ori. Aceast includere dubl poate conduce la erori datorit redefinirii unor variabile i constante coninute n a.h.
Preprocesorul
Compilare condiionat
Directivele #ifdef i #ifndef testeaz dac un nume este sau nu este definit. Reluarea exemplului anterior: #ifndef ANTET1 #define ANTET1 /* continutul fisierului antet1.h */ #endif
Preprocesorul
Compilare condiionat
Includerea de fiiere diferite n funcie de anumite variabile sistem: #if SYSTEM==SYSV #define ANTET "sysv.h" #elif SYSTEM==BSD #define ANTET "bsd.h" #elif SYSTEM==MSDOS #define ANTET "msdos.h" #else #define ANTET "default.h" #endif #include ANTET
Preprocesorul
Compilare condiionat
Exemplu - Definirea de tipuri mutual recursive: se consider dou tipuri structurate T1 i T2, fiecare dintre acestea conine un cmp de tip pointer la cellalt tip. typedef struct { T2 a; int b;} *T1; typedef struct { T1 a; int b;} *T2; Se pune problema ordinii corecte n care trebuie s fie definite aceste tipuri. Dac T1 este definit naintea lui T2, compilatorul nu l cunoate pe T2 cnd ntlnete definiia lui T1, care are un cmp (a) de acest tip.
Preprocesorul
Compilare condiionat
Problema exist i invers: dac T2 ar fi definit naintea lui T1. Pentru a rezolva problema referirilor reciproce, se utilizeaz declaraii incomplete de structuri, astfel: typedef struct t_T1 * T1; typedef struct t_T2 * T2; struct t_T1 { T2 a; int b; }; struct t_T2 { T1 a; int b; };
Preprocesorul
Compilare condiionat
Cazul: T1 i T2, sunt definite n fiiere separate t1.h i t2.h: /* fisierul t1.h */ #if !defined(tip_T1) #define tip_T1 typedef struct t_T1 *T1; #include "t2.h" struct t_T1 { T2 a; int b; }; #endif
Preprocesorul
Compilare condiionat
/* fisierul t2.h */ #if !defined(tip_T2) #define tip_T2 typedef struct t_T2 *T2; #include "t1.h" struct t_T2 { T1 a; int b; }; #endif /* fisierul main.c */ #include "t1.h" #include "t2.h" void main() { ----------}