Sie sind auf Seite 1von 11

Cursul 3

Limbajul C n dou ore

Vom face aici o prezentare general a limbajului C (cu cteva elemente de C++) plecnd de la rezolvarea urmtoarei probleme de programare: Scriei un program care afieaz pe monitor mesajul Voi scrie litera C de n ori., dup care scrie pe urmtorul rnd mesajul n= i ateapt ca utilizatorul s tasteze valoarea lui n, numr ntreg. Dac n e strict pozitiv programul scrie Ok i afieaz de n ori litera C, altfel apare mesajul Nu pot afisa ceva de ### ori unde, pe monitor, n locul marcat cu ### trebuie s apar valoarea lui n. Iat cum ar trebui s arate monitorul la finalul execuiei n fiecare caz n parte: cazul n>0

i cazul n<=0

Incepem editarea fiierului surs al programului, s l numim primul.cpp, cu urmtoarea linie: /* Primul program ilustrativ */ care conine un comentariu n stilul C. Comentariile nu fac parte din program, ele au doar rolul de a da unele lmuriri celui ce citete textul surs i, n cazul programelor complexe, sunt foarte utile pentru nelegerea diverselor secvene de cod. Compilarea textului surs se execut linie cu linie, de sus n jos, fiecare linie fiind citit de la stnga la dreapta. Compilatorul ignor toate caracterele dintre perechile /* i */ (inclusiv pe acestea), aa c avem la dispoziie un spaiu n care putem scrie ce vrem. Un astfel de comentariu se poate ntinde pe mai multe linii. Exist i comentarii n stilul C++, introduse odat cu extinderea la C++ a limbajului, acestea ncep cu dou slash-uri, //, i se termin automat la captul liniei, ele sunt utilizate pentru a da scurte indicaii asupra coninutului liniei de cod curente. Prezena comentariilor este facultativ, dar recomandabil. Urmtoarea linie este obligatorie: # include <iostream> Aceast linie conine o directiv de preprocesare i nu face nici ea parte din program. Dup ce lansm comanda Compile (Ctrl F7) compilatorul, nainte de a ncepe compilarea propriu-zis a fiierului, lanseaz n execuie preprocesorul un program specializat n prelucrarea automat a textelor (prin substituii) care citete toate directivele de preprocesare i le execut. In cazul directivei include linia directivei este nlocuit cu ntreg coninutul fiierului iostream, fiier care conine tot ce este necesar programului pentru executarea operaiilor de citire/scriere (sinonime: intrare/ieire, input/output). Printre altele, prin acest mecanism ajung n fiierul nostru surs definiiile obiectelor cout i cin (ca instane ale unor clase de fluxuri de date, stream-uri). Identificatorul cout desemneaz consola de ieire (output console), adic monitorul, iar cin desemneaz consola de intrare (input console), adic tastatura. Fr aceast directiv de includere a bibliotecii iostream compilatorul ar da, la prima apariie a cuvntului cout, mesajul de eroare 'cout' : undeclared identifier. Alte biblioteci des utilizate: <math.h> pentru funcii matematice (exponeniale, logaritmi, funcii trigonometrice, etc) i <string.h> pentru operaii cu iruri de caractere (string-uri). Subliniem de la bun nceput c textul surs trebuie editat (scris) numai cu un set restrns de caractere: literele a,b,c, ... , z, A,B,C, ... , Z, cifrele 0,1, ..., 9, i simbolurile direct imprimabile de la tastatur: <, &, +, .a. Dei MS Visual Studio accept acum folosirea caracterelor cu diacritice (, , , etc.) chiar i n identificatori, nu recomandm utilizarea acestei faciliti. Atenie: compilatorul face distincie ntre literele mari i cele mici; de exemplu, dac scriem n mod eronat # INCLUDE < iostream > obinem la compilare eroarea: invalid preprocessor command 'INCLUDE'.

De remacat i faptul c, pentru o scriere mai lizibil, se pot insera oricte blancuri (cu tastele space sau tab) ntre atomii lexicali (tokens) ai directivei. E corect i aa # include < iostream > i aa # include < iostream > dar nu i aa # include < io stream > (pentru c am spart un atom!). Aceast facilitate trebuie folosit pentru aezarea n pagin, ntr-un mod ct mai clar, a textului surs. In faza de preprocesare compilatorul terge toate spaiile care nu sunt neaparat necesare pentru pstrarea sensului directivelor i al instruciunilor. Directivele de preprocesare nu fac parte din limbajul C (nici din C++), ele sunt specifice fiecrui compilator n parte. Ca regul general, ele ncep cu simbolul # i se termin n aer, fr un simbol terminator (cum este, de exemplu, ; pentru instruciunile C). Ca exemplificare, dintre directivele de preprocesare ale mediului de dezvoltare MS Visual C++ mai menionm pe #define i #using (vezi http://msdn.microsoft.com/en-us/library/3sxhs2ty.aspx). Urmeaz acum nc o linie de cod care va fi prezent n toate programele noastre: using namespace std; i care deseori este numit directiva using dei ea este o instruciune C++. Denumirea poate crea confunzii, mai ales c exit chiar o directiv de preprocesare #using, ea este folosit pentru a sugera asemnarea dintre efectul acestei instruciuni i o substituie de texte efectuat la preprocesare: ca i cum, de exemplu, identificatorul cout ar fi nlocuit cu std::cout peste tot unde apare n textul surs. Ca urmare a creterii complexitii programelor i a utilizrii unor biblioteci de funcii i de clase tot mai bogate, pentru a evita coliziunile tot mai frecvente dintre nume (utilizarea aceluiai identificator pentru obiecte diferite) n C++ a fost introdus mecanismul spaiilor de nume: programatorul poate prefixa identificatorii utilizai, cu sintaxa prefix::identificator prefixul desemnnd un anumit spaiu de nume, namespace. Instruciunea noastr using declar c vom utiliza spaiul de nume std (standard) i este necesar pentru a simplifica referirea fluxurilor standard cin i cout. Fr aceasta ar trebui s scriem std::cin n loc de cin, de exemplu. Instruciunea using namespace std; este compus din patru atomi lexicali: cuvintele cheie using i namespace (aparinnd limbajului C++), identificatorul std (care denumete spaiul de nume standard cel mai des folosit) i terminatorul de instruciune ;. Identificatorul std este definit undeva n codul inclus n programul nostru prin directiva #include<iostream>, ca dovad, dac schimbm ordinea acestor dou linii obinem la compilare eroarea: 'std' : a namespace with this name does not exist.

Instruciunile limbajului C se termin, cele mai multe, cu simbolul ;. Instruciunile care nu se termin cu ; sunt instruciunea compus (blocul de instruciuni, { }) i toate instruciunile care se pot termina cu un astfel de bloc de instruciuni (de exemplu for, while, etc). Subliniem c simbolul ; nu este separator ci terminator de instruciuni: el face parte integrant din instruciunea pe care o finalizeaz. De exemplu, pe linia a=1; b=2; c=a+b; am scris cu ajutorul a 14 caractere exact trei instruciuni, prima instruciune este scris cu 4 caractere, a doua tot cu 4, a treia cu 6 caractere, iar ntre instruciuni nu se afl nimic. Regula este urmtoarea: n textul surs, o instruciune ncepe imediat dup ce se termin cea dinaintea ei. Reamintim c blancurile sunt eliminate la preprocesare, aa c pentru a face programul ct mai lizibil, instruciunile se scriu cte una pe linie i ct mai aerisit. Iat acum i codul surs al programului nostru:
/* Primul program ilustrativ */ #include<iostream> using namespace std; char lit='C'; //variabila globala

int citeste(void){ int n; //variabila locala cout<<"n="; cin>>n; return n; } void afiseaza(int nr){ for(int i=0; i<nr; i++) cout<<lit; return; } int main(void){ int k; cout<<"Voi scrie litera "<<lit<<" de n ori."<<endl; k=citeste(); if(k<=0) cout<<"Nu pot afisa ceva de "<<k<<" ori!"<<endl; else{ cout<<"Ok"<<endl; afiseaza(k); } cout<<endl; return 0; }

Programul este compus dintr-o declaraie de variabil global (variabila lit) i trei definiii de funcii (funciile citeste, afiseaza i main). In general, un program C este o colecie de declaraii de variabile globale i de definiii de funcii (subprograme), dintre funcii una (i numai una) are numele main. Execuia programului const n prelucrarea valorilor unor date cu ajutorul acestor funcii. In mod automat, prima care intr n execuie este funcia main, ea ncepe prelucrarea datelor conform instruciunilor sale componente, de regul prin apelarea altor funcii, succesiunea apelurilor fiind determinat de fluxul de control al programului. Datele sunt numere sau coduri numerice, ele pot fi constante (valoarea lor este scris de programator direct n codul surs) sau variabile, caz n care compilatorul trebuie s aloce spaiul de memorie necesar pstrrii valorilor curente ale respectivelor variabile. Orice dat trebuie s aibe un tip declarat n mod explicit de programator astfel nct compilatorul s tie, n principal, cum s organizeze memoria alocat acelei date. Cu instruciunea declaraie: char lit='C'; declarm variabila lit ca fiind o dat de tip char i o iniializm cu valoarea 'C'. Cuvntul lit este identificatorul care d numele variabilei, i a fost ales de noi, spre deosebire de char care este un cuvnt cheie al limbajului (un identificator rezervat) i care poate fi folosit numai pentru a desemna tipul de dat caracter pe un octet. Identificatorii pot fi formai numai din litere, cifre i caracterul _ (underscore), cu restricia c primul caracter nu poate fi o cifr. Lungimea maxim a unui identificator depinde de compilator (pentru MS Visual C++ vezi http://msdn.microsoft.com/en-us/library/565w213d.aspx). Variabila lit fiind declarat de tip char, la ntlnirea instruciunii de mai sus compilatorul rezerv un octet de memorie pentru variabila lit i scrie n acest octet codul ASCII al literei C (i anume 43 hexa). Tipurile uzuale pentru variabile simple sunt: char pentru caractere, int pentru numere ntregi i double pentru numere reale n dubl precizie (fa de float simpl precizie). Variabila lit este global deoarece a fost declarat n exteriorul oricrei funcii. Valorile ei pot fi utilizate direct de ctre toate funciile care sunt definite dup declararea ei, altfel spus, numele ei este vizibil din orice punct al fiierului surs de dup linia n care a fost declarat. Spre deosebire de ea, variabilele declarate n interiorul unei funcii sunt variabile locale, ele pot fi referite numai n interiorul corpului funciei n care sunt declarate. Declaraiile pot fi fcute i fr iniializarea variabilei declarate, n felul urmtor: char lit; dar n acest caz trebuie s avem grij ca, nainte de a o folosi, s i atribuim variabilei lit o valoare iniial, printr-o instruciune expresie, de exemplu, lit='C'; Atenie, spre deosebire de instruciunile declaraii, care pot apare oriunde n textul surs, instruciunile expresie pot s apar numai n interiorul corpului unei funcii. S analizm acum definiia funciei citeste
5

int citeste(void){ int n; cout<<"n="; cin>>n; return n; } O funcie este format dintr-o secven de instruciuni care prelucreaz datele de intrare n funcie (valorile actuale ale argumentelor funciei), mpreun cu alte date proprii, i care returneaz ctre funcia apelant, o dat de ieire, rezultatul funciei. Exist i funcii fr date de intrare, precum i funcii care nu returneaz nimic. Din prima linie a definiiei funciei citeste deducem c aceasta nu are date de intrare (acolo unde ar trebui s fie lista parametrilor este scris cuvntul cheie void - vacant) iar rezultatul ei este de tip int (adic va returna un numr ntreg). In general, definiia unei funcii are forma: tip_returnat nume_funcie (lista_parametrilor_funciei) { corpul_funciei } Dac este nevoie, putem numai s declarm funcia (fr s o i definim), scriind numai prototipul funciei (adic antetul funciei urmat de terminatorul de instruciune ;) astfel tip_returnat nume_funcie (lista_parametrilor_funciei); dar tot va trebui s definim, undeva mai jos, nainte de sfritul fiierului surs, funcia. Corpul funciei se scrie ntotdeauna ntre dou acolade pereche, {}, i este format dintr-o secven de instruciuni. Nu este permis imbricarea funciilor, adic definirea unei funcii n interiorul corpului alteia. In corpul funciei citeste declarm variabila n, scriem pe monitor mesajul "n=", citim de la tastatur valoarea variabilei n i returnm, ca rezultat al funciei, valoarea citit. Pentru afiarea valorii unei variabile oarecare, x de exemplu, utilizm operatorul de scriere , <<, aplicat lui cout i variabilei pe care dorim s o afim, n ordinea cout << x. Dac dorim s afim un anumit mesaj, textul mesajului trebuie ncadrat ntre ghilimele. De exemplu, instruciunea cout << "aha"; va afia textul aha. Intre secvenele de cod int x=123; cout << x; i int x=123; cout << "x"; diferena este foarte mare, n primul caz pe monitor va fi afiat numrul 123, valoarea variabilei x, iar n al doilea caz va apare un text format doar dintr-un singur caracter: litera x.
6

Operatorul de scriere poate fi concatenat; pentru afiarea valorilor variabilelor x, y i z putem folosi instruciunea cout << x << y << z; Pentru a distinge, pe monitor, valorile celor trei variabile e bine s intercalm blancuri, spaii de separare: cout << x <<" "<< y << " "<< z; sau chiar s fim i mai explicii: cout << "x=" << x <<" y= "<< y << " z= "<< z; Pentru a trece la linie nou (dar i pentru a puncta ncheierea transmisiei unui set de date) vom utiliza manipulatorul endl, astfel cout << "x=" << x <<" y= "<< y << " z= "<< z << endl; Citirea de la tastatur se realizeaz cu operatorul de citire, >> , aplicat stream-ului cin i variabilei citite, n ordinea cin >> variabila. Si acest operator poate fi concatenat dar, n cazul cnd vrem s citim mai multe valori, este mai sigur s scriem pentru fiecare citire cte o instruciune n parte sau chiar cte o pereche de instruciuni (prima pentru afiarea unui mesaj explicativ i a doua pentru citirea propriu-zis), aa cum am ntlnit deja: cout<<"n="; cin>>n; Execuia unei funcii se ncheie cnd fluxul de control ajunge la execuia unei instructiuni return, sau, dac funcia nu trebue s ntoarc nici un rezultat, la atingerea acoladei care ncheie corpul funciei. Funcia citeste se ncheie cu instruciunea return n; care depune n registrul EAX al microprocesorului valoarea variabilei n, de unde este preluat de funcia apelant. In exemplul nostru, citeste este apelat o singur dat, n funcia main, de instruciunea k=citeste(); care atribuie variabilei k valoarea citit de funcie de la tastatur. Observai c la apelarea unei funcii fr argumente nu se folosete cuvntul cheie void. Funcia afiseaza void afiseaza(int nr){ for(int i=0; i<nr; i++) cout<<lit; return; } are un singur argument, variabila nr de tip int, i nu returneaz nici un rezultat (spunem, prin abuz de limbaj, c rezultatul are tipul void). Ea nu calculeaz nimic, scrie numai pe monitor de exact nr ori caracterul dat de variabila lit. Corpul funciei este format din dou instruciuni: o instruciune for i o instruciune return fr valoare returnat (deorece funcia nu trebuie s returneze nimic).

Instruciunea for este o instruciune de ciclare, de repetare a unei aciuni (descris n instruciunea corp a for-ului) ct timp o anumit condiie este ndeplinit. Sintaxa (forma) acestei instruciuni este urmtoarea: for ( iniializare; expresie_test; expresie_de_reciclare) instructiune_corp S urmrim ce se ntmpl n program cnd, de exemplu, nr are valoarea 7 n for (int i=0; i<nr; i++) cout<<lit; Execuia for-ului ncepe prin executarea instruciunii de iniializare int i=0, care aloc variabila i local for-ului (adic variabila i este vizibil numai n interiorul acestui for i i nceteaz existena odat acesta) i i atribuie valoarea iniial 0, dup care este evaluat expresia test i<nr care este adevrat (0<7). In acest caz se trece la execuia instruciunii corp cout<<lit; i apare un C pe ecran, dup care, nainte de reluarea ciclrii, este evaluat expresia de reciclare i++, care l incrementeaz pe i (l mrete cu o unitate). Acum i are valoarea 1 i se reia testarea: i<nr ? da, apare nc un C, este incrementat i, i aa mai departe pn cnd i atinge valoarea 7. Acum la evaluarea expresiei test obinem valoarea logic fals i execuia for-ului este ncheiat, prin urmare controlul execuiei este preluat de instruciunea urmtoare, instruciunea return;. Pe monitor au aprut astfel 7 litere C. Dac trebuie s executm mai multe aciuni n corpul for-ului, vom folosi pentru corp o instruciune compus, grupnd n interiorul unei perechi de acolade {} mai multe instruciuni simple (aa cum avem de exemplu ramura else a instruciunii if din funcia main). S analizm acum funcia principal: int main(void){ int k; cout<<"Voi scrie "<<litera<<" de n ori."<<endl; k=citeste(); if(k<=0) cout<<"Nu pot afisa ceva de "<<k<<" ori!"<<endl; else{ cout<<"Ok"<<endl; afiseaza(k); } cout<<endl; return 0; } In general, funcia principal poate avea unul dintre urmtoarele prototipuri: int main(void); int main( ); int main(int argc, char *argv[]); care presupun c la sfritul execuiei funcia main returneaz sistemului de operare un cod de eroare, valoarea zero nsemnnd fr erori. Compilatorul nostru, MS Visual C++, accept i prototipul
8

void main(void); care poate fi utilizat, desigur, dar cu reducerea drastic a portabilitii codului surs (celelalte compilatoare nu accept acest format, vezi http://en.wikipedia.org/wiki/Main_function). Forma cu argumente a funciei main este folosit pentru a transmite programului, n momentul lansrii, anumite informaii prin preluarea lor din linia de comand. In funcia noastr main declarm o variabil ntreag, k, o ncrcm cu valoarea returnat de funcia citeste i apoi o testm dac e mai mic sau egal cu zero. Dac da, scriem pe monitor ceva, dac nu, scriem altceva. Programul este gata, return zero (succes), stop. S remarcm apariia unei instruciuni de selecie, instruciunea if-else. Ea are sintaxa if (expresie_test) instructiune_daca_da else instructiune_daca_nu Identificatorii if i else sunt cuvinte cheie, prezena perechii de paranteze rotunde ( )este obligatorie. Execuia instruciunii este evident: se evalueaz expresia test, dac testul este ndeplinit se execut instruciunea daca_da, altfel se execut instruciunea daca_nu. In ambele cazuri, la terminarea execuiei, controlul este preluat de instruciunea imediat urmatoare n textul surs - cu excepia cazului n care apare o instructiune de salt (cum ar fi return, de exemplu). Exist i forma numai cu if, avnd sintaxa if (expresie_test) instructiune_corp i care, dac testul este ndeplinit, insereaz n fluxul de control al execuiei instruciunea corp, altfel nu face nimic. In mod obinuit, expresiile test sunt comparaii de forma a<b, a<=b, a==b, a>=b sau a>b. De reinut c expresia a==b testeaz dac a este egal cu b, n timp ce expresia a=b atribuie lui a valoarea lui b (operatorul == este operatorul de comparaie, iar operatorul = este operatorul de atribuire). Deoarece n limbajul natural semnul egal este folosit n ambele sensuri (i comparaie i atribuire), n limbajul C se produc adesea erori prin scrierea unui singur semn de egalitate acolo unde ar trebui dou semne. O astfel de eroare este greu de depistat, ea nu iese la compilare (compilatorul accept i atribuirea a=b ca expresie test n if sau for - vom vedea mai trziu de ce), iar la rulare programul d rezultate haotice. Pentru evitarea acestei situaii unele compilatoare de C, cnd ntlnesc atribuiri n expresii condiionale, avertizeaz utilizatorul asupra posibilei confuzii. Mai mult, unele limbaje de programare provenite din C, cum ar fi Java sau C#, au introdus tipuri logice de date astfel nct s fac imposibil aceast eroare de editare. MS Visual C++ nu d nici un avertisment, aa c trebuie s fim ateni la comparaii. S urmrim acum evoluia mijloacelor de calcul prin prisma creterii complexitii: primile maini mecanice de calcul efectuau doar calcule cu cte o singur operaie (cum fac acum cele mai simple calculatoarele de buzunar). Au aprut apoi calculatoare electronice capabile s efectueze mai multe operaii la o singur comand, s evalueze o ntreag expresie aritmetic (exist i astzi calculatoare de buzunar care pot evalua formule). Urmtoarea etap: calculatoare capabile s evalueze mai multe expresii la o singur comand, prin rularea unui program format dintr-o secven de instruciuni executate ntr-o ordine dictat de rezultatele
9

expresiilor evaluate. Iniial calculatoarele programabile dispuneau de puin memorie intern, de ordinul kiloocteilor, aa c programele trebuiau s fie scurte, cu cteva instruciuni; pentru prelucrri complexe ele au fost organizate ca sub-programe (funcii, proceduri, subrutine, etc.) i ncrcate n memorie doar atunci cnd erau apelate. Dup cum am vzut deja, un program C este o colecie de astfel de sub-programe. Evoluia a continuat: pentru utilizarea mai multor programe C n acelai timp, acestora li s-au scos funciile principale i au fost transformate n clase, variabilele globale devenind variabile membru iar funciile rmase devenind funcii membru ale claselor. Incrcarea lor n memorie a fost transformat n instanierea claselor, iar lansrile n execuie au fost nlocuite cu apeluri ctre funciile membru, apeluri fcute prin intermediul instanelor (obiectelor) clasei. Pe scurt, n C++ a fost introdus tipul class, programatorul avnd posibilitatea s-i defineasc propriile clase sau s foloseasc unele deja existente, prin definirea unei clase se definete un nou tip de dat avnd complexitatea unui vechi program C , pentru utilizarea unei clase trebuie s declarm i s iniializm o variabil de tipul dat de acea clas, adic s instaniem un obiect al clasei. De exemplu, n fiierul iostream din directorul \include al compilatorului MS Visual C++ gsim urmtoarea declaraie: extern ostream &cout; Aceast instruciune declar cout ca fiind o referin (iniializat ntr-un fiier extern) ctre un obiect din clasa ostream, care este o clas specializat n operaii de ieire (output stream). Tot n fiierul iostream gsim directiva #include<istream>, iar n fiierul istream gsim #include<ostream>. Noi am inclus n fisierul nostru surs fiierul iostream i astfel, prin includeri succesive, definiia clasei ostream i declaraia lui cout ajung n fiierul surs al programului nostru, transformndu-l ntr-un program C++, dei n liniile de cod scrise de noi nu este definit nici o clas. In final, deoarece programele noastre C sunt de fapt programe C++, trebuie s precizm urmtoarele: limbajul C++ este o extensie a limbajului C realizat prin adugarea tipului de dat class, un program C++ este format din declaraii de variabile globale i din definiii de clase i de funcii care manipuleaz obiecte din aceste clase, dintre funcii una se numete main i cu ea ncepe execuia programului. Aceast extindere a limbajului a fost iniiat n 1979 de ctre Bjarne Stroustrup, cercettor la AT&T Bell Laboratories (tot acolo unde a fost creat i limbajul C), care iniial a numit noul limbaj "C with Classes" i apoi, n 1983, i-a dat denumirea definitiv de C++. Manualul fundamental a fost scris de Bjarne Stroustrup n 1985, The C++ Programming Language. Pentru mai multe amnunte, vezi pagina personal a autorului, http://www.research.att.com/~bs/.

Pentru a ilustra cele spuse mai sus despre noiunea de clas, prezentm n final o rezolvare n stil C++ a problemei noastre, obinut prin schimbri minimale, prin transformarea programului C prezentat mai sus ntr-o clas C++, clasa scriitor:
10

/* Exemplu de transformare a unui program C intr-o clasa C++ */ #include<iostream> using namespace std; class scriitor{ public: char lit; int citeste(void){ int n; cout<<"n="; cin>>n; return n; } void afiseaza(int nr){ for(int i=0; i<nr; i++) cout<<lit; return; } void lucreaza(void){ int k; cout<<"Voi scrie litera "<<lit<<" de n ori."<<endl; k=citeste(); if(k<=0) cout<<"Nu pot afisa ceva de "<<k<<" ori!"<<endl; else{ cout<<"Ok"<<endl; afiseaza(k); } cout<<endl; } }; int main(void){ scriitor ionescu; ionescu.lit='C'; ionescu.lucreaza(); return 0; }

11

Das könnte Ihnen auch gefallen