Sunteți pe pagina 1din 217

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

CUPRINS
Cuvnt inainte ___________________________________________________________________6 Capitolul IB.01. Rezolvarea algoritmic a problemelor __________________________________8
IB.01.1. Introducere n programare _______________________________________________________8 IB.01.2. Algoritm______________________________________________________________________9 IB.01.3. Obiecte cu care lucreaz algoritmii ________________________________________________9 IB.01.4. Etapele specifice unui algoritm i fluxul de execuie al acestora ________________________10 IB.01.5. Scheme logice ________________________________________________________________12 IB.01.6 Exemple de algoritmi reprezentai n schem logic __________________________________16 IB.01.7. Pseudocod ___________________________________________________________________19 IB.01.8 Exemple de algoritmi descrii n pseudocod _________________________________________19

Capitolul IB.02. Introducere n limbajul C. Elemente de baz ale limbajului _________________22


IB.02.1 Limbajul C - Scurt istoric ________________________________________________________22 IB.02.2 Caracteristicile limbajului C ______________________________________________________23 IB.02.3 Procesul dezvoltrii unui program C _______________________________________________23 IB.02.4 Structura unui program C _______________________________________________________25 IB.02.5 Elemente de baz ale limbajului C ________________________________________________26 IB.02.6 Conversii de tip. Operatorul de conversie explicita (cast) ______________________________45

Capitolul IB.03. Funcii de intrare/ieire n limbajul C __________________________________47


IB.03.1 Funcii de intrare/ieire n C _____________________________________________________47 IB.03.2 Funcii de citire/scriere pentru caractere i iruri de caractere __________________________47 IB.03.3 Funcii de citire/scriere cu format _________________________________________________48 IB.03.4 Fluxuri de intrare/ieire in C++ ___________________________________________________55

Capitolul IB.04. Instruciunile limbajului C ___________________________________________56


IB.04.1 Introducere___________________________________________________________________56 IB.04.2 Instruciunea expresie __________________________________________________________56 IB.04.3 Instruciunea compus (bloc) ____________________________________________________57 IB.04.4 Instruciuni de decizie (condiionale) ______________________________________________58 IB.04.5. Instruciuni repetitive __________________________________________________________63 IB.04.6. Instruciunile break i continue __________________________________________________69 IB.04.7. Terminarea programului________________________________________________________70 IB.04.8. Anexa A. Sfaturi practice pentru devoltarea programelor C. Depanare __________________71 IB.04.9. Anexa B. Programele din capitolul IB.01 rezolvate n C _______________________________72

Capitolul IB.05. Tablouri. Definire i utilizare n limbajul C ______________________________85


IB.05.1 Tablouri _____________________________________________________________________85 IB.05.2 Tablouri unidimensionale: vectori ________________________________________________85

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.05.3 Tablouri multidimensionale _____________________________________________________89 IB.05.4 Tablouri bidimensionale: matrici _________________________________________________89 IB.05.5 Probleme propuse _____________________________________________________________92

Capitolul IB.06. Funcii. Definire i utilizare n limbajul C ________________________________96


IB.06.1. Importana funciilor n programare ______________________________________________96 IB.06.2. Definirea i utilizarea funciilor __________________________________________________98 IB.06.3. Declararea unei funcii ________________________________________________________100 IB.06.4. Domeniu de vizibilitate (scope) _________________________________________________101 IB.06.5.Apelul unei funcii ____________________________________________________________103 IB.06.6. Instruciunea return __________________________________________________________105 IB.06.7. Transmiterea parametrilor _____________________________________________________106 IB.06.8. Funcii cu argumente vectori ___________________________________________________107 IB.06.9. Funcii recursive _____________________________________________________________108 IB.06.10. Anex: Funcii n C++_________________________________________________________112

Capitolul IB.07. Pointeri. Pointeri i tablouri. Pointeri i funcii __________________________114


IB.07.1. Pointeri ____________________________________________________________________114 IB.07.2. Declararea pointerilor ________________________________________________________115 IB.07.3. Operaii cu pointeri la date ____________________________________________________116 IB.07.4 Vectori i pointeri ____________________________________________________________121 IB.07.5 Transmiterea tablourilor ca argumente ale funciilor ________________________________122 IB.07.6 Pointeri n funcii_____________________________________________________________126 IB.07.7 Pointeri la funcii _____________________________________________________________130 IB.07.8 Funcii generice ______________________________________________________________133 IB.07.9 Anex. Tipul referin n C++ ____________________________________________________133

Capitolul IB.08. iruri de caractere. Biblioteci standard ________________________________136


IB.08.1. iruri de caractere n C ________________________________________________________136 IB.08.2. Funciile de intrare/ieire pentru iruri de caractere sunt: ____________________________137 IB.08.3. Funcii standard pentru operaii cu iruri _________________________________________138 IB.08.4. Extragerea atomilor lexicali ____________________________________________________139 IB.08.5. Alte funcii de lucru cu iruri de caractere _________________________________________140 IB.08.6. Erori uzuale la operaii cu iruri de caractere ______________________________________141 IB.08.7. Definirea de noi funcii pe iruri de caractere ______________________________________141 IB.08.8. Argumente n linia de comand _________________________________________________144

Capitolul IB.09. Structuri de date. Definire i utilizare n limbajul C ______________________146


IB.09.1. Definirea de tipuri i variabile structur __________________________________________146 IB.09.2. Asocierea de nume sinonime pentru tipuri structuri - typedef ________________________148 IB.09.3. Utilizarea tipurilor structur ____________________________________________________150

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.09.4. Funcii cu parametri i/sau rezultat structur ______________________________________151 IB.09.5. Structuri predefinite __________________________________________________________155 IB.09.6. Structuri cu coninut variabil (uniuni) ____________________________________________156 IB.09.7. Enumerri __________________________________________________________________157 IB.09.8. Exemple ____________________________________________________________________158

Capitolul IB.10. Alocarea memoriei n limbajul C _____________________________________160


IB.10.1. Clase de memorare (alocare a memoriei) n C ______________________________________160 IB.10.2. Clase de alocare a memoriei: Auto ______________________________________________160 IB.10.3. Clase de alocare a memoriei: Static ______________________________________________161 IB.10.4. Clase de alocare a memoriei: Register ____________________________________________162 IB.10.5. Clase de alocare a memoriei: extern____________________________________________163 IB.10.6. Alocarea dinamic a memoriei __________________________________________________163 IB.10.7. Vectori alocai dinamic ________________________________________________________166 IB.10.8. Matrice alocate dinamic _______________________________________________________167 IB.10.9. Funcii cu rezultat vector ______________________________________________________169 IB.10.10. Vectori de pointeri la date alocate dinamic _______________________________________170 IB.10.11. Anexa A: Structuri alocate dinamic _____________________________________________173 IB.10.12. Anexa B: Operatori pentru alocare dinamic in C++ ________________________________174

Capitolul IB.11. Operaii cu fiiere n limbajul C ______________________________________176


IB.11.1. Noiunea de fiier ____________________________________________________________176 IB.11.2. Tipuri de fiiere n C __________________________________________________________176 IB.11.3. Operarea cu fiiere ___________________________________________________________177 IB.11.4. Funcii pentru deschidere i nchidere fiiere ______________________________________178 IB.11.5. Operaii uzuale cu fiiere text __________________________________________________179 IB.11.6. Intrri/ieiri cu conversie de format _____________________________________________181 IB.11.7. Funcii de citire-scriere pentru fiiere binare ______________________________________183 IB.11.8. Funcii pentru acces direct la datele dintr-un fiier__________________________________185 IB.11.9. Fiiere predefinite ____________________________________________________________187 IB.11.10. Redirectarea fiierelor standard _______________________________________________189 IB.11.11. Anexa. Fiiere n C++ _________________________________________________________190

Capitolul IB.12. Convenii i stil de programare ______________________________________191


IB.12.1 Stil de programare coding practices _____________________________________________191 IB.12.2. Convenii de scriere a programelor ______________________________________________195 IB.12.3. Anexa: Directive preprocesor utile n programele mari. Macrouri ______________________201

Capitolul IB.13. Autoevaluare ____________________________________________________204


Capitol IB.01. Rezolvarea algoritmic a problemelor _______________________________________204 Capitol IB.02. Introducere n limbajul C. Elemente de baz ale limbajului ______________________212

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Aplicaii module 3-12 ________________________________________________________________216

Bibliografie ___________________________________________________________________217

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Cuvnt inainte
Noiunea de limbaj de programare este definit ca fiind ansamblul format de un vocabular i un set de reguli gramaticale, care permit programatorului specificarea exact a aciunilor pe care trebuie s le execute calculatorul asupra unor date n scopul obinerii anumitor rezultate. Specificarea const practic n ntocmirea/scrierea programelor necesare ("programare"). Altfel spus, un limbaj de programare ofer o notaie sistematic prin care poate fi descris un proces de calcul. Notaia const dintr-un set de reguli sintactice i sematice. Sintaxa reprezint un set de reguli ce guverneaz alctuirea propoziiilor dintr-un limbaj. n cazul limbajelor de programare echivalentul propoziiei este programul. Semantica este un set de reguli ce determin nelesul sau semnificaia propoziiilor ntr-un limbaj. Putem defini dou mari categorii de limbaje de programare: 1. Limbaje de nivel cobort, dependente de calculator. Aici avem: Limbajul main Limbajul de asamblare

Limbajul main este limbajul pe care calculatorul l nelege n mod direct; n acest limbaj programele se scriu n cod binar ca succesiuni de 0 i 1, fiecare instruciune din limbajul main fiind o combinaie de 4 bii (exemple: 0000, 0001). Pentru aceasta programatorul trebuie s cunoasc detaliat structura hardware a calculatorului, trebuie s gestioneze fr greeal alocarea adreselor de memorie pentru un program. Pot aprea multe erori datorate concepiei programului, sintaxei, suprapunerii adreselor de memorie, etc. Limbajele de asamblare introduc cuvinte cheie pentru desemnarea operaiilor (de exemplu: LOAD pentru operaia cu codul binar 0000, ADD pentru operaia cu codul 0001, etc) precum i simbo luri pentru adrese (exemplu....), simplificnd astfel programarea. Pentru execuia unui program scris n limbaj de asamblare este necesar o faz preliminar prin care programul este transformat ntr-unul echivalent n limbaj main. Transformarea este realizat automat de un program numit assembler (asamblor). Asamblorul nlocuiete codarea mnemonic (cum ar fi ADD) cu coduri binare corespunztoare limbajului main i aloc adrese de memorie pentru toate variabilele simbolice utilizate (A, B, C). Astfel, limbajele de asamblare uureaz procesul de programare dar sunt la fel de apropiate de hardware ca i limbajele main. n prezent, limbajele de asamblare sunt utilizate pentru unele programe critice, care necesit controlul exact al resurselor hardware ale calculatorului (procesorul central i memoria intern). Limbaje de nivel nalt, independente de structura calculatorului. Cteva exemple n ordinea apariiei lor: Fortran (FORmula TRANslation) 1955, IBM, pentru probleme tehnicotiinifice Cobol 1959, pentru probleme economice Pascal, C, s.a. anii 1970, odat cu apariia conceptelor de Programare structurat C++, Java, s.a. anii 1980, odat cu apariia conceptelor de Programare orientat pe obiecte Programarea structurata se bazeaza pe teorema programrii structurate (structured program theorem) a lui Bhm i Jacopini, pe care am folosit-o deja n elaborarea algoritmilor. Aceast teorem spune c orice algoritm poate fi compus din numai trei structuri de calcul: 6

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

1. structura secvenial - secvena; 2. structura alternativ - decizia; 3. structura repetitiv - ciclul. Bazele programrii structurate au fost puse de Dijkstra i Hoare. Structura unui program se obtne printr-o abordare top-down (de regul) i orientat pe prelucrri: o problem care presupune o prelucrare complex este descompus n subprobleme/prelucrri mai simple; fiecare subproblem poate fi descompus la rndul su n prelucrri i mai simple, pn cnd se ajunge la un nivel de complexitate cobort, la care fiecare prelucrare obinut este descris printr-o unitate program (funcie, procedur). Tehnicile de programare structurat pot fi aplicate n majoritatea limbajelor de programare, dar ele sunt adecvate limbajelor de programare procedural (n care unitatea program este procedura/funcia) cum sunt Pascal, C i altele. Programarea orientata pe obiecte introduce ideea structurrii programelor n jurul obiectelor. Fiecare obiect aparine unei clase de obiecte care este descris n cadrul unui program. Toate obiectele dintr-o clas au aceeai structur (descris prin variabile i constante) i un acelai comportament (descris prin operaii). Obiectele sunt entiti dinamice, care apar, interacioneaz cu alte obiecte (prin intermediul operaiilor) i dispar, in timpul execuiei programului. Obiectele din programele cu structur orientat obiect sunt, de obicei, reprezentri ale obiectelor din viaa real, astfel nct programele realizate prin tehnica POO sunt mai uor de neles, de testat i de extins dect programele procedurale. Aceast constatare este adevrat mai ales n cazul sistemelor software complexe i de dimensiuni mari, a cror dezvoltare trebuie s fie ghidat de principii ale Ingineriei Programelor (Software Engineering). Dup modul de transformare a programelor (translatare) n vederea execuiei pe un calculator, limbajele de programare de nivel nalt pot fi mprite n: Limbaje compilate: C, C++, Pascal, Java; Limbaje interpretate: PHP, Javascript, Prolog, Matlab La limbajele compilate translatorul se numete compilator; acesta transform programul sursa (scris n limbjul de programare de nivel nalt) ntr-un program exprimat n limbajul main, rezultatul fiind un fiier executabil. Viteza de execuie a programului compilat este mare, ntruct programul este deja transpus n ntregime n cod main. La limbajele interpretate translatorul poart denumirea de interpretor i funcioneaz n felul urmtor: preia prima comand din codul surs, o traduce n limbajul main i o execut, apoi a doua comand i tot aa. De aceea, viteza de executie a unui program interpretat este mult mai mica decat a unui program compilat. Multe limbaje moderne combin compilarea cu interpretarea: codul surs este compilat ntr-un limbaj binar numit bytecode, care la executie este interpretat de ctre o main virtual. De remarcat faptul c unele interpretoare de limbaje pot folosi compilatoare aa-numite just-in-time, care transform codul n limbaj main chiar naintea executrii.

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.01. Rezolvarea algoritmic a problemelor


Cuvinte-cheie
Algoritm, date, constante, variabile, expresii, operaii, schem logic, pseudocod

IB.01.1. Introducere n programare S considerm urmtorul exemplu. Se definete funcia F(x), unde x este numr real, astfel: x2-2, x<0 3, x=0 x+2, x>0

F(x)=

Se cere s se scrie un program care s calculeze valoarea acestei funcii pentru urmtoa rele 100 de valori ale lui x: x = {-3, 0,1,7, 2.23, etc} 100 valori. Pentru a putea rezolva aceast cerin trebuie s vedem mai nti care sunt etapele rezolvrii unei probleme utiliznd un program informatic. Etapele rezolvrii unei probleme utiliznd un program informatic: 1. Formularea clar a problemei: date disponibile prelucrri necesare rezultate dorite 2. Elaborarea algoritmului ce implic analiza detaliat a problemei: date: sursa (consola/ suport magnetic/), semnificaie, tip (numeric/ alfanumeric), restricii asupra valorilor rezultate: destinaie (ecran/imprimant/suport magnetic /), mod de prezentare principalele etape de rezolvare (schem logic sau pseudocod) eventuale restrictii impuse de limbajul de programare n care vom transpune algoritmul 3. Transpunerea algoritmului n limbajul de programare utilizat 4. Rulare i ... din nou etapa 2 dac nu am obinut ceea ce trebuia. n cadrul acestui capitol ne vom ocupa de primele dou etape, i anume cea de formulare a problemei i cea de elaborare a algoritmului, pentru ca pe parcursul urmtoarelor capitole s detaliem modalitile de implementare a programelor utiliznd limbajul de programare C. S revenim acum la problema noastr i s parcurgem prima etap, cea de formulare clar a problemei: datele disponibile sunt cele 100 de valori de intrare x prelucrri necesare sunt calculul lui F(x) pentru cele 100 de valori ale lui x rezultatele dorite sunt cele 100 de valori ale lui F(x) calculate prin prelucrri. n acest moment putem spune c tim foarte bine ce avem de fcut. Urmeaz s vedem mai departe cum anume facem aceasta. Pentru a trece la etapa a doua a rezolvrii problemei nostre vom detalia n cele ce urmeaz noiunea de algoritm precum i obiectele acestuia. 8

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.01.2. Algoritm

Algoritm - succesiune de etape de calcul ce se poate aplica pentru rezolvarea unei clase de
probleme. Cerinele pe care trebuie s le ndeplineasc un algoritm sunt urmtoarele: Claritate s nu existe ambiguiti in descrierea etapelor de calcul Generalitate s poat fi aplicat pentru o clas de probleme si nu pentru o problema particulara ( de exemplu, algoritmul pentru rezolvarea ecuatiilor de gradul 2 trebuie sa descrie modul de rezolvare a oricarei ecuatii de gadul 2 si nu a unei ecuatii particulare de gradul 2). Finitudine s furnizeze rezultatul n timp finit Descrierea unui algoritm poate fi efectuat utiliznd: Scheme logice Pseudocod n momentul n care vom cpta suficient experien n programare iar problema de rezolvat nu este foarte complex putem s ne reprezentm mental algoritmul ei de rezolvare. Totui, n fazele de nceput sau pentru un algoritm mai complex este foarte indicat s schim algoritmul de rezolvare a unei probleme nainte de implementarea rezolvrii ntr-un limbaj de programare. Se practic descrierea algoritmului fie sub form grafic (organigrame sau scheme logice), fie folosind un pseudocod, ca un text intermediar ntre limbajul natural i un limbaj de programare. O problem poate avea mai muli algoritmi de rezolvare. Cum l alegem pe cel mai bun i ce nseamn cel mai bun algoritm? Pentru a rspunde la aceast ntrebare se va analiza eficiena algoritmului ( timpul de execuie, memoria intern necesar,alte resurse de calcul necesare) i se va alege, dintre algoritmii identificai cel mai eficient (timp minim de executie, resurse de calcul minime), innd cont i de scopul i natura problemei rezolvate (de exemplu, timpul minim de execuie poate fi mai important decat dimensiunea memoriei interne necesare). Aceast etap este o etap ce implic o analiz complex a algoritmilor pe care deocamdat, pe parcursul acestui prim curs de programare, nu o vom lua dect arareori n considerare. IB.01.3. Obiecte cu care lucreaz algoritmii Date Principalele obiecte ale unui algoritm sunt datele. Ele pot fi: Date de intrare - care sunt cunoscute Date de ieire - rezultate furnizate Dup tipul lor, datele pot fi: ntregi: 2, -4 Reale: 3.25, 0.007 Logice: true i false adevrat i fals Caracter: y, a ir de caractere: ab23_c 9

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Constante n descrierea unui algoritm pot apare constantele; acestea sunt date coninute n program, care nu sunt citite sau calculate. Un exemplu este constanta din matematic.

Variabile Programele, i implicit algoritmii, lucreaza cu date. O variabil este utilizat pentru a stoca (a pstra) o dat. Se numete variabil pentru c valoarea stocat se poate schimba pe parcursul execuiei algoritmului. O variabil are un nume unic i un coninut care poate s difere de la un moment la altul al execuiei algoritmului. Mai precis, o variabil este o locaie de memorie care are un nume i care pstreaz o valoare de un anumit tip. O variabil este caracterizat prin: Nume Tip Valoarea la un moment dat Locul n memorie (adresa) Nume variabil numr Valoare Tipul variabilei int

123

suma

-456

int

pi

3.1415

double

medie

-12.734

double

Orice variabil are un nume, conine o valoare declarat de un anumit tip, valoare memorat mereu la o aceeai adres de memorie De exemplu, n problema propus anterior, putem pstra valorile datelor de intrare (1.5, 3.6, 4.2, etc) ntr-o variabil numit x, care va lua pe rnd fiecare dintre datele de intrare. Variabila x este de tip real, se afl n memorie, de exemplu la adresa 0xFF32 (adres care rmne fix) - care ns nu are importan pentru un programator ncepator- i poate avea valoarea 3.6 la un moment dat. n mod analog, rezultatele ( F(1.5), F(3.6), F(4.2), etc) le vom stoca ntr-o variabil F, tot de tip real. Expresii Expresiile sunt construite utiliznd constante, variabile i operatori. Ele pot fi de mai multe tipuri, la fel ca i datele. Exemplu 3*x+7, x<y, etc. Operatorii sunt: matematici (+, - , *, /, mod restul mpririi ntregi), relaionali (<, >, <=, >=, != - diferit, == - comparaie la egalitate) i logici (i ambele adevrate, sau cel puin una adevrat, not negarea unei expresii).

IB.01.4. Etapele specifice unui algoritm i fluxul de execuie al acestora 10

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Un algoritm poate efectua operaii de: Intrare: preluarea unei date de la un dispozitiv de intrare (consola, suport magnetic, etc) Ieire: transferul unei date din memoria intern ctre un dispozitiv de ieire (ecran, imprimanta, suport magnetic, etc) Atribuire: x=3; y=x; y=x+y (variabila ia valoarea(=) expresiei) o Operaia de atribuire se realizeaz astfel: Se evalueaz expresia din partea dreapta a semnului = Valoarea obinut este atribuit variabilei din stnga (stocat n locaia sa de memorie), care i pierde vechea valoare Decizie: o ntrebare ridicat de programator la un moment dat n program, operaie prin care, n funcie de valoarea curent a unei condiii, se alege urmtoarea etap a algoritmului n programarea structurat apare teorema de structur a lui Bohm i Jacopini: Orice algoritm poate fi compus din numai trei structuri de calcul: 1. structura secvenial - secvena; 2. structura alternativ - decizia; 3. structura repetitiv - ciclul. Secvena este cea mai ntlnit, n cadrul ei instruciunile sunt executate n ordinea n care sunt scrise, de sus n jos, secvenial. Decizia implic o ntrebare ridicat de programator la un moment dat n program. In funcie de rspunsul la ntrebare - care poate fi ori Da, ori Nu - programul se continu pe una din ramuri, executndu-se blocul corespunztor de operaii. Ciclul exprim un calcul (compus din una sau mai multe etape) care poate fi executat de mai multe ori. Numrul de execuii este controlat de valoarea unei expresii care se evalueaz fie nainte fie dup execuia calculului. De exemplu, s presupunem c vrem s calculm funcia F(x) din exemplul anterior pentru toate numerele ntregi de la 1 la 1000. Pentru aceasta ar trebui s folosim o structur repetitiv pentru a descrie operaiile care trebuie executate n mod repetat:citirea unei date de intrare, evaluarea funciei pentru acea data de intrare i transferul valorii functiei la un dispozitiv de ieire. Iat, de exemplu, la ce sunt bune calculatoarele! Totui, ine de priceperea noastr s scriem algoritmi ce conin structuri repetitive care s poat uura foarte mult rezolvarea unei clase ntregi de probleme; de exemplu, evaluarea funciei F(x) nu doar pentru o valoare particular, ci pentru orice numr real i pentru oricte numere!! n programarea structurat sunt definite trei structuri repetitive: 1. Structura repetitiv cu conditie iniial, format din: O condiie, definit la nceputul structurii Un bloc de instruciuni, care se execut dac rezultatul evalurii condiiei este adevrat. Evaluarea condiiei are loc nainte de execuia blocului de instruciuni, de aceea, dac rezultatul primei evaluri a condiiei este fals, blocul de instruciuni nu este executat (nici o dat). Se mai numete i structur repetitiv de tip while. 11

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

2. Structura repetitiv cu condiie final, n care condiia este definit dup blocul de instruciuni Condiia este evaluat dup fiecare execuie a blocului de instruciuni. De aceea, blocul de instruciuni se execut cel puin o dat. Se mai numete i structur repetitiv de tip do-while. 3. Structura repetitiv cu contor Caz particular al structurii repetitive cu condiie iniial. Condiia este exprimat folosind o variabil cu rol de contor (numrtor de repetri). Se definesc, pentru variabila contor: Valoarea iniiala (naintea primei execuii a blocului de instruciuni); Valoarea final(corespunztoare ultimei execuii a blocului de instruciuni); Pasul: valoarea care se adaug la valoarea variabilei contor dup fiecare execuie a blocului de instruciuni. Se mai numete i structur repetitiv de tip for.

IB.01.5. Scheme logice Schemele logice sunt reprezentri grafice ale algoritmilor. Fiecrei operaii i corespunde un simbol grafic: Start/Stop: marcheaz nceputul/ sfritul schemei logice (execuiei algoritmului)

STAR TT

STOP

Atribuirea: operaia prin care unei variabile i se atribuie o valoare. var = expresie

Atribuirea: Se evalueaz expresia, apoi se atribuie valoarea expresiei variabilei din stnga semnului = Operaia de intrare (citire): programatorul preia de la tastatur una sau mai multe valori (intrri) Citeste x, z Programul preia de la un dispozitiv (extern, de exemplu tastatura) una sau mai multe valori (date de intrare), pe care le atribuie in ordine variabilelor din lista specificat n operaia de citire. Operaia de ieire (scriere): programul transmite la un dispozitiv (extern, de exemplu, ecran sau imprimant) valorile variabilelor/expresiilor specificate in operaia de scriere. 12

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Scrie x, a*b Operaia de scriere (denumit i operaia de ieire) presupune evaluarea n ordine a expresiilor specificate i afiarea pe ecran a valorilor lor pe aceeai linie. Aciuni nedetaliate (bloc de operaii): un bloc de operaii nedetaliat Bloc Se execut operaia specific blocului, fr ca aceast operaie s fie detaliat. Este utilizat n general pentru operaii mai complexe, pentru a nu ncrca schema logic principal. Acest bloc va fi detaliat dup realizarea schemei logice principale. Exemplu: dac vrem s afim primele n numere prime, putem avea un bloc de operatii nedetaliat care testeaz dac un numr este prim sau nu. Secvena se reprezint prin simboluri grafice conectate prin sgei ce sugereaz fluxul operaiilor (secvenial): Secvenial Operaie 1 Operaie 2
. . .

Operaie n

Se execut n ordine Operaie 1, apoi Operaie 2, .a.m.d. pn la Operaie n.

Decizia

DA (Adevrat )

Conditi e

NU (Fals)

13

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Se evalueaz Condiie (o expresie cu valoare logic); Dac valoarea Condiiei este adevrat, atunci execuia se continu pe ramura DA; Dac valoarea Condiiei este fals, se continu execuia pe ramura NU. Decizia poate avea una din urmtoarele forme:

DA Conditi e DA Bloc DA Bloc DA NU Conditi e

NU

Bloc NU

Forma 1

Forma 2

Forma 1: Se evalueaz Condiie; Dac valoarea Condiiei este adevrat, atunci se execut Blocul DA; Dac valoarea Condiiei este fals, executia se continu cu operaia care urmeaz imediat dup blocul DA. Forma 2: Se evalueaz Condiie; Dac valoarea Condiiei este adevrat se execut Blocul DA; Dac valoarea Condiiei este fals se execut Blocul NU.

Structura repetitiv cu condiie iniial (structur repetitiv de tip while) Pas 1 : se evalueaz Conditie ; Pas 2: dac valoarea Conditiei este fals (NU), se iese din structura repetitiv; dac valoarea expresiei este adevrat (DA), se execut Bloc DA , apoi se reia execuia de la Pas 1

14

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Bucla

Conditi e NU

DA

Bloc DA

Structura repetitiv cu condiie final (structur repetitiv de tip do-while)

Bloc

Bucla DA

Conditi e NU

Pas 1 : se execut instruciunea sau instruciunile din Bloc; Pas 2: se evalueaz Conditie; Pas 3: dac valoarea Conditiei este fals (NU), se iese din instruciunea repetitiv; dac valoarea expresiei este adevrat (DA) se reia de la Pas 1 Structura repetitiv cu contor (structur repetitiv de tip for)

15

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Iniializare

Bucla DA

Conditi e NU

Bloc repetitiv

Trecere pas urmator

Pas 1: se execut instruciunea sau instruciunile din blocul Iniializare. n general, aceasta nseamn atribuirea unei valori iniiale unei variabile contor, folosit pentru numrarea (contorizarea) execuiilor repetitive efectuate. Fie aceast valoare iniial expresie_1; Pas 2: se evalueaz Conditie; o n general, aceast condiie (care poate fi dat sub forma unei expresii) verific dac valoarea variabilei contor este mai mic dect valoarea corespunztoare ultimei execuii a Blocului repetitiv. Dac valoarea expresiei Condiie este fals, atunci se iese din structura repetitiv. o dac valoarea expresiei Condiie este adevrat, atunci : se execut Blocul repetitiv se execut blocul Trecere la pas urmtor (care de obicei const n modificarea valorii variabilei contor cu o valoare specificata) se reia execuia de la Pas 2.

IB.01.6 Exemple de algoritmi reprezentai n schem logic Problema 1: Se definete funcia F(x), unde x este numr real, astfel: x2-2, x<0 3, x=0 x+2, x>0

F(x)=

S se scrie un program care calculeaz valoarea acestei funcii pentru 100 de valori ale lui x. Rezolvare: A dou etap a rezolvrii acestei probleme este elaborarea algoritmului ce implic analiza detaliat a problemei: Date: x - valoare real preluat de la consol Rezultate: y - valoare real care va fi afiat pe ecran (nsoit de un text explicativ) Principalele etape de rezolvare 16

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

citirea datelor (se presupun corecte) calculul valorii lui y n funcie de intervalul n care se ncadreaz valoarea x citit repetm aceste etape pentru cele 100 de valori ale lui x. Pentru a ti cte valori am citit vom folosi o variabil numit contor (deoarece contorizeaz/numr). Este o variabil ntreag i o vom numi i. Ea pornete cu valoarea iniial 0 (Atenie! S nu uitm acest pas, este important!) i la fiecare valoare citit i vom mri valoarea cu 1. Vom continua citirile atta timp ct i va fi mai mic dect 100. Urmeaz schema logic ce descrie n detaliu algoritmul propus: STAR TT

i =0 i =i+1

DA i < 100 NU STOP Citeste x x2-2 222222 2 x< 0 NU

DA Scrie y=x*x-2 2222222 DA

x == 0 NU

Scrie y=3 2222222

Scrie y=x+2 3xx222222 Problema 2: Actualizarea unei valori intregi cu un procent dat. 2 S se actualizeze o valoare natural cu un procent dat. Etapele elaborrii algoritmului sunt : 1. Formularea problemei: date: o valoare curenta (v) o procent actualizare (p) prelucrare: o calculul valorii actualizate rezultat: o valoarea calculat (r) 2. Analiza detaliat Date: 17

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

2 valori preluate de la consol: v - valoare ntreag, strict pozitiv p - valoare reala, pozitiv (majorare) sau negativ (diminuare) Rezultat: r - valoare real, strict pozitiv, afiat pe ecran (nsoit de un text explicativ) Principalele etape de rezolvare citirea datelor (se presupun corecte) calculul valorii actualizate cu formula v + v * p sau v * (1 + p) afiarea rezultatului Urmeaz schema logic ce descrie n detaliu algoritmul propus: STA RTT Citeste v, p r=v+v*p

Scrie Valoarea actualizt este , r STO P Pentru problemele care urmeaz vom oferi doar schema logic ce descrie algoritmul. Problema 3: Una dintre cele mai simple probleme este citirea i scrierea unei valori reale. Date de intrare: x variabil reala Date de ieire: acelai x. n acest caz rezultatul este chiar data de intrare. STAR TT Citeste x

Scrie x

STOP

18

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.01.7. Pseudocod Un pseudocod este un text intermediar ntre limbajul natural i un limbaj de programare. Are mai puine reguli dect un limbaj de programare i descrie numai operaiile de prelucrare (nu i variabilele folosite). Nu exist un pseudocod standardizat sau unanim acceptat. De asemenea, descrierea unor prelucrri n pseudocod se poate face la diferite niveluri de detaliere. Vom descrie n continuare operaiile unui algoritm ntr-un posibil pseudocod pe care l vom folosi n continuare (tabel): Operaia
Start Terminare Atribuire Citire Scriere Decizie

Pseudocod
start stop. variabila=valoare; citeste var; scrie var; daca conditie instructiuni_1; altfel instructiuni_2; atata timp cat conditie instructiuni; executa instructiuni; atata timp cat conditie pentru contor de la val_initiala la val_finala cu pasul pas instructiuni;

Structura repetitiv cu conditie iniial while Structura repetitiv cu condiie final do Structura repetitiv cu contor - for

Vom face urmtoarea convenie: liniile ce conin instruciuni care se execut n interiorul unciclu sau a unei decizii vor fi indentate (scrise deplasat n interior cu cateva spaii) fa de linia pe care ncepe ciclul sau decizia de care aparin, pentru a pune n eviden execuia acestora n cadrul ciclului (deciziei). O alt posibilitate de a marca, i mai puternic, un bloc de instruciuni este aceea de a delimita blocul utiliznd acolade: { instruciune 1 instruciune 2 . instruciune 3 } Aceast ultim convenie este utilizat i n limbajul C. O alt observaie legat de notaiile din tabelul anterior este legat de simbolul punct i virgul ;, care apare la sfritul instruciunilor. n limbajul C prezena acestuia este obligatorie, de aceea, pentru a uura trecerea de la pseudocod la C l-am introdus i n pseudocod, fr ns ca aici prezena lui s fie neaprat obligatorie. Totui, n cele exemplele ce urmeaz va fi folosit. IB.01.8 Exemple de algoritmi descrii n pseudocod Problema 1: Calculul funciei F(x) start 19

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

pentru i de la 0 la 100 cu pasul 1 citeste x; daca x < 0 scrie x * x 2; altfel daca x = 0 scrie 3; altfel scrie x + 2; stop.

Problema 2: Actualizarea unei valori naturale cu un procent dat

start citeste v, p; r = v + v * p; scrie r; stop. Problema 3: Citirea i scrierea unei valori.

start citeste x; scrie x; stop. Problema 4: Rezolvarea ecuaiei de grad 1: ax+b=0

start citeste a,b; daca a = 0 daca b = 0 scrie Ecuatia are o infinitate de solutii; altfel scrie Ecuatia nu are nici o solutie; altfel x = -b / a; scrie x; stop. Problema 5: S se afieze suma primelor n numere naturale, n citit de la tastatur.

start citeste n; s = 0; pentru i de la 0 la n cu pasul 1 20

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

s = s + i; scrie s; stop. Problema 6: Algoritmul lui Euclid care determin cel mai mare divizor comun a doi ntregi, prin mpriri repetate:

start citeste a,b; r = a mod b; atata timp cat r > 0 a = b; b = r; r = a mod b; scrie b; stop. Observaie: operatorul mod ntoarce restul mpririi ntregi a lui a la b.

21

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.02. Introducere n limbajul C. Elemente de baz ale limbajului


Cuvinte-cheie
Limbajul C, istoric, caracteristici, compilare, rulare, main, alfabet, atomi lexicali, identificatori, cuvinte cheie, tipuri de date, constante, variabile, comentarii, operatori, expresii, conversii de tip

IB.02.1 Limbajul C - Scurt istoric Limbajul C a fost proiectat i implementat de Dennis Ritchie ntre anii 1969 i 1973 la AT&T Bells Laboratories, pentru programe de sistem (programe care gestioneaz direct resursele hardware ale calculatorului, de exemplu, sistemul de operare), care pn atunci erau dezvoltate doar n limbaje de asamblare. A fost numit C deoarece este un succesor al limbajului B, limbaj creat de Ken Thompson. Originile limbajului C sunt strns legate de cele ale sistemului de operare UNIX, care iniial fusese implementat n limbajul de asamblare PDP-7 tot de Ritchie i Thompson. Deoarece limbajul B nu putea folosi eficient unele din facilitile sistemului de calcul PDP-11, pe care vroiau s porteze UNIX-ul, au avut nevoie de un limbaj simplu pentru scrierea nucleului sistemului de operare UNIX. n 1973, sistemul de operare UNIX este aproape n totalitate rescris n C, fiind astfel unul din primele sisteme de operare implementate n alt limbaj dect cel de asamblare. Cartea de referin care definete un standard minim al limbajului este scris de Brian W. Kernighan i Dennis Ritchie, "The C Programming Language" i a aprut n 1978 . ntre 1983-1989 a fost dezvoltat un standard international -- ANSI C (ANSI - American National Standards Institute). n 1990, ANSI C a fost adoptat de International Organization for Standardization (ISO) ca ISO/IEC 9899:1990, care mai este numit i C90. Acest standard este suportat de compilatoarele curente de C. Majoritatea codului C scris astzi are la baz acest standard. Orice program scris doar n ANSI C va rula corect pe orice platform ce are instalat o variant de C. n anii urmtori au fost dezvoltate medii de programare C performante sub UNIX i DOS, care au contribuit la utilizarea larg a limbajului. Necesitatea gruprii structurilor de date cu operaiile care prelucreaz respectivele date a dus la apariia noiunilor de obiect si clas. In 1980, Bjarne Stroustrup elaboreaz C with Classes. Acest limbaj a dus la mbuntirea C-ului prin adugarea unor noi faciliti, printre care i lucrul cu clase. n vara anului 1983, C-with-classes a ptruns i n lumea academic i a instituiilor de cercetare. Astfel, acest limbaj a putut s evolueze datorit experienei acumulate de ctre utilizatorii si. Denumirea final a acestui limbaj a fost C++. Succesul extraordinar pe care l-a avut limbajul C++ a fost asigurat de faptul c a extins cel mai popular limbaj al momentului, C. Programele scrise n C funcioneaz i n mediile de programare C++, i ele pot fi transformate n C++ cu eforturi minime. Cea mai recent etap n evoluia acestui limbaj o constituie limbajul JAVA (1995) realizat de firma SUN (firm cumprat n 2010 de ctre Oracle). n concluzie, sintaxa limbajului C a stat la baza multor limbaje create ulterior i nc populare azi: C++, Java, JavaScript, C#.

22

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.02.2 Caracteristicile limbajului C Caracteristicile limbajului C, care i-au determinat popularitatea, sunt prezentate pe scurt mai jos i vor fi analizate pe parcursul cursului: limbaj de nivel nalt, portabil, structurat, flexibil produce programe eficiente ( lungimea codului sczut, vitez de execuie mare ) set bogat de operatori multiple faciliti de reprezentare i prelucrare a datelor utilizare extensiv a apelurilor de funcii i a pointerilor verificare mai sczut a tipurilor - loose typing - spre deosebire de PASCAL permite programarea la nivel sczut - low-level - , apropiat de hardware Este utilizat n multe aplicaii: programe de sistem proiectare asistat de calculator grafic prelucrare de imagini aplicaii de inteligen artificial.

IB.02.3 Procesul dezvoltrii unui program C Procesul dezvoltrii unui program C Principalele etape n dezvoltarea unui program C sunt: 1. Analiza problemei i stabilirea cerinelor pe care trebuie s le satisfac programul (formatul i suportul datelor de intrare i al celor de ieire, cerine de memorie intern, timp de execuie i altele). 2. Proiectarea programului, utiliznd conceptele programrii structurate. Rezultatul este o structur ierarhic de uniti program care vor fi codificate n limbajul C. 3. Codificarea (implementarea) programului: reprezentarea algoritmilor prin instruciuni ale limbajului C. 4. Compilarea programului (eliminarea erorilor de sintax). 5. Testarea programului: execuia sa pentru date de intrare semnificative i compararea rezultatelor cu cele asteptate. Etapele 3, 4 i 5 sunt efectuate, de regul, cu ajutorul unui mediu integrat de dezvoltare (IDE Interactive Development Environment) precum CodeBlocks, DevCpp, MS Visual Studio, Eclipse sau Netbeans. Un mediu integrat de dezvoltare include un editor de text (program care permite i uureaz editarea textului surs al programului), compilatorul, editorul de legturi (care asambleaza ntr-un singur program main modulele rezultate din compilarea separat a unitilor program), precum i faciliti de depanare a programelor(de exemplu, execuia linie cu linie pentru

23

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

descoperirea cauzei unei erori). n absena unui mediu integrat de dezvoltare (ceea ce este mai putin uzual n prezent) putem face aceste operaii n mod linie de comand. Detaliat, etapele necesare pentru introducerea i execuia unui program C sunt urmtoarele: Etapa 1: Editarea programului surs (codificarea algoritmului ntr-un limbaj de programare) - Edit Aceast codificare se va realiza ntr-un program surs. Se salveaz fiierul surs, de exemplu cu numele "Hello.c". Un fiier C++ trebuie salvat cu extensia ".cpp", iar un fiier C cu extensia .c. Trebuie ales un nume care s reflecte scopul programului. Aceast etap se poate realiza utiliznd comenzile respective ale IDE-ului de exemplu New, Save - sau un editor de texte Notepad. Etapa 2: Compilarea i editarea legturilor Sunt compilate separat fiierele surs apoi prin editarea legturilor se obine programul n limbaj main salvat ntr-un fiier executabil. Exemplu: Hello.exe. Aceast etap se poate realiza utiliznd comenzile corespunztoare ale IDE-ului de exemplu, Compile, Build - sau o linie de comand, dac se utilizeaz compilatorul GNU GCC: n Windows (CMD shell) - genereaz fiierul executabil Hello.exe n Unix or Mac (Bash shell) - genereaz fiierul executabil Hello > gcc Hello.c -o Hello.exe $ gcc Hello.c -o Hello

Etapa 3: Execuia programului Aceast etap se poate realiza utiliznd comanda respectiv a IDE-ului Run - sau linie de comand. De exemplu, rularea (sau lansarea n execuie) n mod linie de comand se poate face astfel: n Windows (CMD shell) - Rulare "Hello.exe" (.exe este opional) n Unix or Mac (Bash shell) - Rulare "Hello" Tabelul conine o descriere sintetic a pailor detaliai: Pas 1 Descriere Crearea fiierului surs (cu extensia .c sau .cpp n cazul limbajului C/C++) folosind un editor de texte sau un IDE. Rezult un fiier surs. Poate fi creat i un fiier antet (header), cu extensia .h Preprocesarea codului surs n concordan cu directivele de preprocesare (#include, #define n C). Aceste directive indic operaii (includerea unui alt fiier, nlocuire de text etc) care se vor realiza NAINTE de compilare. Compilarea codului surs preprocesat. Rezult un fiier obiect (.obj, .o). Legarea (linked-itarea) codului obiect rezultat cu alte fiiere obiect i biblioteci pentru a furniza fiierul executabil (.exe). ncrcarea codului executabil n memoria calculatorului (RAM). Rularea codului executabil. > Hello $ ./Hello

3 4 5 6

Detaliarea acestor etape este reflectat n figura urmtoare: n stnga avem tipul de fi iere, la mijloc cine realizeaz acel pas, pentru ca n partea dreapt a figurii s apar paii efectivi:

24

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Editor sau IDE Cod surs (.c, .cpp), Header (.h) Preprocesor Includerea altor fiiere, nlocuire text Compilator Cod obiect .obj) Biblioteci statice (.lib, .l) Cod executabil Biblioteci dinamice (.dll, .so) DATE INTRARE (.o, Linker

Pas 1: Scriere cod surs

Pas Preprocesare Pas Compilare

2:

3:

BUIL D

Pas 4: Linkeditare LinkPreprocesare Pas 5: ncrcare ncancrcar encrcarePre Pas 6: Execuie procesare

Loader

RUN

CPU DATE IEIRE

Pentru mai multe detalii vom include aici legturi ctre tutoriale ale unor medii integrate de dezvoltare C: Tutorial CodeBlocks Tutorial NetBeans IB.02.4 Structura unui program C Un program C este compus dintr-o ierarhie de funcii, orice program coninnd cel puin funcia main, prima care se execut la lansarea programului C. Funciile pot face parte dintr -un singur fiier surs sau din mai multe fiiere surs. Un fiier surs C este un fiier text care conine o succesiune de funcii i, eventual, declaraii de variabile. Structura unui program C este, n principiu, urmtoarea: directive preprocesor definiii de tipuri prototipuri de funcii - tipul funciei i tipurile parametrilor funciei definiii de variabile externe definiii de funcii

O funcie C are un antet i un bloc de instruciuni (prin care se execut anumite aciuni) ncadrat de acolade. n interiorul unei funcii exist de obicei declaraii de variabile precum i alte blocuri de instruciuni. 25

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Primul program C Urmeaz un exemplu de program C minimal, ce afieaz un text Hello World! pe ecran. Exemplul conine o funcie main cu o singur instruciune (apelul funciei printf). Sunt prezentate trei variante pentru funcia main: Varianta 1 Cod
#include<stdio.h> void main(void) { printf(Hello World!); } #include<stdio.h> void main() { printf(Hello World!); } #include<stdio.h> int main() { printf(Hello World!); return 0; }

2 fr void n antetul funciei main

3 - fr warning la compilare (n Code::Blocks, de exemplu)

Execuia programului ncepe cu prima linie din main. Cuvntul din faa numelui funciei (main) reprezint tipul funciei (void arat c aceast funcie nu transmite nici un rezultat prin numele su, int arat c trimite un rezultat de tip ntreg, prin instruciunea return). Parantezele care urmeaz cuvntului main arat c main este numele unei funcii (i nu este numele unei variabile), dar o funcie fr parametri (acel void care poate lipsi varianta 2). Acoladele sunt necesare pentru a delimita corpul unei funcii, corp care este un bloc de instruciuni i declaraii. Funcia printf realizeaz afiarea pe ecran a textului Hello World!. Directiva #include este o directiv de preprocesare, permite includerea unor funcii de bibliotec. nm acest caz, indic necesitatea includerii n programul surs a fiierului stdio.h, n care este declarat funcia predefinit (de bibliotec) printf. Fr aceast includere, compilatorul nu recunoate funcia printf i semnaleaz eroare. Alte observaii care pot fi fcute: cuvintele cheie sunt scrise cu litere mici instruciunile se termin cu ';' irurile de caractere sunt incluse ntre ghilimele limbajul C este case sensitive face diferen ntre literele mici i literele mari ( a este diferit de A ) \n folosit n funcia printf poziioneaz cursorul la nceputul liniei urmtoare IB.02.5 Elemente de baz ale limbajului C Elementele de baz ale limbajului C sunt urmtoarele: Alfabetul i atomii lexicali Identificatorii Cuvintele cheie Tipurile de date Constantele i variabilele Comentariile Operatorii i expresiile 26

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.02.5.1 Alfabetul limbajului Alfabetul limbajului este compus din toate caracterele care pot fi folosite ntr-un program C. Caracterele se codific conform codului ASCII (American Standard Code for Information Interchange), codificarea realizndu-se pe 8 bii (un octet). Sunt 256 (codificate de la 0 la 255) de caractere n codul ASCII, alfabetul cuprinznd simboluri grafice i simboluri fr corespondent grafic. De exemplu, caracterul A cu codul ASCII 65 este codificat pe 8 bii 1 octet astfel:

Puterea lui 2 Valoare biti

76543210 01000001

Pentru a nelege mai bine ce nseamn mod de codificare urmtoarele noiuni auxiliare sunt descrise n anexa Tutorial despre reprezentarea datelor. Octet reprezentare n baza 2, 8, 10, 16 Limbajul C este case-sensitive (se face diferen ntre litere mici i litere mari). O alt observaie este aceea c spaiul are codul mai mic decat simbolurile grafice (32), iar cifrele (n ordine cresctoare), literele mari i literele mici (n ordine alfabetic) ocup cte trei zone compacte - cu intervale ntre ele. Pentru mai multe detalii legate de codul ASCII v rugm s urmai unul din urmtoarele link-uri: Tabel Coduri ASCII Tabel Coduri ASCII 2 Atomii lexicali pot fi: identificatori constante (explicite) - numerice, caracter, ir operatori semne de punctuaie.

Un atom lexical trebuie scris integral pe o linie i nu se poate extinde pe mai multe linii. n cadrul unui atom lexical nu se pot folosi spaii (excepie fac spaiile dintr-o constant ir), putem ns folosi caracterul '_'. Respectarea acestei reguli poate fi mai dificil n cazul unor iruri constante lungi, dar exist posibilitatea prelungirii unui ir constant de pe o linie pe alta folosind caracterul '\'. Atomii lexicali sunt separai prin separatori, care pot fi: spaiul caracterul de tabulare orizontal \t terminatorul de linie \n comentariul Un program este adresat unui calculator pentru a i se cere efectuarea unor operaii, dar programul trebuie citit i neles i de ctre oameni; de aceea se folosesc comentarii care explic de ce se fac anumite aciuni. Comentariile nu sunt incluse n codul executabil al programului. 27

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Iniial n limbajul C exista un singur tip de comentariu, comentariul multilinie, care ncepe cu secvena /* i se termin cu secvena */. Ulterior s-au adoptat i comentariile din C++, care ncep cu secvena // i se termin la sfritul liniei curente. Identificatorii pot fi : nume utilizator - nume de variabile, constante simbolice, funcii, tipuri, structuri, uniuni - este bine sa fie alese ct mai sugestiv pentru scopul utilizrii; cuvinte cheie ale limbajului C - pot fi folosite doar cu nelesul cu care au fost definite cuvinte rezervate - nelesul poate fi modificat, de evitat acest lucru;

Limbajul C impune urmtoarele reguli asupra identificatorilor: Un identificator este o secven de caractere, de o lungime maxim ce depinde de compilator (n general are maxim 255 de caractere). Secvena poate conine litere mici i mari(a-z, AZ), cifre (0-9) i simbolul '_' (underscore, liniua de subliniere). Primul caracter trebuie s fie o liter sau '_'. Pentru c multe nume rezervate de compilator, invizibile programatorului, ncep cu '_', este indicat a nu utiliza '_' pentru nceputul numelor utilizator. Un identificator nu poate fi un cuvnt cheie (int, double, if, else, for). Deoarece limbajul C este case-sensitive identificatorii sunt i ei la rndul lor case-sensitive. Astfel, suma este diferit de Suma i de SUMA. Identificatorii sunt atomi lexicali, deci n cadrul lor nu e permis utilizarea spaiilor i a altor caractere speciale (ca +, -, *, /, @, &, virgul, etc.), putem ns folosi caracterul '_' n locul spaiului i a caracterelor speciale. Exemple de identificatori: suma, _produs, x2, X5a, PI, functia_gauss etc. Recomandri: Este important s alegem un nume , care reflect foarte bine semnificaia identificatorului respectiv (este self-descriptive), de exemplu, vom alege numele nrStudenti sau numarDeStudenti ca identificator pentru o variabil care memoreaz numrul de studeni. ncercai s nu folosii identificatori care nu spun nimic (lipsii de un sens clar): a, b, c, d, i, j, k, i1, j99. Evitai numele de un singur caracter care sunt mai uor de folosit dar de cele mai multe ori lipsite de vreun neles. Acest lucru este ns utilizat dac sunt nume commune cum ar fi x, y, z pentru coordonate sau i, j pentru indici. Nu folosii nume foarte lungi, sunt greu de utilizat, ncercai s optimizai lungimea numelui cu nelesul acestuia. Folosii singularul i pluralul pentru a diferenia. De exemplu, putem folosi numele linie pentru a ne referi la numrul unei singure linii i numele linii pentru a ne referi la mai mu lte linii (de exemplu un vector de linii). Cuvintele cheie se folosesc n declaraii i instruciuni i nu pot fi folosite ca nume de variabile sau de funcii (sunt cuvinte rezervate ale limbajului). Exemple de cuvinte cheie: int char unsigned do extern register typedef else 28 double float static for

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

while switch case short long

struct union sizeof break auto

goto return default if continue

Standardul ANSI C a mai adugat: signed enum const volatile void

Numele de funcii standard (scanf, printf, sqrt etc.) nu sunt cuvinte cheie, dar nu se recomand utilizarea lor n alte scopuri, deoarece aceasta ar produce schimbarea sensului iniial, atribuit n toate versiunile limbajului. IB.02.5.2 Tipuri de date n C exist tipuri de date fundamentale i tipuri de date derivate. Sunt prezentate mai jos principale tipuri de date i numrul de octei pe care acestea le ocup pe un sistem Unix de 32 de bii. Tipurile de date fundamentale sunt: caracter (char 1 octet) ntreg (int 4 octe i) virgul mobil (float 4 octe i) virgul mobil dubl precizie (double 8 octe i) nedefinit (void) Tipurile de date derivate sunt: tipuri structurate (tablouri, structuri) tipul pointer (4 octe i)

Pentru a utiliza eficient memoria i a satisface necesitile unei multitudini de aplicaii exist n C mai multe tipuri de ntregi i respectiv de reali, ce difer prin memoria alocat i deci prin numrul de cifre ce pot fi memorate i prin domeniul de valori. Tipurile ntregi Numerele intregi se pot reprezenta n C folosind urmtoarele tipuri: char , short , int , long, long long. Implicit toate numerele ntregi sunt numere cu semn (signed), dar prin folosirea cuvntului cheie unsigned la declararea lor se poate cere interpretarea ca numere fr semn. Utilizarea cuvintelor long sau short face ca tipul respectiv s aib un domeniu mai mare, respectiv mai mic de valori. Tipuri n virgul mobil (reale) Exist dou tipuri, float i double, pentru numere reale reprezentate n virgul mobil cu simpl i respectiv dubl precizie. Unele implementri suport i long double (numai cu semn). Trebuie semnalat faptul c nu toate numerele reale pot fi reprezentate, ntruct memorarea valorilor reale, fiind realizat pe un anumit numr de bii, nu poate reine dect o parte dintre cifrele semnificative. Deci numai anumite valori reale au reprezentarea exact n calculator, restul confundndu-se cu reprezentarea cea mai apropiat. 29

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Tipul caracter Caracterele (exemplu 'a', 'Z', '0', '9') sunt codificate ASCII sub form de valori ntregi, i pstrate n tipul char. De exemplu, caracterul '0' are codul 48 (zecimal, adic n baza 10) sau 30H (hexazecimal baza 16); caracterul 'A' are codul 65 (zecimal) sau 41H (hexazecimal); caracterul 'a' are codul 97 (zecimal) sau 61H (hexazecimal). Se observ c tipul char poate fi folosit pentru a reprezenta ntregi fr semn n domeniul 0 255 sau pentru a reprezenta caractere n codul ASCII. Standardul C din 1999 prevede i tipul boolean _Bool (sau bool), reprezentat pe un octet. Reprezentarea intern i numrul de octei necesari pentru fiecare tip nu sunt reglementate de standardul limbajului C, dar limitele fiecrui tip pentru o anumit implementare a limbajului pot fi aflate din fiierul limits.h (care conine i nume simbolice pentru acest e limite - INT_MAX i INT_MIN) sau utiliznd operatorul sizeof; de exemplu: sizeof(short) ne d numrul de octei pe care se memoreaz o valoare de tip short. De obicei tipul int ocup 4 octei, iar valoarea maxim este de cca. 10 cifre zecimale pentru tipul int. Depirile la operaii cu ntregi de orice lungime nu sunt semnalate dei rezultatele sunt incorecte n caz de depire. De obicei valorile de tipul int se memoreaza pe 4 octei, iar valoarea maxim este de circa 10 cifre zecimale. Depirile la operaii cu ntregi de orice lungime nu sunt semnalate dei rezultatele sunt incorecte n caz de depire. Reprezentarea numerelor reale n diferite versiuni ale limbajului C este mai uniform deoarece urmeaz un standard IEEE de reprezentare n virgul mobil. Pentru tipul float domeniul de valori este ntre 10E-38 i 10E+38 iar precizia este de 6 cifre zecimale exacte. Pentru tipul double domeniul de valori este ntre 10E-308 i 10E+308 iar precizia este de 15 cifre zecimale. Tabelul urmtor prezint dimensiunea tipic, valorile minim i maxim pentru tipurile primitive (n cazul general). nc o dat, dimensiunea este dependent de implementarea C folosit. Categorie Tip int signed int unsigned int Descriere Octei 4 (2) 4 (2) 1 Valoare minim -2147483648 0 Valoare Maxim 2147483647 (=231-1) 4294967295 (=232-1 )

Numere ntregi

ntreg cu semn (cel puin 16 bii) ntreg fr semn (cel puin 16 bii) Caracter char (poate fi cu semn sau fr semn, depinde de implementare) Caracter sau ntreg signed char mic cu semn (garanteaz c e cu semn) Caracter or sau unsigned char ntreg mic fr semn (garanteaz c e fr semn) ntreg scurt cu short short int semn (cel puin 16 signed short bii) signed short int 30

-128

127 (=27-1 )

255 (=28-1 )

-32768

32767 (=215-1 )

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Numere Reale

Numere booleene

unsigned short ntreg scurt fr semn (cel puin 16 unsigned short int bii) ntreg lung cu long semn (cel puin 32 long int bii) signed long signed long int unsigned long ntreg lung fr semn (cel puin 32 unsigned long int bii) long long ntreg foarte lung long long int cu semn (cel puin 64 bii) (de la signed long long signed long long standardul C99) int unsigned long long ntreg foarte lung unsigned long long fr semn (cel puin 64 bii) (de la int standardul C99) Numr n virgul float mobil, 7 cifre(IEEE 754 format virgul mobil simpl precizie) Numr n virgul double mobil dubl precizie, 15 cifre(IEEE 754 format virgul mobil dubl precizie) Numr lung n long double virgul mobil dubl precizie, 19 cifre(IEEE 754 format virgul mobil cvadrupl precizie) Valoare boolean bool care poate fi fie true fie false (de la standardul C99)

65535 (=216-1 ) 2147483647 (=231-1 )

4 (8)

-2147483648

4 (8)

0 -263

4294967295 (=232-1 ) (=263-1)

264-1

3.4e-38

3.4e38

1.7e-308

1.7e308

12 (8)

3.4E-4932

1.1E4932

false (0)

true (1 sau diferit de zero)

IB.02.5.3 Valori corespunztoare tipurilor fundamentale Aceste valori constante, ca 123, -456, 3.14, 'a', "Hello", pot fi atribuite direct unei variabile sau pot fi folosite ca parte component a unei expresii. Valorile ntregi se reprezint implicit n baza 10 i sunt de tipul signed care este acoperitor pentru reprezentarea lor. 31

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Pentru reprezentarea constantelor fr semn se folosete sufixul u sau U O constant ntreag poate fi precedat de semnul plus (+) sau minus. Se poate folosi prefixul '0' (zero) pentru a arta c acea valoare este reprezentat n octal, prefixul '0x' pentru o valoare n hexadecimal i prefixul '0b' pentru o valoare binar (acceptat de unele compilatoare). O constanta de tip ntreg long este identificat prin folosirea sufixului 'L' sau 'l'. Un long long int este identificat prin folosirea sufixului 'LL'. Putem folosi sufixul 'U' sau 'u' pentru unsigned int, 'UL' pentru unsigned long, i 'ULL' pentru unsigned long long int. Pentru valori constante de tip short nu e nevoie de sufix. Exemple:
-10000 65000 32780 32780u 1234; 01234; 0x1abc; 0b10001001; 12345678L; 123UL; 987654321LL; // int // long // long // unsigned int // Decimal // Octal 1234, Decimal 2322 // hexadecimal 1ABC, decimal 15274 // binar (doar n unele compilatoare) // Sufix 'L' pentru long // int 123 auto-cast la long 123L // sufix 'LL' pentru long long int

Valorile reale Sunt implicit de tipul double; sufixul f sau F aplicat unei constante, o face de tipul float, iar l sau L de tipul long double. Un numr cu parte fracionar, ca 55.66 sau -33.442, este tratat implicit ca double. O constanta real se poate reprezenta i sub forma tiinific. Exemplu:
1.2e3 -5.5E-6 // 1.2*103 // -5.5*10-6

unde E denot exponentul puterii lui 10. Mantisa, partea de dinaintea lui E poate fi precedat de semnul plus (+) sau minus (-). mantisa are forma: parte_intreag.parte_zecimala ( oricare din cele dou pri poate lipsi, dar nu ambele ) exponentul are forma: eval_exponent sau Eval_exponent, unde val_exponent este un numr ntreg. Valoarea constantei este produsul dintre mantisa si 10 la puterea daa de val_exponent. In tabelul de mai jos apar cateva exemple de constante reale: Constante de tip float 1.f .241f Constante de tip double 1. .241 32 Constante de tip long double 1.L .241l

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

-12.5e5f 98.E-12f

-12.5e5 98.E-12

-12.5e5l 98.E-12L

Valori caracter Valorile de tip caracter se reprezint pe un octet i au ca valoare codul ASCII al caracterului respectiv. n reprezentarea lor se folosete caracterul apostrof : A (cod ASCII 65), b, +. Pentru caractere speciale se folosete caracterul \. Exemple:
\ - pentru apostrof \\ - pentru backslash Este greit: sau \

Constantele caracter pot fi tratate n operaiile aritmetice ca ntregi cu semn reprezentai pe 8 bii. Cu alte cuvinte, char i signed int pe 8 bii sunt interschimbabile. De asemenea, putem atribui un ntreg n domeniul [-128, 127] unei variabile de tip char i [0, 255] unui unsigned char. Caracterele non-tipribile i caracterele de control pot fi reprezentate fie prin secvene escape, care ncep cu un back-slash (\) urmat de o liter (\n = new line , \t =tab , \b = backspace etc), fie prin codul numeric al caracterului n octal sau n hexazecimal (\012 = \0x0a = 10 este codul pentru caracterul de trecere la linie nou \n). Cele mai utilizate secvene escape sunt: Secvena escape \n \r \t \" \' \\ Valori ir de caractere Valorile ir de caractere sunt compuse din zero sau mai multe caractere precizate ntre ghilimele. Exemple:
"Hello, world!" "The sum is " "" //reprezint irul vid

Descriere Linie nou (Line feed) Carriage-return Tab Ghilimele Apostrof Back-slash

Hexa (Decimal) 0AH (10D) 0DH (13D) 09H (9D) 22H (34D) 27H (39D) 5CH (92D)

Fiecare caracter din ir poate fi un simbol grafic, o secven escape sau un cod ASCII (n octal sau hexazecimal). Spaiul ocupat este un numr de octei cu unu mai mare dect numrul caracterelor din ir, ultimul octet fiind rezervat pentru terminatorul de ir- caracterul cu codul ASCII 0, adica '\0'. Dac se dorete ca i caracterul ghilimele s faca parte din ir, el trebuie precedat de \. 33

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exemple:
"CURS" "\x43URS" // scrieri echivalente ale unui ir ce ocup 5 octei "1a24\t" - "\x31\x61\x32\x34\11" //scrieri echivalente ale unui sir ce ocup 6 octei "'\"" //ir ce conine caracterele '" i terminatorul - ocup 3 octei

Tipul void nu are constante (valori) i este utilizat atunci cnd funciile nu ntorc valori sau cnd funciile nu au parametri:
// o funcie nu ntoarce o valoare: void f ( int a) { if (a) a = a / 2; } // sau cnd funciile nu au parametri: void f (void); // echivalent cu void f(); int f (void); //echivalent cu int f();

IB.02.5.4 Constante simbolice Acestea sunt constante definite cu ajutorul unor identificatori. Pot fi: predefinite sau definite de programator. Literele mari se folosesc n numele unor constante simbolice predefinite: EOF, M_PI, INT_MAX, INT_MIN. Aceste constante simbolice sunt definite n fiiere header (de tip h) : EOF n stdio.h, M_PI n math.h. Definirea unei constante simbolice se face utiliznd directiva #define astfel: #define identificator [text] Exemple:
#define begin { #define end } #define N 100 // unde apare begin acesta va fi nlocuit cu { // unde apare end acesta va fi nlocuit cu } // unde apare N acesta va fi nlocuit cu 100

Tipul constantelor C rezult din forma lor de scriere, n concordan cu tipurile de date fundamentale. n C++ se prefer alt definire pentru constante simbolice, utiliznd cuvntul cheie const. Pentru a face diferenierea dintre variabile i constante este de preferat ca definirea constantelor s se fac prin scrierea acestora cu majuscule. Exemple:
const double PI = 3.1415; const int LIM = 10000;

34

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.02.5.5 Variabile Variabila este o entitate folosit pentru memorarea unei valori de un anumit tip, tip asociat variabilei. O variabil se caracterizeaz prin nume, tip, valoare i adres: Orice variabil are un nume (identificator), exemplu: raza, area, varsta, nr. Numele identific n mod unic fiecare variabil permind utilizarea acesteia, cu alte cuvinte, numele unei variabile este unic (nu pot exista mai multe variabile cu acelai nume n acelai domeniu de definiie) Orice variabil are asociat un tip de date. Tipul poate fi orice tip fundamental sau derivat, precum i un tip definite de programator. O variabil poate stoca o valoare de un anumit tip. De menionat c n majoritatea limbajelor de programare, o variabil asociat cu un anumit tip poate stoca doar valori aparinnd acelui tip. De exemplu, o variabil de tipul int poate stoca valoarea 123, dar nu irul Hello. Oricrei variabile i se aloc (rezerv) un spaiu de memorie corespunztor tipului variabilei. Acest spaiu este identificat printr-o adres de memorie. Pentru a folosi o variabil trebuie ca mai nti s i declarm numele i tipul, folosind una din urmtoarele forme sintactice: tip nume_variabila; // Declar o variabil de un anumit tip tip nume_variabila1, nume_variabila2,...; // Declaraie multipl pentru mai multe variabile de acelai tip tip var-name = valoare_iniiala; // Declar o variabil de un anumit tip i i atribuie o valoare iniial tip nume_variabila1 = valoare_iniiala1, nume_variabila2 = valoare_iniiala2, ... ; // Declar mai multe variabile unele putnd fi iniializate, nu e obligatoriu s fie toate! Sintetiznd, definirea variabilelor se face astfel: Tip lista_declaratori; lista_declaratori cuprinde unul sau mai multi declaratori, desprii prin virgul declarator poate fi: nume_variabila sau nume_variabila = expresie_de_initializare n expresie_de_initializare pot apare doar constante sau variabile iniializate!

Exemple:
int sum; // Declar a variabil sum de tipul int int nr1, nr2; // Declar dou variabile nr1 i nr2 de tip int double media; // Declar a variabil media de tipul double int height = 20; /* Declar a variabil de tipul int i i atribuie o valoare iniial */ char c1; // Declar o variabil c1 de tipul char char car1 = 'a', car2 = car1 + 1; // car2 se initializeaza cu 'b' float real = 1.74, coef; /*Declar dou variabile de tip float prima din ele este i iniializat*/

35

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n definirea tip lista_declaratori; tip poate fi precedat sau urmat de cuvntul cheie const, caz n care variabilele astfel definite sunt de fapt constante ce trebuie s fie iniializate i care nu-i mai pot modifica apoi valoarea:
const int coef1 = -2, coef2 = 14; coef1 = 5; /* modificarea valorii variabilei declarate const e gresita, apare eroare la compilare!! */

Odat ce o variabil a fost definit i putem atribui sau reatribui o valoare folosind operatorul de atribuire "=". Exemple:
int number; nr = 99; //atribuie valoarea ntreag 99 variabilei nr nr = 88; //i reatribuie valoarea 88 nr = nr + 1; //evalueaz nr+1 i atribuie rezultatul lui nr int sum = 0; int nr; //ERROR: O variabil cu numele nr e deja definit /* WARNING: Variabila sum este de tip int (va primi valoarea 55)*/ sum = "Hello"; /* ERROR: Variabila sum este de tip int. Nu i se poate atribui o valoare ir */

Observaii: O variabil poate fi declarat o singur dat. n fiiere cu extensia cpp putem declara o variabil oriunde n program, atta timp ct este declarat nainte de utilizare. Tipul unei variabile nu poate fi schimbat pe parcursul programului. Numele unei variabile este un substantiv, sau o combinaie de mai multe cuvinte. Prin convenie, primul cuvnt este scris cu liter mic, n timp ce celelalte cuvinte pot fi scrise cu prima liter mare, fr spaii ntre cuvinte. Exemple: dimFont, nrCamera, xMax, yMin, xStangaSus sau acestaEsteUnNumeFoarteLungDeVariabila. IB.02.5.6 Comentarii Comentariile sunt utilizate pentru documentarea i explicarea logicii i codului programului. Comentariile nu sunt instruciuni de programare i sunt ignorate de compilator, dar sunt foarte importante pentru furnizarea documentaiei si explicaiilor necesare pentru nelegerea programului nostru de ctre alte persoane sau chiar i de noi nine peste o sptmn. Exist dou tipuri de comentarii: Comentarii Multi-linie: ncep cu /* i se termin cu */, i se pot ntinde pe mai multe linii Comentarii n linie: ncep cu // i in pn la sfritul liniei curente. Exemple: /* Acesta este un comentariu n C */ 36

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// acesta este un alt comentariu C (C++) n timpul dezvoltrii unui program, n loc s tergem o parte din instruciuni pentru totdeauna, putem s le transformm in comentarii, astfel nct s le putem recupera mai trziu dac vom avea nevoie. IB.02.5.7 Operatori, operanzi, expresii Limbajul C se caracterizeaz printr-un set foarte larg de operatori. Operatorii sunt simboluri utilizate pentru precizarea operaiilor care trebuie executate asupra operanzilor. Operanzii pot fi: constante, variabile, nume de funcii, expresii. Expresiile sunt entiti construite cu ajutorul operanzilor i operatorilor, respect nd sintaxa (regulile de scriere) i semantica (sensul, nelesul) limbajului. Cea mai simpl expresie este cea format dintr-un singur operand. Cu alte cuvinte, putem spune c o expresie este o combinaie de operatori ('+', '-','*', '/', etc.) i operanzi (variabile sau valori constante), care poate fi evaluat ca avnd o valoare fix, de un anumit tip. Expresiile sunt de mai multe tipuri, ca i variabilele: 3*x+7, x<y etc. La evaluarea expresiilor se ine cont de preceden i asociativitatea operatorilor! Exemple
1 + 2 * 3 int sum, number; sum + number; double princ, rata; princ * (1 + rata); sum + princ // rezult int 7 // evaluat la o valoare de tip int // evaluat la o valoare de tip double // evaluata la o valoare de tip double

Operatorii Clasificarea operatorilor se poate face: dup numrul operanzilor prelucrai: unari binari ternari - cel condiional; dup ordinea de succedare a operatorilor i operanzilor: prefixati infixati postfixati dup tipul operanzilor i al prelucrrii: aritmetici relaionali logici la nivel de bit. Operatorii se mpart n clase de preceden, fiecare clas avnd o regul de asociativitate, care indic ordinea aplicrii operatorilor consecutivi de aceeai preceden (prioritate). Regula de 37

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

asociativitate de la stnga la dreapta nseamn c, de exemplu, expresia 1+2+3-4 este evaluat astfel: ((1+2)+3)-4. Tabelul operatorilor C indic att regula de asociativitate, implicit de la stnga la dreapta (s -a figurat doar unde este dreapta-stnga), ct i clasele de preceden, de la cea mai mare la cea mai mic preceden: Operator () [] . -> -++ + -++ ! ~ * & sizeof () * / % + << >> < <= > >= == != & ^ | && || ?: = Semnificaie paranteze indexare selecie selecie indirect postdecrementare postincrementare schimbare semn plus unar ( fr efect) predecrementare preincrementare negaie logic complementare ( negare bit cu bit ) adresare indirect preluare adres determinare dimensiune ( n octei ) conversie de tip ( cast ) nmulire mprire rest mprire ( modulo ) adunare scdere deplasare deplasare dreapta mai mai mic mai mai mare sau egal egal diferit i pe bii sau exclusiv pe bii sau pe bii i logic ( conjuncie ) sau logic ( disjuncie ) operator condiional ( ternar ) atribuire 38 sau stnga mic egal mare Utilizare (e) t[i] s.c p->c a-a++ -v +v --a ++a !i ~i *p &a sizeof(x) (d) e v1 * v2 v1 / v2 v1 % v2 v1 + v2 v1 - v2 i1 << i2 i1 >> i2 v1 < v2 v1 <= v2 v1 > v2 v1 >= v2 v1 == v2 v1 != v2 i1 & i2 i1 ^ i2 i1 | i2 i1 && i2 i1 || i2 expr ? v1 : v2 a=v dreapta - stnga Asociativitate

dreapta - stnga

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

*= /= %= += -= &= ^= |= <<= >>= ,

variante operatorului de atribuire

ale

a *= v

dreapta - stnga

secveniere

e1, e2

Legend: a - variabila ntreag sau real c - cmp d - nume tip e - expresie f - nume de funcie x - nume tip sau expresie

i v p s t -

valoare

ntreag sau structur sau -

ntreg real pointer uniune tablou

Operatorii aritmetici Limbajul C pune la dispoziie urmtorii operatori aritmetici pentru numere de tip ntreg: short, int, long, long long, char (tratat ca 8-bit signed integer), unsigned short, unsigned int, unsigned long, unsigned long long, unsigned char, float, double i long double. expr1 i expr2 sunt dou expresii de tipurile enumerate mai sus. Operator + * / Semnificaie schimbare semn plus unar ( fr efect) Utilizare -expr1 +expr1 expr1 * expr2 expr1 / expr2 expr1 % expr2 expr1 + expr2 expr1 - expr2 Exemple -6 -3.5 +2 +2.5 2 * 3 6; 3.3 * 1.0 3.3 1 / 2 0; 1.0 / 2.0 0.5 5 % 2 1; -5 % 2 -1 1 + 2 3; 1.1 + 2.2 3.3 1 - 2 -1; 1.1 - 2.2 -1.1

nmulire mprire rest mprire ( modulo ) adunare scdere

% +

Este important de reinut c int / int produce un int, cu rezultat trunchiat, i anume partea ntreag a mpririi: 1/2 0 (n loc de 0.5), iar operatorul modulo (% ) este aplicabil doar pentru numere ntregi. n programare, urmtoarea expresie aritmetic:

Trebuie scris astfel: (1+2*a)/3 + (4*(b+c)*(5-d-e))/f - 6*(7/g+h). Simbolul pentru nmulire '*' nu se poate omite (cum este n matematic). 39

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Ca i n matematic, nmulirea i mprirea au preceden (prioritate) mai mare dect adunarea i scderea. Aceasta nseamn c la evaluarea unei expresii n care apar operatori de adunare, scdere, nmulire i mprire, se vor efectua mai nti operaiile de nmulire i mprire n ordinea lor de la stnga la dreapta, apoi operaiile de adunare i scdere, tot de la stnga la dreapta. Parantezele sunt ns cele care au cea mai mare preceden: ntr-o expresie cu paranteze se evalueaz mai nti coninutul fiecrei paranteze (innd cont de precedena operatorilor) apoi se evalueaz expresia fr paranteze (n care fiecare parantez a fost nlocuit cu rezultatul evalurii sale). Depirile la calculele cu valori reale sunt semnalate, dar nu i cele de la calculele cu valori ntregi (valoarea rezultat este trunchiat). Se semnaleaz, de asemenea, eroarea la mprirea cu 0. Operatori relaionali i logici Deseori, e nevoie s comparm dou valori nainte s decidem ce aciune s realizm. De exemplu, dac nota este mai mare dect 50 afieaz "Admis". Orice operaie de comparaie implic doi operanzi ( x <= 100 ). n C exist ase operatori de comparaie (mai sunt numii i operatori relaionali): Operator == != > >= < <= Semnificaie egal cu diferit mai mare mai mare egal mai mic mai mic egal Utilizare expr1 == expr2 expr1 != expr2 expr1 > expr2 expr1 >= expr2 expr1 < expr2 expr1 >= expr2 Exemple (x=5, y=8) (x == y) false (x != y) true (x > y) false (x >= 5) true (y < 8) false (y <= 8) true

Aceste operaii de comparaie returneaz valoarea 0 pentru fals i o valoare diferit de zero pentru adevrat. Convenie C: Valoarea 0 este interpretat ca fals i orice valoare diferit de zero ca adevrat. n C exist patru operatori logici: Operator && || ! ^ Semnificaie i logic sau logic negaie logic sau exclusiv logic Utilizare expr1 && expr2 expr1 || expr2 !expr1 expr1 ^ expr2 Exemple (expr1=0, expr2=1) 0 Diferit de 0 Diferit de 0 1

Tabelele de adevr sunt urmtoarele: AND (&&) true false true true false false false false 40

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

OR (||) true false NOT (!)

true true true true false true false true

false true false false true false true false

XOR (^) true false

Este incorect s scriem 1 < x < 100; operaia trebuie separat n dou operaii de comparaie, x>1, x < 100, pe care le unim cu un operator logic I: (x > 1) && (x < 100). Exemple:
// Returneaz true dac x este ntre 0 i 100 (inclusiv) (x >= 0) && (x <= 100) // greit 0 <= x <= 100 // Returneaz true dac x nu este ntre 0 i 100 (inclusiv) (x < 0) || (x > 100) //sau x < 0 || x > 100 /* operatorii relationali au prioritate mai mare decat cei logici */ !((x >= 0) && (x <= 100)) /* Returneaz true dac year este bisect. Un an este bisect dac este divizibil cu 4 dar nu cu 100, sau este divisibil cu 400.*/ ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0) //sau: year % 4 == 0 && year % 100 != 0 || year % 400 == 0 /* operatorii aritmetici au prioritate mai mare decat cei relationali */

Operatorii de atribuire Pe lng operatorul de atribuire '=', utilizat n exemplele anterioare, limbajul C mai pune la dispoziie aa numiii operatori de atribuire compui: Operator = += -= *= /= %= Semnificaie atribuire var = var + expr var = var expr var = var * expr var = var / expr var = var % expr Utilizare var = expr var + = expr var - = expr var * = expr var / = expr var %= expr Exemple x=5 x+=5 (echivalent cu x=x+5) x-=5 (echivalent cu x=x-5) x*=5 (echivalent cu x=x*5) x/=5 (echivalent cu x=x/5) x%=5 (echivalent cu x=x%5)

Limbajul C mai introduce i cei doi operatori aritmetici pentru incrementare i decrementare cu 1:

41

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Operator --var

Semnificaie predecrementare

Exemple y = --x;

var-++var var++

post decrementare

y=x--; y=++x; y=x++;

preincrementare post incrementare

Rezultat echivalent cu x=x-1; y=x; echivalent cu y=x; x=x-1; echivalent cu x=x+1; y=x; echivalent cu y=x; x=x+1;

Exemple:
int i, j, k; // variabilele i = j = k = 0; sunt initializate cu aceeasi valoare, 0

float lungime, latime, inaltime, baza, volum; // calculeaza baza si volumul unui paralelipiped volum = inaltime * ( baza = lungime * latime );

Operatorii pe bii Se aplic fiecrui bit din reprezentarea operanzilor ntregi spre deosebire de restul operatorilor care se aplic valorilor operanzilor. Din aceast categorie fac parte operatorii urmtori, care apar in ordinea descresctoare a priorit ii: Operatori pe bii ~ complementare >> deplasare la dreapta << deplasare la stanga & si ^ sau exclusiv | sau Operatorii logici pe bii sunt cuprini n urmtorul tabel: Operator Descriere Utilizare I pe bii expr1 & expr2 & SAU pe bii expr1 | expr2 | XOR pe bii expr1 ^ expr2 ^ De asemenea, sunt disponibili i operatorii compui de atribuire: &=, |= i ^=. Operatorii &, ^, | realizeaz operaiile i, sau exclusiv, respectiv sau, ntre toate perechile de bii corespunztori ai operanzilor. Daca b1 si b2 reprezint o astfel de pereche, tabelul urmtor prezint valorile obinute prin aplicarea operatorilor &, ^, |. b1 0 0 b2 0 1 b1&b2 0 0 b1^b2 0 1 b1|b2 0 1 42

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

0 0 1 1 1 1 1 0 1 1 Din tabela de mai sus se observ c aplicnd unui bit: operatorul & cu 0, bitul este ters operatorul & cu 1, bitul este neschimbat operatorul | cu 1, bitul este setat (are valoarea 1) operatorul ^ cu 1, bitul este complementat. Operatorul ~ transform fiecare bit 0 i cei 0 n 1) Exemplu: char a,b; a b ~a 00000000 00000001 11111111 11111111 10101010 00000000 din reprezentarea operandului n complementarul su (biii 1 n

!a ~b !b a&b a^b a|b 1 11111110 0 00000000 00000001 00000001 0 01010101 0 10101010 01010101 11111111

Operatorii de deplasare sunt descrii n tabelul urmtor: Operator Utilizare Descriere operand << number Deplasare la stnga << operand >> number Deplasare la dreapta >> n cazul operatorilor de deplasare, care sunt binari, primul operand este cel al carui bii sunt deplasai, iar al doilea indic numrul de bii cu care se face deplasarea -- deci numai primul operand este prelucrat la nivel de bit: a<<n, a>>n. La deplasarea la stnga cu o poziie, bitul cel mai semnificativ se pierde, iar n dreapta se completeaz cu bitul 0. La deplasarea la dreapta cu o poziie, bitul cel mai puin semnificativ se pierde, iar n stnga se completeaz cu un bit identic cu cel de semn. n tabelul urmtor apar valorile obinute (n binar i zecimal) prin aplicarea operatorilor de deplasare: char a; a a<<1 a<<2 a>>1 a<<2 00000010 00000100 00000000 00000000 00000001 2 4 0 0 1 00011100 00111000 00000111 00000011 00001110 28 56 7 3 14 11111110 11111100 11111111 11111111 11111111 -2 -4 -1 -1 -1 10111010 01110100 11101110 11110111 11011101 -70 116(depasire) -18 -9 -35 Cu excepia cazurilor cnd se produce depire, deplasarea la stnga cu n bii echivaleaz cu nmulirea cu 2 la puterea n. Deplasarea la dreapta cu n bii echivaleaz cu mprirea cu 2 la puterea n. Este indicat s se realizeze nmulirile i mpririle cu puteri ale lui 2 prin deplasri (necesit un timp mult mai scurt): Exemple: Cele doua secvene din tabelul urmtor conduc la aceleai rezultate, unde i este de tipul int (int i; ) 43

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

operaie i*=8; i/=4; i*=10;

echivalent cu i<<=3; i>>=2; i=i<<3+i<<1;

Se consider n, p ntregi. S se seteze pe 1 bitul p din reprezentarea lui n, (ceilali bii rmn nemodificai).
unsigned int n=5, p=1; n = n | (1<<p);

Se consider n i p ntregi. S se seteze pe 0 bitul p din reprezentarea lui n.


unsigned int n=7,p=1; n&= ~(1<<p);

Operatorul sizeof Rezultatul aplicrii acestui operator unar este un ntreg reprezentnd numrul de octei necesari pentru stocarea unei valori de tipul operandului sau pentru stocarea rezultatului expresiei dac operandul este o expresie. Operatorul are efect la compilare, pentru c atunci se stabilete tipul operanzilor. Sintaxa: sizeof (tip) sizeof (expresie) Exemple:
sizeof('a') sizeof(int) sizeof(2.5 + 3) // =1 // =2 // =4

Operatorul condiional Operatorul condiional ? : este singurul operator ternar. Are prioritatea mai ridicat doar dect a operatorilor de atribuire i a celui secvenial (virgul), iar asociativitatea este de la dreapta spre stnga. El se folosete n situaiile n care exist dou variante de obinere a unui rezultat, dintre care se alege una singur, funcie de ndeplinirea sau nendeplinirea unei condiii. Cei trei operanzi sunt expresii, prima reprezentand condiia testat. expr0 ? expr1 : expr2

Dac valoarea expr0 este adevarat ( !=0 ), se evalueaz expr1, altfel expr2, rezultatul expresiei evaluate fiind rezultatul final al expresiei condiionale. Exemple: /* Expresia de mai jos determin valoarea maxim dintre a si b, pe care o memoreaz n max:*/ max = a > b ? a : b; /* n funcie de ora memorat n variabila hour, funcia puts va tipri mesajul corespunztor. Evaluare dreapta -> stnga a expresiilor condiionale multiple */ 44

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

puts( hour < 0 || hour > 24 ? "Ora invalida" : hour < 12 ? "Buna dimineata!" : hour < 18 ? "Buna ziua!" : hour < 22 ? "Buna seara" : "Noapte buna!"); Operatorul secvenial Operatorul secvenial , ( virgul ) este cel cu prioritatea cea mai sczut. Se folosete atunci cnd sintaxa limbajului impune prezena unei singure expresii, iar prelucrarea presupune evaluarea a dou sau mai multe expresii; acestea se evalueaz de la stnga la dreapta, iar rezultatul ntregii expresii este cel al ultimei expresii ( exprn ): expr1, expr2, ..., exprn Exemplu:
/* Expresia de mai jos memoreaz n max valoarea maxim dintre a si b, realiznd i ordonarea descresctoare a acestora (le interschimb dac a<b). A se observa c interschimbarea presupune utilizarea unei variabile auxiliare. Operatorul secvenial e necesar pentru a avea o singur expresie dup : */ int a, b, max = a >= b ? a : (aux = b, b = a, a = aux); // aux, max;

IB.02.6 Conversii de tip. Operatorul de conversie explicita (cast) Conversia de tip se efectueaz[ asupra unui operand de un anumit tip i are ca rezultat o valoare echivalent de un alt tip. Exist dou tipuri de conversii de tip: 1. Implicit, realizat automat de compilator 2. Explicit, utiliznd operatorul unar de conversie de tip n forma: (tip_nou) operand Conversii de tip implicite n limbajul C, dac atribuim o valoare de tip double unei variabile ntregi, compilatorul realizeaz o conversie de tip implicit, returnnd o valoare ntreag. Partea fracionar se va pierde. Unele compilatoare genereaz o avertizare (warning) sau o eroare "possible loss in precision"; altele nu.

La evaluarea expresiilor pot apare conversii implicite: dac o expresie are doar operanzi ntregi, ei se convertesc la int; dac o expresie are doar operanzi reali sau intregi, ei se convertesc la double; dac o expresie are operanzi de tipuri diferite, compilatorul converteste valoarea tipului mai mic la tipul mai mare. Operaia se realizeaz apoi n domeniul tipului mai mare. De exemplu, int / double double / double double. Deci, 1/2 0, 1.0/2.0 0.5, 1.0/2 0.5, 1/2.0 0.5.

45

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n expresia variabila=expresie se evalueaz prima dat expresia, fr a ine cont de tipul variabilei; dac tipul rezultatului obinut este diferit de cel al variabilei, se realizeaz conversia implicit la tipul variabilei astfel: Exemple: Tip int double mixt int double mixt Operaie int 2 + int 3 int 5 double 2.2 + double 3.3 double 5.5 int 2 + double 3.3 double 2.0 + double 3.3 double 5.3 int 1 / int 2 int 0 double 1.0 / double 2.0 double 0.5 int 1 / double 2.0 double 1.0 + double 2.0 double 0.5

Exemplu 2+3 2.2 + 3.3 2 + 3.3 1/2 1.0 / 2.0 1 / 2.0

Exemple conversii implicite:


int i; char c = 'c'; long l; float f; i = 2.9; // 2.9 e convertit implicit la int, deci i va fi 2 f = 'A'; // f va fi 65.0 ( codul ASCII ) i = 30000 + c; // expresia se calculeaza in domeniul int, i va fi 30099 i = 30000 + 10000;// calcul in dom. int, depasire nesemnalata, i = 25536 l = 30000 + 10000; // -25536 va fi convertit la long l=30000u+10000; // rezultat corect l=30000l+10000; // rezultat corect

Conversii de tip explicite Operatorul de conversie explicit (denumit cast) se utilizeaz atunci cnd se dorete ca valoarea unui operand (expresie) s fie de un alt tip dect cel implicit. Operatorul este unar, are prioritate ridicat si are sintaxa: ( tip ) expresie Exemple de conversii explicite
float r; r = 5 / 2; // impartirea se face in domeniul int, deci r va fi 2.0

r = (float) 5 / 2; /* r va fi 2.5; pentru ca primul operand este de tip float calculul se face n domeniul real */ int x = (int) r; //x va fi 2

C++ suport i conversii de tip de genul new-type(operand):


medie = double(sum) / 100; // echivalent cu (double)sum / 100

46

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.03. Funcii de intrare/ieire n limbajul C


Cuvinte-cheie Funcii de intrare/ieire caractere, funcii de intrare/ieire iruri de caractere, citire/scriere cu format IB.03.1 Funcii de intrare/ieire n C n limbajul C, nu exist instruciuni de intrare/ieire (citire/scriere), tocmai pentru a mri portabilitatea limbajului. Pentru a realiza citiri i scrieri se apeleaz funcii de intrare/ieire din bibliotecile mediului de programare. Pentru operaii de citire a datelor iniiale i de afiare a rezultatelor sunt definite funcii standard, declarate n fiierul antet stdio.h. Se vor prezenta i funciile de intrare/ieire nestandard cele mai uzuale, care sunt declarate n conio.h. Utilizarea acestor funcii ntr-un program, va presupune deci, includerea respectivelor fiiere antet. n acest capitol vom prezenta numai acele funcii folosite pentru citire de la tastatur i pentru afiare pe ecran, deci pentru lucru cu fiierele standard numite stdin i stdout. Fiierele standard stdin i stdout sunt fiiere text. Un fiier text este un fiier care conine numai caractere ASCII grupate n linii (de lungimi diferite), fiecare linie fiind terminat cu un terminator de linie format din unul sau dou caractere. n sistemele Windows se folosesc dou caractere ca terminator de linie: \n i \r, adic newline (trecere la linie nou) i return (trecere la nceput de linie). n sistemele Unix/Linux se folosete numai caracterul \n (newline) ca terminator de linie, caracter generat de tasta Enter. Funciile I/O ( Input/Output - intrare/ieire ) din C pot fi grupate n cteva familii: Funcii de citire/scriere caractere individuale: getchar, putchar, getch, putch; Funcii de citire/scriere linii de text: gets, puts; Funcii de citire/scriere cu format (cu conversie): scanf, printf.

IB.03.2 Funcii de citire/scriere pentru caractere i iruri de caractere Pentru operaiile I/O la nivel de caracter: int getchar (); int putchar (int c); int getche (); int getch (); int putch (int c);

Pentru operaii I/O cu iruri de caractere: char * gets(char * s); int puts(const char * ir);

47

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Tabelul urmtor conine o descriere detaliat a funciilor pentru operaiile I/O la nivel de caracter: Funcie int getchar(); Exemple
char c; c=getchar();

Descriere Citete un caracter

int putchar(int c); int getche();

char c; putchar(c); char c; c=getche();

Afieaz pe ecran caracterul transmis ca parametru

Citete un caracter (ateapt apsarea unei taste, chiar dac n buffer-ul de intrare mai sunt caractere nepreluate) i afieaz caracterul pe ecran char c; Analog cu funcia de mai sus, dar int getch(); c=getch(); caracterul nu se transmite n ecou (nu se afieaz pe ecran). Tiprete pe ecran caracterul Returneaz codul int putch(int char c; putch(c); transmis ca parametru caracterului sau EOF n c); cazul unei erori. Funciile getch, putch, getche nu sunt standard, de aceea este bine s se evite utilizarea lor!

Rezultat Returneaz codul unui caracter citit de la tastatura sau valoarea EOF (End Of File constant simbolic definit n stdio.h, avnd valoarea -1) dac s-a tastat Ctrl+Z. Returneaz codul caracterului sau EOF n cazul unei erori. Returneaz EOF la tastarea lui Ctrl+Z, respectiv CR ( \r, cu codul 13) la tastarea lui Enter.

Tabelul urmtor conine o descriere detaliat a funciilor pentru operaiile I/O cu iruri de caractere: Funcie Exemple Descriere Rezultat utilizare Citete caractere pn la Returneaz adresa char * gets(char * char sir[10]; gets(sir); ntlnirea lui Enter; primului caracter din ir. s); acesta nu se adaug la Dac se tasteaz irul s. CTRL+Z returneaz Plaseaz /0 la sfritul lui NULL. s. Obs: codul lui Enter e scos din buffer-ul de intrare. char sir[10]; Tiprete irul primit ca Returneaz codul int puts(const char * puts(sir); parametru, apoi ultimului caracter din ir s); NewLine, cursorul sau EOF la insucces trecnd la nceputul rndului urmtor IB.03.3 Funcii de citire/scriere cu format Funciile scanf i printf permit citirea cu format i respectiv scrierea cu format pentru orice tip de date. Antetul i descrierea acestor funcii sunt: 48

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int printf ( format, arg1, arg2, ... ); afieaz pe ecran valorile expresiilor din lista argumentelor, conform formatului specificat; argumentele pot fi constante, variabile, expresii dac nu apare nici un argument, pe ecran se tipresc doar caracterele din irul format. formatul este un ir de caractere care trebuie s includ cte un descriptor de format pentru fiecare din argumente. caracterele din format care nu fac parte din descriptori se tipresc pe ecran. Returneaz numrul de valori tiprite sau EOF n cazul unei erori. int scanf ( format, adr1, adr2, ... ); Citete caracterele introduse de la tastatur, pe care le interpreteaz conform specificatorilor din format, memornd valorile citite la adresele transmise ca parametri. Formatul este un ir de caractere care trebuie s includ cte un descriptor de format pentru fiecare dintre valorile citite. Adresele sunt pointeri sau adresele variabilelor ale cror valori se citesc; Adresa unei variabile se obine folosind operatorul de adresare &, astfel: &nume_variabila Valorile ntregi sau reale consecutive introduse de la tastatur trebuie separate de cel puin un spaiu (enter, spaiu, tab) Returneaz numrul de valori citite sau EOF dac s-a tastat Ctrl/Z. IB.03.3.1 Funcia printf Descriptorii de format admii n funcia printf sunt: Specificatori format Descriere %d ntreg zecimal cu semn %i ntreg zecimal, octal (0) sau hexazecimal (0x, 0X) %o %u %x, %X %c %s %f, %F %e, %E ntreg n octal, fr 0 la inceput ntreg zecimal fr semn ntreg hexazecimal, fr 0x/0X; cu a-f pt. %x, A-F pt. %X caracter ir de caractere, pn la '\0' sau nr. de caractere dat ca precizie real fr exponent; precizie implicit 6 pozitii; la precizie 0: fr punct real (posibil cu exponent) numere reale cu mantis i exponent (al lui 10); precizie implicit 6 poz.; la precizie 0: fr punct numere reale n format %f sau %e, funcie de valoare real, ca %e, %E dac exp. < -4 sau precizia; altfel ca %f. Nu tiprete zerouri sau punct zecimal n mod inutil pointer, n formatul tiprit de printf numere ntregi lungi 49

%g, %G

%p %ld, %li

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

%lf, %le, %lg %Lf, %Le, %Lg %% Exemple de utilizare printf:

numere reale n precizie dubl (double) numere reale de tip long double caracterul procent

printf ("\n"); printf ("\n Eroare \n");

// trecere la o noua linie // afieaz Eroare pe o linie // i trece la linia urmtoare

int a=3; printf ("%d\n",a); // afieaz 3 ca ntreg i trece la linia urmtoare int a=5, b=7; printf ("a=%d b=%d\n", a, b); // afieaz a=5 b=7 i trece la linia urmtoare int g=30, m=5, s=2; printf ("%2d grade %2d min %2d sec\n", g,m,s); // afieaz 30 grade 5 min 2 sec i trece la linia urmtoare int anInt = 12345; float aFloat = 55.6677; double aDouble = 11.2233; char aChar = 'a'; char aStr[] = "Hello"; printf("The int is %d.\n", anInt); // afieaz:The int is 12345. printf("The float is %f.\n", aFloat); // afieaz:The float is 55.667702. printf("The double is %f.\n", aDouble); // afieaz:The double is 11.223300. printf("The char is %c.\n", aChar); printf("The string is %s.\n", aStr); // afieaz:The char is a. // afieaz:The string is Hello.

printf("The int (in hex) is %x.\n", anInt); // afieaz:The int (in hex) is 3039. printf("The double (in scientic) is %e.\n", aDouble); // afieaz: The double (in scientic) is 1.122330e+01. printf("The float (in scientic) is %E.\n", aFloat); // afieaz: The float (in scientic) is 5.566770E+01.

Programatorul trebuie s asigure concordana dintre descriptorii de format i tipul variabilelor sau expresiilor argument, deoarece funciile scanf i printf nu fac nici o verificare i nu semnaleaz neconcordane. Exemplele urmtoare trec de compilare (fr mesaj de eroare) dar afieaz incorect valorile variabilelor:
int i=3; float f=3.14; printf ("%f \n", i); printf ("%i \n", f);

// scrie 0.00 (n Dev-Cpp) // scrie 1610612736 (Dev-Cpp)

Funciile scanf i printf folosesc noiunea de cmp (field): un cmp conine o valoare i este separat de alte cmpuri prin spaii. Fiecare descriptor de format poate conine mrimea cmpului, ca numr 50

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

ntreg. Aceast mrime se folosete mai ales la afiare, pentru afiare numere pe coloane, aliniate la dreapta. n lipsa acestei informaii mrimea cmpului rezult din valoarea afiat.

Exemple:
printf("%d %d",a,b); printf("%8d8%d",a,b); // 2 campuri separate prin blanc(spatiu) // 2 cmpuri alturate de cate 8 caractere

int number = 123456; printf("number=%d.\n", number); // afieaz number=123456. intr-un camp de 7 pozitii printf("number=%8d.\n", number); // afieaz number= 123456. intr-un camp de 8 pozitii printf("number=%3d.\n", number); /* dimensiunea campului mica;este ignorata.*/ // afieaz number=123456.

este

prea

double value = 123.14159265; printf("value=%f;\n", value); // afieaz value=123.141593; printf("value=%6.2f;\n", value); // afieaz value=123.14; intr-un camp de 6 pozitii, cu 2 zecimale printf("value=%9.4f;\n", value); // afieaz value= 123.1416; intr-un camp de 9 pozitii, cu 4 zecimale printf("value=%3.2f;\n", value); /* dimensiunea campului este prea mica; este ignorata. */ // afieaz value=123.14;

Dac nu se precizeaz mrimea cmpului i numrul de cifre de la partea fracionar pentru numere, atunci funcia printf alege automat aceste valori: dimensiunea cmpului rezult din numrul de caractere necesar pentru afiarea cifrelor, semnului i altor caractere cerute de format; numrul de cifre de la partea fracionar este 6 indiferent dac numerele sunt de tip float sau double sau long double, dac nu este precizat explicit. Se poate preciza numai mrimea cmpului sau numai numrul de cifre la partea fracionar. Exemple:
float a=1.; double b=0.0002; long double c=7.5; float d=-12.34; printf ("%.0f %20lf %20.10Lf %f \n", a, b, c, d);

Specificnd dimensiunea cmpului n care se afieaz o valoare, putem realiza scrierea mai multor valori n coloane. Dac valoarea de afiat necesit mai puine caractere dect este mrimea cmpului, atunci aceast valoare este aliniat la dreapta n cmpul respectiv. Secvena urmtoare va scrie trei linii, iar numerele afiate vor apare ntr-o coloan cu cifrele de aceeai pondere aliniate unele sub altele:
int a=203, b=5, c=16; printf (%10d \n %10d \n %10d \n,a,b,c);

Formatul cu exponent (%e) este util pentru numere foarte mari, foarte mici sau despre ale cror valori nu se tie nimic. Numrul este scris cu o mantis fracionar (ntre 0 i 10) i un exponent al lui 10, dup litera E (e). La formatul %g printf alege ntre formatul %f sau %e n funcie de ordinul de mrime al numrului afiat: pentru numere foarte mari sau foarte mici formatul cu exponent, iar pentru celelalte formatul cu parte ntreag i parte fracionar. ntre caracterul % i literele care desemneaz tipul valorilor scrise mai pot apare, n ordine: 51

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

a) un caracter ce exprim anumite opiuni de scriere: - (minus): aliniere la stnga n cmpul de lungime specificat + (plus): se afieaz i semnul + pentru numere pozitive 0 : numerele se completeaz la stnga cu zerouri pe lungimea w # : form alternativ de scriere pentru numere b) un numr ntreg w ce arat lungimea cmpului pe care se scrie o valoare, sau caracterul * dac lungimea cmpului se d ntr-o variabil de tip int care precede variabila a crei valoare se scrie. c) punct urmat de un ntreg, care arat precizia (numrul de cifre de dup punctul zecimal) cu care se scriu numerele nentregi. d) una din literele h, l sau L care modific lungimea tipului numeric.

/* Exemplu de utilizare a opiunii 0 pentru a scrie ntotdeauna dou cifre, chiar i pentru numere de o singur cifr: */ int ora=9, min=7, sec=30; printf ("%02d:%02d:%02d\n",ora, min, sec); // scrie 09:07:30 //Exemplu ce afiseaza 10 spatii printf(afisez 10 spatii: %*c,10, ); // Exemplu de utilizare a opiunii - pentru aliniere iruri la stnga: char a[] = "unu", b[] ="cinci", c[]= "sapte" ; printf (" %-10s \n %-10s \n %-10s \n", a, b, c); int i1 = 12345, i2 = 678; printf("Hello, first int is %d, second int is %5d.\n", i1, i2); //Hello, first int is 12345, second int is 678. printf("Hello, first int is %d, second int is %-5d.\n", i1, i2); //Hello, first int is 12345, second int is 678 . char msg[] = "Hello"; printf("xx%10sxx\n", msg); printf("xx%-10sxx\n", msg); //xx Helloxx //xxHello xx

n general trebuie s existe o concordan ntre numrul i tipul variabilelor i formatul de citire sau scriere din funciile scanf i printf, dar aceast concordan nu poate fi verificat de compilator i nici nu este semnalat ca eroare la execuie, dar se manifest prin falsificarea valorilor citite sau scrise. O excepie notabil de la aceast regul general este posibilitatea de a citi sau scrie corect numere de tip double cu formatul %f (pentru tipul float), dar nu i numere de tip long double (din cauza diferenelor de reprezentare intern a exponentului). IB.03.3.2 Funcia scanf Descriptorii de format admii n funcia scanf sunt: Specificatori format Descriere %d ntreg zecimal cu semn %i ntreg zecimal, octal (0) sau hexazecimal (0x, 0X) %o %u %x, %X %c ntreg n octal, precedat sau nu de 0 ntreg zecimal fr semn ntreg hexazecimal, precedat sau nu de 0x, 0X orice caracter; nu sare peste spaii (doar " %c") 52

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

%s

ir de caractere, pn la primul spaiu. Se adaug '\0'.

%e, %E, %f, %F, %g, real (posibil cu exponent) %G, %a, %A %p pointer, n formatul tiprit de printf %ld, %li numere ntregi lungi %lf %Lf %[] %[^] %% numere reale n precizie dubl (double) numere reale de tip long double ir de caractere din mulimea indicat ntre paranteze ir de caractere exceptnd mulimea indicat ntre paranteze caracterul procent

Dup cum spuneam mai sus, argumentele funciei scanf sunt de tip pointer i conin adresele unde se memoreaz valorile citite. Pentru variabile numerice aceste adrese se obin cu operatorul de adresare (&) aplicat variabilei care primete valoarea citit. Numerele citite cu scanf pot fi introduse pe linii separate sau n aceeai linie dar separate prin spaii sau caractere Tab. ntre numere succesive pot fi oricte caractere separator (\n,\t, ). Un numr se termin la primul caracter care nu poate apare ntr-un numr. Exemple:
int n, a,b; scanf("%d", &n); scanf("%d%d", &a,&b); float rad; scanf("%f", &rad); char c; scanf("%c", &c); // program test scanf #include <stdio.h> int main() { int anInt; float aFloat; double aDouble; printf("Introduceti un int: "); // Mesaj afisat scanf("%d", &anInt); // citete un ntreg n variabila anInt printf("Valoarea introdusa este %d.\n", anInt); printf("Introduceti un float: ");// Mesaj afisat scanf("%f", &aFloat); // citete un float n variabila aFloat printf("Valoarea introdusa este %f.\n", aFloat); printf("Introduceti un double: ");// Mesaj afisat scanf("%lf", &aDouble); // citete un ntreg n variabila aDouble printf("Valoarea introdusa este %lf.\n", aDouble); return 0; } // citete un ntreg n variabila n // citete doi ntregi in a i b // citete un numar real in rad // citete un caracter in c

La citirea de iruri trebuie folosit un vector de caractere, iar n funcia scanf se folosete numele vectorului (echivalent cu un pointer). Exemplu:

53

INFORMATIC*I* char s[100]; scanf(%s, s);

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

//uneori funcioneaz, dar este greit:

scanf(%s, &s);

Chiar i spaiile trebuie folosite cu atenie n irul format din scanf. Exemplu de citire care poate crea probleme la execuie din cauza blancului din format:
scanf("%d ",&a); // corect este scanf (%d,&a);

Funcia scanf nu poate afia nimic, de aceea pentru a precede introducerea de date de un mesaj (prin care este anunat utilizatorul c se ateapt introducerea unei date) trebuie folosit secvena printf, scanf. Exemplu:
printf ("n= "); scanf ("%d", &n); // NU: scanf(n=%d, &n);

De reinut diferena de utilizare a funciilor scanf i printf! Exemplu:


scanf("%d%d", &a, &b); // citete dou numere ntregi n a i b printf("%d %d", a, b); // scrie valorile din a i b separate de un spaiu

Alte exemple de utilizare a funciilor scanf i printf:


int i; float f; double d; scanf("%d%f%lf",&i, &f, &d); //citeste un intreg, un real si un double char c; printf("Rezultatul este: %c\n",c); //afiseaza un mesaj si un character float a; double b; scanf(%f, &a); printf(%5.2f,a); scanf(%lf, &b); printf(%-4.2lf,b); printf(%+4.2lf,b);

// // // // //

citire variabila float sunt afisate minim 5 caractere, maxim 2 zecimale citire variabila double afisare cu aliniere stanga afisare cu adaugare semn (+,-)

Observaii Toate funciile de citire menionate (excepie getch, getche) folosesc o zon tampon (buffer) n care se adun caracterele tastate pn la apsarea tastei Enter, moment n care coninutul zonei buffer este transmis programului. n acest fel este posibil corectarea unor caractere introduse greit nainte de apsarea tastei Enter. Caracterul \n este prezent n zona buffer numai la funciile getchar i scanf, dar funcia gets nlocuiete acest caracter cu un octet zero, ca terminator de ir n memorie (rezultatul funciei gets este un ir terminat cu zero). Funcia scanf recunoate n zona buffer unul sau mai multe cmpuri (fields), separate i terminate prin caractere spaiu (blanc, \n, \r,\f); drept consecin, preluarea de caractere din buffer se oprete la primul spaiu alb, care poate fi i caracterul terminator de linie. La urmtorul apel al funciei getchar sau scanf se verifica dac n zona buffer mai sunt caractere, nainte de a atepta introducerea de la tastatura i va gsi caracterul terminator de linie rmas de la citirea anterioar. Pentru ca un program s citeasc corect un singur caracter de la tastatur avem mai multe soluii: apelarea funciei fflush(stdin) nainte de oricare citire; aceast funcie golete zona buffer asociat tastaturii i este singura posibilitate de acces la aceast zon tampon. 54

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

introducerea unei citiri false care s preia terminatorul de linie din buffer (cu getchar(), de exemplu). utilizarea funciei nestandard getch (declarat n conio.h), funcie care nu folosete o zon buffer la citire citirea unui singur caracter ca un ir de lungime 1:
char c[2]; scanf (%1s,c); // memorie ptr un caracter i pentru terminator de ir // citete ir de lungime 1

Unele medii integrate nchid automat fereastra n care se afieaz rezultatele unui program. Pentru meninerea rezultatelor pe ecran vom folosi fie o citire fals (pune programul n ateptare) fie instruciunea: system(pause); din stdlib.h. IB.03.4 Fluxuri de intrare/ieire in C++ n C++ s-a introdus o alt posibilitate de exprimare a operaiilor de citire-scriere, pe lng funciile standard de intrare-ieire din limbajul C. n acest scop se folosesc cteva clase predefinite pentru fluxuri de I/O (declarate n fiierele antet iostream.h i fstream.h). Un flux de date (stream) este un obiect care conine datele i metodele necesare operaiilor cu acel flux. Pentru operaii de I/O la consol sunt definite variabile de tip flux, numite cin (console input) respectiv cout (console output). Operaiile de citire sau scriere cu un flux pot fi exprimate prin metode ale claselor flux sau prin doi operatori cu rol de extractor din flux (>>) sau insertor n flux (<<). Atunci cnd primul operand este de un tip flux, interpretarea acestor operatori nu mai este cea de deplasare binar ci este extragerea de date din flux (>>) sau introducerea de date n flux (<<). Operatorii << i >> implic o conversie automat a datelor ntre forma intern (binar) i forma extern (ir de caractere). Formatul de conversie poate fi controlat prin cuvinte cheie cu rol de "modificator". Exemplu de scriere i citire cu format implicit:
#include <iostream.h> void main ( ) { int n; char s[20]; cout << " n= "; cin >> n; cout << " un ir: "; cin >> s; }

cout << s << "\n";

ntr-o expresie ce conine operatorul << primul operand trebuie s fie cout (sau o alt variabil de un tip ostream), iar al doilea operand poate s fie de orice tip aritmetic sau de tip char* pentru afiarea irurilor de caractere. Rezultatul expresiei fiind de tipul primului operand, este posibil o expresie cu mai muli operanzi (ca la atribuirea multipl). Exemplu:
cout << "x= " << x << "\n";

n mod similar, ntr-o expresie ce conine operatori >> primul operand trebuie s fie cin sau de un alt tip istream, iar ceilali operanzi pot fi de orice tip aritmetic sau pointer la caractere. Exemplu:
cin >> x >> y;

Este posibil i un control al formatului de scriere prin utilizarea unor modificatori, ns nu vom detalia aici acest aspect, deoarece nu vom folosi aceste faciliti care in de C++, ele fiind date doar ca titlu informativ.

55

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.04. Instruciunile limbajului C


Cuvinte-cheie
Instruciunea expresie, instruciunea compus bloc, instruciunea if, instruciunea switch, instruciuni repetitive, instruciunea while, instruciunea for, instruciunea do, instruciunea break, instruciunea continue, terminarea programului: exit i return

IB.04.1 Introducere
Dup cum spuneam, exist trei tipuri de construcii de baz pentru controlul fluxului operaiilor: secvena, decizia (condiia) i iteraia (bucla, ciclu, repetiia), aa cum sunt ilustrate mai jos.

Operaie 1 DA Operaie 2 Bucla


. . .

NU

Operaie n NU Secvena Decizia

DA

Iteraia

IB.04.2 Instruciunea expresie O expresie urmat de caracterul terminator de instruciune ; devine o instruciune expresie. Sintaxa: expresie; Cele mai importante cazuri de instruciuni expresie sunt: Tip instruciune expresie Descriere Instruciunea vid conine doar terminatorul ;
este folosit pentru a marca absena unei prelucrri ntr-o alt instruciune (if, while, etc)

Exemple for (i=0; i<10000; i++) ; /* temporizare, de 10000 de ori nu fac nimic */

Instruciunea de apelare un apel de funcie terminat cu ; a unei funcii Instruciunea atribuire

getchar(); sqrt(a); system(pause); de o expresie de atribuire terminat cu a=1; ; ++a; c=r/(a+b); i=j=k=1; 56

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Utilizarea neatent a caracterului punct-i-virgul poate introduce uneori erori grave (nesemnalate de compilator), dar alteori nu afecteaz execuia (fiind interpretat ca o instruciune vid). Exemple:
char a = 1, b =c; printf (" %c \n %c \n", a, b); int a,b,c,m,n,p=2; // liniile de mai jos reprezinta instructiuni expresie scanf("%d",&a); // apel de functie, valoarea returnata de functie nu este folosita b = 5; c = a > b ? a:b; n = printf("%d %d %d\n",a,b,c); //valoarea returnata este memorata in n p = a*b/c; p++; m = p+ = 5; a+b; /* valoarea expresiei nu este folosita - apare un avertisment ( warning ) la compilare: Code has no effect */

IB.04.3 Instruciunea compus (bloc) Pe parcursul elaborrii programelor, intervin situaii n care sintaxa impune specificarea unei singure operatii iar codificarea sa necesita mai multe instruciuni; n acest caz se incadreaz instruciunile ntre acolade, formnd un bloc ce grupeaz mai multe instruciuni (i declaraii). Sintaxa: { declaraii_variabile_locale_blocului // opionale, valabile doar n fiiere cpp! instruciuni } Observaii: Corpul oricrei funcii este un bloc; Instruciunile unui bloc pot fi de orice tip, deci i alte instruciuni compuse; instruciunile bloc pot fi deci incuibate; Un bloc corespunde structurii de control secven de la schemele logice; O instruciune compus poate s nu conin nici o declaraie sau instruciune ntre acolade; n general un astfel de bloc poate apare n faza de punere la punct a programului (funcii cu corp vid ); Acoladele nu modific ordinea de execuie, dar permit tratarea unui grup de instruciuni ca o singur instruciune n cadrul instruciunilor de control (if, while, do, for .a). Instruciunile de control au ca obiect, prin definiie, o singur instruciune. Pentru a extinde domeniul de aciune al acestor instruciuni la un grup de operaii se folosete instruciunea compus. Un bloc poate conine i doar o singur instruciune, aceasta pentru punerea n eviden a terminrii aciunii anumitor instruciuni. Un bloc nu trebuie terminat cu ; dar nu este greit dac se folosete (este interpretat ca instruciune vid). Exemplu: if(a>b) {max=a; 57

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

if(c>a) max=c; }; Dac un bloc conine doar instruciuni expresie, el se poate nlocui cu o instruciune expresie n care expresiile iniiale se separ prin operatorul secvenial. Exemple:
{ int t; t=a; a=b; b=t; } // sau: { int t; t=a; a=b; b=t; } // schimba a i b prin t

//Blocul: { a++; c=a+ --b; printf("%d\n",c); } // este echivalent cu instructiunea expresie: a++, c=a+ --b, printf("%d\n",c);

IB.04.4 Instruciuni de decizie (condiionale)


Exist urmtoarele tipuri de instrucini de decizie if-then, if-then-else, if ncuibat (if-elseif-elseif-...-else), switch-case.

IB.04.4.1 Instruciunea if Instruciunea introdus prin cuvntul cheie if exprim o decizie binar i poate avea dou forme: o form fr cuvntul else i o form cu else: Sintaxa:
If (expresie) instructiune1 else instructiune2 SAU: If (expresie) instructiune

Semantica: Se evalueaz expresie; dac valoarea ei este adevrat (diferit de 0) se execut instruciune1, altfel, dac exist ramura else, se execut instruciune2. Observaii: Instruciunea corespunde structurii de control decizie din schemele logice; Pentru ca programele scrise s fie ct mai clare este bine ca instruciunile corespunztoare lui if si else s fie scrise pe liniile urmtoare i deplasate spre dreapta, pentru a pune n evident structurile i modul de asociere ntre if i else. Acest mod de scriere permite citirea corect a unor cascade de decizii. Valoarea expresiei dintre paranteze se compar cu zero, iar instruciunea care urmeaz se va executa numai atunci cnd expresia are o valoare nenul. n general expresia din instruciunea if reprezint o condiie, care poate fi adevrat (valoare nenul) sau fals (valoare nul). De obicei expresia este o expresie relaional (o comparaie de valori 58

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

numerice) sau o expresie logic n care sunt combinate mai multe expresii relaionale, dar poate fi orice expresie cu rezultat numeric. Instruciunea corespunztoare valorii adevrat sau fals, poate fi orice instruciune C: o instruciune expresie terminat cu simbolul ; o instruciunea bloc ( atunci cnd trebuie executate mai multe prelucrri ) o alta instruciune de decizie - deci instruciunile if-else pot fi incluse una n alta; fiecare else corespunznd ultimului if, fr pereche. O problem de interpretare poate apare n cazul a dou (sau mai multe) instruciuni if incluse, dintre care unele au alternativa else, iar altele nu conin pe else. Regula de interpretare este aceea c else este asociat cu cel mai apropiat if fr else (dinaintea lui). O sintez a celor trei moduri de utilizare a lui if precum i fluxul de operaii n schem logic sunt date n cele ce urmeaz: Sintax Flux operaii

// if-then if ( expresie ) { bloc_DA; }

expresie DA bloc DA

NU

DA // if-then-else if ( expresie ) bloc_DA ; else bloc_NU ; expresie

NU

bloc DA

bloc NU

59

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// if inclus if (expresie_1 ) bloc_1 ; else if (expresie_2 ) bloc_2 ; else if (expresie_3 ) bloc_3 ; else if (expresie_4 ) ...... else bloc_Else ;

DA

Expresie1

NU

Expresie2 bloc 1 DA bloc 2

NU

bloc Else

Exemple de utilizare a lui if: Sintax


// if-then if ( expresie ) bloc_DA; // if-then-else if ( expresie ) bloc_DA ; else bloc_NU ; // if incuibat if (expresie_1 ) bloc_1 ; else if (expresie_2 ) bloc_2 ; else if (expresie_3 ) bloc_3 ; else if (expresie_4 ) ...... else bloc_Else ;

Exemple
if (nota >= 5) { printf("Congratulation!\n"); printf("Keep it up!\n"); } if (nota >= 5) { printf("Congratulation!\n"); printf("Keep it up!\n"); } else printf("Try Harder!\n");

if (nota >= 80) printf("A\n"); else if (nota >= 7) printf("B\n"); else if (nota >= 6) printf("C\n"); else if (nota >= 5) printf("D\n"); else printf("E\n");

Exemple:
/* urmatoarele trei secvente echivalente verifica daca trei valori pot reprezenta lungimile laturilor unui triunghi */ if(a<b+c && b<a+c && c<a+b) puts("pot fi laturile unui triunghi"); else puts("nu pot fi laturile unui triunghi"); if(a<b+c && b<a+c && c<a+b) ;

60

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/*pentru conditie adevarata nu se executa nimic: apare instructiunea vida */ else printf("nu "); puts("pot fi laturile unui triunghi"); if(!(a<b+c && b<a+c && c<a+b)) // sau if(a>=b+c || b>=a+c || c>=a+b) printf("nu "); puts("pot fi laturile unui triunghi"); // Pentru gruparea mai multor instruciuni folosim o instruciune bloc: if ( a > b) { t=a; a=b; b=t; } /* Pentru comparaia cu zero nu trebuie neaprat folosit operatorul de inegalitate (!=), dei folosirea lui poate face codul surs mai clar:*/ if (d) return; // if (d != 0) return; // determinare minim dintre doua numere if ( a < b) min=a; else min=b; /* Pentru a grupa o instruciune if-else care conine un if fr else utilizm o instruciune bloc:*/ if ( a == b ) { if (b == c) printf ("a==b==c \n"); } else printf (" a==b i b!=c \n"); // Expresia coninut n instruciunea if poate include i o atribuire: if ( d = min2 - min1) printf(%d,d); //se atribuie lui d o valoare apoi aceasta se compara cu zero /*Instruciunea anterioar poate fi derutant la citire i chiar este semnalat cu avertisment de multe compilatoare, care presupun c s-a folosit eronat atribuirea n loc de comparaie la egalitate (o eroare frecvent):*/ if (i=0) printf( Variabila i are valoarea 0); else printf( Variabila i are o valoare diferita de 0);

IB.04.4.2 Instruciunea de selecie switch Selecia multipl (dintre mai multe cazuri posibile), se poate face cu mai multe instruciuni if incluse unele n altele sau cu instruciunea switch. Instruciunea switch face o enumerare a cazurilor posibile (fiecare precedat de cuvntul cheie case) folosind o expresie de selecie, cu rezultat ntreg. Forma general este: Sintaxa: switch (expresie) { case c1: prelucrare_1 case c2: prelucrare_2 case cn: prelucrare_n default: prelucrare_x } Unde: expresie - de tip ntreg, numit expresie selectoare c1, cn - constante sau expresii constante ntregi (inclusiv char) orice prelucrare const din 0 sau mai multe instruciuni 61

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

default e opional, corespunde unor valori diferite de cele n etichete Semantica: Se evalueaz expresie; dac se gsete o etichet avnd valoarea egal cu a expresiei, se execut att secvena corespunztoare acelui caz ct i secvenele de instruciuni corespunztoare tuturor cazurilor care urmeaz (chiar dac condiiile acestora nu sunt ndeplinite) inclusiv ramura de default! Aceast interpretare permite ca mai multe cazuri s foloseasc n comun aceleai operaii. Cazul default poate lipsi; n cazul n care avem ramura default, se intr pe aceast ramur atunci cnd valoarea expresiei de selecie difer de toate cazurile enumerate explicit. Observaie: Deseori cazurile enumerate se exclud reciproc; pentru aceasta fiecare secven de instruciuni trebuie s se termine cu break, pentru ca dup selecia unui caz s se execute doar prelucrarea corespunzatoare unei etichete, nu i cele urmtoare: switch (expresie) { case c1: case c2: case cn: default: } O sintez a celor dou moduri de utilizare a lui switch precum i fluxul de operaii n schem logic este dat n cele ce urmeaz: Sintax // switch-case switch (expresie) { case c1: prelucrare_1 case c2: prelucrare_2 case cn: prelucrare_n default: prelucrare_x } Flux operaii

prelucrare_1 break; prelucrare_2 break; prelucrare_n break; prelucrare_x ;

c1? NU c2? NU

DA

prel_1

DA

prel_2

default

62

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// switch-case switch (expresie) { case c1: prelucrare_1 break; case c2: prelucrare_2 break; case cn: prelucrare_n break; default: prelucrare_x } Exemple

c1? NU c2? NU

DA

break prel_1

DA

prel_2

break

default

//calculeaza rezulatul expresiei num1 oper num2 char oper; int num1, num2, result; ...... switch (oper) { case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; case '*': result = num1 * num2; break; case '/': result = num1 / num2; break; default: printf("Operator necunoscut"); }

// determina nr de zile dintr-o lun a unui an nebisect switch (luna) { // februarie case 2: zile=28; break; // aprilie, iunie,..., noiembrie case 4: case 6: case 9: case 11: zile =30; break; // ianuarie, martie, mai,.. decembrie, celelalte (1,3,5,..) default: zile=31; }

IB.04.5. Instruciuni repetitive Exist trei tipuri de instruciuni de ciclare (bucle, iteraii): while, for i do-while. IB.04.5.1 Instruciunea while Instruciunea while exprim structura de ciclu cu condiie iniial i cu numr necunoscut de pai ; are forma urmtoare: Sintaxa: while (expresie) instructiune 63

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Semantica Se evalueaz expresie; dac valoarea ei este adevrat (diferit de 0) se execut instructiune, dup care se evalueaz din nou expresie; daca valoarea este 0, se trece la instruciunea urmtoare. Efectul este acela de executare repetat a instruciunii coninute n instruciunea while ct timp expresia din paranteze are o valoare nenul (este adevrat). Este posibil ca numrul de repetri s fie zero dac expresia are valoarea zero de la nceput. Observatii: Instruciunea while corespunde structurii repetitive cu test iniial de la schemele logice; n general expresie conine variabile care se modific n instructiune, astfel nct expresie s devin fals, deci ciclarea s nu se fac la infinit; n unele programe se poate s apar
while(1) instructiune

Atunci, corpul ciclului poate s conin o instruciune de ieire din ciclu, altfel tastarea Ctrl/Break ntrerupe programul; Ca i n cazul altor instruciuni de control, este posibil s se repete nu doar o instruciune ci un bloc de instruciuni; Exemple:
// cmmdc prin incercari succesive de posibili divizori, presupunem a>b d = b; // divizorul maxim posibil este minimul dintre a i b while ( a%d || b%d ) // repeta cat timp nici a nici b nu se divid prin d d = d -1; // incearca alt numar mai mic }

n exemplul anterior, dac a=8 i b=4 atunci rezultatul este d=4 i nu se execut niciodat instruciunea din ciclu (d=d-1).
// determinare cmmdc prin algoritmul lui Euclid. While cu instructiune bloc while (a%b > 0) { r = a % b; a = b; b = r; }// la ieirea din ciclu b este cmmdc

Expresia din instruciunea while poate s conin atribuiri sau apeluri de funcii care se fac nainte de a evalua rezultatul expresiei:
// algoritmul lui Euclid rescris while (r=a%b) { a=b; b=r; }

IB.04.5.2 Instruciunea for Instruciunea for din C permite exprimarea compact a ciclurilor cu condiie iniial sau a ciclurilor cu numr cunoscut de pai i are forma: Sintaxa: for (expresie1; expresie2; expresie3) instructiune

64

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Semantica: Se evalueaz expresie1 care are rol de iniializare; se evalueaz apoi expresie2, cu rol de condiie dac valoarea ei este adevrat (diferit de 0) se execut instructiune - corpul ciclului, dup care se evalueaz expresie3, cu rol de actualizare, apoi se evalueaz din nou expresie2; dac valoarea este 0, se trece la instruciunea urmtoare. Cu alte cuvinte, instructiune se execut atta timp ct expresie2 este adevrat, deci de 0 sau mai multe ori. Efectul acestei instruciuni este echivalent cu al secvenei urmtoare: expresie1; while (expresie2) { instruciune; expresie3; } // operaii de iniializare // cat timp exp2 !=0 repeta // instruciunea repetata // o instruciune expresie

Observaii: Instructiunea for permite o scriere mult mai compact dect celelalte dou instruciuni de ciclare, fiind foarte des utilizat n scrierea programelor; Oricare din cele trei expresii poate lipsi, dar separatorul ; rmne. Absena expresie2 echivaleaz cu condiia adevrat, deci 1; in tabelul de mai jos sunt date echivalenele cu instruciunea while, pentru cazuri cnd expresii din sintaxa for lipsesc: for
for(;expresie;) instructiune for(;;) instructiune

while
while(expresie) instructiune while(1) instructiune

Cele trei expresii din instruciunea for sunt separate prin ';' deoarece o expresie poate conine operatorul virgul. Este posibil ca prima sau ultima expresie s reuneasc mai multe expresii separate prin virgule; Este posibil mutarea unor instruciuni din ciclu n paranteza instruciunii for, ca expresii, i invers - mutarea unor operaii repetate n afara parantezei. Un caz particular al instruciunii for este instruciunea de ciclare cu contor numrul de cicluri fiind (val_finala-val_initiala)/increment: for ( var_contor = val_initiala; var_contor <= val_finala; var_contor += increment ) instructiune Nu se recomand modificarea variabilei contor folosit de instruciunea for n interiorul ciclului, prin atribuire sau incrementare; Pentru ieire forat dintr-un ciclu se folosesc instruciunile break sau return;

65

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exemple:
// tergere linii ecran for (k=1;k<=24;k++) putchar('\n'); // avans la linie noua // alta secvena de tergere ecran for (k=24;k>0;k--) putchar('\n'); // calcul factorial de n for (nf=k=1 ; k<=n ; k++) nf = nf * k;

// alta varianta de calcul pentru factorial de n for (nf=1, k=1 ; k<=n ; nf=nf * k, k++) ; // repeta instr. vida

IB.04.5.3 Instruciunea do Instruciunea do-while se folosete pentru exprimarea ciclurilor cu condiie final, cicluri care se execut cel puin o dat. Forma uzual a instruciunii do este urmtoarea: Sintaxa: do instructiune while ( expresie ) ;

Semantica: Se execut instruciune - corpul ciclului, apoi se evalueaz expresie care are rol de condiie - dac valoarea ei este adevrat (diferit de 0) se execut instruciune, dup care se evalueaz din nou expresie; dac valoarea este 0, se trece la instruciunea urmtoare. Cu alte cuvinte, instruciune se execut atta timp ct expresie este adevrat; ca observaie, instruciune se execut cel puin o dat. Observaii: Instruciunea echivaleaz cu structura repetitiv cu test final de la scheme logice; Instruciunea do-while se utilizeaz n secvenele n care se tie c o anumit prelucrare trebuie executat cel puin o dat; Spre deosebire de while, n ciclul do instruciunea se execut sigur prima dat chiar dac expresia este fals. Exist i alte situaii cnd instruciunea do poate reduce numrul de instruciuni, dar n general se folosete mult mai frecvent instruciunea while. Exemplu:
// calcul radical din x prin aproximatii succesive r2=x; // aproximatia iniiala do { r1=r2; // r1 este aproximatia veche r2=(r1+x/r1) / 2; // r2 este aproximatia mai noua } while ( abs(r2-r1)) ; // pana cand r2-r1 este o valoare foarte mica

Un ciclu do tipic apare la citirea cu validare a unei valori, citire repetat pn la introducerea corect a valorii respective:

66

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

do { printf ("n (<1000): "); // n trebuie sa fie sub 1000 scanf("%d", &n); if ( n <=0 || n>=1000) printf ( Eroare la valoarea lui n ! \n); } while (n>=1000) ;

IB.04.5.4 Sinteza instruciunilor repetitive Sintax

Flux operaii

Expresie1
// for for(expresie1;expresie2;expresie3) instruciune

Expresie3 Bucla

Expresie 2 NU

DA

Instruciune

// while-do while ( expresie ) instruciune

Bucla

Expresie NU

DA

Instruciune

// do-while do { instruciune } while ( expresie ) ;

Instruciune

Bucla DA

Expresie NU Exemple scrise utiliznd cele trei instruciuni repetitive: 1. Suma primelor 1000 de numere naturale 67

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

2. Secvene echivalente care citesc cu validare o variabil - n urma citirii, variabila ntreag trebuie s aparin intervalului [inf,sup]: Instruciune Exemple
// Suma de la 1 la 1000 int suma = 0; for (int nr = 1; nr <= 1000; ++nr) { suma += nr; } // Citire cu validare in intervalul [inf, sup] puts(Valoare); scanf("%d",&var); for(;var < inf || var > sup;){ puts(Valoare); scanf("%d",&var); } // Citire cu validare in intervalul [inf, sup] for(puts(Valoare),scanf("%d",&var);var < inf || var > sup;){ puts(Valoare); scanf("%d",&var); } // Citire cu validare in intervalul [inf, sup] for(puts(Valoare), scanf("%d",&var); var<inf || var>sup; puts(Valoare), scanf("%d",&var)); // Citire cu validare in intervalul [inf, sup] for( ;puts(Valoare), scanf("%d",&var), var<inf || var>sup;); // Suma de la 1 la 1000 int suma = 0, nr = 1; while (nr <= 1000) { suma += nr; ++nr; } // Citire cu validare in intervalul [inf, sup] puts(Valoare); scanf("%d",&var); while ( var < inf || var > sup){ //valoare invalida, se reia citirea puts(Valoare); scanf("%d",&var); } // Citire cu validare in intervalul [inf, sup] while( puts(Valoare), scanf("%d",&var),var<inf ||var>sup); // Suma de la 1 la 1000 int suma = 0, nr = 1; do { suma += nr; ++nr; } while (nr <= 1000); // Citire cu validare in intervalul [inf, sup] do{ puts(Valoare); scanf("%d",&var); }while( var<inf || var>sup);

for

while

do while

68

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.04.6. Instruciunile break i continue Instruciunea break determin ieirea forat dintr-un ciclu - adic ieirea din corpul celei mai apropiate instruciuni while, for, do-while - sau dintr-un switch care o conine, i trecerea la execuia instruciunii urmtoare. Sintaxa instruciunii este simpl: Sintaxa: break;

Semantica Efectul instruciunii break este un salt imediat dup instruciunea sau blocul repetat prin while, do, for sau dup blocul switch. Observaii: Un ciclu din care se poate iei fie dup un numr cunoscut de pai fie la ndeplinirea unei condiii (ieire forat) este de obicei urmat de o instruciune if care stabilete cum s-a ieit din ciclu: fie dup numrul maxim de pai, fie mai nainte, datorit satisfacerii condiiei. Exemplu:
// verifica daca un numar dat n este prim for (k=2; k<n;k++) if ( n%k==0) break; //daca gasim un divizor, iesim, n nu este prim! if (k==n) printf ("prim \n"); /*s-a iesit normal din ciclu-nu are divizor*/ else printf ("neprim \n"); /*s-a iesit fortat prin break - are divizor */

Utilizarea instruciunii break poate simplifica expresiile din while sau for i poate contribui la urmrirea mai uoar a programelor, dei putem evita instruciunea break prin complicarea expresiei testate n for sau while. Secvenele urmtoare sunt echivalente:
//se iese cand e este diferita de 0 for (k=0 ; k<n; k++) if (e) break; for (k=0 ; k<n && !e ; k++);

Instruciunea continue este mai rar folosit fa de break. Sintaxa: continue; Semantica Efectul instruciunii continue este opirea iteraiei curente a ciclului i un salt imediat la prima instruciune din ciclu, pentru a continua cu urmtoarea iteraie. Nu se iese n afara ciclului, ca n cazul instruciunii break. n exemplu urmtor se citete repetat un moment de timp dat sub forma ora, minut, secund pn la introducerea unui momnet corect (care are toate cele 3 componente: h, m s i pentru care ora (h) se incadreaz ntre 0 i 24, minutele i secundele (m, s) ntre 0 i 59. Este realizat validarea doar pentru or:
int h,m,s; int corect=0; // initial nu avem date corecte nu avem de fapt deloc date

while ( ! corect ) { // atata timp cat nu s-au citit date corecte

69

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf ( ore, minute, secunde: ); if ( scanf(%i%i%i, &h, &m, &s) !=3 ) { //nu s-au citit 3 nr intregi printf ( Eroare insuficiente date numerice\n); fflush(stdin); //stergere buffer de intrare continue; // salt peste instructiunile urmatoare, reia citirea } if (h < 0 || h > 24) { printf ( Valoare incorecta pentru ora!\n); fflush(stdin); //stergere buffer de intrare continue; // salt peste instructiunile urmatoare, reia citirea } .... // testare m si s intre 0 si 59 corect=1; }

Observaii: Uneori se recomand s evitm utilizarea instruciunilor break i continue deoarece programele care le folosesc sunt mai greu de citit i de neles. ntotdeauna putem scrie acelai program fr s folosim break i continue. Exemplu:
// Suma de la 1 la n, excluzand 11, 22, 33,... int n = 100; int suma = 0; for (int nr = 1; nr <= n; nr++) { if (nr % 11 == 0) continue; /* sare peste restul corpului buclei i trece la urmat. iteraie nr+1 */ suma += nr; // aici ajung doar daca nr nu e divizibil cu 11 } // Este mai bine s rescriem bucla for astfel: for (int nr = 1; nr <= n; nr++) { if (nr % 11 != 0) suma += nr; }

IB.04.7. Terminarea programului Un program se termin n mod normal n momentul n care s-au executat toate instruciunile sale. Dac dorim s form terminarea lui, putem folosi funcia exit sau instruciunea return. Funcia exit are urmtoarea sintax: Sintaxa:
exit(); sau: exit(int codIesire);

Semantica: Termin programul

Prin convenie, returnarea codului 0 indic terminarea normal a programului, n timp ce o valoare diferit de zero indic o terminare anormal. Exemplu:
if (nrErori > 10) { printf( "prea multe erori!\n");

returneaz

controlul

sistemului

de

operare

(OS).

70

INFORMATIC*I* exit(1); }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// Terminarea programului

Instruciunea return are urmtoarea sintax: Sintaxa: return; sau: return expresie;

Semantica: Se revine din funcia care conine instruciunea, n cea apelant, la instruciunea urmtoare apelului; se returneaz valoarea expresiei pentru cazul al doilea. Putem folosi instruciunea "return valoareReturnata;" n funcia main() pentru a termina programul. Exemplu:
int main() { ... if (nrErori > 10) { printf( "prea multe erori!\n"); return 1; // Termina programul si reda controlul OS } ... }

n continuare, gsii dou anexe, una cu sfaturi practice pentru devoltarea programelor C (good practices) i modul de depanare a programelor C n Netbeans i CodeBlocks, iar cea de -a doua cu programele din cursul 1 rezolvate n C. IB.04.8. Anexa A. Sfaturi practice pentru devoltarea programelor C. Depanare Este important s scriem programe care produc rezultate corecte, dar de asemenea este important s scriem programe pe care alii (i chiar noi peste cteva zile) s le putem nelege, astfel nct s poat fi uor ntreinute. Acesta este ceea ce se numete un program bun. Iat cteva sugestii: Respect conveia stabilit deja la proiectul la care lucrezi astfel nc t ntreaga echip s respecte aceleai reguli. Formateaz codul surs cu indentare potrivit, cu spaii i linii goale. Folosete 3 sau 4 spaii pentru indentare i linii goale pentru a marca seciuni diferite de cod. Alege nume bune i descriptive pentru variabile si funcii: coloan, linie, xMax, numElevi. Nu folosii nume fr sens pentru variabile, cum ar fi a, b, c, d. Evitai nume de variabile formate doar dintr-o liter (mai uor de scris dar greu de neles), excepie fcnd nume uzuale cum ar fi coordonatele x, y, z i nume de index precum i. Scrie comentarii pentru bucile de cod importante i complicate. Comenteaz propriul cod ct de mult se poate. Scrie documentaia programului n timp ce scrii programul. Evit construciile nestructurate, cum ar fi break i continue, deoarece sunt greu de urmrit. 71

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Erori n programare Exist trei categorii de erori n progamare: Erori de compilare (sau de sintax): pot fi reparate uor. Erori de rulare: programul se oprete prematur fr a produce un rezultat de asemenea se repar uor. Erori de logic: programul produce rezultate eronate. Eroarea este uor de gsit dac rezultatele sunt eronate mereu. Dar dac programul produce de asemenea rezultate corecte ct i rezultate eronate cteodat, eroarea este foarte greu de identficat. Astfel de erori devin foarte grave dac nu sunt detectate nainte de utilizarea efectiv a programului n mediul su de operare. Implementarea unor programe bune ajut la mimimizarea i detectarea acestor erori. De asemenea, o strategie de testare bun este necesar pentru a certifica corectitudinea programului. Programe de depanare Exista cteva tehnici de depanare a programaelor: 1. Uit-te mult la cod (inspecteaz codul)! Din pcate, erorile nu o s-i sar n ochi nici dac te uii destul de mult. 2. Nu nchide consola de erori cnd apar mesaje pretinznd c totul este n regul. Analizeaz mesajele de eroare! Asta ajut de cele mai multe ori. 3. Insereaz n cod afiri de variabile n locuri potrivite pentru a observa valori intermediare. Este folositor pentru programe mici, dar la programe complexe i pierde din eficien. 4. Folosete un depanator grafic. Aceasta este cea mai eficient metod. Urmrete execuia programului pas cu pas urmrind valorile variabilelor. 5. Folosete unelte avansate pentru a descoperi folosirea ineficient a memoriei sau nealocarea ei. 6. Testeaz programul cu valori de test utile pentru a elimina erorile de logic. Testarea programulul pentru a vedea dac este corect Cum te poi asigura c programul tu produce rezultate corecte mereu? Este imposibil s ncerci toate variantele chiar i pentru un program simplu. Testarea programului folosete de obicei un set de teste reprezentative, concepute pentru a detecta clasele de erori majore. n continuarea acestui material gsii modul de depanare a programelor C n Netbeans: Ecuatia de grad 1 Suma primelor n numere naturale i n CodeBlocks: Inversul unui numar natural Interschimbarea valorilor- Problema paharelor IB.04.9. Anexa B. Programele din capitolul IB.01 rezolvate n C Probleme rezolvate n limbajul C Problema 1: Problema cu functia F(x)
/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram variabilele. */

72

INFORMATIC*I* int i, x;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Citim cele 100 de valori, utilizand functia scanf, si calculam valoarea * functiei. */ for(i = 0; i < 100; i++) { /* Atentie la string-ul de formatare si la caracterul '&'! */ scanf("%d", &x); if(x < 0) printf("%d\n", x * x - 2); else if(x == 0) printf("3\n"); else printf("%d\n", x + 3); } /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programului Observaie n filmulet avem for(i=0;i<3;i+), pentru a putea pune n eviden depanarea.

Problema 2: Actualizarea unei valori naturale cu un procent dat


/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram doua variabile de tip double. */ int v, p; /* Citim valorile celor doua variabile utilizand functia scanf. * Specificatorul pentru tipul double este "%lf". Atentie la string-ul de * formatare si la caracterul "&"! */ scanf("%d%d", &v, &p); /* Actualizam valoarea variabilei v. */ v = v + v * p; /* Afisam noua valoare a variabilei v, utilizand printf. */ printf("%d\n", v); /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programlui Problema 3: Citirea i scrierea unei valori.


/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram variabila ce trebuie citita si afisata. */ int a;

73

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Citim valoarea variabilei a utilizand scanf. Atentie la string-ul de * formatare si la caracterul '&'! */ scanf("%d", &a); /* Afisam valoarea variabilei a utilizand printf. */ printf("%d\n", a); /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programului Problema 4: Rezolvarea ecuaiei de grad 1: ax+b=0


/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram doua variabile de tip double care reprezinta coeficientii * ecuatiei. */ double a, b; /* Citim cei doi coeficienti, primul fiind cel dominant, utilizand scanf. * Atentie la sirul de formatare si la caracterul "&"! */ scanf("%lf%lf", &a, &b); /* Rezolvam ecuatia luand in calcul toate cazurile posibile. */ if(a == 0) { if(b == 0) printf("Ecuatia are o infinitate de solutii\n"); else printf("Ecuatia nu are nicio solutie\n"); } else /* Solutia ecuatiei este de forma -b/a si o afisam. */ printf("%lf\n", -b / a); /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programului Problema 5: S se afieze suma primelor n numere naturale, n citit de la tastatur.
/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram trei variabile de tip int. */ int n, s, i; /* Citim numarul n utilizand functia scanf. Atentie la sirul de foramtare * si la caracterul "&"! */ scanf("%d", &n); /* In scop didactic vom utiliza o instructiune de tip for pentru a calcula * suma primeor n numere naturale. */ s = 0;

74

INFORMATIC*I* for(i = 1; i <= n; i++) s += i;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Afisam variabila s utilizand functia printf. */ printf("%d\n", s); /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programului Problema 6: Algoritmul lui Euclid care determin cel mai mare divizor comun a doi ntregi, prin mpriri repetate:
/** Includem stdio.h pentru a putea folosi functiile de citire/scriere. */ #include <stdio.h> int main() { /* Declaram cele doua numere si o variabila ce va retine restul * impartirii. */ int a, b, r; /* Citim cele doua numere utilizand functia scanf. Atentie la string-ul de * formatare si la caracterul "&"! */ scanf("%d%d", &a, &b); /* Calculam restul impartirii lui a la b. Cat timp acesta este diferit de 0 * se modifica valorile lui a si b si se recalculeaza restul. Cel mai mare * divizor comun va fi ultimul rest diferit de 0 care, la final, va fi * retinut in variabila b. */ r = a % b; while(r > 0) { a = b; b = r; r = a % b; } /* Afisam valoarea celui mai mare divizor comun utilizand functia printf. */ printf("%d\n", b); /* Valoarea 0 semnaleaza faptul ca programul s-a incheiat cu succes. */ return 0; }

Link execuia programului Probleme propuse i rezolvate n limbajul C Problema 1. Interschimbul valorilor a dou variabile a i b. Rezolvare: Atenie! Trebuie s folosim o variabil auxiliar. Nu funcioneaz a=b i apoi b=a deoarece deja am pierdut valoarea iniial a lui a!
#include <stdio.h> int main() { /* Declaram cele doua variabile ale caror valori vrem sa le intreschimbam * si o variabila auxliara. */

75

INFORMATIC*I* int a, b, aux;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Citim valorile variabilelor a si b utilizand scanf. Atentie la string-ul * de formatare si la caracterul '&'. */ scanf("%d%d", &a, &b); /* Interschimbam valorile variabilelor. */ aux = a; a = b; b = aux; /* Afisam variabilele a si b folosind fucntia printf. */ printf("a=%d b=%d\n", a, b); return 0; }

Link execuia programului Problema 2. Rezolvarea ecuaiei de grad 2: ax2+bx+c=0. Rezolvare: Atenie la cazurile speciale! Dac a este 0 ecuaia devine ecuaie de gradul 1!
#include <stdio.h> #include <math.h> int main() { double a, b, c, delta; /* Citim coeficientii incpeand cu cel dominant utilizand scanf. Atentie la * string-ul de formatare si la caracterul '&'! */ scanf("%lf %lf %lf", &a, &b, &c); /* Rezolvam ecuatia de gradul 2 luand in calcul toate posibilitatile. */ if(a == 0) { if(b == 0) { if(c == 0) printf("Ecuatia are o infinitate de solutii\n"); else printf("Ecuatia nu are nicio solutie\n"); } else { /* Avem o ecuatie de gradul 1 si ii afisam solutia. */ printf("Ecuatia de gradul 1 are solutia: %lf\n", -b / c); } } else { delta = b * b - 4 * a * c; if(delta < 0) printf("Ecuatia nu are solutie\n"); else { if(delta == 0) { /* Ecuatia are solutie dubla. */ printf("Ecuatia are doua radacini egale cu: "); printf("%lf\n", -b / (2 * a)); } else { /* Ecuatia are doua solutii diferite. */ printf("Ecuatia are doua radacini distincte: "); printf("%lf ", (-b - sqrt(delta)) / (2 * a)); printf("%lf\n", (-b + sqrt(delta)) / (2 * a)); } }

76

INFORMATIC*I* } return 0; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Link execuia programului Problema 3. S se afieze n ordine cresctoare valorile a 3 variabile a, b i c. Rezolvare: Putem rezolva aceast problem comparnd dou cte dou cele 3 variabile. n cazul n care nu sunt n ordine cresctoare interschimbm valorile lor.
#include <stdio.h> int main() { /* Declaram 3 varaibile ce vor retine cele 3 numere si o varaiabila * auxiliara. */ int a, b, c, aux; /* Citim cele 3 numere utilizand functia scanf. Atentie la string-ul de * formatare si la caracterul '&'! */ scanf("%d%d%d", &a, &b, &c); if(a > aux a = b = } if(a > aux a = c = } if(b > aux b = c = } b) { = a; b; aux; c) { = a; c; aux; c) { = b; c; aux;

/* Afisam cele 3 numere folosinf printf. */ printf("%d %d %d\n", a, b, c); return 0; }

Link execuia programului O alt variant este urmtoarea, n care valorile variabilelor nu se modific ci doar se afieaz aceste valori n ordine cresctoare:
#include <stdio.h> int main() { /* Declaram 3 varaibile ce vor retine cele 3 numere. */ int a, b, c; /* Citim cele 3 numere utilizand functia scanf. Atentie la string-ul de * formatare si la caracterul '&'! */ scanf("%d %d %d", &a, &b, &c); if(a < b && b < c) {

77

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf("%d %d %d\n", a, b, c); /* Semnalam ca programul se incheie cu succes. */ return 0; } if(a < c && c < b) { printf("%d %d %d\n", a, c, b); /* Semnalam ca programul se incheie return 0; } if(b < a && a < c) { printf("%d %d %d\n", b, a, c); /* Semnalam ca programul se incheie return 0; } if(b < c && c < a) { printf("%d %d %d\n", b, c, a); /* Semnalam ca programul se incheie return 0; } if(c < a && a < b) { printf("%d %d %d\n", c, a, b); /* Semnalam ca programul se incheie return 0; } if(c < b && b < a) { printf("%d %d %d\n", c, b, a); /* Semnalam ca programul se incheie return 0; } return 0; }

cu succes. */

cu succes. */

cu succes. */

cu succes. */

cu succes. */

Link execuia programului Problema 4. S se calculeze i s se afieze suma: S=1+1*2+1*2*3+..+n! Rezolvare: Vom folosi o variabil auxiliar p n care vom calcula produsul parial.
#include <stdio.h> int main() { int n, i, j; /* Deoarece valoarea lui n! poate depasi tipul int, declaram varaibila s * de tip long. Mai avem nevoie de o variabila in care sa calculam i!, cu * i =1:n. */ long fact, s; /* Citim valoarea lui n utilizand scanf. */ scanf("%d", &n); /* Calculam suma de factoriale. */ s = 0; for(i = 1; i <= n; i++) { /* Calculam i!. */ fact = 1; for(j = 1; j <= i; j++) fact *= j; s += fact; } /* Afisam valoarea variabilei s utilizand printf. Specificatorul de tip

78

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

* pentru tipul long este "%ld". */ printf("%ld\n", s); return 0; }

Link execuia programului O alt modalitate este urmtoarea, n care produsul parial este actualizat la fiecare pas, fr a mai fi calculat de fiecare dat de la 1:
#include <stdio.h> int main() { int n, i; /* Deoarece valoarea lui n! poate depasi tipul int, declaram varaibila s * de tip long. Mai avem nevoie de o variabila in care sa calculam i!, cu * i =1:n. */ long fact, s; /* Citim valoarea lui n utilizand scanf. */ scanf("%d", &n); /* Calculam suma de factoriale. */ s = 0; fact = 1; for(i = 1; i <= n; i++) { fact *= i; s += fact; } /* Afisam valoarea variabilei s utilizand printf. Specificatorul de tip * pentru tipul long este "%ld". */ printf("%ld\n", s); return 0; }

Link execuia programului Problema 5. S se calculeze i s se afieze suma cifrelor unui numr natural n. Rezolvare: Vom folosi o variabil auxiliar c n care vom calcula rnd pe rnd cifrele. Pentru aceasta vom lua ultima cifr a lui ca rest al mpririi lui n la 10, dup care micorm n mprindu -l la 10 pentru a ne pregti pentru a calcula urmtoarea cifr, amd.
#include <stdio.h> int main() { int n, s, c; /* Citim valoarea variabilei n utilizand scanf. */ scanf("%d", &n); /* Calculam suma cifrelor numarului n stiind ca putem obtine ultima cifra * a acestuia ca restul impartirii lui n la 10, iar numarul n fara ultima * cifra este egal cu, catul impartirii lui n la 10. */ s = 0; while(n > 0) {

79

INFORMATIC*I* c = n % 10; s += c; n /= 10; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Afisam variabila s utilizand printf. */ printf("%d\n", s); return 0; }

Link execuia programului Problema 6. S se calculeze i s se afieze inversul unui numr natural n. Rezolvare: Vom folosi o variabil auxiliar c n care vom calcula rnd pe rnd cifrele ca n problema anterioar, i vom construi inversul pe msur ce calculm aceste cifre.
#include <stdio.h> int main() { int n, inv, c; /* Citim valoarea variabilei n utilizand scanf. */ scanf("%d", &n); /* Calculam inversul numarului n stiind ca putem obtine ultima cifra * a acestuia ca restul impartirii lui n la 10, iar numarul n fara ultima * cifra este egal cu, catul impartirii lui n la 10. */ inv = 0; while(n > 0) { c = n % 10; inv = inv * 10 + c; n /= 10; } /* Afisam variabila inv utilizand printf. */ printf("%d\n", inv); return 0; }

Link execuia programului Problema 7. S se afieze dac un numr natural dat n este prim. Rezolvare: Pentru aceasta vom mpri numrul pe rnd la numerele de la 2 la radical din n (este suficient pentru a testa condiia de prim, dup aceast valoare numerele se vor repeta). Dac gsim un numr care s-l mpart exact vom seta o variabil auxiliar b (numit variabila flag, sau indicator) pe 0. Ea are rolul de a indica c s-a gsit un numr care divide exact pe n. Iniial presupunem c nu exist un astfel de numr, i deci, b va avea valoarea 1 iniial.
#include <stdio.h> #include <math.h> int main() { int n, i, prim; /* Citim valoarea variabilei n utilizand scanf. */

80

INFORMATIC*I* scanf("%d", &n);

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Pentru a verifica daca n este prim vom cauta un numar din intervalul * [0, sqrt(n)] un divizor al sau. Daca nu exista niciun astfel de numar * atunci numarul este prim. Pentru aceasta vom folosi o varaibila numita * "prim" care are valoarea 1 in caz ca numarul este prim sau 0 in caz * contrar. Initial presupunem ca n este prim. */ prim = 1; for(i = 2; i <= sqrt(n); i++) if((n % i) == 0) { /* S-a gasit un divizor si cautarea se incheie. */ prim = 0; break; } if(prim) printf("Numarul %d este prim", n); else printf("Numarul %d nu este prim", n); return 0; }

Link execuia programului O alt variant este cea n care nu mai folosim variabila prim:
#include <stdio.h> #include <math.h> int main() { int n, i; /* Citim valoarea variabilei n utilizand scanf. */ scanf("%d", &n); /* Pentru a verifica daca n este prim vom cauta un numar din intervalul * [0, sqrt(n)] un divizor al sau. Daca nu exista niciun astfel de numar * atunci numarul este prim. Pentru aceasta vom folosi o varaibila numita * "prim" care are valoarea 1 in caz ca numarul este prim sau 0 in caz * contrar. Initial presupunem ca n este prim. */ for(i = 2; i <= sqrt(n); i++) if((n % i) == 0) { /* S-a gasit un divizor si cautarea se incheie. */ break; } if(i > sqrt(n)) //s-a iesit normal din for printf("Numarul %d este prim", n); else //s-a iesit fortat din for, prin break printf("Numarul %d nu este prim", n); return 0; }

Link execuia programului Problema 8. S se afieze primele n numere naturale prime. Rezolvare: Folosim algoritmul de la problema anterioar la care adugm o variabil de contorizare numrare, k.
#include <stdio.h> #include <math.h>

81

INFORMATIC*I* int main() { int n, i, prim, nr, j;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

/* Citim valoarea variabilei n utilizand scanf. */ scanf("%d", &n); nr = 0; i = 2; while(nr < n) { prim = 1; for(j = 2; j <= sqrt(i); j++) if((i % j) == 0) { /* S-a gasit un divizor si cautarea se incheie. */ prim = 0; break; } if(prim) { printf("%d\n", i); nr++; } i++; } return 0; }

Link execuia programului Problema 9. S se descompun n factori primi un numr dat n. Rezolvare: Pentru aceasta vom mpri numrul pe rnd la numerele ncepnd cu 2. Dac gsim un numr care s-l mpart exact vom mpri pe n de cte ori se poate la numrul gsit, calculnd astfel puterea. n modul acesta nu va mai fi necesar s testm c numerele la care mprim sunt prime!
#include <stdio.h> int main() { int n, div, nr; scanf("%d", &n); /* vom mpri numrul pe rnd la numerele ncepnd cu 2. Dac gsim un * numr care s-l mpart exact vom mpri pe n de cte ori se poate la * numrul gsit, calculnd astfel puterea. */ div = 2; printf("n = "); while(n != 1) { nr = 0; while((n % div) == 0) { nr++; n /= div; } if(nr != 0) printf("%d^%d * ", div, nr); div++; } return 0;

82

INFORMATIC*I* }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Link execuia programului Problema 10. S se afieze toate numerele naturale mai mici dect 10000 care se pot descompune n dou moduri diferite ca sum de dou cuburi. Rezolvare: Aceast problem prezint foarte bine avantajul utilizrii unui calculator n rezolvarea anumitor probleme. Calculm, pe rnd, suma de cuburi a perechilor de numere de la 1 la 21 (cel mai apropiat ntreg de radical de ordin 3 din 10000). Cutm apoi, pentru fiecare sum, o a doua pereche a crei sum de cuburi este aceeai. Dac este o pereche diferit de prima, afim numerele.
#include <stdio.h> int main() { int a, b, c, d, x, y; /* Numerele pe care le cautam cor fi in intervalul [1, 21], deoarece * 22^3 > 10000. */ for(a = 1; a < 22; a++) for(b = 1; b < 22; b++) { x = a * a * a + b * b * b; /* Cautam inca o pereche de numere cu aceeasi proprietate. */ for(c = 1; c < 22; c++) for(d = 1; d < 22; d++) { y = c * c * c + d * d * d; if(x == y && c != a && d != b) printf("%d=%d^3+%d^3=%d^3+%d^3\n", x, a, b, c, d); } } return 0; }

Link execuia programului Problema 11. S se calculeze valoarea minim, respectiv maxim, dintr-o secven de n numere reale. Rezolvare: Vom utiliza dou variabile, max i min pe care le iniializm cu o valoare foarte mic i respectiv cu o valoare foarte mare. Vom compara pe rnd valorile citite cu max i respectiv cu min, iar dac gsim o valoare mai mare, respectiv mai mic dect acestea modificm max (sau min, dup cum e cazul) la noua valoare maxim, respectiv minim.
#include <stdio.h> int main() { int min, max, i, n, x; scanf("%d", &n); min = 32000; max = -32000; for(i = 0; i < n; i++) { scanf("%d", &x); if(x > max) max = x; if(x < min)

83

INFORMATIC*I* min = x; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf("Valoarea maxima este: %d\n", max); printf("Valoarea minima este: %d\n", min); return 0; }

Link execuia programului

84

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.05. Tablouri. Definire i utilizare n limbajul C


Cuvinte-cheie
Tablou, tablouri unidimensionale, vector, indexare, tablouri multidimensionale, tablouri bidimensionale, matrice

IB.05.1 Tablouri S presupunem c avem urmtoarea problem: S se afieze numele tuturor studenilor care au nota maxim; numele i notele se citesc de la tastatur. Pn acum, problemele rezolvate de genul acesta, presupuneau citirea unor date i prelucrarea lor pe msur ce sunt citite, fr a reine toate valorile citite (vezi problema 1 (capitolul IB.01) cu funcia F(x) nu se reineau toate valorile lui x, ci doar una la un moment dat!) . Acest lucru ns nu este posibil aici, deoarece trebuie ca mai nti s aflm nota maxim printr-o prim parcurgere a datelor de intrare i apoi s mai parcurgem nc o dat aceste date pentru a afia studenii cu nota maxim. Pentru aceasta este necesar memorarea tuturor studenilor (a datelor de intrare, nume -nota). Cum memorm ns aceste valori? Cu siguran nu vom folosi n variabile pentru nume: nume1, nume2, etc. i n variabile pentru nota: nota1, nota2, etc., mai ales c nici nu tim exact ct este n - ci studeni vor fi! Vom folosi n loc o singur variabil de tip tablou, cu mai multe elemente pentru nume i o singur variabil de tip tablou cu mai multe elemente, pentru note. Prin tablou se nelege n programare o colecie finit, liniar, de date de acelai tip numit tip de baz al tabloului colecie care ocup un spaiu continuu de memorie. n limba englez se folosete cuvntul array. n funcie de numrul de dimensiuni putem avea mai multe tipuri de tablouri, cele mai utilizate fiind cele unidimensionale, numite, de obicei, vectori, i cele bidimensionale cunoscute sub numele de matrice. IB.05.2 Tablouri unidimensionale: vectori Un tablou unidimensional care conine valorile Val1, Val2, etc., poate fi reprezentat grafic astfel: Nume vector: a Dimensiune vector: n Elemente:
Val1 Val2 Val3 . Val4 Val5

Primul element

Ultimul element

Elementele sale sunt memorate unele dup altele ocup un spaiu continuu de memorie. Modul de declarare al unui vector cu dimensiune constant este urmtorul: Sintaxa: tip_de_baz nume_tablou [dimensiune] = { const0, const1, ...} Unde: tip_de_baz este tipul elementelor; dimensiune (lungimea vectorului) reprezint numrul maxim de elemente ale tabloului i este n general o constant ntreag (de obicei o constant simbolic); const0, const1, etc. reprezint valori constante de iniializare, i prezena lor este opional. 85

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Memoria ocupat de un vector este egal cu dimensiune * sizeof(tip_de_baz). Exemple:


int tab[10]; float v[60]; #define N 10 int tab[N]; //definete un tablou de 10 elemente ntregi //definete un tablou de 60 elemente reale // definitie echivalenta cu prima

Pentru a crea un vector, trebuie s cunoatem dimensiunea sa (lungimea vectorului) n avans, deoarece odat creat, dimensiunea sa este fix: este alocat la compilare i nu mai poate fi modificat la execuie! Uneori nu putem cunoate n avans dimensiunea vectorului, aceasta fiind o dat de intrare a programului (de exemplu, ci studeni vom avea?). n astfel de cazuri vom estima o dimensiune maxim pentru vector, dimensiune care este o limit a acestuia i vom declara vectorul ca avnd aceast dimensiune maxim acoperitoare (ns ct mai apropiat de cea real). Acesta este probabil principalul dezavantaj al utilizrii unui vector. De obicei se folote o constant simbolic pentru a desemna dimensiunea maxim a unui vector i se verific dac[ valoarea citit este cel mult egal cu valoarea constantei. De asemenea, vom mai avea o variabil n care vom pstra numrul efectiv de elemente din vector numele variabilei este de obicei n! Exemplu:
#define M 100 // dimensiune maxima vector int main () { int a[M], n; scanf ("%d", &n); // citeste dimensiune efectiva if ( n > M) { printf ("Eroare: n > %d \n",M); return; } ... // citire i utilizare elemente vector

n acest fel, codul programului este independent de dimensiunea efectiv a vectorului, care poate fi diferit de la o execuie la alta. Este permis iniializarea unui vector la declarare, prin precizarea constantelor de iniializare ntre acolade i separate prin virgule. Dac numrul acestora: este egal cu dimensiune - elementele tabloului se iniializeaz cu constantele precizate < dimensiune - constantele neprecizate (lips) sunt considerate implicit 0 > dimensiune - apare eroare la compilare. Dac este prezent partea de iniializare, dar lipsete dimensiune, aceasta este implicit egal cu numrul constantelor din partea de iniializare i este singurul caz n care este posibil omiterea dimensiunii! Exemple:
// Declararea si initializarea unui vector de 3 numere intregi: int azi[3] = { 01, 04, 2001 }; // zi,luna,an /* Vectorul a va avea dimensiune 3, de initializare: */ double a[]={2,5.9}; aceasta fiind data de numarul constantelor

//numarul constantelor < numarul elementelor: #define NR_ELEM 5 float t[NR_ELEM]={1.2,5,3}; int prime[1000] = {1, 2, 3}; /*primele trei elemente se initializeaza cu constantele precizate, urmatoarele cu 0. Poate fi confuz. Nu se recomand! */ int a[1000] = { 0 }; int numbers[2] = {11, 33, 44}; //toate elementele sunt iniializate cu zero // EROARE: prea multe iniializri

86

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Putem afla dimensiunea cu care a fost declarat un vector folosind expresia:


sizeof(nume_tablou) / sizeof(tip_de_baz) sizeof(nume_tablou)

returneaz numrul total de octei ocupai de vector.

Fiecare element din vector este identificat printr-un indice ntreg, pozitiv, care arat poziia sa n vector; pentru selectarea unui element se folosete operatorul de indexare: [] paranteze drepte. Se spune c accesul este direct la orice element din vector. Selectarea unui element dintr-un vector: nume_tablou [indice] unde indice este o expresie ntreag cu valori ntre 0 i dimensiune -1. Un element de tablou poate fi prelucrat ca orice variabil avnd tipul de baz. Numerotarea elementelor fiind de la zero, primul element din orice vector are indicele zero, iar ultimul element dintr-un vector are un indice mai mic cu 1 dect numrul elementelor din vector. Nume vector: a Dimensiune vector: n Index: Elemente: 0 a[0] 1 a[1] 2 a[2] . n-2 a[n-2] n-1 a[n-1]

Primul element

Ultimul element

Exemple:
//dac avem un vector de 5 note: int note[5]; //atribuim valori elementelor astfel: note[0] = 95; note [1] = 85; note [2] = 77; note [3] = 69; note [4] = 66; //afisarea valorii unor elemente: printf("prima nota este %d\n", note[0]); printf("suma ultimelor doua note este %d\n", note[3]+note[4]);

Utilizarea unui vector presupune, n general, repetarea unor operaii asupra fiecrui element din vector deci folosirea unor structuri repetitive. De obicei, pentru a realiza o prelucrare asupra tuturor elementelor unui tablou se folosete instruciunea for cu o variabil contor care s ia toate valorile indicilor ( ntre 0 i dimensiune -1 ). Exemplu:
#define N 10 int tab[N], i; for(i=0; i<N; i++) //prelucrare tab[i]

87

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Dup cum spuneam, de obicei nu toate elementele tabloului (alocate in memorie) sunt folosite, ci doar primele n elem<=dimensiune ( vezi programul urmtor, se pot citi doar primele n elem ):
int a[100], n, i; // vectorul a de max 100 de intregi, n numrul efectiv de elemente folosite //citirea i afiarea unui vector de ntregi: scanf ("%d",&n); // citete nr efectiv de elemente din vector for (i=0;i<n;i++) scanf ("%d", &a[i]); // citire elemente vector for (i=0;i<n;i++) printf ("a[%d]=%d\n", i, a[i]); // scrie elemente vector //suma elementelor 0..n-1 din vectorul a int s; for (i=0, s=0; i<n; i++) s = s + a[i];

Observaii: Nici compilatorul, nici mediul de execuie nu verific valorile indicilor. Cu alte cuvinte, nu sunt generate n mod normal avertizri/erori (warning/error) dac indexul este n afara limitelor; programatorul trebuie s codifice astfel nct indicele s ia valori n intervalul 0 .. dimensiune-1, deoarece pot apare erori imprevizibile, ca de exemplu modificarea nedorit a altor variabile. Exemple:
#define N 10 int tab[N]; tab[N]=5; // se modifica zona de 2 octeti urmatoare tabloului tab[-10]=6; /* se modifica o zona de 2 octeti situata la o adresa cu 20 de octeti inferioara tabloului */ // Program ce poate compila i chiar rula dar cu colaterale: const int dim = 5; int numere[dim]; // vector cu index de la 0 la 4 numere[88] = 999; printf("%d\n", numere[77]); // Index in afara limitelor, nesemnalat! posibile erori

Aceasta este un alt dezavantaj C/C++. Verificarea limitelor indexului ia timp i putere de calcul micornd performanele. Totui, e mai bine s fie sigur dect rapid! Numele unui vector nu poate s apar n partea stng a unei atribuiri deoarece are asociat o adres constant (de ctre compilator), care nu poate fi modificat n cursul executiei. Exemplu de greeal:
int a[30]={0}, b[30]; b=a; // eroare !

Pentru copierea datelor dintr-un vector ntr-un alt vector se va scrie un ciclu pentru copierea element cu element, sau se va folosi funcia memcpy. Exemplu:
int a[30]={1,3,5,7,9}, b[30], i, n; .... for (i=0;i<n;i++) b[i]=a[i];

88

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Dimensiunea unui vector poate fi i valoarea unei variabile, dar atenie!, declararea vectorului se va face dup iniializarea variabilei, nu nainte, deoarece tentativa de a citi valoarea variabilei dup declararea vectorului va genera eroare! Exemple:
int size; printf("Introduceti dimensiunea vectorului:"); scanf("%d", &size); float values[size]; // NU! int size; float values[size]; printf("Introduceti dimensiunea vectorului:"); scanf("%d", &size);

Totui, acesta nu este un mod recomandat de declarare a vectorilor datorit complicaiilor care pot apare. IB.05.3 Tablouri multidimensionale Modul de declarare al unui tablou multidimensional este urmtorul: Sintaxa: tip_de_baza nume_tablou [dim1] [dim2] ... [dimn] = { {const10,...}, {const20,...}, ..., {constn0,...} }; unde: tip_de_baz este tipul elementelor; dim1, dim2, ..., dimn - expresii ntregi constante ( de obicei constante simbolice ). const10, const20, etc. reprezint valori constante de iniializare, i prezena lor este opional. dim1, dim2, ..., dimn reprezint numrul maxim de elemente pentru prima dimensiune, a 2-a dimensiune, etc. Observaie: n practic se folosesc rar tablouri cu mai mult de 2 dimensiuni. Memoria continu ocupat de tablou este de dimensiune: dim1 * dim2 * ... * dimn * sizeof(tip_de_baza). Elementele tabloului multidimensional sunt memorate astfel nct ultimul indice variaz cel mai rapid. Tabloul poate fi iniializat la definire - vezi partea opionala marcat - prin precizarea constantelor de iniializare. Selectarea unui element dintr-un tablou multidimensional: nume_tablou [ind1] [ind2] [indn] Unde ind1, ind2, ..., indn - expresii ntregi cu valori ntre 0 i dimi-1 pentru i=1..n. Un element de tablou poate fi prelucrat ca orice variabila avnd tipul de baz. IB.05.4 Tablouri bidimensionale: matrici Un tablou bidimensional (caz particular de tablou multidimensional), numit i matrice, are dou dimensiuni, orizontal i vertical, i este definit prin dimensiune maxim pe orizontal - numr maxim de linii i dimensiune maxim pe vertical - numr maxim de coloane. n limbajul C matricele sunt liniarizate pe linii, deci n memorie linia 0 este urmat de linia 1, linia 1 este urmat de linia 2 .a.m.d., cu alte cuvinte, elementele unei matrici sunt memorate pe linii, 89

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

unele dup altele ocup un spaiu continuu de memorie. O matrice bidimensional este privit n C ca un vector de vectori (de linii). Modul de declarare al unei matrici este urmtorul: Sintaxa: tip_de_baz nume_tablou [dim1] [dim2] = {{const10,...},{const20,...},...,{constn0,...}}; unde: tip_de_baz este tipul elementelor; dim1 reprezint numrul maxim de linii (prima dimensiune) iar dim2 numrul maxim de coloane (a -2-a dimensiune) i sunt constante ntregi (de obicei constante simbolice); const10, const20, etc. reprezint valori constante de iniializare, i prezena lor este opional. Memoria continu ocupat de tablou este dim1 * dim2 * sizeof(tip_de_baz). O matrice poate fi reprezentat grafic astfel:

Coloana Coloana Coloana Coloana 0 1 a[0][1 2 a[0][2 3a[0][3 Linia 0 a[0][0 ] ] ] ] a[1][1 a[1][2 a[1][3 a[1][0 Linia ] ] ] ] 1 Index Linie Index Coloana

Exemple:
int m[10][5]; /* defineste un tablou dimensional de elemente intregi, cu maxim 10 linii si 5 coloane*/ // definitie echivalenta cu cea de mai sus: #define NL 10 #define NC 5 int m[NL][NC];

Rmn valabile toate observaiile fcute la vectori, legate de dimensiunile matricii. De obicei se folosesc constante simbolice pentru aceste dimensiuni i se verific ncadrarea datelor citite n dimensiunile declarate. De cele mai multe ori, vom folosi dou variabile n care vom pstra numrul efectiv (cel cu care se lucreaz la execuia curent) de linii, respectiv de coloane din matrice! Este posibil iniializarea unei matrice la definirea ei, iar elementele care nu sunt iniializate explicit primesc valoarea zero. Avem aceleai observaii ca la vectori. Exemple:
int b[2][3] = {1,2,3,4,5,6}; // echivalent cu: int b[2][3] = {{ 1,2,3},{ 4,5,6}}; // echivalent cu: int b[][ 3] = {{ 1,2,3},{ 4,5,6}} double a[3][2]={{2},{5.9,1},{-9}}; //elementele pe linii sunt: 2 0 / 5.9 1 / -9 0 double a[3][2]={2,5.9,1,-9}; //elementele pe linii sunt: 2 5.9 / 1 -9 / 0 0

90

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Selectarea unui element dintr-o matrice: nume_tablou [ind1] [ind2] De exemplu, notaia a[i][j] desemneaz elementul din linia i i coloana j a unei matrice a, sau altfel spus elementul din poziia j din vectorul a[i]. Prelucrarea elementelor unei matrice se face prin dou cicluri; un ciclu pentru parcurgerea fiecrei linii i un ciclu pentru parcurgerea fiecrei coloane dintr-o linie: Exemplu:
// afiare matrice cu nl linii i nc coloane pe linii for (i=0;i<nl;i++) { for (j=0;j<nc;j++) printf (%6d, a[i][j]); printf(\n); }

Observaii: Exemplul de mai sus corespunde unei prelucrri pe linii a elementelor matricei; n cazul unei prelucrri pe coloane, ciclul exterior este cel cu contorul corespunztor indicelui de coloan. Numrul de cicluri incluse poate fi mai mare dac la fiecare element de matrice se fac prelucrri repetate. De exemplu, la nmulirea a dou matrice a i b, fiecare element al matricei rezultat c se obine ca o sum: Exemplu:
for (i=0;i<n;i++) for (j=0;j<m;j++) { c[i][j]=0; //initializare element matrice produs for (k=0;k<p;k++) c[i][j] += a[i][k]*b[k][j]; //calcul suma de produse }

Numerotarea liniilor i coloanelor de la 0 n C este diferit de numerotarea uzual din matematic, care ncepe de la 1. Pentru numerotarea de la 1 putem s nu folosim linia zero i coloana zero (ceea ce nu se recomand) sau s ajustm indicii matematici scznd 1. Exemplu de citire i afiare matrice cu numerotare linii si coloane de la 1, n care linia zero i coloana zero nu se folosesc:
int nl, nc, i, j; float a[50][50]; //citire numar de linii, numar de coloane printf("nr.linii: "); scanf("%d",&nl); printf("nr.coloane: "); scanf("%d",&nc); //citire matrice pe linii for (i=1;i<=nl;i++) for (j=1;j<=nc;j++) scanf ("%f", &a[i][j]); //afisare matrice pe linii for (i=1;i<=nl;i++) { for (j=1;j<=nc;j++) printf ("%f printf ("\n"); }

",a[i][j]);

91

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.05.5 Probleme propuse Programele din capitolul IB.01 rezolvate n C folosind tablouri Problema 12. Se d o secven de n numere ntregi pozitive. S se afieze cele mai mari numere de 2 cifre care nu se afl n secvena respectiv. Rezolvare: n acest caz vom folosi un vector ca variabil auxiliar n care vom ine minte dac un numr de dou cifre a fost citit de la tastatur (v[nr] este 0 dac nr nu a fost citit i v[nr] devine 1 dac nr a fost citit de la tastatur). Iniial, toate valorile din vector sunt 0. Pentru a afla cele mai mari numere de dou cifre care nu sunt n secvena citit vom parcurge vectorul v de la coad (99) la cap pn ntlnim dou valori zero. Atenie! n cazul n care nu exist una sau dou valori de dou cifre care s nu aparin secvenei citite nu se va afia nimic!
#include <stdio.h> #define N 103 int main() { int n, v[N], i, nr; scanf(%d, &n); for(i = 10; i < 100; i++) v[i] = 0; for(i = 0; i < n; i++) { scanf(%d, &nr); if(nr > 9 && nr < 100) v[nr] = 1; } i = 99; while(v[i] != 0 && i > 0) i--; if(i > 9) printf(%d , i); i--; while(v[i] != 0 && i > 0) i--; if(i > 9) printf(%d , i); return 0; }

Problema 13. Se d o secven de n numere ntregi, ale cror valori sunt cuprinse n intervalul 0 100. S se afieze valorile care apar cel mai des. Rezolvare: Vom utiliza de asemenea un vector n care vom ine minte de cte ori a aprut fiecare valoare de la 0 la 100 v[nr] reprezint de cte ori a fost citit nr. Iniial toate valorile din vector sunt 0. Vom determina apoi valoarea maxim din acest vector, dup care, pentru a afia toate numerele care apar de cele mai multe ori mai parcurgem nc o dat vectorul i afim indicii pentru care gsim valoarea maxim.
#include <stdio.h> #define MaxN 103 int main() { int i, n, v[MaxN], nr, max; scanf(%d, &n);

92

INFORMATIC*I* for(i = 0; i < 100; i++) v[i] = 0; for(i = 0; i < n; i++) { scanf(%d, &nr); v[nr]++; } max=0; for(i = 0; i < 100; i++) if(v[i] > max) max = v[i]; for(i = 0; i < 100; i++) if(v[i] == max) printf(%d\n, i); return 0; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Alte exemple propuse 1. Se consider un vector de N elemente ntregi (N este constant predefinit). S se prelucreze tabloul astfel: s se citeasc cele N elemente de la tastatur (varianta: pn la CTRL/Z - se decomenteaza linia comentat din partea de citire i se comenteaz cea anterioar ) s se afieze elementele s se afieze maximul i media aritmetic pentru elementele vectorului s se caute n vector o valoare citit de la tastatur s se construiasc un vector copie al celui dat s se afieze elementele tabloului copie n ordinea invers.
#include <stdio.h> #define N 8 int main(){ int tablou[N], copie[N], nr_elem; /* tablou va contine nr_elem de prelucrat */ int i,max,suma, de_cautat; float meda; // citire puts("Introduceti elementele tabloului:"); for(i=0;i<N;i++){ printf("elem[%d]=",i); //se afiseaza indicele elem ce se citeste scanf("%d",&tablou[i]); //if(scanf("%d",&tablou[i])==EOF)break; } nr_elem=i; // tiparire puts("Elementele tabloului:"); for(i=0;i<nr_elem;i++) printf("elem[%d]=%d\n",i,tablou[i]); // info for(i=suma=0,max=tablou[0];i<nr_elem;i++){ suma+=tablou[i]; if(tablou[i]>max) max=tablou[i]; } printf("Val maxima=%d, media aritm=%f\n", max, (float)suma/nr_elem); // cautare printf("Se cauta:"); scanf("%d",&de_cautat); for(i=0;i<nr_elem;i++)

93

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

if(de_cautat==tablou[i])break; //cele doua linii de mai sus se pot scrie echivalent: // for(i=0;i<nr_elem && de_cautat!=tablou[i];i++) if(i<nr_elem) printf("S-a gasit valoarea la indicele %d!\n",i); else puts("Nu s-a gasit valoarea cautata!"); // copiere for(i=0;i<nr_elem;i++) copie[i]=tablou[i]; // tiparire inversa puts("Elementele tabloului copie in ordine inversa:"); for(i=nr_elem-1;i>=0;i--) printf("copie[%d]=%d\n",i,copie[i]); return 0; }

2. S se scrie un program care citete coeficienii unui polinom de x, cu gradul maxim N ( N este o constant predefinit), calculnd apoi valoarea sa n puncte x citite, pn la introducerea pentru x a valorii 0. S se afieze i valoarea obinut prin apelarea funciei de biblioteca poly. S se modifice programul astfel nct s citeasc ciclic polinoame, pe care s le evalueze.
#include <stdio.h> #define N 10 int main(){ double coeficient[N+1], x, val; //numarul de coeficienti este cu 1 mai mare decat gradul int grad, i; //grad variabil <=N // citire grad cu validare do { printf("grad maxim(0..%d)=",N);scanf("%d",&grad); } while ( grad<0 || grad>N ); // citire coeficienti polinom puts("Introduceti coeficientii:"); for(i=0;i<=grad;i++){ printf("coef[%d]=",i); scanf("%lf",&coeficient[i]); // se afiseaza indicele elem ce se citeste } //afisare polinom printf("P(x)="); for(i=grad;i>0;i--) if(coeficient[i]) printf("%lf*x^%d+",coeficient[i],i); if(coeficient[0]) printf("%lf\n",coeficient[0]); /* termenul liber */ //citire repetata pana la introducerea valorii 0 while(printf("x="),scanf("%lf",&x),x){ /*while(printf("x="),scanf("%lf",&x)!=EOF) //daca oprire la CTRL/Z */ // calcul valoare polinom in x val=coeficient[grad]; for(i=grad-1;i>=0;i--){ val*=x; val+=coeficient[i]; } //afisare valoare calculata

94

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf("P(%lf)=%lf\n",x,val); } return 0; }

3. Pentru o matrice de numere ntregi cu NL linii si NC coloane ( NL, NC constante simbolice, s se scrie urmtoarele programe: a. citete elementele matricii pe coloane; b. tiprete elementele matricii pe linii; c. determin i afieaza valoarea i poziia elementului maxim din matrice; d. construiete un tablou unidimensional, ale crui elemente sunt sumele elementelor de pe cte o linie a matricii; e. interschimb elementele de pe dou coloane ale matricii cu indicii citii; f. caut n matrice o valoare citit, afindu-i poziia; g. calculeaz i afieaz matricea produs a dou matrici de elemente ntregi.

95

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.06. Funcii. Definire i utilizare n limbajul C


Cuvinte cheie
Subrutin, top down, funcie apelat, funcie apelant, parametri, argumente, definire funcii, funcii void, declarare funcii, domeniu de vizibilitate (scope), apel funcie, instruciunea return, transmiterea parametrilor, funcii cu argumente vectori, recursivitate

IB.06.1. Importana funciilor n programare n unele cazuri, o anumit poriune de cod este necesar n mai multe locuri (puncte) ale programului. n loc s repetm acel cod n mai multe locuri, este preferabil s-l reprezentm ntr-o aa numit subrutin i s chemm (apelm) aceast subrutin n toate punctele programului n care este necesar execuia acelui cod. Acest lucru permite o mai bun nelegere a programului, uureaz depanarea, testarea i eventuala sa modificare ulterioar. n C/C++ subrutinele se numesc funcii. De ce discutam acum despre funcii? Deoarece programele prezentate n continuare vor crete n complexitate, aa nct, pentru dezvoltarea lor, vom aplica tehnica de analiza i proiectare Top Down sau Stepwise Refinement ce cuprinde urmtorii pai: 1. problema se descompune n subprobleme - n pai de prelucrare 2. fiecare subproblem poate fi descompus la rndul su n alte subprobleme 3. fiecare subproblem este implementat ntr-o funcie 4. aceste funcii sunt apelate n main, ceea ce va duce la execuia pe rnd a pailor necesari pentru rezolvarea problemei.

Funcia este un concept important n matematic i programare. n limbajul C prelucrrile sunt organizate ca o ierarhie de apeluri de funcii. Orice program trebuie s conin cel puin o funcie, funcia main. Funciile ncapsuleaz prelucrri bine precizate i pot fi reutilizate n mai multe programe. Practic, nu exist program care s nu apeleze att funcii din bibliotecile existente ct i funcii definite n cadrul aplicaiei respective. Ceea ce numim uzual program sau aplicaie este de fapt o colecie de funcii (subprograme). Motivele utilizrii funciilor sunt multiple: Evit repetarea codului: este uor s faci copy i paste, dar este greu s menii i s sincronizezi toate copiile; Utilizarea de funcii permite dup cum spuneam dezvoltarea progresiv a unui program mare, fie de jos n sus (bottom up), fie de sus n jos (top down), fie combinat. Astfel, un program mare poate fi mai uor de scris, de neles i de modificat dac este modular, adic format din module funcionale relativ mici; O funcie poate fi reutilizat n mai multe aplicaii - prin adugarea ei ntr-o bibliotec de funcii - ceea ce reduce efortul de programare al unei noi aplicaii; O funcie poate fi scris i verificat separat de restul aplicaiei, ceea ce reduce timpul de punere la punct al unei aplicaii mari (deoarece erorile pot apare numai la comunicarea ntre subprograme corecte); ntreinerea unei aplicaii este simplificat, deoarece modificrile se fac numai n anumite funcii i nu afecteaz alte funcii (care nici nu mai trebuie recompilate);

96

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Standardul limbajului C conine o serie de funcii care exist n toate implementrile limbajului. Declaraiile acestor funcii sunt grupate n fiiere antet cu acelai nume pentru toate implementrile. n afara acestor funcii standard exist i alte biblioteci de funcii: funcii specifice sistemului de operare, funcii utile pentru anumite aplicaii (grafic, baze de date, aplicaii de reea .a.). Dou entiti sunt implicate n utilizarea unei funcii: un apelant, care apeleaz (cheam) funcia i funcia apelat. Apelantul, care este i el o funcie, transmite parametri (numiti i argumente) funciei apelate. Funcia primete aceti parametri, efectueaz operaiile din corpul funciei i returneaz rezultatul/rezultatele napoi funciei apelante.

Funcia APELANT c = max(a,b);

Parametri

Funcie APELAT int max(int n1, int n2){ return (n1>n2)?n1:n2; }

Rezultate

Comunicarea de date ntre funcii se face de regul prin argumente i numai n mod excepional prin variabile externe funciilor vezi domeniu de definiie.

Exemplu S presupunem c avem nevoie s evalum aria unui cerc de mai multe ori (pentru mai multe valori ale razei). Cel mai bine este s scriem o funcie numit calculAria(), i s o folosim cnd avem nevoie.
#include <stdio.h> #include <math.h> // prototipul funciei (declararea) double calculAria (double); int main() { double raza1 = 1.1, aria1, aria2; // apeleaza functia calculAria: aria1 = calculAria (raza1); printf(Aria 1 este %lf\n ", area1); // apeleaza functia calculAria: aria2 = calculAria (2.2); printf(Aria 2 este %lf\n ", area2); // apeleaza functia calculAria: printf(Aria 3 este %lf\n ", calculAria (3.3)); return 0; } // definirea functiei double calculAria (double raza) { return raza* raza*M_PI; // M_PI definit in biblioteca math }

97

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Rezultatul va fi:
Aria 1 este 3.80134 Aria 2 este 15.2053 Aria 3 este 34.212

n acest exemplu este definit o funcie numit calculAria care primete un parametru de tip double de la funcia apelant n acest caz main-ul efectueaz calculul i returneaz un rezultat, tot de tip double funciei care o apeleaz. n main, funcia calculAria este chemat de trei ori, de fiecare dat cu o alt valoare a parametrului. IB.06.2. Definirea i utilizarea funciilor Pentru a putea fi utilizat ntr-un program, definiia unei funcii trebuie s precead utilizarea (apelarea) ei. Forma general a unei definiii de funcie, conform standardului, este: Sintaxa:
tip_rezultat_returnat nume_functie (lista_parametri_formali) {

/* corpul functiei: */
definirea variabilelor locale prelucrari }

//declaraii // instructiuni

unde: este tipul rezultatului returnat de funcie. n limbajul C o parte din funcii au o valoare ca rezultat iar altele nu au(sunt de tip void). Pentru o funcie cu rezultat diferit de void tipul funciei este tipul rezultatului funciei. Tipul unei funcii C poate fi orice tip numeric, orice tip pointer, orice tip structur (struct) sau void. Dac tip_rezultat_returnat este: int - funcia este ntreag void - funcia este void float - funcia este real. Lista parametrilor formali cuprinde declaraia parametrilor formali, separai prin virgul: Sintaxa:
tip_rezultat_returnat lista_parametri_formali = tip_p1 nume_p1, tip_p2 nume_p2, ..., tip_pn nume_pn

unde:
tip_p1, tip_p2,., tip_pn nume_p1, formali nume_p2, ..., sunt tipurile parametrilor formali nume_pn sunt numele parametrilor

Exemple: 1. S se calculeze i s se afieze valoarea expresiei xm+yn+(xy)m^n , x, y, m, n fiind citii de la tastatur, astfel nct ntregii m,n s fie pozitivi. Ridicarea la putere se va realiza printr-o funcie putere care primete baza i exponentul ca parametri i returneaz rezultatul. Modul n care se va apela (folosi) aceasta funcie l vom arta la Apelul unei funcii.

98

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// functia care calculeaza bazaexp double putere (double baza, int exp){ int i; //declarare variabila locale float rez; //declarare variabila locale //instructiuni: for(i=rez=1;i<=exp;i++) rez*=baza; return rez; } // returnare valoare calculata in functia apelanta

2. Numrarea i afiarea numerelor prime mai mici ca un ntreg dat n. Pentru aceasta vom scrie o funcie care testeaz dac un numr este prim. Modul n care se va apela (folosi) aceast funcie l vom arta la Apelul unei funcii.
//returneaza 0 daca numarul nu este prim, 1 daca este prim int prim (int numar){ int div; //declarare variabila locale //instructiuni: for (div=2; div<=sqrt(numar); div++) if (numar % div ==0) return 0;// returnare 0 daca am gasit divizor divizor /*aici se ajunge doar daca nu s-a iesit pe return 0, adica daca nu am gasit niciun divizor*/ return 1; // returnare 1 daca nu am gasit niciun divizor }

Parametrii formali sunt vizibili doar n corpul funciei care i definete. Prin parametrii formali, funcia primete datele iniiale (de intrare) necesare i poate transmite rezultate. Parametrii formali pot fi doar nume de variabile, adrese de variabile (pointeri) sau nume de vectori, deci nu pot fi expresii sau componente de vectori. Observaii: Definiiile funciilor nu pot fi incluse una n alta ( ca n Pascal ). Se recomand ca o funcie s ndeplineasc o singur sarcin i s nu aib mai mult de cteva zeci de linii surs (preferabil sub 50 de linii). Este indicat ca numele unei funcii s fie ct mai sugestiv, poate chiar un verb (denot o aciune) sau o expresie ce conine mai multe cuvinte neseparate ntre ele. Primul cuvnt este scris cu liter mic, n timp ce restul cuvintelor sunt scrise cu prima liter mare. Exemple: calculAria(), setRaza(), mutaJos(), ePrim(), etc. Funcii void S presupunem c avem nevoie de o funcie care s efectueze anumite aciuni (de exmplu o tiprire), fr s fie nevoie s returneze o valoare apelantului. Putem declara aceast funcie ca fiind de tipul void. Dac funcia nu returneaz nici un rezultat dar primete parametri, definiia va fi: Sintaxa:
void nume_functie (lista_parametri_formali) {

/* corpul functiei: */
definirea variabilelor locale prelucrari }

//declaraii // instructiuni 99

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Dac funcia nu returneaz nici un rezultat i nu primete parametri, definiia va fi: Sintaxa:
void nume_functie ( ) {

/* corpul functiei: */
definirea variabilelor locale prelucrari }

//declaraii // instructiuni

Observaie: o funcie void poate ntoarce rezultatele prelucrrilor efectuate prin parametri. IB.06.3. Declararea unei funcii O funcie trebuie s fie declarat nainte de utilizare. Putem realiza acest lucru n dou moduri: - amplasnd n textul programului definiia funciei naintea definiiei funciei care o apeleaz main sau alta funcie - declarnd prototipul (antetul) funciei nainte de definiia funciei care o apeleaz; n acest caz, definiia funciei poate fi amplasat oriunde n program. Cu alte cuvinte, dac funcia este definit dup funcia n care este apelat, atunci este necesar o declaraie anterioar a prototipului funciei. Declaraia unei funcii se face prin precizarea prototipului (antetului) funciei: Sintaxa:
tip_rezultat_returnat nume_functie (lista_parametri_formali);

n prototip, numele parametrilor formali pot fi omii, aprnd doar tipul fiecruia. Exemple:
// prototip funcie, plasat naintea locului n care va fi utilizat double calculAria(double); // fr numele parametrilor int max(int, int); /* Numele parametrilor din antet vor fi ignorate de compilator dar vor servi la documentare: */ double calculAria(double raza); int max(int numar1, int numar2);

Antetele funciilor sunt uzual grupate mpreun i plasate ntr-un aa numit fiier antet (header file). Acest fiier antet poate fi inclus n mai multe programe. Exemple: O funcie max(int, int), care primete dou numere ntregi i ntoarce valoarea maxim. Funcia max este apelat din main.
int max(int, int); // prototip funcie (declarare) int main() { printf(%d\n ", max(5, 8)); // apel al funciei max cu valori constante int a = 6, b = 9, c; c = max(a, b); // apel max() cu variabile printf(%d\n ", c); printf(%d\n ", max(c, 99)); // apel max() return 0;

100

INFORMATIC*I* }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// Definire functie int max(int num1, int num2) { return (num1 > num2) ? num1 : num2; }

// functia lines definit dup main, dar declarat nainte: void lines ( int); // declaraie funcie int main () { lines (3); } // utilizare funcie

void lines (int n) { for (int i=0; i<n; i++) printf("\n"); }

Prototipul implicit al unei funcii, este: int nume_functie(void); Cu alte cuvinte, n lipsa unei declaraii de tip explicite se consider c tipul implicit al funciei este int. i argumentele formale fr un tip declarat explicit sunt considerate implicit de tipul int, dar nu trebuie abuzat de aceast posibilitate. Exemplu:
rest (a,b) { //echivalent cu: int rest (int a, int b) return a%b; }

n limbajul C se pot defini i funcii cu numr variabil de argumente, care pot fi apelate cu numr diferit de argumente efective. Mai jos apar dou variante de scriere a programelor. Prima variant, n care funcia main e prezent la nceputul programului, dup prototipurile funciilor apelate, ofer o imagine a prelucrrilor realizate de program. Varianta 1: prototipuri definiia funciei definiii funcii urmtoarea convenie : Convenie C: Un nume (variabil, funcie) poate fi utilizat numai dup ce a fost declarat, iar domeniul de vizibilitate (scope) al unui nume este mulimea instruciunilor (liniilor de cod) n care poate fi utilizat acel nume (numele este vizibil). Prin urmare, avem ca regul de baz: identificatorii sunt accesibili doar n blocul de instruciuni n care au fost declarai; ei sunt necunoscui n afara acestor blocuri. 101 Varianta 2: main definiii definiia funciei main funcii IB.06.4. Domeniu vizibilitate (scope) n C/C++ de

exist

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

ntr-un program putem avea dou categorii de variabile: locale - variabilele declarate: n funcii (corpul unei functii este un bloc de instructiuni) n blocuri de instructiuni ca parametri formali. externe (globale) - variabile definite n afara funciilor. Locul unde este definit o variabil determin domeniul de vizibilitate al variabilei respective: o variabil definit ntr-un bloc poate fi folosit numai n blocul respectiv, iar variabilele cu acelai nume din funcii (blocuri) diferite sunt alocate la adrese diferite i nu nici o legtur ntre ele. Numele unei variabile este unic (nu pot exista mai multe variabile cu acelai nume), dar o variabil local poate avea numele uneia globale, caz n care, n interiorul funciei, e valabil noua semnificaie a numelui. Pentru variabilele locale memoria se aloc la activarea funciei/blocului (deci la execuie) i este eliberat la terminarea executrii funciei/blocului. Iniializarea variabilelor locale se face tot la execuie i de aceea se pot folosi expresii pentru iniializarea lor (nu numai constante). Exemple:
#include<stdio.h> #include<stdlib.h> int fact=1; // declarare varriabila externa - globala // parametru formal // declarare variabila locala

void factorial(int n) { int i; fact=1; for(i=2;i<=n;i++) fact=fact*i; } int main(void) { int v; factorial(3); printf("3!=%d\n",fact); printf("Introd o valoare:"); // utilizare variabila locala: scanf("%d",&v); factorial(v); printf("%d!=%d\n",v,fact); return 0; }

// declarare variabila locala // utilizare variabila externa

Atenie! Definiia unui identificator mascheaz pe cea a aceluiai identificator declarat ntr-un suprabloc sau n fiier (global)! Apariia unui identificator face referin la declararea sa n cel mai mic bloc care conine aceast apariie!
#include<stdio.h> #include<stdlib.h> int fact=1; void factorial(int n) { int i; int fact=1; }

// declarare variabila externa - globala // declarare variabila locala /* declarare variabila locala, acesta va fi folosita in functie mai departe! */

for(i=2;i<=n;i++) fact=fact*i;

102

INFORMATIC*I* int main(void) { int v; factorial(3); printf("3!=%d\n",fact); printf("Introd o valoare:"); scanf("%d",&v); factorial(v); printf("%d!=%d\n",v,fact); return 1; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// declarare variabila locala // utilizare variabila externa fact!!!

// utilizare variabila externa fact!!!

Atenie! Presupunnd ca valoarea introdus pentru v este 3, se va afia 3!=1 deoarece valoarea variabilei globale fact nu este modificat. Rezultatul este 1 oricare ar fi valoarea lui v !!! Observaii: Unul dintre motivele pentru care se vor evita variabile externe este acela c, din neatenie, putem defini variabile locale cu acelai nume ca variabila extern, ceea ce poate conduce la erori. Nu se recomand utilizarea de variabile externe dect n cazuri rare, cnd mai multe funcii folosesc n comun mai multe variabile i se dorete simplificarea utilizrii funciilor, sau n cadrul unor biblioteci de funcii. O funcie poate deci utiliza variabilele globale, cele locale, precum si parametrii formali. Pentru reutilizare i pentru a nu modifica accidental variabilele globale, este indicat ca o funcie s nu acceseze variabile globale. IB.06.5. Apelul unei funcii Apelul unei funcii, adic utilizarea acesteia se poate face astfel: Sintaxa:
nume_functie (lista_parametri_actuali)

/* poate apare ca operand intr-o expresie, dac funcia returneaz un rezultat */ Observaii: pentru funcie fr tip (void) apelul este: nume_funcie (lista_parametrii_efectivi); pentru funcie cu tip T, unde t este de tipul T, apelul poate fi: t = nume_funcie (lista_parametri_efectivi); sau poate apare ntr-o expresie n care se folosete rezultatul ei: if (nume_funcie (lista_parametri_efectivi) == 1) Parametrii folosii la apelul funciei se numesc parametri efectivi i pot fi orice expresii (constante, funcii etc.). Parametrii efectivi trebuie s corespund ca numr i ca tipuri (eventual prin conversie implicit ) cu parametrii formali (cu excepia unor funcii cu numr variabil de argumente). Este posibil ca tipul unui parametru efectiv s difere de tipul parametrului formal corespunztor, cu condiia ca tipurile s fie "compatibile" la atribuire. Conversia de tip (ntre numere sau pointeri) se face automat, la fel ca la atribuire: 103

INFORMATIC*I* x = sqrt(2); y = pow(2,3);

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // param. formal double, param.efectiv int // arg. formale de tip double

n cazul funciilor void fr parametri, apelul se face prin: Sintaxa:


nume_functie ( );

// instruciune expresie

La apelul unei funcii, pe stiva de apeluri (pstrat de Sistemul de Operare) se creaz o nregistrare de activare, care cuprinde, de jos n sus: adresa de revenire din funcie valorile parametrilor formali valorile variabilelor locale. n momentul apelrii unei funcii, se execut corpul su, dup care se revine n funcia apelant, la instruciunea urmtoare apelului. Revenirea dintr-o funcie se face fie la ntlnirea instruciunii return, fie la terminarea execuiei corpului funciei (funcia poate s nu conin instruciunea return doar n cazul funciilor void). Exemple - apelul funciilor putere i prim: 1. S se calculeze i s se afieze valoarea expresiei x m+yn+(xy)m^n , x, y, m, n fiind citii de la tastatur, astfel nct ntregii m,n s fie pozitivi. Ridicarea la putere se va realiza printr-o funcie putere care primete baza i exponentul ca parametri i returneaz rezultatul vezi definirea unei funcii. Modul de apel al funciei putere:
int main(void){ int m,n; double x,y; while( printf(" m(>=0):"), scanf("%d",&m), m<0); while( printf(" n(>=0):"), scanf("%d",&n), n<0); if ( m==0 && n==0 ) { //semnalare eroare 0^0 puts("0^0 imposibil"); return 0; // din main } printf("valorile lui x,y:"); scanf("%lf%lf",&x,&y); printf("%lf^%d+%lf^%d+(%lf*%lf)^%d^%d (cu putere ):%lf\n", x, m, y, n, x, y, m, n, putere(x,m)+putere(y,n) + putere(x*y,putere(m,n))); // apel functie putere scrisa de utilizator printf("Rezultat cu pow: %lf\n", pow(x,m)+pow(y,n)+pow(x*y,pow(m,n))); // apel functie pow din biblioteca math return 0; }

2.

Numrarea i afiarea numerelor prime mai mici ca un ntreg dat n. Pentru aceasta vom scrie o funcie care testeaz dac un numr este prim vezi definirea unei funcii. Modul n care se va apela (folosi) aceasta funcie:

int main () { int n,m,contor ; /* contor de numere prime*/ printf ("n= "); scanf ("%d",&n); contor=0; for (m=2;m<=n;m++) if ( prim(m) == 1 ){

// incearca numerele m // dac m este prim

104

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR printf ("%d\n",m); contor++;

} printf ("exist return 0; } %d numere prime mai mici decat %d\n", contor,n);

IB.06.6. Instruciunea return n corpul funciei se poate folosi instruciunea return pentru a returna o valoare funciei apelante (o valoare corespunztoare antetului funciei). Forme ale instruciunii return: Sintaxa: return; //dac funcia e void return expresie; Expresia e de acelai tip cu tip_rezultat al funciei(eventual prin conversie implicit). Corpul unei funcii poate conine una sau mai multe instruciuni return. n corpul unei funcii de tip void, se poate folosi instruciunea return - fr o valoare returnat - pentru a reda controlul apelantului, ns n acest caz, al funciilor void, utilizarea lui return este opional; dac lipsete, se adaug automat return ca ultim instruciune. n funcia main instruciunea return are ca efect terminarea ntregului program. Exemplu de program cu dou funcii:
#include <stdio.h> void clear () { int i; for (i=0; i<24; i++) putchar('\n'); } int main(){ clear(); } // terge ecran prin defilare // variabila locala funciei clear

// apel funcie

Observaii: O funcie de un tip diferit de void trebuie s conin cel puin o instruciune return prin care se transmite rezultatul funciei:
// factorial de n long fact (int n) { long nf=1L; while (n) nf=nf * n--; return nf; }

// initializare rezultat // echivalent cu: nf=nf * n; n=n-1; // rezultat funcie

Compilatoarele C nu verific i nu semnaleaz dac o funcie de un tip diferit de void conine sau nu instruciuni return, iar eroarea se manifest la execuie. Cuvntul else dup o instruciune return poate lipsi, dar de multe ori este prezent pentru a face codul mai clar. Exemplu fr else:
// transforma caracterul din variabila c in liter mare char toupper (char c) { if (c>='a' && c<='z') // daca c este litera mica

105

INFORMATIC*I* return c+'A'-'a'; return c; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // returneaz cod litera mare // ramane neschimbat

Instruciunea return poate fi folosit pentru ieirea forat dintr-un ciclu i din funcie, cu reducerea lungimii codului surs. Exemplu de funcie care verific dac un numr dat este prim:
int prim (int numar){ int div; for(div=2; div<=sqrt(numar); div++) if (numar % div == 0) return 0; return 1; }

IB.06.7. Transmiterea parametrilor n C, transmiterea parametrilor se face prin valoare: parametrii actuali sunt transmii prin valoare, la apelul funciei (valorile lor sunt depuse pe stiva program iar apoi copiate n parametrii formali. Modificarea valorii lor de ctre funcie nu este vizibil n exterior! Urmeaz un exemplu ce pune n eviden modul de transmitere a parametrilor prin valoare:

Observaii: Dac se dorete ca o funcie s modifice valoarea unei variabile, trebuie s i se transmit pointerul la variabila respectiv - vezi Pointeri! 106

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Dac parametrul este un tablou, cum numele este echivalent cu pointerul la tablou, funcia poate modifica valorile elementelor tabloului, primind adresa lui. A se observa c trebuie s se transmit ca parametri i dimensiunea/ dimensiunile tabloului. Dac parametrul este ir de caractere, dimensiunea tabloului de caractere nu trebuie s se transmit, sfritul irului fiind indicat de caracterul terminator '\0'. IB.06.8. Funcii cu argumente vectori Pentru argumentele formale de tip vector nu trebuie specificat dimensiunea vectorului ntre parantezele drepte, oricum aceasta va fi ignorat. Exemplu:
// calcul valoare maxima dintr-un vector: float maxim (float a[], int n ) { float max=a[0]; for (int k=1;k<n;k++) if ( max < a[k]) max=a[k]; return max; } // exemplu de utilizare - in main: float xmax, x[]= {3,6,2,4,1,5,3}; xmax = maxim(x,7);

Un argument formal vector poate fi declarat i ca pointer, deoarece are ca valoare adresa de nceput a zonei unde este memorat vectorul. Exemplu: float maxim (float *a, int n ) { ... } De remarcat c pentru un parametru efectiv vector nu mai trebuie specificat explicit c este un vector, deoarece exist undeva o declaraie pentru variabila respectiv, care stabilete tipul ei. Este chiar greit sintactic s se scrie: xmax = maxim ( x[ ], 7 ) ; // NU !

O funcie C nu poate avea ca rezultat direct un vector, dar poate modifica elemen tele unui vector primit ca parametru. Exemplu de funcie pentru ordonarea unui vector prin metoda bulelor Bubble Sort:
void sort (float a[ ], int n) { int n,i,j, gata; float aux; do { gata = 1;// presupunem initial ca nu sunt necesare schimbari de elemente for (i=0;i<n-1;i++) // compara n-1 perechi de elemente vecine if ( a[i] > a[i+1] ) {// daca nu sunt in ordine crescatoare aux=a[i]; a[i]=a[i+1]; a[i+1]=aux; // am interschimbat a[i] cu a[i+1] gata =0; // seteaza ca s-a facut o schimbare } } while (!gata); // repeta cat timp au mai fost schimbari de elemente }

107

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Probleme pot apare la parametrii efectivi de tip matrice din cauza interpretrii diferite a zonei ce conine elementele matricei de ctre funcia apelat i respectiv de funcia apelant. Pentru a interpreta la fel matricea liniarizat este important ca cele dou funcii s foloseasc acelai numr de coloane n formula de liniarizare. Din acest motiv nu este permis absena numrului de coloane din declaraia unui parametru formal matrice. Exemple: void printmat(int a[][10], int nl, int nc); void printmat(int a[][], int nl, int nc); // corect, cu nc <= 10 // greit !

O alt soluie la problema parametrilor matrice este utilizarea de matrice alocate dinamic i transmise sub forma unui pointer. O sintez a transmiterii parametrilor de tip tablou este dat n cele ce urmeaz: Parametru formal tip_baza nume_vector[] tip_baza nume_ vector[dim] tip_baza nume_ matrice[][dim2] //dim2 trebuie sa apara tip_baza nume_ matrice[dim1][dim2] IB.06.9. Funcii recursive n continuare se va prezenta conceptul de recursivitate i cteva exemple de algoritmi recursivi, pentru a putea nelege mai bine aceast tehnic puternic de programare, ce permite scrierea unor soluii clare, concise i rapide, care pot fi uor nelese i verificate. IB.06.9.1 Ce este recursivitatea ? Un obiect sau un fenomen se definete n mod recursiv dac n definiia sa exist o referire la el nsui. Recursivitatea este folosit cu multa eficien n matematic. Spre exemplu, defintii matematice recursive sunt: Definiia numerelor naturale: 0N dac i N, atunci succesorul lui i N Definiia funciei factorial fact : N -> N fact (n) = 1, daca n=0 n * fact (n-1), daca n>0 Definiia funciei Ackermann ac: N x N -> N n+1, dac m=0 ac(m,n) = ac(m-1,1), dac n=0 ac(m-1, ac(m,n-1) ), dac m, n >0 Definiia funciei Fibonacci 108 Parametru actual nume_vector

nume_matrice

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

fib : N -> N fib(n) = 1, dac n=0 sau n=1 fib(n-2) + fib(n-1), dac n>1 Utilitatea practic a recursivitii rezult din posibilitatea de a defini un set infinit de obiecte printro singur relaie sau printr-un set finit de relaii. Recursivitatea s-a impus n programare odat cu apariia unor limbaje de nivel nalt, ce permit scrierea de module ce se autoapeleaz (PASCAL, LISP, ADA, ALGOL, C sunt limbaje recursive, spre deosebire de FORTRAN, BASIC, COBOL, nerecursive). Recursivitatea este strns legat de iteraie. Iteraia este execuia repetat a unei poriuni de program, pn la ndeplinirea unei condiii (exemple: while, do-while, for din C). Recursivitatea presupune execuia repetat a unui modul, ns n cursul execuiei lui (i nu la sfrit, ca n cazul iteraiei), se verific o condiie, a crei nesatisfacere implic reluarea execuiei modulului de la nceput, fr ca execuia curent s se fi terminat. n momentul satisfacerii condiiei se revine n ordine invers din lanul de apeluri, relundu-se i ncheindu-se apelurile suspendate. Un program recursiv poate fi exprimat: P = M ( Si , P) , unde M este mulimea ce conine instruciunile Si i pe P nsui. Structurile de program necesare i suficiente n exprimarea recursivitii sunt funciile: Definiie: O funcie recursiv este o funcie care se apeleaz pe ea nsi, direct sau indirect. Recursivitatea poate fi direct - o funcie P conine o referin la ea nsi, sau indirect - o funcie P conine o referin la o funcie Q ce include o referin la P. Vom pune accentul mai ales pe funciile direct recursive. Se pot deosebi dou feluri de funcii recursive: Funcii cu un singur apel recursiv, ca ultim instruciune, care se pot rescrie uor sub forma nerecursiv (iterativ). Funcii cu unul sau mai multe apeluri recursive, a cror form iterativ trebuie s foloseasc o stiv pentru memorarea unor rezultate intermediare. Recursivitatea este posibil n C datorit faptului c, la fiecare apel al funciei, adresa de revenire, variabilele locale i argumentele formale sunt puse ntr-o stiv (gestionat de compilator), iar la ieirea din funcie (prin return) se scot din stiv toate datele puse la intrarea n funcie (se "descarc" stiva). Exemplu generic:
void p(){ //functie recursiva p(); //apel infinit } //apelul trebuie conditionat in una din variantele: if(cond) p(); while(cond) p(); do p() while(cond);

Exemplu de funcie recursiv de tip void:

109

INFORMATIC*I* void binar (int n) { if (n>0) { binar(n/2); printf("%d",n%2); } }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // se afieaza n in binar // scrie echiv. binar al lui n/2 // i restul impartirii n la 2

Funcia de mai sus nu scrie nimic pentru n=0, dar poate fi uor completat cu o ramur else la instruciunea if. Apelul recursiv al unei funcii trebuie s fie condiionat de o decizie care s mpiedice apelul n cascad (la infinit ); aceasta ar duce la o eroare de program - depirea stivei. Orice funcie recursiv trebuie s conin cel puin o instruciune if, plasat de obicei chiar la nceput! Prin aceast instruciune se verific dac mai este necesar un apel recursiv sau se iese din funcie. Absena instruciunii if conduce la o recursivitate infinit (la un ciclu fr condiie de terminare)! Pentru funciile de tip diferit de void apelul recursiv se face printr-o instruciune return, prin care fiecare apel preia rezultatul apelului anterior! Anumite funcii recursive corespund unor relaii de recuren. Exemple:
// Calculul an: double putere (double a, int n) { if (n==0) return 1.; else return a * putere(a, n-1); } // a0 = 1 // an=a*an-1

// Algoritmul lui Euclid poate folosi o relaie de recuren: int cmmdc (int a,int b) { if ( a%b==0) return b; return cmmdc( b,a%b); // cmmdc(a,b)=cmmdc(b,a%b) }

Observaii: Funciile recursive nu conin n general cicluri explicite (cu unele excepii), iar repetarea operaiilor este obinut prin apelul recursiv. O funcie care conine un singur apel recursiv ca ultim instruciune poate fi transformat ntr-o funcie nerecursiv, nlocuind instruciunea if cu while. Fiecare apel recursiv are parametri diferii, sau mcar o parte din parametri se modific de la un apel la altul. Se pune ntrebarea: "Ce este mai indicat de utilizat: recursivitatea sau iteraia?" Algoritmii recursivi sunt potrivii pentru a descrie probleme care utilizeaz formule recursive sau pentru prelucrarea structurilor de date definite recursiv ( liste, arbori ), fiind mai elegani i mai simplu de neles i verificat. Iteraia este uneori preferat din cauza vitezei mai mari de execuie i a memoriei necesare la execuie mai reduse. Spre exemplu, varianta recursiv a funciei Fibonacci duce la 15 apeluri pentru n=5, deci varianta iterativ este mult mai performant. Apelul recursiv al unei funcii face ca pentru toi parametrii s se creeze cop ii locale apelului curent (n stiv) , acestea fiind referite i asupra lor fcndu-se modificrile n timpul execuiei curente a funciei. Cnd execuia funciei se termin, copiile sunt extrase din stiv, astfel nct 110

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

modificrile operate asupra parametrilor nu afecteaz parametrii efectivi de apel, corespunztori. De asemenea, pentru toate variabilele locale se rezerv spaiu la fiecare apel recursiv. Pe parcursul unui apel, sunt accesibile doar variabilele locale i parametrii pentru apelul respectiv, nu i cele pentru apelurile anterioare, chiar dac acestea poart acelai nume! De reinut c, pentru fiecare apel recursiv al unei funcii se creaz copii locale ale parametrilor valoare i ale variabilelor locale, ceea ce poate duce la risipa de memorie. IB.06.9.2 Verificarea i simularea programelor recursive Se face ca i n cazul celor nerecursive, printr-o demonstraie formal, sau testnd toate cazurile posibile: Se verific nti dac toate cazurile particulare (ce se execut cnd se ndeplinete condiia de terminare a apelului recursiv) funcioneaz corect. Se face apoi o verificare a funciei recursive, pentru restul cazurilor, presupunnd c toate componentele din codul funciei funcioneaz corect. Verificarea e deci inductiv. Acesta e un avantaj al programelor recursive, ce permite demonstrarea corectitudinii lor simplu i clar. Exemplu: Funcia recursiv de calcul a factorialului
//varianta recursiva int fact(int n){ if(n==1) return 1; return n*fact(n-1); //apel recursiv } //varianta nerecursiva int fact(int n){ int i,f; for(i=f=1; i<=n; i++) f*=i; return f; } Verificarea n acest caz cuprinde doi pai: // Obs: corectitudinii tipul functiei trebuie sa devina long pentru n>=8 (8!>32767) 1. pentru n=1 valoarea 1 ce se atribuie factorialului este corect 2. pentru n>1, presupunnd corect valoarea calculat pentru predecesorul lui n de

ctre fact(n-

1), prin nmulirea acesteia cu n se obine valoarea corect a factorialului lui n. IB.06.9.3 Utilizarea recursivitatii n implementarea algoritmilor de tip Divide et Impera Tehnica Divide et Impera, fundamental n elaborarea algoritmilor, const n descompunerea unei probleme complexe n mai multe subprobleme a cror rezolvare e mai simpl i din solu iile crora se poate determina soluia problemei iniiale (exemple: gsirea minimului si maximului valorilor elementelor unui tablou, cutarea binar, sortare Quicksort, turnurile din Hanoi). Un algoritm de divizare general s-ar putea scrie:

111

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

void rezolva ( problema pentru x ) { dac x e divizibil in subprobleme { divide pe x in parti x1,...,xk rezolva(x1); ... rezolva(xk); combina solutiile partiale intr-o solutie pentru x } altfel rezolva pe x direct } Vom oferi ca exemplu cutarea binar a unei valori v ntr-un vector ordonat a, prin metoda njumtirii intervalului de cutare. Se returneaz indicele n vector sau -1 dac valoarea v nu se afl n vector. Pentru o mai bun nelegere, prima variant este cea iterativ.

// varianta iterative, l- limita stanga, r limita dreapta a intervalului int bsearchIter(int v, int *a, int l, int r) { while(l<r) { int m = (l+r)/2; // m mijlocul intervalului if (v == a[m]) return m; // element gasit if (v > a[m]) l=m+1; // reduc intervalul la jumatatea lui dreapta else r=m; // reduc intervalul la jumatatea lui stanga } return -1; // element negasit } // varianta recursiva int bsearchRec (int v, int *a,int l, int r) { int m; if (l<r) { int m = (l+r)/2; if (v == a[l]) return l; else if ( v>a[m] ) return bsearchRec(v, a, m+1, r); else return bsearchRec(v, a, l, m); } else return -1; } int main() { int v, a[N], n; // citire date intrare printf(Elem. %d se afla in poz %d\n ", v, bsearchRec(v,a,0,n)); printf(Elem. %d se afla in poz %d\n ", v, bsearchIter(v,a,0,n)); return 0; }

IB.06.10. Anex: Funcii n C++ n C++ toate funciile folosite trebuie declarate i nu se mai consider c o funcie nedeclarat este implicit de tipul int. 112

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Absena unei declaraii de funcii este eroare grav n C++ i nu doar avertisment ca n C (nu trece de compilare)! n C++ se pot declara valori implicite pentru parametrii formali de la sfritul listei de parametri; aceste valori sunt folosite automat n absena parametrilor efectivi corespunztori la un apel de funcie. O astfel de funcie poate fi apelat deci cu un numr variabil de parametri. Exemplu:
// afiare vector, precedat de un titlu (irul primit sau irul nul) void printv ( int v[], int n, char * titlu="") { printf ("\n %s \n", titlu); for (int i=0; i<n; i++) printf ("%d ", v[i]); } // Exemple de apeluri: printv ( x,nx ); printv (a,na," multimea A este"); // cu 2 parametri // cu 3 parametri

n C++ funciile scurte pot fi declarate inline, iar compilatorul nlocuiete apelul unei funcii inline cu instruciunile din definiia funciei, eliminnd secvenele de transmitere a parametrilor. Funciile inline sunt tratate ca i macrourile definite cu define. Orice funcie poate fi declarat inline, dar compilatorul poate decide c anumite funcii nu pot fi tratate inline i sunt tratate ca funcii obinuite. De exemplu, funciile care conin cicluri nu pot fi inline. Utilizarea unei funcii inline nu se deosebete de aceea a unei funcii normale. Exemplu de funcie inline:
inline int max (int a, int b) { return a > b ? a : b; }

n C++ pot exista mai multe funcii cu acelai nume dar cu parametri diferii (ca tip sau ca numr). Se spune c un nume este suprancrcat cu semnificaii ("function overloading"). Compilatorul poate stabili care din funciile cu acelai nume a fost apelat ntr-un loc analiznd lista de parametri i tipul funciei:
float abs (float f) { return fabs(f); } long abs (long x) { return labs(x); } printf ("%6d%12ld %f \n", abs(-2),abs(-2L),abs(-2.5) );

113

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.07. Pointeri. Pointeri i tablouri. Pointeri i funcii


Cuvinte cheie
Pointer, refereniere, derefereniere, indirectare, vectori i pointeri, tablouri ca argumente ale funciilor, pointeri n funcii, pointeri la funcii, funcii generice, tipul referin

IB.07.1. Pointeri Pointerii reprezint cea mai puternic caracteristic a limbajului C/C++, permind programatorului s acceseze direct coninutul memoriei, pentru a eficientiza astfel gestiunea memoriei; programul i datele sale sunt pstrate n memoria RAM ( Random Access Memory ) a calculatorului. n acelai timp, pointerii reprezint i cel mai complex i mai dificil subiect al limbajului C/C++, tocmai datorit libertilor oferite de limbaj n utilizarea lor. Prin utilizarea corect a pointerilor se paote mbunti drastic eficiena i performanele programului. Pe de alt parte, utilizarea lor incorect duce la apariia multor probleme, de la cod greu de citit i de ntreinut la greeli penibile de genul pierderi de memorie sau depirea unei zone de date. Utilizarea incorect a pointerilor poate expune programul atacurilor externe (hacking). Multe limbaje noi (Java, C#) au eliminat pointerii din sintaxa lor pentru a evita neplcerile cauzate de acetia. O locaie de memorie are o adres i un coninut. Pe de alt parte, o variabil este o locaie de memorie care are asociat un nume i care poate stoca o valoare de un tip particular. n mod normal, fiecare adres poate pstra 8 bii (1 octet) de date. Un ntreg reprezentat pe 4 octei ocup 4 locaii de memorie. Un sistem pe 32 de bii folosete n mod normal adrese pe 32 de bii. Pentru a stoca adrese pe 32 de bii sunt necesare 4 locaii de memorie. Definiie: O variabil pointer (pe scurt vom spune un pointer) este o variabil care pstreaz adresa unei date, nu valoarea datei. Cu alte cuvinte, o variabil pointer este o variabil care are ca valori adrese de memorie. Aceste adrese pot fi: Adresa unei valori de un anumit tip (pointer la date) Adresa unei funcii (pointer la o funcie) Adresa unei zone cu coninut necunoscut (pointer la void).

n figura urmtoare, s-a reprezentat grafic un pointer ptrNr care este un pointer la o variabil nr (de tip int); cu alte cuvinte, ptrNr este o variabil pointer ce stocheaz adresa lui nr. n general, vom reprezenta grafic pointerii prin sgei.

ptrNr ( int * ptrNr; )

nr ( int nr=88; ) 88

Un pointer poate fi utilizat pentru referirea diferitelor date i structuri de date, cel mai frecvent folosindu-se pointerii la date. Schimbnd adresa memorat n pointer, pot fi manipulate informaii 114

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

situate la diferite locaii de memorie, programul i datele sale fiind pstrate n memoria RAM a calculatorului. Dei adresele de memorie sunt de multe ori numere ntregi pozitive, tipurile pointer sunt diferite de tipurile ntregi i au utilizri diferite. Unei variabile pointer i se pot atribui constante ntregi ce reprezint adrese, dup conversie . n limbajul C tipurile pointer se folosesc n principal pentru: declararea i utilizarea de vectori, mai ales pentru vectori ce conin iruri de caractere; parametri de funcii prin care se transmit rezultate (adresele unor variabile din afara funciei); acces la zone de memorie alocate dinamic i care nu pot fi adresate printr-un nume; parametri de funcii prin care se transmit adresele altor funcii. IB.07.2. Declararea pointerilor Ca orice variabil, pointerii trebuie declarai nainte de a putea fi utilizai. n sintaxa declarrii unui pointer se folosete caracterul * naintea numelui pointerului. Declararea unei variabile (sau parametru formal) de un tip pointer include declararea tipului datelor (sau funciei) la care se refer acel pointer. Sintaxa declarrii unui pointer la o valoare de tipul tip este: Sintaxa: tip * ptr; tip* ptr; tip *ptr; // sau //sau

Reprezentarea grafic corespunztoare acestei declaraii este urmtoarea:

ptr ( int * ptr; )

Exemple de variabile i parametri pointer:


int * pi; // void * p; // int * * pp; // char* str; // str - adresa unui pi - adresa unui intreg sau vector de int p - adresa de memorie pp - adresa unui pointer la un intreg ir de caractere

Atunci cnd se declar mai multe variabile pointer de acelai tip, nu trebuie omis asteriscul care arat c este un pointer. Exemple:
int *p, m; int *a, *b ; // m de tip "int", p de tip "int *" // a i b de tip pointer

Convenia de nume pentru pointeri sugereaz s se pun un prefix sau sufix cu valoarea "p" sau "ptr". Exemplu: iPtr, numarPtr, pNumar, pStudent. Dac se declar un tip pointer cu typedef atunci se poate scrie astfel:

115

INFORMATIC*I* typedef int* intptr; intptr p1, p2, p3;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // intptr este nume de tip // p1, p2, p3 sunt pointeri

Tipul unei variabile pointer este important pentru c determin ci octei vor fi folosii de la adresa coninut n variabila pointer i cum vor fi interpretai. Un pointer la void nu poate fi utilizat pentru a obtine date de la adresa din pointer, deoarece nu se tie ci octei trebuie folosii i cum. Exist o singur constant de tip pointer, cu numele NULL i valoare zero, care este compatibil la atribuire i comparare cu orice tip pointer. Observaii: Totui, se poate atribui o constant ntreag convertit la un tip pointer unei variabile pointer:
char * p = (char*)10000; // o adresa de memorie

IB.07.3. Operaii cu pointeri la date IB.07.3.1 Iniializarea pointerilor, operaia de refereniere (&) Atunci cnd declarm un pointer, el nu este iniializat. Cu alte cuvinte, el are o valoare oarecare ce reprezint o adres a unei locaii de memorie oarecare despre care bineneles c nu tim dac este valid (acest lucru este foarte periculos, pentru c poate fi de exemplu adresa unei alte variabile!). Trebuie s iniializm pointerul atribuindu-i o adres valid. Aceasta se poate face n general folosind operatorul de refereniere - de luare a adresei - (&). Sintaxa: Operatorul unar & aplicat unei variabile are ca rezultat adresa variabilei respective (deci un pointer). De exemplu, dac nr este o variabil de tip int, &nr returneaz adresa lui nr. Aceast adres o putem atribui unei variabile pointer:
int number = 88; // o variabila int cu valoarea 88 int *pNumber; // declaratia unui pointer la un intreg pNumber = &number; // atribuie pointerului adresa variabilei int pNumber int *pAnother = &number; /* declaratia unui pointer la un intreg si initializare cu adresa variabilei int */

n figur, variabila number, memorat ncepnd cu adresa 0x22ccec, conine valoarea ntreag 88. Expresia &number returneaz adresa variabilei number, adres care este 0x22ccec. Aceast adres este atribuit pointerului pNumber, ca valoare a sa iniial, dar i pointerului pAnother, ca urmare cei doi pointeri vor indica aceeai celul de memorie!

116

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Nume: pNumber( int *) Adresa: 0x?????? 0x22ccec (&number)


O variabila pointer la int ce contine adresa de memorie a unei valori int

Nume: number( int) Adresa:0x22ccec (&number) 88


O variabila int ce contine o valoare de tip int

Nume: pAnother( int *) Adresa: 0x?????? 0x22ccec (&number)


O variabila pointer la int ce contine adresa de memorie a unei valori int

Exemplu:
int **ptrPtrA, *ptrA, a=1; // ptrPtrA pointer la un pointer la un intreg // ptrA - pointer la un intreg // a - variabila int cu valoarea 1 ptrA = &a; // atribuie pointerului ptrA adresa variabilei int a ptrPtrA = & ptrA; /* atribuie pointerului ptrPtrA adresa variabilei pointer la int ptrA */

n figur, variabila a, memorat ncepnd cu adresa 0x22ccec, conine valoarea ntreag 88. Expresia &a returneaz adresa variabilei a, adres care este 0x22ccec. Aceast adres este atribuit pointerului ptrA, ca valoare a sa iniial, iar pointerul ptrPtrA va avea ca valoare adresa pointerului ptrA! Nume: ptrPtrA (int **) Adresa: 0x?????? 0x22cdea (&ptrA)
O variabila pointer la int* ce contine adresa de memoriea unui int pointer

Nume: ptrA (int *) Adresa: 0x22cdea 0x22ccec (&a)


O variabila pointer la int ce contine adresa de memorie a unei valori int

Nume: a (int) Adresa: (&number) 88

0x22ccec

O variabila int ce contine o valoarea int

IB.07.3. 2 Indirectarea sau operaia de derefereniere(*) Indirectarea printr-un pointer (diferit de void *), pentru acces la datele adresate de acel pointer, se face prin utilizarea operatorul unar *, operator de derefereniere (indirectare).

117

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Sintaxa: Operatorul unar * - operator de derefereniere (indirectare) - returneaz valoarea pstrat la adresa indicat de pointer.

Exemple:
int *p, m; m=*p; // p poinetr la int, m variabila int // m ia valoarea indicata de pointerul p

int number = 88; int *pNumber = &number; /* Declara i atribuie adresa variabilei number pointer-ului pNumber (acesta poate fi de exemplu 0x22ccec)*/ printf(%p\n, pNumber); // Afiseaza pointerul (0x22ccec) printf(%d\n, *pNumber); /* Afiseaza valoarea indicata de pointer, valoare care este de tip int (88)*/ *pNumber = 99; /* Atribuie o valoare care va fi stocata la adresa indicata de pointer. Atentie! NU variabilei pointer!*/ printf(%d\n, *pNumber); /* Afiseaza noua valoare indicata de pointer, 99*/ printf(%d\n, number); /* Valoarea variabilei number s-a schimbat de asemenea (99)*/

stocheaz adresa unei locaii de memorie; *pNumber se refer la valoarea pstrat la adresa indicat de pointer, sau altfel spus la valoarea indicat de pointer:
pNumber

Nume: pNumber( int *) Adresa: 0x?????? 0x22ccec (&number)


O variabila pointer la int ce contine adresa de memorie a unei valori int

Nume: number( int) Adresa: 0x22ccec (&number) 88 99


O variabila int ce contine o valoarea int

Putem spune c o variabil face referire direct la o valoare, n timp ce un pointer face referire indirect la o valoare, prin adresa de memorie pe care o stocheaz. Referirea unei valori n mod indirect printr-un pointer se numete indirectare. Observaie: Simbolul * are nelesuri diferite. Atunci cnd este folosit ntr-o declaraie (int *pNumber), el denot c numele care i urmeaz este o variabil de tip pointer. n timp ce, atunci cnd este folosit ntr-o expresie/instruciune (ex. *pNumber = 99; printf(%d\n, *pNumber); ), se refer la valoarea indicat de variabila pointer. IB.07.3.3 Operaia de atribuire n partea dreapt poate fi un pointer de acelai tip (eventual cu conversie de tip), constanta NULL sau o expresie cu rezultat pointer. Exemple:
int *p, *q=NULL; float x=1.23; p=q; p=&x;

118

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Unei variabile de tip void* i se poate atribui orice alt tip de pointer fr conversie de tip explicit i un argument formal de tip void* poate fi nlocuit cu un argument efectiv de orice tip pointer. Atribuirea ntre alte tipuri pointer se poate face numai cu conversie de tip exp licit (cast) i permite interpretarea diferit a unor date din memorie. De exemplu, putem extrage cei doi octei dintr-un ntreg scurt astfel:
short n; char * p = (char*) &n; c1= *p; c2 = *(p+1);

IB.07.3.4 Operaii de comparaie Compararea a doi pointeri (operaii relaionale cu pointeri) se poate face utiliznd operatorii cunoscui: == != < > <= >= IB.07.3.5 Aritmetica pointerilor Adunarea sau scderea unui ntreg la (din) un pointer, incrementarea i decrementarea unui pointer se pot face astfel: Sintaxa: p++; p--; p=p+c; p=p-c; Exemplu:
void printVector( int a[], int n) { while (n--) printf (%d , *a++); } // afiarea unui vector

p=p+sizeof(tip); p=p-sizeof(tip); p=p+c*sizeof(tip); p=p-c*sizeof(tip);

Trebuie observat c incrementarea unui pointer i adunarea unui ntreg la un pointer nu adun ntotdeauna ntregul 1 la adresa coninut n pointer; valoarea adugat (sczut) depinde de tipul variabilei pointer i este egal cu produsul dintre constant i numrul de octei ocupat de tipul adresat de pointer. Aceast convenie permite referirea simpl la elemente succesive dintr-un vector folosind indirectarea printr-o variabil pointer. O alt operaie este cea de scdere a dou variabile pointer de acelai tip (de obicei adrese de elemente dintr-un acelai vector), obinndu-se astfel distana dintre dou adrese, atenie, nu n octei ci n blocuri de octei, n funcie de tipul pointerului. Exemplu de funcie care ntoarce indicele n irul s1 a irului s2 sau un numr negativ dac s1 nu conine pe s2:
int pos ( char* s1, char * s2) { char * p =strstr(s1,s2); //p va fi adresa la care se gsete s2 in s1 if (p) return p-s1; else return -1; }

IB.07.3.6 Dimensiunea Spaiul ocupat de o variabil pointer se determin utiliznd operatorul sizeof: Valoarea expresiei este 2 (n modelul small); oricare ar fi tip_referit, expresiile de mai jos conduc la aceeai valoare 2: 119

INFORMATIC*I* sizeof( &var ) sizeof( tip_referit * )

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.07.3.7 Afiarea unui pointer Tiprirea valorii unui pointer se face folosind funcia printf cu formatul %p, valoarea aprnd sub forma unui numr n hexa. Observaii: Adresa unei variabile pointer este un pointer, la fel ca adresa unei variabile de orice alt tip:
&var_pointer

Un pointer este asociat cu un tip i poate conine doar o adres de tipul specificat.
int i = 88; double d = 55.66; int *iPtr = &i; // pointer int ce contine adresa unei variabile int double *dPtr = &d; // pointer double ce indica spre o valoare double iPtr = &d; dPtr = &i; iPtr = i; int j = 99; iPtr = &j; // EROARE, nu poate contine o adresa de alt tip // EROARE, nu poate contine o adresa de alt tip /* EROARE, pointerul pastreaza adresa unui int, NU o valoare int */ // putem schimba adresa continuta de un pointer

O eroare frecvent este utilizarea unei variabile pointer care nu a primit o valoare (adic o adres de memorie) prin atribuire sau prin iniializare la declarare. Iniializarea unui pointer se face prin atribuirea adresei unei variabile, prin alocare dinamic, sau ca rezultat al executrii unei funcii. Compilatorul nu genereaz eroare sau avertizare pentru astfel de greeli. Exemple incorecte:
int * a; // declarata dar neiniializata !! scanf ("%d",a) ; // citete la adresa coninuta in variabila a int *iPtr; // declarata dar neiniializata!! *iPtr = 55; print("%d\n",*iPtr);

Putem iniializa un pointer cu valoarea 0 sau NULL, aceasta nsemnnd c nu indic nicio adres pointer null. Dereferenierea unui pointer nul duce la o excepie de genul STATUS_ACCESS_VIOLATION, i un mesaj de eroare segmentation fault, eroare foarte des ntlnit!
int *iPtr = 0; print("%d\n",*iPtr); int *p = NULL; // Declara un pointer int si-l initializeaza cu 0 // EROARE! STATUS_ACCESS_VIOLATION! // declara tot un pointer NULL

Iniializarea unui pointer cu NULL la declarare este o practic bun, deoarece elimin posibilitatea uitrii iniializrii cu o valoare valid! void * nseamn un pointer de tip neprecizat i utilizarea acestui tip de pointeri ne permite pstrarea gradului de generalitate al unui program la maximum. Atenie ns: Nu putem face operaii aritmetice asupra acestor pointeri sau asupra pointerului nul. Exemplu:
/* Test pentru declarare si utilizare pointeri */ int number = 88; // number - intreg cu valoarea initiala 88 int *pNumber = &number; /* Declara i atribuie adresa variabilei number pointer-ului pNumber (acesta poate fi de exemplu 0x22ccec)*/ printf(%p\n, pNumber); // Afiseaza pointerul (0x22ccec)

120

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf(%p\n, &number); // Afiseaza adresa lui number (0x22ccec) printf(%d\n, *pNumber); /* Afiseaza valoarea indicata de pointer, valoare care este de tip int (88)*/ *pNumber = 99; /* Atribuie o valoare care va fi stocata la adresa indicata de pointer. Atentie! NU variabilei pointer!*/ printf(%p\n, pNumber); // Afiseaza pointerul (0x22ccec) printf(%p\n, &number); // Afiseaza adresa lui number (0x22ccec) printf(%d\n, *pNumber); // Afiseaza noua valoare indicata de pointer 99 printf(%d\n, number); /*Valoarea variabilei number s-a schimbat de asemenea (99)*/ printf(%p\n, &pNumber); //Afiseaza adresa pointerului pNumber 0x22ccf0

Nume: pNumber( int *) Adresa: 0x?????? 0x22ccec (&number)


O variabila pointer la int ce contine adresa de memorie a unei valori int

Nume: number( int) Adresa:0x22ccec (&number) 88 99


O variabila int ce contine o valoarea int

Not: Valoarea pe care o vei obine pentru adres este foarte puin probabil s fie cea din acest exemplu!

Exemplu:
int *p, n=5, m; p=&n; m=*p; // m este 5 m=*p+1; // m este 6 int *p; float x=1.23, y; p=&x; y=*p;

// valoare eronata pentru y!

int *a,**b, c=1, d; a=&c; b=&a; d=**b; // d este 1

IB.07.4 Vectori i pointeri Convenie! Numele unui tablou este un pointer constant spre primul element (index 0) din tablou. Cu alte cuvinte, o variabil de tip tablou conine adresa de nceput a acestuia (adresa primei componente) i de aceea este echivalent cu un pointer la tipul elementelor tabloului. Aceasta echivalen este utilizat de obicei n argumentele de tip tablou i n lucrul cu tablouri alocate dinamic. Expresiile de mai jos sunt deci echivalente:

121

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

nume_tablou &nume_tablou &nume_tablou[0] i: *nume_tablou nume_tablou[0] *( nume_tablou +i) nume_tablou [i] n concluzie, exist urmtoarele echivalene de notaie pentru un vector a: a[0] &a[0] a[1] &a[1] a[k] &a[k] *a a *(a+1) a+1 *(a+k) a+k

Declaraii echivalente pentru tablouri: tip v [dim1] [dim2][dimn]; tip **v;

Exemple:
int v[10]; // vector cu dimensiune fix int *v=(int *)malloc(10*sizeof(int)); // vector alocat dinamic // Referire elemente pentru ambele variante de declarare: v[i] // sau: *(v+i) int i; double v[100], x, *p; p=&v[0]; // corect, neelegant p=v; x=v[5]; x=*(v+5); v++; // incorect p++; //corect Obs: p[4]=2.5 //corect sintactic, dar nu e alocat memorie pentru p!!!

IB.07.5 Transmiterea tablourilor ca argumente ale funciilor Un tablou este trimis ca parametru unei funcii folosind pointerul la primul element al tabloului. n declararea funciei putem folosi notaia specific tabloului (ex. int[]) sau notaia specific pointerilor (ex. int*). Compilatorul l trateaz ntotdeauna ca pointer (ex. int*). De exemplu, pentru declararea unei funcii care primete un vector de ntregi i dimensiunea lui avem urmtoarele declaraii echivalente:

122

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

void printVec (int a [ ], int n); void printVec (int * a, int n); void printVec (int a [50 ], int n);

Ele vor fi tratate ca int* de ctre compilator. Dimensiunea din parantezele drepte este ignorat. Numrul de elemente din tablou trebuie trimis separat, sub forma unui al doilea parametru de tip int. Compilatorul nu va lua n calcul acest parametru ca dimensiune a tabloului i ca urmare nu va verifica dac aceast dimensiune se ncadreaz n limitele specificate (>0 i <dim_maxim_declarat). Elementele unui tablou pot fi modificate n funcie, deoarece se transmite adresa acestuia prin referin (vezi Transmiterea parametrilor cap IB.06). n interiorul funciei ne putem referi la elementele vectorului a fie prin indici (cu a[i] ), fie prin indirectare (*(a+i)), indiferent de felul cum a fost declarat vectorul a.
// prin indexare void printVec (int a[ ], int n) { int i; for (i=0;i<n;i++) printf (%6d",a[i]); } // prin indirectare void printVec (int *a, int n) { int i; for (i=0;i<n;i++) printf (%6d", *a++); } //Citirea elementelor unui vector se poate face asemntor: for (i=0;i<n;i++) scanf ("%d", a+i); // echivalent cu &a[i] i cu a++

Apelul funciei se poate face astfel:


int main() { int v[10], n; printVec(v,n); } // SAU: int main() { int *v, n; /* Atentie! Vectorul v nu are alocata memorie, va trebui sa ii alocam dinamic!!*/ printVec(v,n); }

#include <stdio.h> #include <stdlib.h> #define N 5 int citire1(int tab[]){ //citeste elementele lui tab prin accesarea indexata int i=0; printf("Introduceti elementele tabloului:\n"); while(scanf(%d,&tab[i]!=EOF) i++; return i; } a elementelor

123

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

void tiparire1(int *tab, int n){ //tipareste elementele tabloului prin accesarea indexata a elementelor int i; printf("Elementele tabloului:\n"); for(i=0;i<n;i++) printf("%d ",tab[i]); printf(\n); } int citire2(int tab[]){ /* citeste elementele lui tab - accesarea fiecarui element se face printr-un pointer la el */ int *pi; pi=tab; printf("Introduceti elementele tabloului:\n"); while(scanf(%d,pi)!=EOF) pi++; return pi-tab; } void tiparire2 (int tab[], int n){ // tipareste elementele lui tab prin accesare prin pointeri int *pi; printf("Elementele tabloului:\n"); for (pi=tab; pi<tab+n; pi++) printf("%d ",*pi); printf(\n); } int main(){ int tab1[N], tab2[N], n, m; n=citire1(tab1); tiparire1(tab1,n); m=citire2(tab2); tiparire2(tab2,m); return 0; }

Program pentru citirea unui vector de ntregi i extragerea elementelor distincte ntr-un al doilea vector, care se va afisa. Se vor utiliza funcii. Ce funcii trebuie definite?
#define MAX 30 #include <stdio.h> /* cauta pe x n vectorul a*/ int gasit(int *v, int n, int x){ int m=0,i; for (i=0;i<n; i++) if (v[i]==x) return i; return -1; } int main () { int a[MAX]; // un vector de intregi int b[MAX]; // aici se pun elementele distincte din a int n,m,i,j; // n=dimensiune vector a, m=dimensiune vector b printf("Numar de elemente vector n="); scanf("%d",&n); printf ("Introducere %d numere intregi:\n",n); // citire vector: for (i=0;i<n;i++) scanf("%d",&a[i]); m=0; for (i=0;i<n;i++) if(gasit(b,m,a[i])==-1) b[m++]=a[i];

124

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// afiseaza elemente vector b: printf("Elementele distincte sunt:"); for (j=0;j<m;j++) printf ("%5d",b[j]); return 0; }

Pentru declararea unei funcii care primete o matrice ca parametru avem urmtoarele posibiliti: int min( int t[][NMAX], int m, int n); int min( int *t [NMAX], int m, int n); int min( int **t, int m, int n); Apelul funciei se va face astfel: int a[NMAX][NMAX], m, n; .. int mimim = min( a, m, n); Observaii: n aplicaiile numerice se prefer argumentele de tip vector i adresarea cu indici, iar n funciile cu iruri de caractere se prefer argumente de tip pointer i adresarea indirect prin pointeri. O funcie poate avea ca rezultat un pointer dar nu i rezultat vector. Diferena major dintre o variabil pointer i un nume de vector este aceea c un nume de vector este un pointer constant (adresa este alocat de compilatorul C i nu mai poate fi modificat la execuie). Un nume de vector nu poate apare n stnga unei atribuiri, n timp ce o variabil pointer are un coninut modificabil prin atribuire sau prin operaii aritmetice. Exemple:
int a[100], *p; p=a; ++p; a=p; ++a; // corect // ambele instruciuni produc erori

Cnd un nume de vector este folosit ca argument, se transmite un pointer cu aceeai valoare ca numele vectorului, iar funcia poate folosi argumentul formal n stnga unei atribuiri. Declararea unui vector (alocat la compilare) nu este echivalent cu declararea unui pointer, deoarece o declaraie de vector aloc memorie i iniializeaz pointerul ce reprezint numele vectorului cu adresa zonei alocate (operaii care nu au loc automat la declararea unui pointer).
int * a; a[0]=1; int *a={3,4,5}; // greit ! // echivalent cu: int a[]={3,4,5}

Operatorul sizeof aplicat unui nume de vector cu dimensiune fix are ca rezultat numrul total de octei ocupai de vector, dar aplicat unui argument formal de tip vector (sau unui pointer la un vector alocat dinamic) are ca rezultat mrimea unui pointer:
float x[10]; float * y=(float*)malloc (10*sizeof(float)); /*vector caruia i s-a alocat dinamic memorie pentru 10 numere float */ printf (%d,%d \n,sizeof(x), sizeof(y)); // scrie 40, 4

Numrul de elemente dintr-un vector alocat la compilare sau iniializat cu un ir de valori se poate afla prin expresia: sizeof (x) / sizeof(x[0]). 125

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int x[10]; printf (%d\n,sizeof(x)); printf (%d\n,sizeof(x[0])); printf (%d\n,sizeof(x)/sizeof(x[0]);

// scrie 40 // scrie 4 // scrie 10

IB.07.6 Pointeri n funcii Reamintim c n C/C++, parametrii efectivi sunt transmii prin valoare - valorile parametrilor actuali sunt depuse pe stiv, fiind apoi prelucrate ca parametri formali de ctre funcie. Ca urmare, modificarea valorii lor de ctre funcie nu este vizibil n exterior! Un exemplu clasic este o funcie care ncearc s schimbe ntre ele valorile a dou variabile, primite ca argumente:
void swap (int a, int b) { int aux; aux=a; a=b; b=aux; } int main () { int x=3, y=7; swap(x,y); printf ("%d,%d \n", x, y); return 0; }

// scrie 3, 7

nu e ceea ce ne doream!

Pentru a nelege mai bine mecanismul transmiterii parametrilor prin valoare oferim urmtoarea detaliere a pailor efectuai n cazul funciei de interschimbarea a valorilor a dou variabile:

126

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n multe situaii, se dorete modificarea parametrilor unei funcii. Acest lucru poate fi realizat prin transmiterea ca parametru al funciei a unui pointer la obiectul a crui valoare vrem s o modificm, modalitate cunoscut sub numele de transmitere prin referin. Observaie: Se pot modifica valorile de la adresele trimise ca parametri! Nu se pot modifica adresele trimise! O funcie care: trebuie s modifice mai multe valori primite prin argumente, sau care trebuie s transmit mai multe rezultate calculate de funcie trebuie s foloseasc argumente de tip pointer. Versiunea corect pentru funcia swap este urmtoarea:
void swap (int * pa, int * pb) { // pointeri la intregi int aux; aux=*pa; *pa=*pb; *pb=aux;// Adresare indirecta pt a accesa valoarile de la adresele pa, pb } // apelul acestei funcii folosete argumente efective pointeri: int main(void) { int x=5, y=7; swap(&x, &y); //transmitere prin adres printf(%d %d\n, x, y); /*valorile sunt inversate adic se va afia 7 5*/ return 0; }

Exemple:

/* calcul nr2 */ #include <stdio.h> void square(int *pNr) { *pNr *= *pNr; //Adresare indirecta pt a accesa valoarea de la adresa pNr } int main() { int nr = 8; printf(%d\n, nr); // 8 square(&number); // transmitere prin referinta explicita - pointer printf(%d\n, nr); // 64 return 0; }

Pentru a nelege mai bine mecanismul transmiterii parametrilor prin pointeri oferim urmtoarea detaliere a pailor efectuai n cazul funciei de interschimbarea a valorilor a dou variabile:

127

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Observaii: O funcie care primete dou sau mai multe numere pe care trebuie s le modifice va avea argumente de tip pointer sau un argument vector care reunete toate rezultatele (datele modificate). Dac parametrul este un tablou, funcia poate modifica valorile elementelor tabloului, primind adresa tabloului. A se observa c trebuie s se transmit ca parametri i dimensiunea/dimensiunile vectorului/matricii. Dac parametrul este ir de caractere, dimensiunea vectorului de caractere nu trebuie s se transmit, sfritul irului fiind indicat de caracterul terminator de ir, \0! O funcie poate avea ca rezultat un pointer, dar acest pointer nu trebuie s conin adresa unei variabile locale, deoarece o variabil local are o existen temporar, garantat numai pe durata executrii funciei n care este definit (cu excepia variabilelor locale statice) i de aceea adresa unei astfel de variabile nu trebuie transmis n afara funciei, pentru a fi folosit ulterior. Un rezultat pointer este egal cu unul din argumente, eventual modificat n funcie, fie o adres obinut prin alocare dinamic ( care rmne valabil si dup terminarea funciei). Pentru detalii a se vedea IB.10. Exemplu corect:
int *plus_zece( int *a ) { *a=*a+10; return a; }

Exemplu de programare greit:


// Vector cu cifrele unui nr intreg

128

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int *cifre (int n) { int k , c[5]; // Vector local for (k=4; k>=0; k--) { c[k]=n%10; n=n/10; } return c; // Gresit }

Pointerii permit: o s realizm modificarea valorilor unor variabile transmise ca parametri unei funcii; o s accesm mult mai eficient tablourile; o s lucrm cu zone de memorie alocate dinamic; o s se acceseze indirect o valoare a unui tip de date. Exemple: Program pentru determinarea elementelor minim i maxim dintr-un vector ntr-o aceeai funcie. Funcia nu are tip (void)!
#include<stdio.h> void minmax ( float x[], int n, float* pmin, float* pmax) { float xmin, xmax; int i; xmin=xmax=x[0]; for (i=1;i<n;i++) { if (xmin > x[i]) xmin=x[i]; if (xmax < x[i]) xmax=x[i]; } *pmin=xmin; *pmax=xmax; } // utilizare funcie int main () { float a[]={3,7,1,2,8,4}; float a1,a2; minmax (a,6,&a1,&a2); printf("%f %f \n",a1,a2); getchar(); return 0; } // NU!!! int main () { float a[]={3,7,1,2,8,4}; float *a1, *a2; // pointeri neinitializati !!! minmax (a,6,a1,a2); printf("%f %f \n",*a1,*a2); getchar(); return 0; }

S se scrie o funcie care calculeaz valorile unghiurilor unui triunghi, n funcie de lungimile laturilor. Funcia va fi scris n dou variante: cu 6 argumente: 3 date i 3 rezultate cu 2 argumente de tip vector.
//varianta 1 cu 6 argumente: 3 date i 3 rezultate

129

INFORMATIC*I* #include <stdio.h> #include <math.h>

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

void unghiuri(float ab, float ac, float bc, float *a, float *b, float *c) { *a=acos((ab*ab+ac*ac-bc*bc)/(2*ab*ac))*180/M_PI; *b=acos((ab*ab+bc*bc-ac*ac)/(2*ab*bc))*180/ M_PI; *c=acos((bc*bc+ac*ac-ab*ab)/(2*bc*ac))*180/ M_PI; } int main(){ float a, b, c, ab, ac, bc; scanf("%f%f%f", &ab, &ac, &bc); unghiuri(ab, ac ,bc, &a ,&b, &c); printf("%f %f %f" , a, b, c); return 0; } //varianta 2 cu 2 argumente de tip vector #include <stdio.h> #include <math.h> void unghiuri(float *L , float *U ) { U[0]=acos((L[0]*L[0]+L[1]*L[1]-L[2]*L[2])/(2*L[0]*L[1]))*180/ M_PI; U[1]=acos((L[0]*L[0]+L[2]*L[2]-L[1]*L[1])/(2*L[0]*L[2]))*180/ M_PI; U[2]=acos((L[2]*L[2]+L[1]*L[1]-L[0]*L[0])/(2*L[2]*L[1]))*180/ M_PI; } int main(){ float L[2],U[2]; int i; for (i=0;i<=2;i++) scanf("%f",&L[i]); unghiuri(L,U); for (i=0;i<=2;i++) printf("%f ",U[i]); return 0; }

IB.07.7 Pointeri la funcii Anumite aplicaii numerice necesit scrierea unei funcii care s poat apela o funcie cu nume necunoscut, dar cu prototip i efect cunoscut. De exemplu, o funcie care: s sorteze un vector tiind funcia de comparare a dou elemente ale unui vector s calculeze integrala definit a oricrei funcii cu un singur argument s determine o rdcin real a oricrei ecuaii (neliniare). Aici vom lua ca exemplu o funcie listf care poate afia (lista) valorile unei alte funcii cu un singur argument, ntr-un interval dat i cu un pas dat. Exemple de utilizare a funciei listf pentru afiarea valorilor unor funcii de bibliotec:
int main () { listf (sin,0.,2.*M_PI, M_PI/10.); listf (exp,1.,20.,1.); return 0; }

Problemele apar la definirea unei astfel de funcii, care primete ca argument n umele (adresa) unei funcii. 130

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Convenie C: Numele unei funcii nensoit de o list de argumente este adresa de nceput a codului funciei i este interpretat ca un pointer ctre funcia respectiv. Deci sin este adresa funciei sin(x) n apelul funciei listf. Declararea unui parametru formal (sau unei variabile) de tip pointer la o funcie are forma urmtoare: Sintaxa: tip_returnat (*pf) (lista_param_formali); unde: pf este numele paramatrului (variabilei) pointer la funcie, tip_returnat este tipul rezultatului funciei. lista_ param_ formali include doar tipurile parametrilor.

Observaie: Parantezele sunt importante, deoarece absena lor modific interpretarea declaraiei. Astfel, declaraia:
tip * f (lista_param_formali)

introduce o funcie cu rezultat pointer, nu un pointer la funcie!! De aceea, o eroare de programare care trece de compilare i se manifest la execuie, este apelarea unei funcii fr paranteze; compilatorul nu apeleaz funcia i consider c programatorul vrea s foloseasc adresa funciei! Exemplu:
if ( test ) break; if ( test() ) break; /* gresit, echiv. cu if (1) break; deoarece e luat in calcul pointerul la functia test */ // aici se testeaza valoarea intoarsa de functie

n concluzie, definirea funciei listf este:


void listf (double (*fp)(double), double min, double max, double pas) { double x,y; for (x=min; x<=max; x=x+pas) { y=(*fp)(x); // sau: y=fp(x); printf ("\n%20.10lf %20.10lf, x,y); } }

Pentru a face programele mai explicite se pot defini nume de tipuri pentru tipuri pointeri la funcii, folosind declaraia typedef.
typedef double (* ftype) (double); void listf (ftype fp, double min, double max, double pas) { double x, y; for (x=min; x<=max; x=x+pas) { y = fp(x); printf ("\n%20.10lf %20.10lf, x,y); } }

131

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exemple: 1. Program cu meniu de opiuni; operatorul alege una dintre funciile realizate de programul respectiv.
#include<stdio.h> #include<stdio.h> typedef void (*funPtr) (); /* defineste tipul funPtr, care este pointer la o functie de tip void fara argument */ // funcii pentru operatii realizate de program void unu () { printf ("unu\n"); } void doi () { printf ("doi\n"); } void trei () { printf ("trei\n"); } // selectare i apel funcie int main () { funPtr tp[ ]= {unu,doi,trei}; // vector de pointeri la funcii short option=0; do{ printf(Optiune (1/2/3):); scanf ("%hd", &option); if (option >=1 && option <=3) tp[option-1](); // apel funcie (unu/doi/trei) else break; }while (1); return 0; } //Secvena echivalent, fara a folosi pointeri la functii, este: do { printf(Optiune (1/2/3):); scanf ("%hd", &option); switch (option) { case 1: unu(); break; case 2: doi(); break; case 3: trei(); break; } } while (1);

2. Program pentru operaii aritmetice ntre numere ntregi (doar adunare i scdere, se poate completa):
/* Test pointeri la functii (TestFunctionPointer.cpp) */ #include <stdio.h> int aritmetica(int, int, int (*)(int, int)); /* int (*)(int, int) este un pointer la o functie, care primeste doi intregi si returneaza un intreg */ int add(int, int); int sub(int, int); int add(int n1, int n2) { return n1 + n2; } int sub(int n1, int n2) { return n1 - n2; } int aritmetica(int n1, int n2, int (*operation) (int, int)) {

132

INFORMATIC*I* return (*operation)(n1, n2); } int main() { int number1 = 5, number2 = 6;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// adunare printf(%d\n, aritmetica(nr1, nr2, add)); // scadere printf(%d\n, aritmetica(nr1, nr2, sub)); return 0; }

IB.07.8 Funcii generice n fiierul stdlib.h sunt declarate patru funcii generice pentru sortarea, cutarea liniar i cutarea binar ntr-un vector cu componente de orice tip, care ilustreaz o modalitate simpl de generalizare a tipului unui vector. Argumentul formal de tip vector al acestor funcii este declarat ca void* i este nlocuit cu un argument efectiv pointer la un tip precizat (nume de vector). Un alt argument al acestor funcii este adresa unei funcii de comparare a unor date de tipul celor memorate n vector, funcie furnizat de utilizator i care depinde de datele folosite n aplicaia sa. Pentru exemplificare urmeaz declaraiile pentru trei din aceste funcii (lfind este la fel cu lsearch): void *bsearch (const void *key, const void *base, size_t nelem, size_t width, int (*fcmp)(const void*, const void*)); void *lsearch (const void *key, void *base, size_t * pnelem, size_t width, int (*fcmp)(const void *, const void *)); void qsort (void *base, size_t nelem, size_t width, int (*fcmp)(const void *, const void *)); base este adresa vectorului key este cheia (valoarea) cutat n vector (de acelai tip cu elementele din vector) width este dimensiunea unui element din vector (ca numr de octei) nelem este numrul de elemente din vector fcmp este adresa funciei de comparare a dou elemente din vector. Exemplul urmtor arat cum se poate ordona un vector de numere ntregi cu funcia qsort :
// functie pentru compararea a doua numere intregi int intcmp (const void * a, const void * b) { return *(int*)a-*(int*)b; } int main () { int a[]= {5,2,9,7,1,6,3,8,4}; int i, n=9; // n=dimensiune vector qsort ( a,9, sizeof(int),intcmp); for (i=0;i<n;i++) printf("%d ",a[i]); }

// ordonare vector // afiare rezultat

IB.07.9 Anex. Tipul referin n C++

133

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n C++ s-a introdus tipul referin, folosit n primul rnd pentru parametri modificabili sau de dimensiuni mari, dar i funciile care au ca rezultat un obiect mare pot fi declarate de un tip referin, pentru a obine un cod mai performant. Sintaxa: Caracterul ampersand (&) folosit dup tipul i naintea numelui unui parametru formal sau al unei funcii arat compilatorului c pentru acel parametru se primete adresa i nu valoarea argumentului efectiv.

Spre deosebire de un parametru pointer, un parametru referin este folosit de utilizator n interiorul funciei la fel ca un parametru transmis prin valoare, dar compilatorul va genera automat indirectarea prin pointerul transmis (n programul surs nu se folosete explicit o peratorul de indirectare *). Exemplu:
void schimb (int & x, int & y) { // schimba intre ele doua valori int t = x; x = y; y = t; } void sort ( int a[], int n ) { // ordonare vector ... if ( a[i] > a[i+1]) schimb ( a[i], a[i+1]); ... }

Sintaxa declarrii unui tip referin este urmtoarea: Sintaxa: tip & nume unde nume poate fi: numele unui parametru formal numele unei funcii (urmat de lista argumentelor formale) numele unei variabile (mai rar). Efectul caracterului & n declaraia anterioar este urmtorul: compilatorul creeaz o variabil nume i o variabil pointer la variabila nume, iniializeaz variabila pointer cu adresa asociat lui nume i reine c orice referire ulterioar la nume va fi tradus ntr-o indirectare prin variabila pointer anonim creat. O funcie poate avea ca rezultat o referin la un vector dar nu poate avea ca rezultat un vector. O funcie nu poate avea ca rezultat o referin la o variabil local, aa cum nu poate avea ca rezultat un pointer la o variabila local. Referinele simplific utilizarea unor parametri modificabili de tip pointer, eliminnd necesitatea unui pointer la pointer. Exemplu de funcie care primete adresa unui ir i are ca rezultat adresa primei litere din acel ir:
void skip (char * & p){ while ( ! isalpha(*p)) p++; }

Parametrul efectiv transmis unei funcii pentru un parametru referin trebuie s fie un pointer modificabil i deci nu poate fi numele unui vector alocat la compilare: 134

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int main () { char s[]=" 2+ana -beta "; // un sir char *p=s; // nu se poate scrie: skip(p); puts(p); return 0; }

skip(s);

Exist riscul modificrii nedorite, din neatenie, a unor parametri referin, situaie ce poate fi evitat prin copierea lor n variabile locale ale funciei.

135

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.08. iruri de caractere. Biblioteci standard


Cuvinte cheie
iruri de caractere, funcii de intrare/ieire iruri de caractere, biblioteca string, extragere atomi lexicali, funcii stdlib, funcii ctype, erori, argumente n linia de comand

IB.08.1. iruri de caractere n C n limbajul C nu exist un tip de date ir de caractere, dei exist constante ir. Reamintim c o constant ir de caractere se reprezint ntre ghilimele. Definiie: Un ir de caractere este un vector de caractere terminat cu caracterul '\0'. Exist ns anumite particulariti n lucrul cu iruri fa de lucrul cu ali vectori. Prin natura lor, irurile pot avea o lungime variabil n limite foarte largi, iar lungimea lor se poate modifica chiar n cursul execuiei unui program ca urmare a unor operaii cum ar fi concatenarea a dou iruri, tergerea sau inseria ntr-un ir . Pentru simplificarea listei de parametri i a utilizrii funciilor pentru operaii cu iruri s-a decis ca fiecare ir memorat ntr-un vector s fie terminat cu un caracter numit terminator de ir, caracterul \0 ce are codul ASCII egal cu zero, i astfel s nu se mai transmit explicit lungimea irului. Multe funcii care produc un nou ir precum i funciile standard de citire adaug automat un octet terminator la irul produs (citit), iar funciile care prelucreaz sau afieaz iruri detecteaz sfritul irului la primul octet zero. Exemplu: irul de caractere "Anul 2012" ocup 10 octei, ultimul fiind \0. Exist dou posibiliti de definire a irurilor: ca tablou de caractere; Exemple:
char sir1[30]; char sir2[10]="exemplu"; #define MAX_SIR 100 char s[MAX_SIR];

ca pointer la caractere ( iniializat cu adresa unui ir sau a unui spaiu alocat dinamic). La definire se poate face i iniializare:
Exemple: char *sir3; /* sir3 trebuie iniializat cu adresa unui ir sau a unui spaiu alocat pe heap */ sir3=sir1; // sir3 ia adresa unui ir static sir3=sir1; sir3=&sir1; sir3=&sir1[0]; // sunt echivalente sir3=(char *)malloc(100); // se aloca dinamic un spatiu pe heap char *sir4="test"; / * sir2 este initializat cu adresa sirului constant */

136

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.08.2. Funciile de intrare/ieire pentru iruri de caractere sunt: Funcie char * gets(char * s); Exemple utilizare
char sir[10]; gets(sir);

Descriere Citete caractere pn la ntlnirea lui Enter; acesta nu se adaug la irul s. Plaseaz /0 la sfritul lui s. Obs: codul lui Enter e scos din buffer-ul de intrare. Tiprete irul primit ca parametru, apoi NewLine, cursorul trecnd la nceputul rndului urmtor Citete caractere pn la ntlnirea primului blanc sau Enter; acestea nu se adaug la irul s. Plaseaz \0 la sfritul lui s. Codul lui blanc sau Enter rmn n buffer-ul de intrare Tiprete irul s.

Rezultat Returneaz adresa primului caracter din ir. Dac se tasteaz CTRL+Z returneaz NULL. Returneaz codul ultimului caracter din ir sau EOF la insucces Returneaz numrul de valori citite sau EOF n cazul n care se tasteaz CTRL/Z Returneaz numrul de valori tiprite sau EOF n cazul unei erori.

int puts(const char * char sir[10]; puts(sir); s);

int scanf("%s", char* char sir[10]; scanf("%s",sir); s);

int printf("%s", char sir[10]; printf("%s",sir); char*s);

Citirea unei linii care poate include spaii albe se va face cu gets. Citirea unui cuvnt (ir delimitat prin spaii albe) se va face cu scanf. Exemplu de citire i afiare linii de text, cu numerotare linii:
char lin[128]; int nl=0; while ( gets (lin) != NULL) { printf (%4d ,++nl); printf (%d: %s \n, nl, lin); } // linii de maxim 128 car // citire linie in lin // mareste numar linie // scrie numar i coninut linie

Nu se recomand citirea caracter cu caracter a unui ir, cu descriptorul %c sau cu funcia getchar, dect dup apelul funciei fflush, care golete zona tampon de citire. n caz contrar se citete caracterul \n (cod 10), care rmne n zona tampon dup citire cu scanf sau cu getchar. Pentru a preveni erorile de depire a zonei alocate pentru citirea unui ir se poate specifica o lungime maxim a irului citit n funcia scanf. Exemplu:
char nume[30]; while(scanf(%30s,nume) != EOF) //numai primele 30 de caractere citite printf (%s \n, nume);

Din familia funciilor de intrare-ieire se consider c fac parte i funciile standard sscanf i sprintf, care au ca prim argument un ir de caractere ce este analizat (scanat) de sscanf i respectiv produs 137

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

de sprintf (litera s provine de la cuvntul string). Aceste funcii se folosesc fie pentru conversii interne n memorie, dup citire sau nainte de scriere din/n fiiere text, fie pentru extragere de subiruri dintr-un ir cu delimitatori diferii de spaii. Exemplu:
char d[]="25-12-1989"; int z, l, a; sscanf(d, "%d-%d-%d", &z, &l, &a); printf ("\n %d, %d, %d \n", z, l, a); //z=25, l=12, a=1989 //25, 12, 1989

IB.08.3. Funcii standard pentru operaii cu iruri Funciile standard pentru iruri de caractere sunt declarate n fiierul string.h. Urmeaz o descriere puin simplificat a celor mai folosite funcii: Funcie
char* strcpy(char *s1, const char *s2) char* strncpy(char *s1, const char *s2, unsigned int n) int strcmp(const char *s1, const char *s2) int strncmp(const char *s1, const char *s2)

Descriere
Copiaz la adresa s1 tot irul de la adresa s2 (inclusiv terminator ir) Copiaz primele n caractere de la s2 la s1 Returneaz s1 Compar irurile de la adresele s1 i s2 Compar primele n caractere din irurile s1 i s2

Au urmtorul rezultat: 0 dac irurile comparate conin aceleai caractere (sunt identice) < 0 dac primul ir (s1) este inferior celui de al doilea ir (s2) > 0 dac primul ir (s1) este superior celui de al doilea ir (s2)
Returneaz lungimea sirului s, excluznd '\0'.
char *msg="Hello"; strlen(msg); //5

unsigned int strlen(const char *s)

char* strcat(char *s1, const char *s2) char* strncat(char *s1, const char *s2, unsigned int n) char* strtok(char *s1, const char *s2)

Adaug irul s2 la sfritul irului s1 Adaug primele n caractere de la adresa s2 la irul s1 mparte s1 n tokeni atomi lexicali, s2 conine delimitatorii Returneaz adresa lui c n irul s (prima apariie a lui c) Returneaz adresa ultimei apariii a lui c n irul s Returneaz adresa n irul s1 a irului s2 Crearea unei copii a unui ir (aloc memorie i copiaz coninutul lui s)

char * strchr (char *s, char c); char *strrchr (char *s, char c); char * strstr (char *s1, char *s2); char * strdup (char *s);

Observaii: Rezultatul funciei de comparare nu este doar -1, 0 sau 1 ci orice valoare ntreag cu semn, deoarece comparaia de caractere se face prin scdere. Funciile standard strcpy i strcat adaug automat terminatorul zero la sfritul irului produs de funcie! Funcia strncpy nu adaug subirului copiat la adresa d terminatorul de ir dac n < strlen(s) dar subirul este terminat cu zero dac n > strlen(s). 138

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Funciile pentru operaii pe iruri nu pot verifica depirea memoriei alocate pentru iruri, deoarece primesc numai adresele irurilor; cade n sarcina programatorului s asigure memoria necesar rezultatului unor operaii cu iruri. Funciile de copiere i de concatenare ntorc rezultatul n primul parametru (adresa irului destinaie) pentru a permite exprimarea mai compact a unor operaii succesive pe iruri. Exemplu:
char fnume[20], *nume="test", *ext="cpp"; strcat( strcat( strcpy( fnume,nume),"."),ext);

Utilizarea unor iruri constante n operaii de copiere sau de concatenare poate conduce la erori prin depirea memoriei alocate (la compilare) irului constant specificat ca ir destinaie. Exemplu:
strcat (test,.cpp); // efecte nedorite !

Funcia strdup aloc memorie la o alt adres, copiaz n acea memorie irul s i ntoarce adresa noului ir. Se folosete pentru crearea de adrese distincte pentru mai multe iruri citite ntr-o aceeai zona buffer. Exemplu:
int main () { char buf[40]; int i=0, j; char* tp[100]; //tp este un tabel de pointeri la iruri while (gets(buf)) //gets are rezultat zero la sfrit de fiier tp[i++]= strdup(buf); //copiaza irul citit i memoreaza adresa for (j=0; j<i; j++) //afiarea irurilor folosind tabelul de pointeri puts(tp[j]); return 0; }

IB.08.4. Extragerea atomilor lexicali Un cuvnt sau un atom lexical (token) se poate defini n dou feluri: un ir de caractere separat de alte iruri prin unul sau cteva caractere cu rol de separator ntre cuvinte (de exemplu, spaii albe); un ir care poate conine numai anumite caractere i este separat de ali atomi prin oricare din caracterele interzise n ir. n primul caz sunt puini separatori de cuvinte i acetia pot fi enumerai. Pentru extragerea de iruri separate prin spaii albe ( , \n, \t, \r) se poate folosi o funcie din familia scanf: fscanf pentru citire dintr-un fiier sscanf pentru extragere dintr-un ir aflat n memorie ntre iruri pot fi oricte spaii albe, care sunt ignorate. int sscanf(const char *str, const char *format, ...); Exemplu de funcie care furnizeaz cuvntul k dintr-un ir dat s:
char* kword (const char* s, int k) { char word[256]; while ( k >= 1) { sscanf(s,"%s",word); // extrage un cuvant din linie in word

139

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // trece peste cuvantul extras

s=strstr(s, word)+ strlen(word); k--; } return strdup(word); }

// returneaza o copie a cuvantului k

Pentru extragere de cuvinte separate prin cteva caractere se poate folosi funcia de biblioteca strtok: char *strtok(char *str1, const char *str2); Exemplu care afieaz atomii lexicali dintr-o linie de text:
char linie[128], * cuv; char *sep=.,;\t\n gets(linie); cuv=strtok (linie,sep); while ( cuv !=NULL) { puts (cuv); cuv=strtok(0,sep); // adresa cuvant in linie // ir de caractere separator // citire linie // primul cuvant din linie // scrie cuvant // urmatorul cuvant din linie

} Funcia strtok are ca rezultat un pointer la urmtorul cuvnt din linie i adaug un octet zero la sfritul acestui cuvnt, dar nu mut la alt adres cuvintele din text. Acest pointer este o variabil local static n funcia strtok, deci o variabil care i pstreaz valoarea ntre apeluri succesive. Extragerea de cuvinte este o operaie frecvent, dar funcia strtok trebuie folosit cu atenie deoarece modific irul primit, iar primul apel este diferit de urmtoarele apeluri. In stdlib.h sunt declarate cteva funcii folosite pentru extragerea unor numere dintr-un text (dintrun sir de caractere) i care ilustreaz o alt soluie pentru funcii care primesc o adres ntr -un ir, extrag un subir i modific adresa n cadrul irului (dup subirul extras). Este vorba de funciile: double strtod (char *s, char **p); - string to double long strtod (char *s, char **p); - string to long care extrag, la fiecare apel, un subir care reprezint un numr i au dou rezultate: valoarea numrului n baza zece i adresa imediat urmtoare numrului extras n irul analizat. Exemplu de utilizare:
int main () { char * str =" 1 2.2 3.333 char * p = str; double d; do { d = strtod (p,&p); printf ("%lf\n", d); } while (d != 0); return 0; } 44e-1 ";

IB.08.5. Alte funcii de lucru cu iruri de caractere Din biblioteca stdlib: Funcie
int atoi(char* s) double atof(char* s)

Descriere Transform un ir ntr-un int Transform un ir ntr-un double

140

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Din biblioteca ctype: Funcie


int int int int int int int int isalpha(int isdigit(int isalnum(int isspace(int isupper(int islower(int toupper(int tolower(int ch) ch) ch) ch) ch) ch) ch) ch)

Descriere Returneaz non-zero (true) dac ch este liter sau cifr; sau zero (false) altfel Verific dac ch este spaiu alb (Space, CR, LF, Tab, FF, VTab) Verific dac ch este liter mare/mic Convertete la liter mare/mic

IB.08.6. Erori uzuale la operaii cu iruri de caractere O prim eroare posibil este aceea c numele unui vector este un pointer i nu mai trebuie aplicat operatorul & de obinere a adresei n funcia scanf, aa cum este necesar pentru citirea n variabile simple. O eroare de programare (i care nu produce ntotdeauna erori la execuie) este utilizarea unei variabile pointer neiniializate n funcia scanf (sau gets). Exemplu greit:
char * s; scanf(%s,s); // corect este: char s[80]; 80= lungime maxima // citete la adresa coninuta in s

O alt eroare frecvent (nedetectat la compilare) este compararea adreselor a dou iruri n locul comparaiei celor dou iruri. Exemplu:
char a[50], b[50]; // aici se memoreaza doua iruri scanf (%50s%50s, a,b); // citire iruri a i b if (a==b) printf(egale\n); //greit,rezultat zero

Pentru comparare corect de iruri se va folosi funcia strcmp. Exemplu:


if (strcmp(a,b)==0) printf (egale\n);

Aceeai eroare se poate face i la compararea cu un ir constant. Exemple:


if ( nume == ." ) break; ...} if ( strcmp(nume,.) == 0 ) break;... } // greit ! // corect

Din aceeai categorie de erori face parte atribuirea ntre pointeri cu intenia de copiere a unui ir la o alt adres, dei o parte din aceste erori pot fi semnalate la compilare. Exemple:
char a[100], b[100], *c ; a = b; // eroare semnalata la compilare c = a; // corect sintactic dar nu copiaza ir (doar modifica c) c=strdup(a); // copiaza ir de la adresa a la adresa c, aloca mem pt c strcpy (a,b); // copiaza la adresa a irul de la adresa b

IB.08.7. Definirea de noi funcii pe iruri de caractere Funciile standard pe iruri de caractere din C lucreaz numai cu adrese absolute (cu pointeri) i nu folosesc ca rezultat sau ca argumente adrese relative n ir (indici ntregi). De exemplu, funcia strstr care caut ntr-un ir un subir are ca rezultat un pointer: adresa n irul cercetat a subirului gsit. Parametrii de funcii prin care se reprezint iruri se declar de obicei ca pointeri dar se pot declara i ca vectori. 141

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

La definirea unor noi funcii pentru operaii pe iruri programatorul trebuie s fie sigur de adugarea terminatorului de ir la rezultatul funciei, pentru respectarea conveniei i evitarea unor erori. Pentru realizarea unor noi operaii cu iruri se vor folosi pe ct posibil funciile existente. Deoarece nu exist funcii care s elimine un caracter dintr-un ir, care s insereze un caracter ntrun ir sau care s extrag un subir dintr-o poziie dat a unui ir, le vom defini n continuare:
// terge n caractere de la adresa d char * strdel ( char *d, int n) { if ( n < strlen(d)) strcpy(d,d+n); return d; } // insereaz irul s la adresa d void strins (char *d, char *s) { char *aux=strdup(d); strcpy(d,s); strcat(d,aux); }

n general, nu se recomand funcii care au ca rezultat adresa unei variabile locale, dei erorile de utilizare a unor astfel de funcii apar numai la apeluri succesive (n cascad). Precizri la declararea funciilor standard sau nestandard pe iruri : Parametrii sau rezultatele care reprezint lungimea unui ir sunt de tip size_t (echivalent de obicei cu unsigned int) i nu int, pentru a permite iruri de lungime mai mare. Parametrii prin care se reprezint adrese de iruri care nu sunt modificate de funcie se declar const (const char * str), interpretat ca pointer la un ir constant (nemodificabil). Exemplu: size_t strlen (const char * s); Cuvntul cheie const n fata unei declaraii de pointer cere compilatorului s verifice c funcia care are un astfel de argument nu modific datele de la acea adres. Toate funciile de bibliotec cu pointeri la date nemodificabile folosesc declaraia const, pentru a permite verificri n alte funcii scrise de utilizatori dar care folosesc funcii de bibliotec. Exemple 1. Variante de implementare a funciilor de bibliotec strlen, strcmp, strcpy i strcat.
int strlen( char *s){ int lg=0; while (s[lg]!=\0) lg++; return lg; } int strcmp( char *s1, char *s2){ int i; for(i=0; s1[i] || s2[i]; i++) if (s1[i] < s2[i]) return -1; else if (s1[i] > s2[i]) return 1; return 0; }

142

INFORMATIC*I* char *strcpy( char *d, char *s){ int i=0; while(s[i]){ d[i]=s[i]; i++; } d[i]='\0'; // sau d[i]=0; return d; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// secvena ce cuprinde liniile cu verde este echivalent cu: // while(d[i]=s[i]) i++; char *strcat(char *d, char *s){ int i=0,j=0; while(d[i]) i++; /* la iesirea din while(d[i++]=s[j++]); return d; } while, i este indicele caracterului terminator*/

2. Program care: citete cuvinte tastate fiecare pe cte un rnd nou, pn la CTRL/Z ( varianta: pn la introducerea unui cuvnt vid ) afieaz cuvntul cel mai lung afieaz cuvintele ce ncep cu o vocal
#include <stdio.h> #include <string.h> #include <ctype.h> #define LUNG 81 //lungime maxima cuvant #define NR 15 // nr max de cuvinte citite void citire_cuv ( char tab_cuv[][LUNG], int *nr_cuv ) { printf( "Se introduc maxim %d cuvinte, terminate cu CTRL/Z:\n", NR ); while(*nr_cuv<NR && gets ( tab_cuv[*nr_cuv] ) ) (*nr_cuv)++; /* la CTRL/Z gets returneaza NULL (= 0) */ /* citirea se poate face i cu scanf: while(*nr_cuv<NR && scanf("%s",tab_cuv[*nr_cuv])!=EOF) (*nr_cuv)++; */ /* dac terminarea se face cu un cuvant vid: while(*nr_cuv<NR && strcmp("",gets(tab_cuv[*nr_cuv]))) (*nr_cuv)++; */ } void cuv_max ( char tab_cuv[][LUNG], int nr_cuv ){ int i, lung_crt, lung_max=0; char * p_max; /* pointerul spre cuvantul maxim */ /* se poate memora indicele cuvantului maxim: int i_max; sau memora cuvantul maxim intr-un ir: char c_max[LUNG]; */ for(i=0;i<nr_cuv;i++) if ( ( lung_crt = strlen(tab_cuv[i]) ) > lung_max){ p_max = tab_cuv[i]; lung_max = lung_crt; } printf ("Cuvantul de lungime maxima %d este: %s\n", lung_max, p_max); } void cuv_vocale ( char tab_cuv[][LUNG], int nr_cuv ){

143

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int i; puts("Cuvintele ce incep cu vocale:"); for(i=0;i<nr_cuv;i++) switch(toupper(tab_cuv[i][0])){ case 'A': case'E': case 'I': case 'O': case 'U': puts(tab_cuv[i]); } /* n loc de switch se putea folosi: char c; if(c=toupper(tab_cuv[i][0]),c=='A' || c=='E' || ...) puts(tab_cuv[i]); */ } int main(){ char tab_cuv[NR][LUNG]; //vectorul de cuvinte int nr_cuv=0; // numarul cuvintelor introduse citire_cuv(tab_cuv,&nr_cuv); cuv_max(tab_cuv,nr_cuv); cuv_vocale(tab_cuv,nr_cuv); return 1; }

3. Se citesc trei iruri: s1, s2 i s3. S se afieze irul obinut prin nlocuirea n s1 a tuturor apariiilor lui s2 prin s3. ( Observaie: dac s3 este irul vid, din s1 se vor terge toate subirurile s2).
#include <stdio.h> #include <string.h> #define N 81 int main(void){ char s1[N],s2[N],s3[N],rez[N]; char *ps1=s1,*pos, *r=rez; puts("sirul s1:"); gets(s1); puts("subsirul s2:"); gets(s2); puts("s3:"); gets(s3); while ( pos=strstr(ps1,s2) ){ while(ps1<pos) *r++=*ps1++; strcpy(r,s3); r+=strlen(s3); ps1+=strlen(s2); } strcpy(r,ps1); puts("sirul rezultat:"); puts(rez); return 1; } //copiez n r din s1 pana la pos //copiez n r pe s3 //sar peste s3 copiat n r //sar n s1 peste s2 //adaug ce a mai ramas din s1

IB.08.8. Argumente n linia de comand Funcia main poate avea dou argumente, prin care se pot primi date prin linia de comand ce lanseaz programul n execuie. Sistemul de operare analizeaz linia de comand, extrage cuvintele din linie (iruri separate prin spaii albe), aloc memorie pentru aceste cuvinte i introduce adresele lor ntr-un vector de pointeri (alocat dinamic). Primul argument al funciei main este dimensiunea vectorului de pointeri (de tip int), iar al doilea argument este adresa vectorului de pointeri (un pointer).

144

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Primul cuvnt, cu adresa n argv[0], este chiar numele programului executat (numele fiierului ce conine programul executabil), iar celelalte cuvinte din linie sunt date iniiale pentru program: nume de fiiere folosite de program, opiuni de lucru diverse. Programele care preiau date din linia de comand se vor folosi n acelasi mod ca i comenzile predefinite ale sistemului (DIR, TYPE, etc.), deci extind comenzile utilizabile n linie de comand. Exemplu de afiare a datelor primite n linia de comand:
// fisierul se numeste listare.c int main ( int argc, char * argv[]) { // sau : char** argv int i; for (i=0;i<n;i++) // nu se afieaza i argv[0] printf (%s , argv[i]); }

O linie de command de forma: listare iata patru argumente va lansa n execuie listare.exe, care va tipri pe ecran: listare.exe iata patru argumente

145

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.09. Structuri de date. Definire i utilizare n limbajul C


Cuvinte cheie
Structura(struct), cmp, typedef, structuri predefinite, structuri cu coninut variabil (union), enumerri

IB.09.1. Definirea de tipuri i variabile structur O structur este o colecie de valori eterogene ca tip, stocate ntr-o zon compact de memorie. Cu alte cuvinte, o structur este un tip de date care permite gruparea unor date de tipuri diferite sub un singur nume. Componentele unei structuri, denumite cmpuri, sunt identificate prin nume simbolice, denumite selectori. Cmpurile unei structuri pot fi de orice tip, simplu sau derivat, dar nu void sau funcie. Printr-o declaraie struct se definete un nou tip de date de tip structur, de ctre programator. IB.09.1.1 Declararea structurilor Declararea structurilor se face folosind cuvntul cheie struct; definirea unui tip structur are sintaxa urmtoare: Sintax: struct nume_structur { tip_cmp1 nume_cmp1; tip_cmp2 nume_cmp2; } [lista_variabile_structur]; unde: nume_structura este un nume de tip folosit numai precedat de cuvntul cheie struct (n C, dar n C++ se poate folosi singur ca nume de tip). tip_cmp1, tip_cmp2,... este tipul componentei (cmpului) i nume_cmp1, nume_cmp2,... este numele unei componente (cmp) Exemple:
//structura numar complex struct Complex { double real; double imag; };

Ordinea enumerrii cmpurilor unei structuri nu este important, deoarece ne referim la cmpuri prin numele lor. Se poate folosi o singura declaraie de tip pentru mai multe cmpuri:
//structura moment de timp struct time { int ora, min, sec; }; //structura activitate struct activ { char numeact[30]; struct time start; struct time stop; };

// nume activitate // ora de incepere // ora de terminare

146

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Nume_structura sau lista_variabile_structura pot lipsi, dar nu simultan. Dac se precizeaz nume_structura, atunci nseamn c se face definirea tipului struct nume_structura, care poate fi apoi folosit pentru declararea de variabile, ca tip de parametri formali sau ca tip de rezultat returnat de funcii. Declararea unor variabile de un tip structur se poate face fie dup declararea tipului structur, fie simultan cu declararea tipului structur. Nu exist constante de tip structur, dar este posibil iniializarea la declarare a unor variabile structur. Astfel de variabile iniializate i cu atributul const ar putea fi folosite drept constante simbolice. Exemple:
struct time t1,t2, t[100]; struct complex { float re,im; } c1, c2, c3; //t este vector de structuri

//numere complexe

struct complex cv[200]; //un vector de numere complexe struct coordonate{ //se declara tipul struct coordonate float x,y; } punct, *ppunct, puncte[20]; //variabile struct coordonate alt_punct = {1,4.5}, alte_puncte[10] = {{2,4},{8},{9,7}}, mai_multe_puncte[25] = {1,2,3,4}; //variabilele de tip structura se pot initializa //campurile neprecizate sunt implicit 0 struct persoana{ //se declara tipul struct persoana char nume[20]; int varsta; }; //lipseste lista_declaratori struct persoana pers={"Ion Ionescu",21}, *ppers, persoane[12]; struct{ //lipseste nume_struct char titlu[20], autor[15], editura[12]; int an_aparitie; }carte, biblioteca[1000]; /* nu se declara un tip, deci doar aici pot fi facute declaratiile de variabile */

la

declarare;

Un cmp al unei structuri poate fi de tip structur, dar nu aceeai cu cea definit - se poate ns s se declare un cmp pointer la structura definit (aspect care va fi utilizat la implementarea listelor):
struct persoana{ char nume[20]; struct{ int zi,an,luna }data_nasterii; }p; struct nod_lista{ tip_info info; struct nod_lista * urm; }; //se declara tipul struct persoana

//camp de tip structura

//camp pointer la structura definita

Numele de structuri se afl ntr-un spaiu de nume diferit de cel al numelor de variabile - se pot declara deci variabile i tipuri structur cu acelai nume - de evitat ns. Se pot declara tipuri structuri care au nume de cmpuri identice.

147

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

De remarcat c orice declaraie struct se termin obligatoriu cu caracterul ; chiar dac acest caracter urmeaz dup o acolad; aici acoladele nu delimiteaz un bloc de instruciuni ci fac parte din declaraia struct. n structuri diferite pot exista cmpuri cu acelai nume, dar ntr-o aceeai structur numele de cmpuri trebuie s fie diferite. Accesul la cmpurile unei variabile de tip structur se face utiliznd operatorul punct (.). Exemplu:
struct complex c1; ... c1.re struct time t2; ... t2.ora struct time t[10]; ... t[0].min.

Atenie! Cmpurile unei variabile structur nu se pot folosi dect dac numele cmpului este precedat de numele variabilei structur din care face parte, deoarece exist un cmp cu acelai nume n toate variabilele de un acelai tip structur. Exemplu:
int main () { complex c1,c2; scanf (%f%f", &c1.re, &c1.im); c2.re= c1.re; c2.im= -c1.im; printf ((%f,%f) , c2.re, c2.im); } // citire c1 // complex conjugat // scrie c2

Dac un cmp este la rndul lui o structur, atunci numele cmpului poate conine mai multe puncte ce separ numele variabilei i cmpurilor de care aparine (n ordine ierarhic). Exemplu:
//structura moment de timp struct time { int ora, min, sec; }; //structura activitate struct activ { char numeact[30]; // nume activitate struct time start; // ora de incepere struct time stop; // ora de terminare }; .... struct activ a; printf (%s ncepe la %d: %d i se termina la %d: %d \n, a.numeact, a.start.ora, a.start.min, a.stop.ora, a.stop.min);

IB.09.2. Asocierea de nume sinonime pentru tipuri structuri - typedef Printr-o declaraie struct se definete un nou tip de date de ctre programator. Utilizarea tipurilor structur pare diferit de utilizarea tipurilor predefinite, prin existena a dou cuvinte c are desemneaz tipul (struct numestr). Declaraia typedef din C permite atribuirea unui nume oricrui tip, nume care se poate folosi apoi la fel cu numele tipurilor predefinite ale limbajului. Sintaxa declaraiei typedef este la fel cu sintaxa unei declaraii de variabil, dar se declar un nume de tip i nu un nume de variabil. 148

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n limbajul C declaraia typedef se utilizeaz frecvent pentru atribuirea de nume unor tipuri structur. Exemple:
// definire nume tip simultan cu definire tip structur typedef struct { float re,im; } complex; // definire nume tip dup definire tip structura typedef struct activ act;

Deoarece un tip structur este folosit n mai multe funcii (inclusiv main), definirea tipului structur (cu sau fr typedef) se face la nceputul fiierului surs care conine funciile (naintea primei funcii). Dac un program este format din mai multe fiiere surs atunci definiia structurii face parte dintr-un fiier antet (de tip .h). Se pot folosi ambele nume ale unui tip structur (cel precedat de struct i cel dat prin typedef), care pot fi chiar identice. Exemple:
typedef struct complex float re; float im; } complex; typedef struct point { double x,y; } point; ... struct point p[100]; // calcul arie triunghi dat prin coordonatele varfurilor double arie ( point a, point b, point c) { return a.x * (b.y-c.y) - b.x * (a.y-c.y) + c.x * (a.y-b.y); } typedef struct card { int val; char cul[20]; } Carte; .... Carte c1, c2; c1. val = 10; strcpy (c1. cul, caro); c2 = c1; {

Cu typedef structura poate fi i anonim (poate lipsi cuvantul card din ultimul exemplu):
typedef struct { int val; char cul[20]; } Carte;

Atunci cnd numele unui tip structur este folosit frecvent, inclusiv n parametri de funcii, este preferabil un nume introdus prin typedef, dar dac vrem s punem n eviden c este vorba de tipuri structur vom folosi numele precedat de cuvntul cheie struct. n C++ se admite folosirea numelui unui tip structur, fr a fi precedat de struct i fr a mai fi necesar typedef.

149

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.09.3. Utilizarea tipurilor structur Un tip structur poate fi folosit n: declararea de variabile structuri sau pointeri la structuri declararea unor parametri formali de funcii (structuri sau pointeri la structuri) declararea unor funcii cu rezultat de un tip structur Operaiile posibile cu variabile de un tip structur sunt: Selectarea unui cmp al unei variabile structur se realizeaz folosind operatorul de selecie . . Cmpul selectat se comport ca o variabil de acelai tip cu cmpul, deci i se pot aplica aceleai prelucrri ca oricrei variabile de tipul respectiv.
variabila_structura.nume_camp

Exemplu:
struct persoana{ char nume[20]; struct { int zi,an,luna } data_nasterii; } p; //se declara tipul struct persoana

//camp de tip structura

//Selecia cmpurilor pentru variabila p de mai sus: p.nume //tablou de caractere p.nume[0] //primul caracter din nume p.nume[strlen(p.nume)-1] //ultimul caracter din nume p.data_nasterii.an p.data_nasterii.luna p.data_nasterii.an

O variabil structur poate fi iniializat la declarare prin precizarea ntre {} a valorilor cmpurilor; cele neprecizate sunt implicit 0. O variabil structur poate fi copiat n alt variabil de acelai tip. Exemplu cu declaraiile de mai sus:
printf("%d %d\n", sizeof(pers), sizeof(struct persoana)); ppers = &pers; persoane[0] = *ppers; //atribuirea intre doua variabile structura

O variabil structur nu poate fi citit sau scris direct, ci prin intermediul cmpurilor!! Se pot aplica operatorii: & - referin sizeof - dimensiune Deoarece structurile se prelucreaz frecvent prin intermediul pointerilor, a fost introdus operatorul -> care combin operatorul de indirectare * cu cel de selectie.. Cele dou expresii de mai jos sunt echivalente:
->. (*variabila_pointer).nume_camp

variabila_pointer->nume_camp

Transmiterea ca argument efectiv la apelarea unei funcii; Transmiterea ca rezultat al unei funcii, ntr-o instruciune return.

150

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Singurul operator al limbajului C care admite operanzi de tip structura este cel de atribuire. Pentru alte operaii cu structuri trebuie definite funcii: comparaii, operaii aritmetice, operaii de citirescriere etc. Exemplul urmtor arat cum se poate ordona un vector de structuri time:
struct time { int ora, min, sec; }; //structura moment de timp

void wrtime ( struct time t) {// scrie ora, min, sec printf ("%02d:%02d:%02d \n", t.ora,t.min,t.sec); } int cmptime (struct time t1, struct time t2) { // comparare int d; d=t1.ora - t2.ora; if (d) return d; d=t1.min - t2.min; // <0 daca t1<t2 i >0 daca t1>t2 if (d) return d; // rezultat negativ sau pozitiv return t1.sec - t2.sec; // rezultat <0 sau =0 sau > 0 } // utilizare funcii int main () { struct time tab[200], aux; int i, j, n; . . . // citire date // ordonare vector for (j=1;j<n;j++) for (i=1;i<n;i++) if ( cmptime (tab[i-1],tab[i]) > 0) { aux=tab[i-1]; tab[i-1]=tab[i]; tab[i]=aux; } // afiare vector ordonat for (i=0;i<n;i++) wrtime(tab[i]); }

Principalele avantaje ale utilizrii unor tipuri structur sunt: Programele devin mai explicite dac se folosesc structuri n locul unor variabile separate. Se pot defini tipuri de date specifice aplicaiei iar programul reflect mai bin e universul aplicaiei. Se poate reduce numrul de parametri al unor funcii prin gruparea lor n parametri de tipuri structur i deci se simplific utilizarea acelor funcii. Se pot utiliza structuri de date extensibile, formate din variabile structur alocate dinamic i legate ntre ele prin pointeri (liste nlnuite, arbori .a). IB.09.4. Funcii cu parametri i/sau rezultat structur O funcie care produce un rezultat de tip structur poate fi scris n dou moduri, care implic i utilizri diferite ale funciei: funcia are rezultat de tip structur:
// citire numar complex (varianta 1) complex readx () { complex c; scanf (%f%f,&c.re, &c.im); return c; }

151

INFORMATIC*I* // utilizare complex a[100]; for (i=0;i<n;i++) a[i]=readx();

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

funcia este de tip void i depune rezultatul la adresa primit ca parametru (pointer la tip structur):
// citire numar complex (varianta 2) void readx ( complex * px) { // px pointer la o structur complex scanf (%f%f, &px->re, &px->im); } // utilizare complex a[100]; ...... for (i=0;i<n;i++) readx (&a[i]);

// adresa variabilei structur a[i]

Reamintim c notaia px->re este echivalent cu notaia (*px).re i se interpreteaz astfel: cmpul re al structurii de la adresa px. Uneori, mai multe variabile descriu mpreun un anumit obiect i trebuie transmise la funciile ce lucreaz cu obiecte de tipul respectiv. Gruparea acestor variabile ntr-o structur va reduce numrul de parametri i va simplifica apelarea funciilor. Exemple de obiecte definite prin mai multe variabile: obiecte geometrice (puncte, poligoane .a), date calendaristice i momente de timp, structuri de date (stiva, coada, .a), vectori, matrice, etc. Exemplu de grupare ntr-o structur a adresei i dimensiunii unui vector:
typedef struct { int vec[1000]; int dim; } vector; // afiare vector void scrvec (vector v) { int i; for (i=0;i<v.dim;i++) printf ("%d ",v.vec[i]); printf ("\n"); } // elementele comune din 2 vectori vector comun (vector a, vector b) { vector c; int i,j,k=0; for (i=0;i<a.dim;i++) for (j=0;j<b.dim;j++) if (a.vec[i]==b.vec[j]) c.vec[k++]=a.vec[i]; c.dim=k; return c; }

Pentru structurile care ocup un numr mare de octei este mai eficient s se transmit ca parametru la funcii adresa structurii (un pointer) n loc s se copieze coninutul structurii la fiecare apel de funcie i s se ocupe loc n stiv, chiar dac funcia nu face nici o modificare n structura a crei adres o primete. Funciile cu parametri pointeri la structuri pot produce efecte secundare (laterale) nedorite, prin modificarea involuntar a unor variabile din alte funcii. 152

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n concluzie, avantajele utilizrii tipurilor structur sunt urmtoarele: Programele devin mai explicite dac se folosesc structuri n locul unor variabile separate. Se pot defini tipuri de date specifice aplicaiei iar programul reflect mai bine universul aplicaiei. Se poate reduce numrul de parametri al unor funcii prin gruparea lor n parametri de tipuri structur i deci se simplific utilizarea acelor funcii. Se pot utiliza structuri de date extensibile, formate din variabile structur alocate dinamic i legate ntre ele prin pointeri (liste nlnuite, arbori s.a). Exemple 1.S se defineasc o structur Point pentru un punct geometric 2D i o structur Rectangle pentru un dreptunghi definit prin colul stnga sus i colul dreapta jos. S se iniializeze i s se afieze o variabil de tip Rectangle.
#include <stdio.h> typedef struct Point { int x, y; } Point; typedef struct Rectangle { Point topLeft; Point bottomRight; } Rectangle; int main() { Point p1, p2; p1.x = 0; // p1 p1.y = 3; p2.x = 4; // p2 p2.y = 0; printf( "p1 la ( printf( "p2 la (

la (0, 3) la (4, 0) %d, %d)\n", p1.x, p1.y); %d, %d)\n", p2.x, p2.y);

Rectangle rect; rect.topLeft = p1; rect.bottomRight = p2; printf("Stanga sus la(%d,%d)\n", rect.topLeft.x,rect.topLeft.y); printf("Dreapta jos la (%d,%d)\n", rect.bottomRight.x,rect.bottomRight.y); return 0; }

Rezultatul va fi:
p1 la (0,3) p2 la (4,0) Stanga sus la (0,3) Dreapta jos la (4,0)

2. S se defineasca o structura time" care grupeaz 3 ntregi ce reprezint ora, minutul i secunda pentru un moment de timp. S se scrie funcii pentru: Verificare corectitudine ora Citire moment de timp Scriere moment de timp Comparare de structuri time". Program pentru citirea i ordonarea cronologic a unor momente de timp i afiarea listei ordonate, folosind funciile anterioare.
#include<stdio.h>

153

INFORMATIC*I* typedef struct time { int ora, min, sec; }time;

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

void wrtime ( time t) { // scrie ora, min, sec printf ("%02d:%02d:%02d \n", t.ora,t.min,t.sec); } int cmptime (time t1, time t2) { int d; d=t1.ora - t2.ora; if (d) return d; d=t1.min - t2.min; if (d) return d; return t1.sec - t2.sec; } int corect (time if ( t.ora if ( t.min < 0 if ( t.sec < 0 return 1; } time rdtime () { time t; do { scanf ("%d%d%d", &t.ora, &t.min,&t.sec); if ( ! corect (t)) printf ("Date gresite, repetati introducerea: \n"); else break; }while(1); return t; } void sort (time a[], int n) { int i,gata; time aux; // ordonare vector de date t) { // verifica daca timp plauzibil < 0 || t.ora > 23 ) return 0; || t.min > 59 ) return 0; || t.sec > 59 ) return 0; // plauzibil corect // citire ora // compara momente de timp

// <0 daca t1<t2 i >0 daca t1>t2 // rezultat negativ sau pozitiv // rezultat <0 sau =0 sau > 0

do { gata=1; for (i=0;i<n-1;i++) if (cmptime(a[i],a[i+1]) > 0 ) { aux=a[i]; a[i]=a[i+1]; a[i+1]=aux; gata=0; } }while (!gata); } int main () { time t[30]; int n,i; printf("introducere n: "); scanf("%d",&n); for(i=0;i<n;i++) t[i] = rdtime(); sort (t, n); for ( i=0 ;i<n ;i++) wrtime(t[i]); return 0;

154

INFORMATIC*I* }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.09.5. Structuri predefinite n bibliotecile C standard exist unele structuri predefinite. Un astfel de exemplu este structura struct tm definit n biblioteca time; aceast structur conine componente ce definesc complet un moment de timp: struct tm { int tm_sec, tm_min, tm_hour; // secunda, minut, ora int tm_mday, tm_mon, tm_year; // zi, luna, an int tm_wday, tm_yday; // nr zi n saptamana i n an int tm_isdst; // 1 - se modifica ora (iarna/vara), 0 - nu }; Exist funcii care lucreaz cu aceast structur: asctime, localtime, etc. Exemplu: cum se poate afia ora i ziua curent, folosind numai funcii standard
#include <stdio.h> #include <time.h> int main(void) { time_t t; // time_t este alt nume pentru long struct tm *area; // pentru rezultat funcie localtime t = time (NULL); // obtine ora curenta area = localtime(&t); // conversie din time_t n struct tm printf ("Local time is: %s", asctime(area)); }

Observaii: asctime( struct tm* t) returneaz un ir ce reprezint ziua i ora din strucura t. irul are urmtorul format: DDD MMM dd hh:mm:ss YYYY funcia time transmite rezultatul i prin numele funciei i prin parametru: long time (long*), deci se putea apela i: time (&t); O alt structur predefinit este struct stat definit n fiierul sys/stat.h. Structura reunete date despre un fiier, cu excepia numelui: struct stat { short unix [7]; // fr semnificatie n sisteme Windows long st_size; // dimensiune fisier (octeti) long st_atime, st_mtime; // ultimul acces / ultima modificare long st_ctime; // data de creare }; Funcia stat completeaz o astfel de structur pentru un fiier cu nume dat: int stat (char* filename, struct stat * p); Exemplu: Pentru a afla dimensiunea unui fiier normal (care nu este fiier director) vom putea folosi funcia urmtoare:
long filesize (char * filename) { struct stat fileattr; if (stat (filename, &fileattr) < 0) return -1; // daca fisier negasit

155

INFORMATIC*I* else // fisier gasit return (fileattr.st_size); }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // campul st_size contine lungimea

IB.09.6. Structuri cu coninut variabil (uniuni) Uniunea (reuniunea union) definete un grup de variabile care nu se memoreaz simultan ci alternativ. n felul acesta se pot memora diverse tipuri de date la o aceeai adres de memorie. Cuvntul cheie union se folosete la fel cu struct. Alocarea de memorie se face (de ctre compilator) n funcie de variabila ce necesit maxim de memorie. Declararea uniunilor se face folosind cuvntul cheie union: Sintax: union { tip_comp1 nume_comp1; tip_comp2 nume_comp2; } [lista_variabile_structur]; unde: tip_comp1, tip_comp2,... este tipul componentei i nume_comp1, nume_comp2,... este numele unei componente Exemplu:
union { int ival; long lval; float fval; double dval; } val;

O uniune face parte de obicei dintr-o structur care mai conine i un cmp discriminant, care specific tipul datelor memorate (alternativa selectat la un moment dat). Exemplul urmtor arat cum se poate lucra cu numere de diferite tipuri i lungimi, reunite ntr-un tip generic:
typedef struct numar { char tipn; // tip numar (caracter: i-int, llong, f-float, d-double union { int ival; long lval; float fval; double dval; } val; // valoare numar } Numar; // definire tip de date Numar void write (Numar n) { // in functie de tip afiseaza valoarea switch (n.tipn) { case 'i': printf ("%d ", n.val.ival); break; case 'l': printf ("%ld ", n.val.lval); break; case 'f': printf ("%f ", n.val.fval); break; case 'd': printf ("%.15lf ", n.val.dval); } }

Observaie: 156

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

n locul construciei union se poate folosi o variabil de tip void* care va conine adresa unui numr, indiferent de tipul lui. Memoria pentru numr se va aloca dinamic:
typedef struct number { char tipn; // tip numar void * pv; // adresa numar }number; // in functie de tip afiseaza valoarea void write (number n) { switch (n.tipn) { case i: printf("%d",*(int*)n.pv); //conversie la int* si indirectare break; ... case d: printf("%.15lf",*(double*)n.pv); /*conversie la double * si indirectare*/ break; } }

IB.09.7. Enumerri Tipul enumerare este un caz particular al tipurilor ntregi. Se utilizeaz pentru a realiza o reprezentare comod i sugestiv a unor obiecte ale caror valori sunt identificate printr-un numr finit de nume simbolice. Tipul enumerare declar constante simbolice, crora li se asociaz coduri numerice de tip ntreg. Compilatorul asociaza constantelor enumerate cte un cod ntreg din succesiunea ncepnd cu 0. Implicit, irul valorilor e cresctor cu pasul 1. Un nume de constant nu poate fi folosit n mai multe enumerri. Sintaxa este urmtoarea: Sintax: enum [nume_tip] { lista_constante } [lista_variabile] ; unde nume_tip i lista de variabile pot lipsi! Exemple:
enum zile_lucr { luni, marti, miercuri, joi, vineri }; // luni e asociat cu 0, marti cu 1, ..., vineri cu 4

// se pot defini variabile de tipul zile_lucr, ca mai jos, variabila zi: enum zile_lucr zi=marti; enum Color { red, green, blue } myColor; // Defineste o enumerare i declar o variabil de tipul ei ...... myColor = red; // Atribuie o valoare variabilei Color yourColor; // Declar o variabil de tipul Color yourColor = green; // Atribuie o valoare variabilei

Dac se dorete o alt codificare a constantelor din enumerare dect cea implicit, pot fi folosite n enumerare elemente de forma: nume_constant = valoare_ntreag; Constantelor simbolice ce urmeaz unei astfel de iniializri li se asociaz numerele ntregi urmtoare:
enum transport { tren, autocar=5, autoturism, avion }; /* tren e asociat cu 0, autocar cu 5, autoturism cu 6, avion cu 7 */

157

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

enum luni_curs {ian=1, feb, mar, apr, mai, iun, oct=10, nov, dec};

Dup cum spuneam, tipurile enumerare sunt tipuri ntregi, variabilele enumerare se pot folosi la fel ca variabilele ntregi, asigurnd un cod mai lizibil dect prin declararea separat de constante. Putem folosi declaraiile de tip typedef pentru a introduce un tip enumerare:
enum {D, L, Ma, Mc, J, V, S} zi; /* tip anonim; declar doar variabila zi, tipul nu are nume, deci nu mai putem declara altundeva variabile */ // sau: typedef enum {D, L, Ma, Mc, J, V, S} Zi; // tip enumerare cu numele Zi; se pot declara variabile int nr_ore_lucru[7]; // vector cu numr de ore pe zi for (zi = L; zi <= V; ++zi) nr_ore_lucru[zi] = 8;

typedef enum { inginer=1, profesor, avocat } profesie; profesie profesia_mea=inginer; // definirea variabilei profesia_mea

IB.09.8. Exemple 1. S se scrie un program care declar o strucur pentru un numr generic, folosind o uniune pentru valoarea numrului i un cmp tip ce va spune ce fel de numr este (ntreg i, ntreg lung l, real f, real dubla precizie d). S se scrie o funcie ce citete o variabil de tipul structurii definite. S se scrie o funcie ce afieaz valoarea unei variabile de tipul structurii definite. Rezolvare:
#include<stdio.h> typedef struct numar { char tipn; d-double union { int ival; long lval; float fval; double dval; } val; } Numar; // structura pentru un numr de orice tip // tip numar (caracter: i-int, llong, f-float,

// valoare numar // definire tip de date Numar

// afisare numr void write (Numar n) { switch (n.tipn) { // in functie de tip afiseaza valoarea case 'i': printf ("Intreg %d ", n.val.ival); break; case 'l': printf ("Intreg lung %ld ", n.val.lval); break; case 'f': printf ("Real %f ", n.val.fval); break; case 'd': printf ("Real dublu %.15lf ", n.val.dval); } } // citire numr Numar read (char tip) { Numar n; n.tipn=tip;

158

INFORMATIC*I* switch (tip) { case 'i': scanf case 'l': scanf case 'f': scanf case 'd': scanf } return n; } int main () { Numar a, b, c, d; a = read('i'); b = read('l'); c = read('f'); d = read('d'); write(a); write(b); write(c); write(d); return 0; }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR ("%d", &n.val.ival); break; ("%ld", &n.val.lval); break; ("%f", &n.val.fval); break; ("%lf", &n.val.dval);

2. Pentru cmpul discriminant se poate defini un tip enumerare, mpreun cu valorile constante (simbolice) pe care le poate avea. S se refac programul! Rezolvare:
#include<stdio.h> enum tnum {I, L, F, D}; typedef struct{ tnum tipn; union { int ival; long lval; float fval; double dval; } val; } Numar; void write (Numar switch (n.tipn) case I: printf case L: printf case F: printf case D: printf } } // definire tip tnum // tip numr (un caracter)

// valoare numar // definire tip de date Numar

n) { { ("%d", n.val.ival); break; ("%ld", n.val.lval); break; ("%f", n.val.fval);break; ("%.15lf", n.val.dval);

// // // //

int long float double

Numar read (tnum tip) { Numar n; n.tipn=tip; switch (tip) { case I: scanf ("%d", &n.val.ival); break; ... } return n; } int main () { Numar a,b,c,d; a = read(I); write(a); ... }

159

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.10. Alocarea memoriei n limbajul C


Cuvinte cheie
Clase de memorare, alocare static, alocare dinamic, variabile auto, variabile locale, variabile globale, variabile register, funcii standard, vectori de pointeri, sructuri alocate dinamic

IB.10.1. Clase de memorare (alocare a memoriei) n C Clasa de memorare arat cnd, cum i unde se aloc memorie pentru o variabil. Orice variabil are o clas de memorare care rezult fie din declaraia ei, fie implicit din locul unde este definit variabila. Zona de memorie utilizat de un program C cuprinde 4 subzone: Zona text: n care este pstrat codul programului Zona de date: n care sunt alocate (pstrate) variabilele globale Zona stiv: n care sunt alocate datele temporare (variabilele locale) Zona heap: n care se fac alocrile dinamice de memorie Moduri de alocare a memoriei: Static: variabile implementate n zona de date - globale Memoria este alocat la compilare n segmentul de date din cadrul programului i nu se mai poate modifica n cursul execuiei. Variabilele externe, definite n afara funciilor, sunt implicit statice, dar pot fi declarate static i variabile locale, definite n cadrul funciilor. Auto: variabile implementate n stiv - locale Memoria este alocat automat, la activarea unei funcii, n zona stiv alocat unui program i este eliberat automat la terminarea funciei. Variabilele locale unui bloc (unei funcii) i parametrii formali sunt implicit din clasa auto. Memoria se aloc n stiva ataat programului. Dinamic: variabile implementate n heap Memoria se aloc dinamic (la execuie) n zona heap ataat programului, dar numai la cererea explicit a programatorului, prin apelarea unor funcii de bibliotec ( malloc, calloc, realloc). Memoria este eliberat numai la cerere, prin apelarea funciei free Register: variabile implementate ntr-un registru de memorie IB.10.2. Clase de alocare a memoriei: Auto Variabilele locale unui bloc (unei funcii) i parametrii formali sunt implicit din clasa auto. Durata de via a acestor variabile este temporar: memoria este alocat automat, la activarea blocului/funciei, n zona stiv alocat programului i este eliberat automat la ieirea din bloc/terminarea funciei. Variabilele locale NU sunt iniializate! Trebuie s le atribuim o valoare iniial! Exemplu:
int doi() { int x = 2; return x; } int main() { int a; { int b = 5; a = b*doi(); } printf(a = %d\n, a); return 0; }

160

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Coninut stiv:
(x) 2 (b) 5 (a) 10

IB.10.3. Clase de alocare a memoriei: Static Memoria este alocat la compilare n segmentul de date din cadrul programului i nu s e mai poate modifica n cursul execuiei. Variabilele globale sunt implicit statice (din clasa static). Pot fi declarate static i variabile locale, definite n cadrul funciilor, folosind cuvntul cheie static. O variabil sau o funcie declarat (sau implicit) static are durata de via egal cu cea a programului. In consecin, o variabil static declarat ntr-o funcie i pstreaz valoarea ntre apeluri succesive ale funciei, spre deosebire de variabilele auto care sunt realocate pe stiv la fiecare apel al funciei i pornesc de fiecare dat cu valoarea primit la iniializarea lor (sau cu o valoare imprevizibil, dac nu sunt iniializate). Exemple:
int f1() { int x = 1; ...... } int f2() { static int y = 99; /*Variabil local static, iniializat cu 99 doar la primul apel al lui f2; valoarea ei este reinut pe parcursul apelurilor lui f2*/ ...... } int f() { static int nr_apeluri=0; nr_apeluri++; printf("funcia f() este apelata pentru a %d-a oara\n, nr_apeluri); return nr_apeluri; } int main() { int i; for (i=0; i<10; i++) f(); //f() apelata de 10 ori printf("functia f() a fost apelata de %d ori.", f()); // 11 ori!! return 0; } /*Variabil local, iniializat cu 1 la fiecare apel al lui f1*/

Observaii: Variabilele locale statice se folosesc foarte rar n practica programrii ( funcia de bibliotec strtok este un exemplu de funcie cu o variabil static). Variabilele statice pot fi iniializate numai cu valori constante (pentru c iniializarea are loc la compilare), dar variabilele auto pot fi iniializate cu rezultatul unor expresii (pentru c iniializarea are loc la execuie). Exemplu de funcie care afieaz un ntreg pozitiv n cod binar, folosind cturile mpririi cu puteri descresctoare ale lui 10:
// afisare intreg in binar void binar ( int x) {

161

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int n=digits(x); //functie care intoarce nr-ul de cifre al lui x int d=pw10 (n-1); //functie care calculeaza 10 la o putere intreaga while ( x >0) { printf("%d",x/d); //scrie catul impartirii lui x prin d x=x%d; d=d/10; //continua cu x = x%d si d = d/10 } }

Toate variabilele externe (i statice) sunt automat iniializate cu valori zero (inclusiv vectorii).
Cuvntul cheie static face ca o variabil global sau o funcie s fie privat(proprie) unitii unde a fost definit: ea devine inaccesibil altei uniti, chiar prin folosirea lui extern.

Cantitatea de memorie alocat pentru variabilele cu nume rezult din tipul variabilei i din dimensiunea declarat pentru vectori. Memoria alocat dinamic este specificat explicit ca parametru al funciilor de alocare, n numr de octei.
Memoria neocupat de datele statice i de instruciunile unui program este mprit ntre stiv i heap. Consumul de memorie stack (stiva) este mai mare n programele cu funcii recursive (numr mare de apeluri recursive). Consumul de memorie heap este mare n programele cu vectori i matrice alocate (i realocate) dinamic. De observat c nu orice vector cu dimensiune constant este un vector static; un vector definit ntr-o funcie (alta dect main) nu este static deoarece nu ocup memorie pe toat durata de execuie a programului, dei dimensiunea sa este stabilit la scrierea programului. Un vector definit ntr -o funcie este alocat pe stiv, la activarea funciei, iar memoria ocupat de vector este eliberat automat la terminarea funciei.

Sintez variabile locale / variabile globale O sintez legat de variabilele locale i cele globale din punct de vedere al duratai de via vs. domeniu de vizibilitate este dat n tabelul urmtor:

Alocare Durata de via


Iniializare

Variabile globale Static; la compilare Cea a ntregului program Cu zero

Variabile locale Auto; la execuie bloc Cea a blocului n care e declarat Nu se face automat

IB.10.4. Clase de alocare a memoriei: Register


A treia clas de memorare este clasa register, pentru variabile crora li se aloc registre ale procesorului i nu locaii de memorie, pentru un timp de acces mai bun. O variabil declarat register solicit sistemului alocarea ei ntr-un registru main, dac este posibil. De obicei compilatorul ia automat decizia de alocare a registrelor mainii pentru anumite variabile auto din funcii. Se utilizeaz pentru variabile foarte solicitate, pentru mrirea vitezei de execuie.

Exemplu:
{ register int i; for(i = 0; i < N; ++i){ /* */ }

162

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

} /* se elibereaza registrul */

IB.10.5. Clase de alocare a memoriei: extern O variabil extern este o variabil definit n alt fiier. Declaraia extern i spune compilatorului c identificatorul este definit n alt fiier surs (extern). Ea este este alocat n funcie de modul de declarare din fiierul surs. Exemplu:
// File1.cpp extern int i; // File2.cpp int i = 88; // Declara aceasta variabila ca fiind definita in alt fisier // Definit aici

IB.10.6. Alocarea dinamic a memoriei Reamintim c pentru variabilele alocate dinamic memoria se aloc dinamic (la execuie) n zona heap ataat programului, dar numai la cererea explicit a programatorului, prin apelarea unor funcii de bibliotec (malloc, calloc, realloc). Memoria este eliberat numai la cerere, prin apelarea funciei free. Principalele diferene ntre alocarea static i cea dinamic sunt: La alocarea static, compilatorul aloc i elibereaz memoria automat, ocupndu-se astfel de gestiunea memoriei, n timp ce la alocarea dinamic programatorul este cel care gestioneaz memoria, avnd un control deplin asupra adreselor de memorie i a coninutului lor. Entitile alocate static sau auto sunt manipulate prin intermediul unor variabile, n timp ce cele alocate dinamic sunt gestionate prin intermediul pointerilor! IB.10.6. 1 Funcii standard pentru alocarea dinamic a memoriei
Funciile standard pentru alocarea dinamica a memoriei sunt declarate n fiierele stdlib.h i alloc.h.

Alocarea memoriei:
void *malloc(size_t size); Aloc memorie de dimensiunea size octei

void *calloc(int nitems, size_t size); Aloc memorie pentru nitems de dimensiune size octei i iniializeaz zona alocat cu zerouri

Cele dou funcii au ca rezultat adresa zonei de memorie alocate (de tip void. Dac cererea de alocare nu poate fi satisfcut, pentru c nu mai exista un bloc continuu de dimensiunea solicitat, atunci funciile de alocare au rezultat NULL. Funciile de alocare au rezultat void* deoarece funcia nu tie tipul datelor ce vor fi memorate la adresa respectiv. La apelarea funciilor de alocare se folosesc:
Operatorul sizeof pentru a determina numrul de octei necesar unui tip de date (variabile); Operatorul de conversie cast pentru adaptarea adresei primite de la funcie la tipul datelor memorate la adresa respectiv (conversie necesar atribuirii ntre pointeri de tipuri diferite).

Exemple:
//aloca memorie pentru 30 de caractere:

163

INFORMATIC*I* char * str = (char*) malloc(30);

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

//aloca memorie ptr. n ntregi: int * a = (int *) malloc( n * sizeof(int)); //aloca memorie ptr. n ntregi si initializeaza cu zerouri int * a= (int*) calloc (n, sizeof(int) );

IB.10.6. 2 Realocarea memoriei Realocarea unui vector care crete (sau scade) fa de dimensiunea estimat anterior se poate face cu funcia realloc, care primete adresa veche i noua dimensiune i ntoarce noua adres: void *realloc(void* adr, size_t size); Funcia realloc realizeaz urmtoarele operaii: Aloc o zon de dimensiunea specificat prin al doilea parametru. Copiaz la noua adres datele de la adresa veche (primul parametru). Elibereaz memoria de la adresa veche. Exemple:
// dublare dimensiune curenta a zonei de la adr. a a = (int *)realloc (a, 2*n* sizeof(int));

Atenie! Se va evita redimensionarea unui vector cu o valoare foarte mic de un numr mare de ori; o strategie de realocare folosit pentru vectori este dublarea capacitii lor anterioare.

Exemplu de funcie cu efectul funciei realloc, dar doar pentru caractere:


char * ralloc (char * p, int size) { char *q; // p = adresa veche // q=adresa noua

if (size==0) {
free(p); return NULL; } q = (char*) malloc(size); if (q) { memcpy(q,p,size); free(p); } return q; }

// echivalent cu free

// // // //

aloca memorie daca alocare reusita copiere date de la p la q elibereaza adresa p

// q poate fi NULL

Observaie: La mrirea blocului, coninutul zonei alocate n plus nu este precizat, iar la micorarea blocului se pierd datele din zona la care se renun.
IB.10.6. 3 Eliberarea memoriei

Funcia free are ca argument o adres (un pointer) i elibereaz zona de la adresa respectiv (alocat dinamic). Dimensiunea zonei nu mai trebuie specificat deoarece este memorat la nceputul zon ei alocate (de ctre funcia de alocare): void free(void* adr); Eliberarea memoriei prin free este inutil la terminarea unui program, deoarece nainte de ncrcarea i lansarea n execuie a unui nou program se elibereaz automat toat memoria heap.
Exemple: char *str; str=(char *)malloc(10*sizeof(char));

164

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

str=(char *)realloc(str,20*sizeof(char)); free(str);

Observaie: Atenie la definirea de iruri n mod dinamic! irul respectiv trebuie iniializat cu adresa unui alt ir sau a unui spaiu alocat pe heap (adic alocat dinamic)!
Exemple: char *sir3; char ir1*30+;

// Varianta 1: sir3 ia adresa unui ir static sir3 = sir1; // Echivalent cu: sir3=&sir1; sir3=&sir1[0];

char *sir4="test"; //sir4 este iniializat cu adresa unui ir c onstant


// Varianta 2: se aloc dinamic un spaiu pe heap

sir3=(char *)malloc(100*sizeof(char)); Exemplu Program care aloc spaiu pentru o variabil ntreag dinamic, dup citire i tiprire, spaiul fiind eliberat. Modificai programul astfel nct variabila dinamic s fie de tip double.

Rezolvare

#include <stdlib.h> #include <stdio.h>

int main(){

int *pi; pi=(int *)malloc(sizeof(int)); if(pi==NULL){ puts("*** Memorie insuficienta ***"); return 1; } // revenire din main

printf("valoare:");

165

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

//citirea variabilei dinamice, de pe heap, de la adresa din pi!!! scanf("%d",pi); *pi=*pi*2; // dublarea valorii

printf("val=%d,pi(adresa pe heap)=%p,adr_pi=%p\n", *pi, pi, &pi);

// sizeof aplicat unor expresii: printf("%d %d %d\n",sizeof(*pi), sizeof(pi), sizeof(&pi));

free(pi);

//eliberare spatiu

printf("pi(dupa elib):%p\n",pi); // nemodificat, dar invalid! return 0; }

IB.10.7. Vectori alocai dinamic Structura de vector are avantajul simplittii i economiei de memorie fa de alte structuri de date folosite pentru memorarea unei colecii de date. Dezavantajul unui vector cu dimensiune fix (stabilit la declararea vectorului i care nu mai poate fi modificat la execuie) apare n aplicaiile cu vectori de dimensiuni foarte variabile, n care este dificil de estimat o dimensiune maxim, fr a face risip de memorie. De cele mai multe ori programele pot afla din datele citite dimensiunile vectorilor cu care lucreaz i deci pot face o alocare dinamic a memoriei pentru aceti vectori. Aceasta este o soluie mai flexibil, care folosete mai bine memoria disponibil i nu impune limitri arbitrare asupra utilizrii unor programe. n limbajul C nu exist practic nici o diferen ntre utilizarea unui vector cu dimensiune fix i utilizarea unui vector alocat dinamic, ceea ce ncurajeaz si mai mult utilizarea unor vectori cu dimensiune variabil. Un vector alocat dinamic se declar ca variabil pointer care se iniializeaz cu rezultatul funciei de alocare. Tipul variabilei pointer este determinat de tipul componentelor vectorului. Exemplu:
#include <stdlib.h> #include <stdio.h> int main() { int n, i; int * a; // adresa vector alocat dinamic printf ("n="); scanf ("%d", &n); // dimensiune vector a=(int *) calloc (n,sizeof(int)); // aloca memorie pentru vector // sau: a=(int*) malloc (n*sizeof(int)); // citire component vector: printf ("componente vector: \n"); for (i=0;i<n;i++) scanf ("%d", &a[i]); // afisare vector: for (i=0;i<n;i++) printf ("%d ",a[i]); return 0; }

// sau

scanf (%d, a+i);

166

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exist i cazuri n care datele memorate ntr-un vector rezult din anumite prelucrri, iar numrul lor nu poate fi cunoscut de la nceputul execuiei. n acest caz se poate recurge la o realocare dinamic a memoriei. O strategie de realocare pentru vectori este dublarea capacitii lor anterioare. n exemplul urmtor se citete un numr necunoscut de valori ntregi ntr-un vector extensibil:
Program care citete numere reale pn la CTRL+Z, le memoreaz ntr-un vector alocat i realocat dinamic n funcie de necesiti i le afieaz.

Rezolvare: #include <stdio.h> #include <stdlib.h> #define INCR 4

int main() { int n,n_crt,i ; float x, * v; n = INCR; n_crt = 0; // dimensiune memorie alocata // numar curent elemente n vector //alocare initiala

v = (float *)malloc (n*sizeof(float));

while (scanf("%f",&x) !=EOF){ if (n_crt == n) { n = n + INCR; v = (float *) realloc (v, n*sizeof(float) ); } v[n_crt++] = x; } for (i=0; i<n_crt; i++) printf ("%.2f ", v[i]); return 0; } //realocare

Din exemplele anterioare lipsete eliberarea memoriei alocate pentru vectori, dar fiind vorba de un singur vector alocat n funcia main i necesar pe toat durata de execuie, o eliberare final este inutil. Eliberarea explicit poate fi necesar pentru vectori de lucru, alocai dinamic n funcii. IB.10.8. Matrice alocate dinamic Alocarea dinamic pentru o matrice este important deoarece folosete economic memoria i permite matrice cu linii de lungimi diferite. De asemenea reprezint o soluie bun la problema parametrilor de funcii de tip matrice.

167

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

O matrice alocat dinamic este de fapt un vector de pointeri ctre fiecare linie din matrice, deci un vector de pointeri la vectori alocai dinamic. Dac numrul de linii este cunoscut sau poate fi estimat valoarea lui maxim, atunci vectorul de pointeri are o dimensiune constant. O astfel de matrice se poate folosi la fel ca o matrice declarat cu dimensiuni constante. Exemplu de declarare matrice de ntregi: int * a[M]; // M este o constanta simbolica Dac nu se poate estima numrul de linii din matrice atunci i vectorul de pointeri se aloc dinamic, iar declararea matricei se face ca pointer la pointer: int** a; n acest caz se va aloca mai nti memorie pentru un vector de pointeri (funcie de numrul liniilor) i apoi se va aloca memorie pentru fiecare linie cu memorarea adreselor liniilor n vectorul de pointeri. Notaia a[i][j] este interpretat astfel pentru o matrice alocat dinamic: a[i] conine un pointer (o adres b) b[j] sau b+j conine ntregul din poziia j a vectorului cu adresa b.
Exemplu S se scrie funcii de alocare a memoriei i afiare a elementelor unei matrice de ntregi alocat dinamic.

#include<stdio.h> #include<stdlib.h>

// rezultat adresa matrice sau NULL int ** intmat ( int nl, int nc) { int i; int ** p=(int **) malloc (nl*sizeof (int*)); if ( p != NULL) for (i=0; i<nl ;i++) p[i] =(int*) calloc (nc,sizeof (int)); return p; }

void printmat (int ** a, int nl, int nc) { int i,j;

for (i=0;i<nl;i++) { for (j=0;j<nc;j++) printf (%2d, a*i+*j+ ); printf(\n); }

168

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

} int main () { int **a, nl, nc, i, j;

printf ("nr linii i nr coloane: \n"); scanf ("%d%d", &nl, &nc); a= intmat(nl,nc);

for (i=0;i<nl;i++) for (j=0;j<nc;j++) a[i][j]= nc*i+j+1;

printmat (a ,nl,nc); return 0; }

Funcia printmat dat anterior nu poate fi folosit pentru afiarea unei matrice cu dimensiuni constante. Explicaia este interpretarea diferit a coninutului zonei de la adresa aflat n primul argument. Astfel, chiar dac exemplul urmtor este corect sintactic el nu se execut corect:
int x [2][2]={{1,2},{3,4}}; printmat ( (int**)x, 2, 2); // 2 linii i 2 coloane

IB.10.9. Funcii cu rezultat vector O funcie nu poate avea ca rezultat un vector sub forma: int [] funcie() {} O funcie poate avea ca rezultat doar un pointer !! int *funcie() {} De obicei, rezultatul pointer este egal cu unul din argumente, eventual modificat n funcie. Exemplu corect:
// incrementare pointer p char * incptr ( char * p) { return ++p; }

Atenie! Acest pointer nu trebuie s conin adresa unei variabile locale, deoarece: O variabil local are o existen temporar, garantat numai pe durata executrii funciei n care este definit (cu excepia variabilelor locale statice) Adresa unei astfel de variabile nu trebuie transmis n afara funciei, pentru a fi folosit ulterior!! Exemplu greit:
// vector cu cifrele unui nr intreg de maxim cinci cifre

169

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int * cifre (int n) { int k, c[5]; // vector local for (k=4;k>=0;k--) { c[k]=n%10; n=n/10; } return c; // aici este eroarea ! } //warning la compilare i POSIBIL rezultate greite n main!!

O funcie care trebuie s transmit ca rezultat un vector poate fi scris corect n n mai multe feluri: 1. Primete ca parametru adresa vectorului (definit i alocat n alt funcie) i depune rezultatele la adresa primit (este soluia recomandat!!)
void cifre (int n, int c[ ]) { int k; for (k=4;k>=0;k--) { c[k]=n%10; n=n/10; } } int main(){ int a[10]; . cifre(n,a); . }

2. Aloc dinamic memoria pentru vector (cu "malloc") Aceast alocare (pe heap) se menine i la ieirea din funcie. Funcia are ca rezultat adresa vectorului alocat n cadrul funciei. Problema este unde i cnd se elibereaz memoria alocat.
int * cifre (int n) { int k, *c; // vector local c = (int*) malloc (5*sizeof(int)); for (k=4;k>=0;k--) { c[k]=n%10; n=n/10; } return c; // corect }

3. O soluie oarecum echivalent este utilizarea unui vector local static, care continu s existe i dup terminarea funciei. IB.10.10. Vectori de pointeri la date alocate dinamic Ideea folosit la matrice alocate dinamic este aplicabil i pentru alte date alocate dinamic: adresele acestor date sunt reunite ntr-un vector de pointeri. Situaiile cele mai frecvente sunt: vectori de pointeri la iruri de caractere alocate dinamic vectori de pointeri la structuri alocate dinamic. Exemplu de utilizare a unui vector de pointeri la structuri alocate dinamic:
#include<stdio.h> #include<stdlib.h>
typedef struct { int zi, luna, an; } date;

170

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

// afisare date reunite n vector de pointeri void print_vp ( date * vp[], int n) { int i; for(i=0;i<n;i++) printf ("%4d %4d %4d \n", vp[i]->zi, vp[i]->luna, vp[i]->an); printf ("\n"); } int main () { date d, *dp; date *vp[100]; int n=0; while (scanf ("%d%d%d", &d.zi, &d.luna, &d.an) { dp=(date*)malloc (sizeof(date)); // alocare dinamica ptr structur *dp=d; // copiaza datele citite la dp vp[n++]=dp; // memoreaza adresa in vector } print_vp (vp,n); }

De reinut c trebuie create adrese distincte pentru fiecare variabil structur i c ar fi greit s punem adresa variabilei d n toate poziiile din vector! Este posibil i varianta urmtoare pentru ciclul principal din main dac cunoatem numrul de elemente din structur:
.... scanf (%d,&n); // numar de structuri ce vor fi citite for (k=0; k<n; k++) { dp = (date*) malloc (sizeof(date));// alocare dinamica ptr structur scanf ("%d%d%d", &dp->zi, &dp->luna, &dp->an) vp[n++]=dp; // memoreaza adresa in vector } ....

Exemplu Program pentru citirea unor nume, alocare dinamic a memoriei pentru fiecare ir (n funcie de lungimea irului citit) i memorarea adreselor irurilor ntr-un vector de pointeri. n final se vor afia numele citite, pe baza vectorului de pointeri. S se adauge programului anterior o funcie de ordonare a vectorului de pointeri la iruri, pe baza coninutului fiecrui ir. Programul va afia lista de nume n ordine alfabetic. a. Vectorului de pointeri i se va aloca o dimenisune fix. b. Vectorul de pointeri se va aloca dinamic, funcie de numrul de iruri. Rezolvare a:
void printstr ( char * vp[], int n) { //afisare int i; for(i=0;i<n;i++) printf ("%s\n",vp[i]); }

int readstr (char * vp*+) , // citire siruri i creare vector de pointeri

171

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

int n=0; char * p, sir[80]; while ( scanf ("%s", sir) == 1) { vp[n]= (char*) malloc (strlen(sir)+1); strcpy( vp[n],sir); //sau: vp[n]=strdup(sir); ++n; } return n; }

/* ordonare vector de pointeri la iruri prin Bubble Sort (metoda bulelor)*/ void sort ( char * vp[],int n) { int i,j,schimb=1; char * tmp;

while(schimb){ schimb=0; for (i=0;i<n-1;i++) if ( strcmp (vp[i],vp[i+1])>0) { tmp = vp[i]; vp[i] = vp[i+1]; vp[i+1] = tmp; schimb = 1; } } } int main () { int n; char * vp[1000]; // vector de pointeri, cu dimens. fixa

n=readstr(vp); // citire siruri i creare vector sort(vp,n); // ordonare vector

printstr(vp,n); // afiare iruri return 0; }

172

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.10.11. Anexa A: Structuri alocate dinamic n cazul variabilelor structur alocate dinamic i care nu au nume se va face o indirectare printrun pointer pentru a ajunge la variabila structur. Avem de ales ntre urmtoarele dou notaii echivalente: pt->camp (*pt).camp unde pt este o variabil care conine un pointer la o structur cu cmpul camp. O colecie de variabile structur alocate dinamic se poate memora n dou moduri: Ca un vector de pointeri la variabilele structur alocate dinamic; Ca o list nlntuit de variabile structur, n care fiecare element al listei conine i un cmp de legtur ctre elementul urmtor (ca pointer la structur). Pentru primul mod de memorare a se vedea Vectori de pointeri la date alocate dinamic. Liste nlnuite O list nlnuit (linked list) este o colecie de variabile alocate dinamic (de acelai tip), dispersate n memorie, dar legate ntre ele prin pointeri, ca ntr-un lan. ntr-o list liniar simplu nlnuit fiecare element al listei conine adresa elementului urmtor din list. Ultimul element poate conine ca adres de legatur fie constanta NULL, fie adresa primului element din list (lista circular). Adresa primului element din list este memorat ntr-o variabil cu nume i numit cap de lista (list head). Pentru o list vid variabila cap de list este NULL. Structura de list este recomandat atunci cnd colecia de elemente are un coninut foarte variabil (pe parcursul execuiei) sau cnd trebuie pstrate mai multe liste cu coninut foarte variabil. Un element din list (un nod de list) este de un tip structur i are (cel puin) dou cmpuri: un cmp de date (sau mai multe) un cmp de legtur Definiia unui nod de list este o definitie recursiv, deoarece n definirea cmpului de legtur se folosete tipul n curs de definire. Exemplu pentru o list de ntregi:
typedef struct snod { int val ; struct snod * leg ; } nod; // camp de date // camp de legatura

Programul urmtor arat cum se poate crea i afisa o list cu adugare la nceput (o stiv realizat ca list nlnuit):
int main ( ) { nod *lst=NULL, *nou, * p; // lst = adresa cap de lista int x; // creare lista cu numere citite while (scanf("%d",&x) > 0) { // citire numar intreg x nou=(nod*)malloc(sizeof(nod));// creare nod nou nou->val=x; // completare camp de date din nod nou->leg=lst; lst=nou; // legare nod nou la lista } // afisare list (fara modificare cap de lista) p=lst; while ( p != NULL) { // cat mai sunt noduri printf("%d ", p->val); // afisare numar de la adr p

173

INFORMATIC*I* p=p->leg; } }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // avans la nodul urmator

Cmpul de date poate fi la rndul lui o structur specific aplicaiei sau poate fi un pointer la date alocate dinamic (un ir de caractere, de exemplu). De obicei se definesc funcii pentru operaiile uzuale cu liste. Exemple:
typedef struct nod { int val; struct snod *leg; } nod; // un nod de lista inlantuita // date din fiecare nod // legatura la nodul urmator

// insertie la inceput lista nod* insL( nod* lst, int x) { nod* nou ; // adresa nod nou if ((nou=(nod*)malloc(sizeof(nod)))==NULL) return NULL; nou->val=x; nou->leg=lst; return nou; // lista incepe cu nou } // afisare continut lista void printL ( nod* lst) { while (lst != NULL) { printf("%d ",lst->val); lst=lst->leg; } } int main () { nod* lst; int x; lst=NULL; while (scanf("%d",&x) > 0) lst=insL(lst,x); printL (lst); }

// scrie informatiile din nod // si se trece la nodul urmtor

// creare si afisare lista stiva // initial lista e vida // introduce pe x in lista lst // afisare lista

Alte structuri dinamice folosesc cte doi pointeri sau chiar un vector de pointeri; ntr-un arbore binar fiecare nod conine adresa succesorului la stnga i adresa succesorului la dreapta, ntr-un arbore multici fiecare nod conine un vector de pointeri ctre succesorii acelui nod. IB.10.12. Anexa B: Operatori pentru alocare dinamic in C++ n C++ s-au introdus doi operatori noi: pentru alocarea dinamic a memoriei new pentru eliberarea memoriei dinamice delete destinai s nlocuiasc funciile de alocare i eliberare. Operatorul new are ca operand un nume de tip, urmat n general de o valoare iniial pentru variabila creat (ntre paranteze rotunde); rezultatul lui new este o adres (un pointer de tipul specificat) sau NULL daca nu exist suficient memorie liber. Exemple:
nod * pnod; pnod = new nod; int * p = new int(3); // pointer la nod de lista // alocare fara iniializare // alocare cu iniializare

Operatorul new are o form puin modificat la alocarea de memorie pentru vectori, pentru a specifica numrul de componente. Exemplu: 174

INFORMATIC*I* int * v = new int [n];

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // vector de n intregi

Operatorul delete are ca operand o variabil pointer i are ca efect eliberarea blocului de memorie adresat de pointer, a crui mrime rezult din tipul variabilei pointer sau este indicat explicit. Exemple:
int * v; delete v; delete [ ] v; delete [n] v; // elibereaza sizeof(int) octei // elibereaza n*sizeof(int) octei

Exemplu de utilizare new i delete pentru un vector de ntregi alocat dinamic:


#include <iostream> #include <cstdlib> int main() { const int SIZE = 5; int *pArray; pArray = new int[SIZE]; // alocare memorie

// atribuie numere aleatoare intre 0 and 99 for (int i = 0; i < SIZE; i++) { *(pArray + i) = rand() % 100; } // afisare for (int i = 0; i < SIZE; i++) { cout << *(pArray + i) << " "; } cout << endl; delete[] pArray; return 0; } // eliberare memorie

Dup alocarea de memorie cu new se pot folosi funciile realloc i free pentru realocare sau eliberare de memorie.

175

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.11. Operaii cu fiiere n limbajul C


Cuvinte cheie
Fiiere text, fiiere binare, deschidere/ nchidere fiiere, citire-scriere fiiere, acces direct, fiiere predefinite, funcia fflush, redirectarea fiierelor standard, fiiere n C++

IB.11.1. Noiunea de fiier Un fiier este o colecie de date memorate pe un suport extern i care este identificat printr-un nume. Coninutul fiierelor poate fi foarte variat: texte, inclusiv programe surs numere alte informaii binare: programe executabile, numere n format binar, imagini sau sunete codificate numeric .a. Numrul de elemente ale unui fiier este variabil (poate fi nul). Fiierele de date se folosesc pentru: date iniiale mai numeroase rezultate mai numeroase pstrarea permanent a unor date de interes pentru anumite aplicaii. Fiierele sunt entiti ale sistemului de operare i ca atare ele au nume care respect conveniile sistemului, fr legtur cu un anume limbaj de programare. Operaiile cu fiiere sunt realizate de ctre sistemul de operare, iar compilatorul unui limbaj traduce funciile de acces la fiiere n apeluri ale funciilor de sistem. De obicei prin fiier se subnelege un fiier disc (pe suport magnetic sau optic), dar noiunea de fiier este mai general i include orice flux de date din exterior spre memorie sau dinspre memoria intern spre exterior. De aceea s-a introdus cuvntul stream, tradus prin flux de date si sinonim cu fiier logic, deci orice surs sau destinaie extern a datelor. Stream (flux de date, canal) este n acest context sinonim cu file (fiier): pune accent pe aspectul dinamic al transferului de date. Programatorul se refer la un fiier printr-o variabil; tipul acestei variabile depinde de limbajul folosit i chiar de funciile utilizate (n C). Asocierea dintre numele extern (un ir de caractere) i variabila din program se face la deschiderea unui fiier, printr-o funcie standard. IB.11.2. Tipuri de fiiere n C Fiiere text conin o succesiune de linii, separate prin NewLine fiecare linie are 0 sau mai multe caractere tipribile i/sau tab Fiiere binare conin o succesiune de octei Un fiier text conine numai caractere ASCII, grupate n linii de lungimi diferite, fiecare linie terminat cu unul sau dou caractere terminator de linie. Caracter terminator de linie: 176

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

fiierele Unix/Linux: un singur caracter terminator de linie \n fiierele Windows i MS-DOS: caracterele \r i \n (CR,LF) ca terminator de linie Un fiier text poate fi terminat printr-un caracter terminator de fiier (Ctrl-Z = EOF ) Valoarea efectiv este dependent de sistem, dar n general este -1. Acest terminator nu este ns obligatoriu. Sfritul unui fiier disc poate fi detectat i pe baza lungimii fiierului (numr de octei), memorat pe disc. Funciile de citire sau de scriere cu format din/n fiiere text realizeaz conversia automat din format extern (ir de caractere) n format intern (binar virgul fix sau virgul mobil) la citire i conversia din format intern n format extern, la scriere pentru numere ntregi sau reale. Fiierele binare pot conine: numere n reprezentare intern (binar) articole (structuri de date) fiiere cu imagini grafice, n diverse formate, etc Citirea i scrierea se fac fr conversie de format. Pentru fiecare tip de fiier binar este necesar un program care s cunoasc i s interpreteze corect datele din fiier (structura articolelor). Este posibil ca un fiier binar s conin numai caractere, dar funciile de citire i de scriere pentru aceste fiiere nu cunosc noiunea de linie; ele specific numrul de octei care se citesc sau se scriu. Consola i imprimanta sunt considerate fiiere text. Fiierele disc trebuie deschise i nchise, dar fiierele consol i imprimanta nu trebuie deschise i nchise. IB.11.3. Operarea cu fiiere Pentru operarea cu un fiier (text sau binar) se definete o variabil de tip FILE * pentru accesarea fiierului: FILE * - tip structur definit n stdio.h Conine informaii referitoare la fiier i la tamponul de transfer de date ntre memoria central i fiier: adresa lungimea tamponului modul de utilizare a fiierului indicator de sfrit de fiier indicator de poziie n fiier Etapele pentru operarea cu un fiier n limbajul C sunt: se deschide fiierul pentru un anumit mod de acces, folosind funcia de biblioteca fopen, o realizeaz i asocierea ntre variabila fiier i numele extern al fiierului se prelucreaz fiierul o operaii citire/scriere se nchide fiierul folosind funcia de biblioteca fclose.

177

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

IB.11.4. Funcii pentru deschidere i nchidere fiiere Funciile standard pentru acces la fiiere sunt declarate n stdio.h. Dup cum spuneam mai devreme, funciile de citire/scriere/pozitionare n fiier folosesc pentru identificarea unui fiier o variabil pointer la o structur predefinit FILE. IB.11.4. 1 Deschiderea unui fiier Pentru a citi sau scrie dintr-un/ntr-un fiier disc, acesta trebuie mai nti deschis folosind funcia fopen. FILE *fopen (const char *numefisier, const char *mod); Deschide fiierul cu numele dat pentru acces de tip mod Returneaz pointer la fiier sau NULL dac fiierul nu poate fi deschis Valoarea returnat este memorat n variabila fiier, care a fost declarat (FILE *) pentru accesarea lui. unde: numefisier: numele fiierului mod: ir de caractere (ntre 1 i 3 caractere): r - readonly , este permis doar citirea dintr-un fiier existent w - write, creaz un nou fiier, sau dac exist deja, distruge vechiul coninut a - append, deschide pentru scriere un fiier existent (scrierea se va face n continuarea informaiei deja existente n fiier, deci pointerul de acces se plaseaz la sfritul fiierului) + - permite scrierea i citirea din acelasi fiier - actualizare (ex: "r+", "w+", "a+"). t sau b - tip fiier ("text", "binary"), implicit este t Primul argument al funciei fopen este numele extern al fiierului scris cu respectarea conveniilor limbajului C. Numele fiier extern poate include urmtoarele: Numele unitii de disc sau partiiei disc ( ex: A:, C:, D:, E:) Calea spre fiier: succesiune de nume de fiiere catalog (director), separate printr-un caracter ('\' n MS-DOS i MS-Windows, sau '/' n Unix i Linux) Numele propriu-zis al fiierului Extensia, care indic tipul fiierului i care poate avea ntre 0 i 3 caractere n MS DOS. Sistemele MS-DOS i MS-Windows nu fac deosebire ntre litere mari i litere mici, n cadrul numelor de fiiere. Atenie! pentru separarea numelor de cataloage dintr-o cale se vor folosi: \\ - pentru a nu se considera o secven de caractere escape sau: caracterul / Exemple:
char *numef = "C:\\WORK\\T.TXT"; char *numef = c:/work/t.txt;

La deschiderea unui fiier se iniializeaz variabila pointer asociat, iar celelalte funcii se refer la fiier numai prin intermediul variabilei pointer. Funcia fopen are rezultat NULL (0) dac fiierul specificat nu este gsit dup cutare n directorul curent sau pe calea specificat. 178

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exemplu:
//exemplu 1 char *numef = "C:\\WORK\\T.TXT"; // sau c:/work/t.txt FILE * f; // pentru referire la fiier if ( (f=fopen(numef,"r")) == NULL) { printf("Eroare la deschidere fiier %s \n", numef); return; } //exemplu 2 #include <stdio.h> int main ( ) { FILE * f; // pentru referire la fiier // deschide un fiier binar ptr citire f = fopen ( c:\\t.txt", "rb ); printf ( f == NULL ? "Fiier negasit" : " Fiier gasit"); ... if (f) // dac fiier existent fclose(f); // nchide fiier return 0; }

Diferena dintre b i t este aceea c la citirea dintr-un fiier binar toi octeii sunt considerai ca date i sunt transferai n memorie, iar la citirea dintr-un fiier text anumii octei sunt interpretai ca terminator de linie (\0x0a) sau ca terminator de fiier (\0x1a). Nu este obligatoriu ca orice fiier text s se termine cu un caracter special cu semnificaia sfrit de fiier (CTRL-Z , de exemplu) . Pentru fiierele text sunt folosite modurile: w pentru crearea unui nou fiier r pentru citirea dintr-un fiier a pentru adugare la sfritul unui fiier existent Modul w+ poate fi folosit pentru citire dup creare fiier. Deschiderea n modul w terge orice fiier existent cu acelai nume, fr avertizare, dar programatorul poate verifica existena unui fiier n acelai director nainte de a crea unul nou. Pentru fiierele binare se practic actualizarea pe loc a fiierelor, fr inserarea de date ntre cele existente, deci modurile r+, a+, w+. (literele r i w nu pot fi folosite simultan). Fiierele standard de intrare-ieire (tastatura i ecranul consolei) au asociate variabile de tip pointer cu nume predefinit (stdin i stdout); care pot fi folosite n funciile destinate tuturor fisierelor, cum ar fi fflush. Pentru nchiderea unui fiier disc se folosete funcia fclose: int fclose(FILE *fp); nchide fiierul i elibereaz zona tampon n caz de succes ntoarce 0, altfel, ntoarce EOF. nchiderea este absolut necesar pentru fiierele n care s-a scris ceva, dar poate lipsi dac s-au fcut doar citiri din fiier. IB.11.5. Operaii uzuale cu fiiere text 179

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Accesul la fiiere text se poate face fie la nivel de linie fie la nivel de caracter dar numai secvenial. Deci nu se pot citi/scrie linii sau caractere dect n ordinea memorrii lor n fiier i nu pe srite (aleator)! Nu se pot face modificri ntr-un fiier text fr a crea un alt fiier, deoarece nu sunt de conceput deplasri de text n fiier! Pentru citire/scriere din/n fiierele standard stdin/stdout se folosesc funcii cu nume puin diferit i cu mai puine argumente, dar se pot folosi i funciile generale destinate fiierelor disc. Urmeaz cteva perechi de funcii: Sintaxa Descriere Citete un caracter din f i l ntoarce ca un int fgetc (FILE * f); unsigned char convertit la int, // sau getc (FILE*) Returneaz EOF dac s-a ntlnit sfritul de fiier sau n caz de eroare. Scrie caracterul cu codul ascii c n fiier int fputc (int c, FILE * f); // sau putc (int, FILE*) char * fgets( char * line, int max, FILE Citete maxim n-1 caractere sau pn la \n inclusiv, i le depune n s, adaug la sfrit \0 *f); dar nu elimin terminatorul de linie \n. Returneaz adresa irului. La eroare ntoarce valoarea NULL. Scrie irul line n fiier, fr caracterul '\0'. int fputs (char * line, FILE *f); La eroare ntoarce EOF. Detectarea sfritului de fiier se poate face i cu ajutorul funciei feof (Find End of File): int feof ( FILE *fp); testeaz dac s-a ajuns la end-of-file al fiierului referit de fp returneaz 0 dac nu s-a detectat sfrit de fiier la ultima operaie de citire, respectiv o valoare nenul (adevarat) pentru sfrit de fiier. Atenie! Rezultatul lui feof se modific dup ncercarea de a citi dup sfritul fiierului! Se va scrie n fiierul de ieire i 1, rezultatul ultimului apel al funciei fgetc:
while ( ! feof(f1)) fputc(fgetc(f1),f2);

Soluia preferabil pentru ciclul de citire-scriere caractere este urmtoarea:


while ( (ch=fgetc(f1)) != EOF) fputc ( ch, f2);

Exemplu:
// citire i afiare linii dintr-un fiier #include<stdio.h> #include<stdlib.h> int main() { FILE *fp; char s[80]; if ( (fp=fopen("c:\\test.c","r")) == NULL ) {

180

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } while ( fgets(s,80,fp) != NULL ) printf ( "%s", s); fclose (fp); return 0; } /* Scriere sub form de litere mici caracterele dintr-un fiier n alt fiier numele sursei i destinaiei transmise n linia de comand. Lansarea n execuie a programului: copiere fiier_sursa.dat fiier_dest.dat */ #include<stdio.h> #include<ctype.h> int main(int argc, char** argv){ FILE * f1, * f2; int ch; f1= fopen (argv[1], "r"); f2= fopen (argv[2], "w"); if ( f1==0 || f2==0) { puts (" Eroare la deschidere fiiere \n"); return 1; } while ( (ch=fgetc(f1)) != EOF) // citeste din f1 fputc ( tolower(ch),f2); // scrie n f2 fclose(f1); fclose(f2); return 0; }

n principiu se poate citi integral un fiier text n memorie, dar n practic se citete o singur linie sau un numr de linii succesive, ntr-un ciclu repetat pn se termin fiierul (pentru a se putea prelucra fiiere orict de mari). Pentru actualizarea unui fiier text prin modificarea lungimii unor linii, tergerea sau inseria de linii se va scrie un alt fiier i nu se vor opera modificrile direct pe fiierul existent. IB.11.6. Intrri/ieiri cu conversie de format Datele numerice pot fi scrise n fiiere disc fie n format intern (mai compact), fie transformate n iruri de caractere (cifre zecimale, semn .a). Un fiier text ocup mai mult spaiu deoarece formatul ir de caractere necesit i caractere separator ntre numere. Avantajul este c un fiier text poate fi citit cu programe scrise n orice limbaj sau cu orice editor de texte sau cu alt program utilitar de vizualizare fiiere! Funciile de citire-scriere cu conversie de format i editare sunt: int fscanf (FILE * f, char * fmt, ...) realizeaz citirea cu format dintr-un fiier; analog scanf int fprintf (FILE * f, char * fmt, ...) identic cu printf cu deosebirea c scrie ntr-un fiier Pentru aceste funcii se aplic toate regulile de la funciile scanf i printf.

181

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Un fiier text prelucrat cu funciile fprintf i fscanf conine mai multe cmpuri de date separate ntre ele prin unul sau mai multe spaii albe (blanc, tab, linie nou). Coninutul cmpului de date este scris i interpretat la citire conform specificatorului de format pentru acel cmp. Exemplu de creare i citire fiier de numere:
FILE * f; int x; // creare fiier de date: f = fopen("num.txt", "w"); for (x=1;x<=100;x++) fprintf(f,"%4d", x); fclose(f); // f = pointer la fiier

// deschide fiier // scrie un numar // inchidere fiier

// citire i afiare fiier creat: f=fopen("num.txt", "r"); if ( (f == NULL ) { printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } while (fscanf(f,"%d", &x) == 1) // pana la sfirsit fiier printf("%4d", x); // afiare numar citit

Exemplu: ntr-un fiier de tip text sunt pstrate valorile reale ale unei msuratori sub forma: nr_msuratori val1 val2 val3 ... S se scrie programul care afieaz numrul de msurtori i valorile respective. Se vor aduga la fiier noi msuratori pn la introducerea valorii 0. Rezolvare:
#include <math.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #define MAX 100 void afiseaza(double *mas,int nrm){ int i; for (i=0; i<nrm; i++) printf ("Masuratoarea %d = %6.2e\n", i+1, mas[i]); } void loadmas (FILE *fp, int *nrm, double *mas){ int i=0; fscanf (fp,"%d", nrm); if (*nrm>MAX) *nrm = MAX; for ( ; i<*nrm; i++) fscanf (fp, "%lf", &mas[i]); } int main(){ FILE *fp; double masur[MAX], mas_noua=1; char nume_fis[12]; int nr_mas;

182

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

printf ("nume fisier:"); gets (nume_fis); if ( (fp = fopen(nume_fis, "r+") ) == 0 ){ printf ("nu exista fisierul\n"); exit (1); } loadmas (fp,&nr_mas,masur); afiseaza (masur,nr_mas); fclose(fp); if ( (fp = fopen(nume_fis, a") ) == 0 ){ printf ("nu exista fisierul\n"); exit (1); } printf ("\nmasuratori noi:\n); while( nr_mas++<MAX-1){ scanf(%lf,&mas_noua); if(mas_noua) fprintf (fp, "%lf\n", mas_noua); else break; } fclose(fp); if ( (fp = fopen(nume_fis, "r+") ) == 0 ){ printf ("nu exista fisierul\n"); exit (1); } fprintf (fp, "%d", --nr_mas); fclose (fp); return 0; }

IB.11.7. Funcii de citire-scriere pentru fiiere binare Un fiier binar este format n general din articole de lungime fix, fr separatori ntre articole. Un articol poate conine: un singur octet un numr binar (pe 2, 4 sau 8 octei) structur cu date de diferite tipuri Funciile de acces pentru fiiere binare fread i fwrite pot citi sau scrie unul sau mai multe articole, la fiecare apelare. Transferul ntre memorie i suportul extern se face fr conversie sau ed itare (adugare de caractere la scriere sau eliminare de caractere la citire). Prototipuri funcii intrare/iesire (fiiere binare b): size_t fread (void *ptr, size_t size, size_t nmemb, FILE *fp) Citete la adresa ptr cel mult nmemb elemente de dimensiune size din fiierul referit de fp size_t fwrite (void *ptr, size_t size, size_t nmemb, FILE *fp) Scrie n fiierul referit de fp cel mult nmemb elemente de dimensiune size de la adresa ptr

Exemple:
int a[10]; fread (a, sizeof(int), 10, fp); fwrite(a, sizeof(int),10,fp);

183

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

De remarcat c primul argument al funciilor fread i fwrite este o adres de memorie (un pointer): adresa unde se citesc date din fiier sau de unde se iau datele scrise n fiier. Al doilea argument este numrul de octei pentru un articol, iar al treilea argument este numrul de articole citite sau scrise. Numrul de octei citii sau scrii este egal cu produsul dintre lungimea unui articol i numrul de articole. Rezultatul funciilor este numrul de articole efectiv citite sau scrise i este diferit de argumentul 3 numai la sfrit de fiier (la citire) sau n caz de eroare de citire/scriere! Dac tim lungimea unui fiier i dac este loc n memoria RAM atunci putem citi un ntreg fiier printr-un singur apel al funciei fread sau putem scrie integral un fiier cu un singur apel al funciei fwrite. Citirea mai multor date dintr-un fiier disc poate conduce la un timp mai bun fa de repetarea unor citiri urmate de prelucrri, deoarece se pot elimina timpii de ateptare pentru poziionarea capetelor de citire scriere pe sectorul ce trebuie citit (rotaie disc plus comand capete). Programul urmtor scrie mai multe numere ntregi ntr-un fiier disc (unul cte unul) i apoi citete coninutul fiierului i afieaz pe ecran numerele citite.
int main () { FILE * f; int x; char * numef =num.bin; // creare fiier f=fopen(numef,"wb")); // fisier in directorul curent for (x=1; x<=100; x++) fwrite (&x,sizeof(float),1,f); fclose(f); // citire fiier pentru verificare if ( (f=fopen(numef,"rb")) == NULL ) { // fisier in directorul curent printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } printf("\n"); while (fread (&x,sizeof(float),1,f)==1) printf ("%4d ",x); fclose(f); return 0; }

Lungimea fiierului num.bin este de 200 de octei, cte 2 octei pentru fiecare numr ntreg, n timp ce lungimea fiierului num.txt creat anterior cu funcia fprintf este de 400 de octei (cte 4 caractere ptr fiecare numr). Pentru alte tipuri de numere diferena poate fi mult mai mare. De obicei articolele unui fiier au o anumit structur, n sensul c fiecare articol conine mai multe cmpuri de lungimi i tipuri diferite. Pentru citirea sau scrierea unor astfel de articole n program trebuie s existe (cel puin) o variabil structur care s reflecte structura articolelor. Exemplu de definire a structurii articolelor unui fiier simplu cu date despre elevi i a funciilor ce scriu sau citesc articole ce corespund unor variabile structur:
typedef struct { char nume[25]; float medie; } Elev; // creare fiier cu nume dat void creare(char * numef) { FILE * f; Elev s;

184

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

f=fopen(numef,"wb"); printf ("Nume i medie ptr. fiecare student: \n"); while (scanf ("%s %f ", s.nume, &s.medie) != EOF) fwrite(&s,sizeof(s),1,f); fclose (f); } // afiare coninut fiier pe ecran void listare (char* numef) { FILE * f; Elev e; if ( (f=fopen(numef,"rb")) == NULL ) { // fisier in directorul curent printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } while (fread (&e,sizeof(e),1,f)==1) printf ("%-25s %6.2f \n",e.nume, e.medie); fclose (f); } // adaugare articole la sfritul unui fiier existent void adaugare (char * numef) { FILE * f; Elev e; if ( (f=fopen(numef,"ab")) == NULL ) { // fisier in directorul curent printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } printf ("Adaugare nume i medie:\n"); while (scanf ("%s%f", e.nume, &e.medie) != EOF) fwrite (&e, sizeof(e), 1, f); fclose (f); }

IB.11.8. Funcii pentru acces direct la datele dintr-un fiier Accesul direct la date dintr-un fiier este posibil numai pentru un fiier cu articole de lungime fix i nseamn posibilitatea de a citi sau scrie oriunde ntr-un fiier, printr-o poziionare prealabil nainte de citire sau scriere. Fiierele mari care necesit regsirea rapid i actualizarea frecvent de articole vor conine numai articole de aceeai lungime. n C poziionarea se face pe un anumit octet din fiier, iar funciile standard permit accesul direct la o anumit adres de octet din fiier. Funciile pentru acces direct din stdio.h permit operaiile urmtoare: Poziionarea pe un anumit octet din fiier (fseek). Citirea poziiei curente din fiier (ftell). Memorarea poziiei curente i poziionare (fgetpos, fsetpos). Poziia curent n fiier este un numr de tip long, pentru a permite operaii cu fiiere foarte lungi. Poziia se obine printr-un apel al funciei ftell: long int ftell (FILE *fp) ntoarce valoarea indicatorului de poziie pentru fiier binar: numrul de octei de la nceputul fiierului pentru fiier text: o valoare ce poate fi utilizat de fseek pentru a seta indicatorul de poziie n fiier la aceast poziie. Funcia fseek are prototipul urmtor : int fseek (FILE *fp, long int offset, int poziie) poziioneaz indicatorul de poziie la valoarea dat de offset fa de: SEEK_SET sau 0 nceputul fiierului 185

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

SEEK_CUR sau 1 poziia curent SEEK_END sau 2 sfritul fiierului Offset reprezint numrul de octei fa de punctul de referin. Exemple: poziionarea la sfritul fiierului: fseek (fp, 0, SEEK_END) poziionarea la caracterul precedent: fseek (fp, -1, SEEK_CUR) poziionarea la inceputul fiierului: fseek (fp, 0, SEEK_SET) Atenie! Poziionarea relativ la sfritul unui fiier nu este garantat nici chiar pentru fiiere binare, astfel c ar trebui evitat! Ar trebui evitat i poziionarea fa de poziia curent cu o valoare negativ, care nu funcioneaz n toate implementrile! Funcia fseek este util n urmtoarele situaii: Pentru repoziionare pe nceput de fiier dup o cutare i nainte de o alt cutare secvenial n fiier (fr a nchide i a redeschide fiierul) Pentru poziionare pe nceputul ultimului articol citit, n vederea scrierii noului coninut (modificat) al acestui articol, deoarece orice operaie de citire sau scriere avanseaz automat poziia curent n fiier, pe urmtorul articol. Pentru acces direct dup coninutul unui articol (dup un cmp cheie), dup ce s-a calculat sau s-a gsit adresa unui articol cu cheie dat. ntr-un fiier text poziionarea este posibil numai fa de nceputul fiierului, iar poziia se obine printr-un apel al funciei ftell. Modificarea coninutului unui articol (fr modificarea lungimii sale) se face n mai muli pai: Se caut articolul ce trebuie modificat i se reine adresa lui n fiier (nainte sau dup citirea sa); Se modific n memorie articolul citit; Se readuce poziia curent pe nceputul ultimului articol citit; Se scrie articolul modificat, peste coninutul su anterior. Exemplu de secven pentru modificarea unui articol:
pos=ftell (f); fread (&e,sizeof(e),1,f ); . . . // modifica ceva in variabila fseek (f,pos,0); fwrite (&e,sizeof(e),1,f); // poziia inainte de citire e // repoziionare pe articolul citit // rescrie ultimul articol citit

Memorarea poziiei curente sau poziionarea se pot realiza utiliznd urmtoarele funcii: int fgetpos (FILE *fp, fpos_t *poziie) memoreaz starea curent a indicatorului de poziie al fluxului referit de fp n poziie; ntoarce 0 dac operaia s-a realizat cu succes! int fsetpos (FILE *fp, const fpos_t *poziie) seteaz indicatorul de poziie al fluxului referit de fp la valoarea data de poziie void rewind (FILE *fp) seteaz indicatorul de poziie al fluxului referit de fp la nceputul fiierului 186

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Exemplu: Funcie care modific coninutul mai multor articole din fiierul de elevi creat anterior.
// modificare coninut articole, dupa cautarea lor void modificare (char * numef) { FILE * f; Elev e; char nume[25]; long pos; int ef; if ( (f=fopen(numef,"rb+")) == NULL ) {// fisier in directorul curent printf ( "Nu se poate deschide la citire fiierul!\n ); exit (1); } do { printf ("Nume cautat: "); scanf ("%s",nume); if (strcmp(nume, .) == 0)

break; // datele se termin cu un punct

// cauta "nume" n fiier fseek (f, 0, 0); // readucere pe inceput de fiier while ( (ef=fread (&e, sizeof(e), 1, f)) ==1 ) if (strcmp (e.nume, nume)==0) { pos= ftell(f) - sizeof(e); break; } if ( ef < 1) break; printf ("noua medie: "); scanf ("%f", &e.medie); fseek (f, pos, 0); // pozit. pe inceput articol gasit fwrite (&e, sizeof(e), 1, f); // rescrie articol modificat } while (1); fclose (f); } int main(){ char name[]="c:elev.txt; creare (name); listare (name); adaugare (name); modificare (name); listare (name); return 0; }

IB.11.9. Fiiere predefinite Exist trei fluxuri predefinite, care se deschid automat la lansarea unui program: stdin - fiier de intrare, text, este intrarea standard - tastatura stdout - fiier de ieire, text, este ieirea standard - ecranul monitorului. stderr - fiier de iesire, text, este ieirea standard de erori - ecranul monitorului. Ele pot fi folosite n diferite funcii, un exemplu practic este funcia fflush care golete zona tampon (buffer) asociat unui fiier. Observaii 187

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Nu orice apel al unei funcii de citire sau de scriere are ca efect imediat un transfer de date ntre exterior i variabilele din program! Citirea efectiv de pe suportul extern se face ntr-o zon tampon asociat fiierului, iar numrul de octei care se citesc depind de suport: o linie de la tastatur, unul sau cteva sectoare disc dintr-un fiier disc, etc. Cele mai multe apeluri de funcii de I/E au ca efect un transfer ntre zona tampon (anonim) i variabilele din program. Este posibil ca s existe diferene n detaliile de lucru ale funciilor standard de citire-scriere din diferite implementri (biblioteci), deoarece standardul C nu precizeaz toate aceste detalii! Funcia fflush Are rolul de a goli zona tampon folosit de funciile de I/E, zon altfel inaccesibil programatorului C. Are ca argument variabila pointer asociat unui fiier la deschidere, sau variabilele predefinite stdin i stdout. fflush (FILE* f); Exemple de situaii n care este necesar folosirea funciei fflush: Citirea unui caracter dup citirea unui cmp sau unei linii cu scanf :
int main () { int n; char s[30]; char c; scanf (%d,&n); // fflush(stdin); c = getchar(); printf (%d \n,c); return 0; } /* va afia 10 care este codul numeric al caracterului terminator de linie \n, n loc s afieze codul caracterului c, deoarece dup o citire cu scanf n zona tampon rmn unul sau cteva caractere separator de cmpuri (\n, \t, ), care trebuie scoase de acolo prin fflush(stdin) sau prin alte apeluri scanf. */ // sau scanf(%s,s); // pentru corectare // sau scanf (%c, &c); // afiseaza codul lui c

Funcia scanf oprete citirea unei valori din zona tampon ce conine o linie la primul caracter separator de cmpuri sau la un caracter ilegal n cmp (de ex. liter ntr-un cmp numeric)! In cazul repetrii unei operatii de citire (cu scanf) dup o eroare de introducere n linia anterioar (caracter ilegal pentru un anumit format de citire) n zona tampon rmn caracterele din linie care urmau dup cel care a produs eroarea!
do { printf ("x, y = "); err = scanf ("%d%d", &x, &y); if ( err == 2 ) break; fflush (stdin); } while (err != 2);

Observaie: Dup citirea unei linii cu funciile gets sau fgets nu rmne nici un caracter n zona tampon i nu este necesar apelul lui fflush! Se va folosi periodic fflush n cazul actualizrii unui fiier mare, pentru a evita pierderi de date la producerea unor incidente (toate datele din zona tampon vor fi scrise efectiv pe disc):
int main () {

188

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

FILE * f; int c; char numef[]="TEST.DAT"; char x[ ] = "0123456789"; f=fopen (numef,"w"); for (c=0;c<10;c++) fputc (x[c], f); fflush (f); // sau fclose(f); f=fopen (numef,"r"); while ( (c=fgetc(f)) != EOF) printf ("%c", c); return 0; }

IB.11.10. Redirectarea fiierelor standard Trebuie observat c programele C care folosesc funcii standard de I/E cu consola pot fi folosite, fr modificri, pentru preluarea de date din orice fiier i pentru trimiterea rezultatelor n orice fiier, prin operaia numit redirectare (redirecionare) a fiierelor standard. Prin redirectare, fiierele standard se pot asocia cu alte fiiere. Redirectarea se face prin adugarea unor argumente n linia de comand la apelarea programului. Exemplu:
fiier_exe < fiier_1 > fiier_2

n acest caz, preluarea informaiilor se face din fiier_1, iar afiarea informaiilor de ieire se face n fiier_2. Exemple:
/* * Copierea coninutului unui fiier n alt fiier utilizand redirectarea * Folosind redirectarea fiierelor standard, se va lansa printr -o linie de * comand de forma: * copiere1 <fiier_sursa.dat >fiier_dest.dat * i atunci urmtorul program va avea acelai rezultat ca si cnd s-ar citi * din fisier_sursa.dat * i s-ar scrie n fiier_dest.dat */ #include <stdio.h> int main(void){ char c; while ( (c=getchar()) != EOF ) putchar(c); return 0; } /* * Exemplu de program filter: * filter este numele unui program (fiier executabil) care aplic un filtru * oarecare pe un text pentru a produce un alt text * Folosind redirectarea fiierelor standard, se va lansa printr-o linie de * comand de forma: * filter <input - citire din input i scriere pe ecran * filter >output - citire de la consola i scriere n output * filter <input >output - citire din input i scriere n output */ #include <stdio.h> // pentru funciile gets,puts int main () { char line[256]; while ( gets(line) != NULL)

// aici se citete o linie // repeta citire linie

189

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR // daca linie comentariu // atunci se scrie linia

if ( line[0]==/ && line[1]==/) puts (line); return 0; }

Utilizarea comenzii filter fr argumente citete i afieaz la consol; utilizarea unui argument de forma <input redirecteaz intrrile ctre fiierul input, iar un argument de forma >output redirecteaz ieirile ctre fiierul output. Redirectarea se poate aplica numai programelor care lucreaz cu fiiere text. IB.11.11. Anexa. Fiiere n C++ Fiierele sunt n C++ variabile de tipurile ifstream (input file stream), ofstream (output file stream) sau fstream (care permit att citire ct i scriere din fiier). Operaiile de citire/scriere se pot realiza fie prin funcii specifice, fie prin operatorii de inseri e n flux (<<) sau extragere din flux (>>). Pentru a putea fi folosite, fiierele disc trebuie deschise, utiliznd funcia open i nchise dup folosire utiliznd funcia close. Exemplu de scriere ntr-un fiier text:
ofstream ofile; char nr[4]; ofile.open ("numere.txt"); for (int i=1;i<100;i++) ofile << itoa (i,nr,10)<< endl; ofile.close();

La citirea dintr-un fiier text exist dou diferene fa de scriere: Trebuie detectat sfritul de fiier cu una din funciile eof(), good() sau bad(). Citirea cu operatorul >> repet ultima linie citit din fiier i de aceea se prefer funcia getline (cu argument de tip string i nu vector de caractere). Exemplu de citire din fiierul text creat anterior:
ifstream ifile; char nr[4]; ifile.open ("numere.txt"); while (ifile.good()) { ifile >> nr; cout << nr << endl; } ifile.close();

// while ( ! ifile.eof()) {

// poate lipsi

Se poate verifica dac deschiderea fiierului a reuit cu funcia is_open() :


if (! ifile.is_open()) cout << eroare la deschidere\n;

190

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.12. Convenii i stil de programare


Cuvinte cheie
Stil de programare, convenii de scriere, identificatori, indentare, spaiere, directive preprocesor, macrouri

IB.12.1 Stil de programare coding practices Comparnd programele scrise de diveri autori n limbajul C se pot constata diferene importante n: modul de redactare al textului surs (utilizarea de acolade, utilizarea de litere mici i mari, etc.). Acest mod de redactare poate fi supus utilizrii anumitor convenii de programare modul de utilizare a elementelor limbajului (instruciuni, declaraii, funcii, etc.), aa numitul stil de programare, propriu fiecrui programator, dar care poate fi supus totui unor reguli de baz. O prim diferen de abordare este alegerea ntre a folosi ct mai mult facilitile specifice oferite de limbajul C sau de a folosi construcii comune i altor limbaje (Pascal de ex.). Exemple de construcii specifice limbajului C: Expresii complexe, incluznd prelucrri, atribuiri i comparaii. Utilizarea de operatori specifici: atribuiri combinate cu alte operaii, operatorul condiional, etc. Utilizarea instruciunilor break i continue. Utilizarea de pointeri n locul unor vectori sau matrice. Utilizarea unor declaraii complexe de tipuri, n loc de a defini tipuri intermediare, mai simple. Exemplu:
// definire vector de pointeri la functii void f(int,int) void (*tp[M])(int,int); // greu de citit! // definire cu tip intermediar pointer la functie typedef void (*funPtr) (int,int); // pointer la o functie cu 2 argumente int funPtr tp[M]; // vector cu M elemente de tip funPtr

O alegere oarecum echivalent este ntre programe surs ct mai compacte (cu ct mai puine instruciuni i declaraii) i programe ct mai explicite i mai uor de neles. n general este preferabil calitatea programelor de a fi uor de citit i de modificat i mai puin lungimea codului surs i, eventual, lungimea codului obiect generat de compilator. Deci se recomand programe ct mai clare i nu programe ct mai scurte. Exemplu de secven pentru afiarea a n ntregi cte m pe o linie:
for (i=1; i<=n; i++) printf ( "%5d%c", i, ( i%m==0 || i==n)? '\n':' ');

O variant mai explicit dar mai lung pentru secvena anterioar:


for (i=1; i<=n; i++){ printf ("%6d ", i); if (i%m==0) printf("\n"); } printf("\n");

Alte recomandri Se recomand utilizarea standardului ANSI C pentru portabilitate 191

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Programele nu trebuie s depind de caracteristicile compilatorului (ordinea de evaluare a expresiilor Exemple:


k = ++i + i; y = f(x) + z_glb; k = ++i + j++; a[i++] = j++; /* /* /* /* gresit */ gresit daca f() schimba valoarea lui z_glb*/ OK */ OK */

Orice definire (de structur, enumerare, tip, etc) utilizat n mai multe fiiere va fi inclus ntr-un fiier antet (.h) care va fi apoi inclus n fiierele care folosesc acea definiie Toate conversiile de tip vor fi fcute explicit Variabilele structur se vor transmite prin adresa Pentru constantele utilizate pentru activarea / dezactivarea unor instruciuni se va verifica definirea acestora utiliznd compilrile condiionate
// Nerecomandat: #define DEBUG 4 /* folosit pentru a indica nivelul dorit de debug */ for (i = 0; i < 5 && DEBUG; i++) { printf(i = %d\n, i); } // Recomandat: #define DEBUG #ifdef DEBUG for (i = 0; i < 5; i++) { printf(i = %d\n, i); } #endif

Pentru un simbol testat cu #ifdef, #ifndef sau #if defined nu se va defini o valoare
// Nerecomandat #define DEBUG 0 #ifdef DEBUG for (i = 0; i < 5; i++) { printf(i = %d\n, i); } #endif //Recomandat #define DEBUG #ifdef DEBUG for (i = 0; i < 5; i++) { printf(i = %d\n, i); } #endif

A se vedea anexa Directive preprocesor utile n programele mari. Macrouri Elementele unui vector vor fi accesate utiliznd [] i nu operatorul de derefereniere *.
// Nerecomandat int array[11]; *(array + 10) = 0; // Recomandat int array[11]; array[10] = 0;

192

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Transmiterea parametrilor prin pointeri va fi evitat ori de cate ori este posibil
x = f(a, b, c); // i x = f(a, b, c, x); //sunt mai uor de neles dect: f(a, b, c, &x);

Toate instruciunile switch vor avea clauza default care ntotdeauna va fi ultima
switch (variabila_int) { case:.. break; case:.. break; default:.. }

Operatorul virgul (,) va fi utilizat doar n instruciunea for i la declararea variabilelor Modificarea unui cod existent se va face conform standardului existent deja n acel cod Modulele unui program nu trebuie s depeasc un anumit grad de complexitate i un anumit numr de linii (maxim o jumtate de pagin) Se va utiliza evaluarea condiiei afirmative mai degrab dect a celei negative (!) Condiiile logice vor fi scrise explicit:
//Nerecomandat if (is_available) if (sys_cfg__is_radio_retry_allowed()) if (intermediate_result) //Recomandat if (is_available == FALSE) if (sys_cfg__is_radio_retry_allowed() == TRUE) if (intermediate_result != 0)

Nu se recomand utilizarea variabilelor globale; daca vor fi utilizate trebuie indeplinite urmtoarele cerine: o Toate variabilele globale pentru un proiect vor fi definite ntr-un singur fiier o Variabilele globale vor fi iniializate nainte de utilizare o O variabil global va fi definit o singur dat pentru un program executabil Funcii Se vor folosi pe ct posibil funcii standard n loc de funcii specifice sistemului de operare o System Dependent: open(), close(), read(), write(), lseek(), etc. o ANSI Functions: fopen(), fclose(), fread(), fwrite(), fseek(), etc. Toate funciile vor avea tip definit explicit Funciile care ntorc pointeri vor returna NULL n cazul nendeplinirii unei condiii Numrul parametrilor unei funcii ar trebui limitat la cinci sau mai puin Toate funciile definite trebuie nsoite de antete ce vor conine lista tipurilor parametrilor Constante Toate constantele folosite ntr-un fiier vor fi definite nainte de prima funcie din fiier Dac se definesc constantele TRUE i FALSE, acestea trebuie s aib valoare 1, respectiv 0:
#ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0

193

INFORMATIC*I* #endif

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Definirea de constante simbolice (#define) va fi preferat utilizrii directe a valorilor:


//Recomandat: #define NMAX 100 int a[NMAX]; //Nerecomandat: int a[100];

Variabile Toate variabilele se definesc nainte de partea de cod propriu-zis (instruciuni) Variabilele locale se definesc cte una pe linie; excepie fac indecii, variabilele temporare i variabilele iniializate cu aceeai valoare
int int int Zona; i, j, contor; Mode = k = 0;

Variabilele locale ar trebui iniializate nainte de utilizare Se va evita utilizarea variabilelor globale pe ct posibil Dimensiune vectori Nu se transmite dimensiunea maxim a unui vector cnd acesta este parametru al unei funcii; aceasta se transmite separat ntr-o variabil!
// Nerecomandat char *substring(char string[80], int start_pos, int length) { } // Recomandat char *substring(char string[], int start_pos, int length) { }

Includerea fiierelor Fiierele antet vor defini o constant simbolic pentru a permite includerea multipl. Dac fiierul se numete file.h constanta se poate numi FILE_H
// fisierul example.h #ifndef EXAMPLE_H #define EXAMPLE_H #endif

Se recomand ca fiierele antet s nu includ alte fiiere antet Pentru includerea unui fiier antet definit de utilizator se vor utiliza , iar pentru o bibiloteca standard < > Macrouri n macrourile tip funcie parametrii vor fi scrii ntre paranteze
// Nerecomandat #define prt_debug(a, b) printf("ERROR: %s:%d, %s\n", a,__LINE__, b); // Recomandat #define prt_debug(a, b) printf("ERROR: %s:%d, %s\n", (a),__LINE__,(b));

194

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Macrourile complexe vor fi comentate Un macrou nu va depi 10 linii IB.12.2. Convenii de scriere a programelor Programele sunt destinate calculatorului i sunt analizate de ctre un program compilator. Acest compilator ignor spaiile albe nesemnificative i trecerea de la o linie la alta. Programele sunt citite i de ctre oameni, fie pentru a fi modificate sau extinse, fie pentru comunicarea unor noi algoritmi sub form de programe. Pentru a fi mai uor de neles de ctre oameni se recomand folosirea unor convenii de trecere de pe o linie pe alta, de aliniere n cadrul fiecrei linii, de utilizare a spaiilor albe i a comentariilor. Respectarea unor convenii de scriere n majoritatea programelor poate contribui la reducerea diversitii programelor scrise de diveri autori i deci la facilitarea nelegerii i modificrii lor de ctre ali programatori. O serie de convenii au fost stabilite de autorii limbajului C i ai primului manual de C. Beneficiile utilizrii unor convenii de programare: Uniformizeaz modul de scriere a codului Faciliteaz citirea i nelegerea unui program Faciliteaz ntreinerea aplicaiilor Faciliteaz comunicarea ntre membrii unei echipe ceea ce duce la un randament sporit al lucrului n echip Observaie: Chiar dac unele persoane pot resimi ca o ngrdire aceste convenii, ele permit totui manifestarea creativitii programatorului deoarece orice convenie poate fi imbuntit i adoptat ca standard al echipei respective de programatori. IB.12.2.1 Identificatori Identificatorii trebuie s ndeplineasc standardul ANSI C (lungimea<31, caractere permise: litere, cifre, _) Simbolul _ nu va fi folosit ca prim caracter al unui identificator Nu se vor folosi nume utilizate de sistem dect dac se dorete nlocuirea acestora (constante, fiiere, funcii) Toate fiierele header vor avea extensia .h Toate constantele simbolice definite cu #define vor fi scrise cu litere mari Numele de variabile i de funcii ncep cu o liter mic i conin mai mult lit ere mici (litere mari numai n nume compuse din mai multe cuvinte alturate, cum sunt nume de funcii din MS-Windows) n ceea ce privete numele unor noi tipuri de date prerile sunt mprite Numele unui fiier nu trebuie s depeasc 14 caractere n lungime (cu extensie). Variabilele locale, definiiile de tip (typedef), numele de fiiere ct i membrii unei structuri vor fi scrii cu litere mici Numele variabilelor i funciilor trebuie s fie semnificative i n concordan cu ceea ce reprezint. Exemple: Tip Contor, index Pointeri Variabile temporare Valori returnate

Exemplu nume
i, j, k, l, g, h, ptr_var, var_ptr, var_p, p_var, p tmp_var, var_tmp, t_va status, return_value

195

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR readValues, reset_status, print_array initialData.txt, entry.txt, setFuncions.c, set.h

Funcii Fiiere

Variabilele globale trebuie s se diferenieze de cele locale (fie primul caracter liter mare, fie un sufix de genul global): Vec_initial, vec_global, set_glb IB.12.2.2 Funcii Se va scrie o singur instruciune pe linie Tipul unei funcii i numele acesteia vor fi pe aceeai linie
// Nerecomandat int err_msg(int error_code) { } // Recomandat int err_msg(int error_code) { }

IB.12.2.3 Spaierea Nu vor fi spaii albe: Dup un cast explicit de tip


// Nerecomandat int x = 1; double y = 3.0; y = (double) x + 16.7; // Recomandat int x = 1; double y = 3.0; y = (double)x + 16.7;

ntre operatorii unari (&, *, -, ~, ++, --, !, cast, sizeof) i operanzii lor nainte sau dup operatorii primari ( (),[],.,->) ntre caracterul # i directiva de preprocesare
// Nerecomandat # define TEST 0 // sau # define TEST 0 // Recomandat #define TEST 0

ntre numele unei funcii i paranteza care i urmeaz ntre primul argument al funciei i paranteza deschis ntre ultimul argument al funciei i paranteza nchis
// Nerecomandat just_return ( arg1, arg2 ); // Recomandat just_return(arg1, arg2); if (x == y)

196

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

ntre parantezele deschis, respectiv nchis i expresia unei instruciuni condiionale


if (x == y)

Un singur spaiu va exista ntre expresia condiional a unei instruciuni i numele if


// Nerecomandat if(x == y) // Recomandat if (x == y)

precede i urmeaz operatorii de atribuire, operatorii relaionali, operatorii logici, operatorii aritmetici (excepie cei unari) operatorii pe bii i operatorul condiional
a = 3;

va urma unei virgule


int a, b, c;

Alte recomandri: Va exista cel puin o linie goal care s separe definirea variabilelor locale de instruciuni
int just_return(int first_arg, int second_arg) { /*-------------- LOCAL VARIABLES -------------*/ int i = 0; /* Loop counter. */ int j = 0; /* Loop counter. */ /*-------------------- CODE ------------------*/ /* Body of funcion just_return. */ return(0); }

Membrii unei structuri, uniuni, enumerri vor fi plasai pe linii distincte la declararea lor Componentele logice ale unei expresii condiionale vor fi grupate cu paranteze chiar dac acestea nu sunt necesare
if ((x == y) && (a == b))

IB.12.2.4 Utilizarea acoladelor i parantezelor Una dintre convenii se refer la modul de scriere a acoladelor care ncadreaz un bloc de instruciuni ce face parte dintr-o funcie sau dintr-o instruciune if, while, for etc. Cele dou stiluri care pot fi ntlnite n diferite programe i crti sunt ilustrate de exemplele urmtoare: Stil Kernighan & Ritchie
// exemplu bucla for for (i = 0; i < loop_cntrl; i++){ /* Corp for. */ } // descompunere in factori primi int main(){ int n, k, p ; printf("\n n= "); scanf("%d",&n); printf(1); // pentru simplificarea afisarii for (k=2; k<=n && n>1; k++) { p=0; // puterea lui k in n

197

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

while (n % k == 0) { // cat timp n se imparte exact prin k p++; n = n / k; } if (p > 0) // nu scrie factori la puterea zero printf (" * %d^%d",k,p); } }

Stil Linux: Toate acoladele vor fi cte una pe linie


// exemplu bucla for for (i = 0; i < loop_cntrl; i++) { /* Corp for. */ } // descompunere in factori primi int main() { int n, k, p ; printf("\n n= "); scanf("%d",&n); printf(1); // for (k=2; k<=n && n>1; k++) { p=0; // while (n % k ==0) // { p++; n = n / k; } if (p > 0) // printf (" * %d^%d",k,p); } }

pentru simplificarea afisarii puterea lui k in n cat timp n se imparte exact prin k

nu scrie factori la puterea zero

Uneori se recomand utilizare de acolade chiar i pentru o singur instruciune, anticipnd adugarea altor instruciuni n viitor la blocul respectiv.
if (p > 0) { // scrie numai factori cu putere nenula printf(" * %d^%d",k,p); }

IB.12.2.5 Indentarea Pentru alinierea spre dreapta la fiecare bloc inclus ntr-o structur de control se pot folosi caractere Tab (\t) sau spaii, dar evidenierea structurii de blocuri incluse este important pentru oamenii care citesc programe. Recomandri: Toate definiiile de funcii ncep n coloana 1 Acoladele ce definesc corpul unei funcii vor fi n coloana 1 sau imediat dup antet Acoladele corespunztoare unei instruciuni, unei iniializri de structur, vector, etc vor fi n aceeai coloan cu instruciunea sau iniializarea respectiv
for (i = 0; i < loop_cntrl; i++) { /* corp for */

198

INFORMATIC*I* }

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Instruciunile aflate la acelai nivel de includere vor fi indentate la aceeai coloan


if (conditie == TRUE) { /* corp prim if */ } else if { /* corp al doilea if */ } else { /* else al doilea if */ }

Toate blocurile incluse n alt bloc vor fi indentate cu 2 pn la 4 spaii albe Vor fi indentate cu 2 - 4 spaii: cmpurile unui tip de date
struct example_type { int x; double y; };

ramurile case ale unei instruciuni switch continuarea unei linii, fa de operatorul de atribuire sau fa de paranteza deschis n cazul unei instruciuni sau a unui apel de funcie
num = this_example_test_structure.example_struct_field1 * this_example_test_structure.example_struct_field2; if ((very_long_result_variable_name >= lower_specification_value))

IB.12.2.6 Comentarii O serie de recomandri se refer la modul cum trebuie documentate programele folosind comentarii. Astfel, fiecare funcie C ar trebui precedat de comentarii ce descriu rolul acelei funcii semnificaia argumentelor funciei rezultatul funciei pentru terminare normal i cu eroare precondiii - condiii care trebuie satisfcute de parametri efectivi primii de funcie (limite, valori interzise, etc.) i care pot fi verificate sau nu de funcie plus alte date despre: o autor o data ultimei modificri o alte funcii utilizate sau asemntoare, etc. Exemplu:
/* Functie de conversie numar ntreg pozitiv din binar n sir de caractere ASCII terminat cu zero value = numar intreg primit de functie (pozitiv) string = adresa unde se pune sirul rezultat

199

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

radix = baza de numeratie (intre 2 i 16, inclusiv) are ca rezultat adresa sir sau NULL in caz de eroare trebuie completata pentru numere cu semn */ char *itoa(int value, char *string, int radix) { char digits[] = "0123456789ABCDEF"; char t[20], *tt=t, * s=string; if ( radix > 16 || radix < 0 || value < 0) return NULL; do { *tt++ = digits[ value % radix]; } while ( (value = value / radix) != 0 ); while ( tt != t) *string++= *(--tt); *string=0; return s; }

Alte observaii legate de comentarii: Vor completa codul, nu l vor dubla! Explic mai mult dect este subneles din cod Nu trebuie s fie foarte multe comentarii (ngreuneaz codul) dar nici foarte puine (nu este explicat codul) Pot fi comentarii bloc, pe o linie, sau in-line: Comentarii bloc: descriu seciunile principale ale programului i vor fi indentate la acelai nivel cu codul pe care il comenteaz
/* Acesta este un format care poate fi folosit pentru comentariile bloc */ /********************************************************** * Si acesta este un format care poate fi folosit pentru comentariile bloc * **********************************************************/ /* ********************************************************* * Si acesta este un format care poate fi folosit pentru comentariile bloc * ********************************************************* */

Comentarii pe o linie : Se indenteaz la acelai nivel cu codul pe care l comenteaz


if (argc > 1) { /* ia numele fisierului de intrare din linia de comanda. */ if ((freopen(argv[1], r, stdin) == NULL) { /* Corp if */ } }

Comentarii in-line (pentru descrierea declaraiilor): trebuie s fie indeajuns de scurte nct s intre pe aceeai linie cu codul comentat
int i = 0; int status = TRUE; /* Contor bucla */ /* Rezultatul funciei*/

200

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Pentru comentarii pe o linie sau in- line se pot folosi i comentarii C++:
int i = 0; int status = TRUE; // Contor bucla // Rezultatul funciei

Reguli generale privind comentariile Comentariile nu vor fi incluse unele n altele Fiecare variabil local va avea un comentariu ce va descrie utilizarea ei dac aceasta nu reiese din nume Comentariile in-line trebuie s fie aliniate pe ct posibil la stnga n cadrul unei funcii
j = 5; k = j + 9; /* Assign j to the starting string position */ /* Assign k to the ending string position */

Instruciunile condiionale sau buclele complexe (mai mult de 10 linii necomentate) vor avea ataat un comentariu la acolada de nchidere ce va indica nchiderea instruciunii i unul la nceputul sau n interiorul blocului ce va indica scopul acestuia
if (a>b){ /* scop . .*/ } // end of if(a>b)

IB.12.3. Anexa: Directive preprocesor utile n programele mari. Macrouri Directivele preprocesor C au o sintax i o prelucrare distinct de instruciunile i declaraiile limbajului, dar sunt parte a standardului limbajului C. Directivele sunt interpretate ntr-o etap preliminar compilrii (traducerii) textului C, de ctre un preprocesor. O directiv ncepe prin caracterul # i se termin la sfritul liniei curente (dac nu exist linii de continuare a liniei curente). Nu se folosete caracterul ; pentru terminarea unei directive! Cele mai importante directive preprocesor sunt: Sintaxa Descriere nlocuiete toate apariiile identificatorului ident prin irul text #define ident text #define ident (a1,a2,...) text definete o macroinstruciune cu argumente include n compilare continutul fiierului sursa fiier #include fiier compilare condiionat de valoarea expresiei expr #if expr compilare condiionat de definirea unui identificator (cu #if defined ident #define) terminarea unui bloc introdus prin directiva #if #endif Directiva define are multiple utilizri n programele C: Definirea de constante simbolice de diferite tipuri (numerice, text) Exemple:
#define begin { #define end } #define N 100 // unde apare begin acesta va fi nlocuit cu { // unde apare end acesta va fi nlocuit cu } // unde apare N acesta va fi nlocuit cu 100

201

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Definirea de macrouri cu aspect de funcie, pentru compilarea mai eficient a unor funcii mici, apelate n mod repetat. Exemple:
// maxim dintre a si b #define max(A,B) ( (A)>(B) ? (A):(B) ) // genereaz un numr aleator ntre 0 i num! #define random(num)(int) (((long)rand()*(num))/(RAND_MAX+1)) // initializare motor de generare numere aleatoare #define randomize() srand((unsigned)time(NULL)) // valoarea absoluta #define abs(a) (a)<0 ? -(a) : (a) // numar par cu utilizare! #include<stdio.h> #define PAR(a) a%2==0 ? 1 : 0 int main(void) { if (PAR(9+1)) printf("este par\n"); else printf("este impar\n"); return 0; } Atenie! 9+1%2==0 va conduce la 9+0 == 0 F ->este impar Ar trebui: #define PAR(a) (a)%2==0 ? 1 : 0

Macrourile pot conine i declaraii, se pot extinde pe mai multe linii i pot fi utile n reducerea lungimii programelor surs i a efortului de programare. n standardul din 1999 al limbajului C s-a preluat din C++ cuvntul cheie inline pentru declararea funciilor care vor fi compilate ca macroinstruciuni n loc de a folosi macrouri definite cu define. Definirea unor identificatori specifici fiecrui fiier i care vor fi testai cu directiva ifdef. De exemplu, pentru a evita declaraiile extern n toate fiierele surs, mai puin fiierul ce conine definiiile variabilelor externe, putem proceda astfel: o Se definete n fiierul surs cu definiiile variabilelor externe un nume simbolic oarecare:
// fiier ul DIRLIST.C #define MAIN

o n fiierul dirlist.h se plaseaz toate declaraiile de variabile externe, dar ncadrate de directivele if i endif:
// fiier ul DIRLIST.H #if !defined(MAIN) // sau ifndef MAIN extern char path[MAXC], mask[MAXC], opt[MAXC]; #endif

Directiva include este urmat de obicei de numele unui fiier antet (de tip H = header), fiier care grupeaz declaraii de tipuri, de constante, de funcii i de variabile, necesare n mai multe fiier e surs (C sau CPP). Fiierele antet nu ar trebui s conin definiii de variabile sau de funcii, pentru c pot apare erori la includerea multipl a unui fiier antet. 202

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Un fiier antet poate include alte fiiere antet. Pentru a evita includerea multipl a unui fiier antet (standard sau nestandard) se recomand ca fiecare fiier antet s nceap cu o secven de felul urmtor:
#ifndef HDR #define HDR // continut fiier #endif

HDR.H ...

Fiierele antet standard (stdio.h, etc.) respect aceast recomandare. O soluie alternativ este ca n fiierul ce face includerea s avem o secven de forma urmtoare:
#ifndef STDIO_H #include <stdio.h> #define _STDIO_H #endif

Directivele de compilare condiionat de forma if...endif au i ele mai multe utilizri ce pot fi rezumate la adaptarea codului surs la diferite condiii specifice, cum ar fi: dependena de modelul de memorie folosit ( n sistemul MS-DOS) dependena de sistemul de operare sub care se folosete programul (de ex., anumite funcii sau structuri de date care au forme diferite n sisteme diferite) dependena de fiierul surs n care se afl (de exemplu tcalc.h). Directivele din grupul if au mai multe forme, iar un bloc if ... endif poate conine i o directiv elseif.

203

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Capitolul IB.13. Autoevaluare


Capitol IB.01. Rezolvarea algoritmic a problemelor Probleme rezolvate n schem logic Problema 1: Rezolvarea ecuaiei de grad 1: ax+b=0. Atenie la cazurile speciale: a egal zero i/sau b egal zero! Date de intrare: a i b, variabile reale Date de ieire: x, variabil real

STAR TT Citeste a,b

DA a=0 NU x = -b / a b=0 NU Scrie Nu exista solutii

DA

Scrie Infinitate de solutii

Scrie x

STOP

204

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Problema 2: S se afieze suma primelor n numere naturale, n citit de la tastatur. Date de intrare: n, variabil ntreag Date de ieire: s, variabil ntreag pozitiv ce stocheaz suma primelor n numere naturale Variabile auxiliare: i, variabil natural de tip contor

STAR TT Citeste n

s=0

i =0 i=i+1

DA i<n NU Scrie s

s=s+i

STOP

205

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Problema 3: Algoritmul lui Euclid care determin cel mai mare divizor comun a doi ntregi, prin mpriri repetate. Date de intrare: a i b, variabile ntregi, pozitive. Se consider c a este mai mare ca b. Date de ieire: cmmdc care este calculat n b. Variabile auxiliare: r, variabil natural ce pstreaz restul mpririi lui a la b. Atenie! Dac la final vrem s afim ceva de genul Cel mai mare divizor comun al numerelor: i vrem s urmeze valorile iniiale pentru a i respectiv b, nu vom putea face acest lucru deoarece valorile de intrare ale lui a i b se pierd, ele modificndu-se pe parcursul execuiei algoritmului. Pentru a rezolva aceast problem, vom pstra aceste valori iniiale n dou variabile auxiliare: copie_a i copie_b, iar la final vom afia valorile din aceste dou variabile. STAR TT Citeste a,b

r=a%b

r>0 NU
Scriecmmmdc este , b

DA

a=b b=r r=a%b

STOP

Probleme propuse i rezolvate n pseudocod Problema 1. Interschimbul valorilor a dou variabile a i b. Rezolvare: Atenie! Trebuie s folosim o variabil auxiliar. Nu funcioneaz a=b i apoi b=a deoarece deja am pierdut valoarea iniial a lui a!
start citeste a, b; aux = a; a = b; b = aux; scrie a, b; stop.

Problema 2. Rezolvarea ecuaiei de grad 2: ax2+bx+c=0. 206

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Rezolvare: Atenie la cazurile speciale! Dac a este 0 ecuaia devine ecuaie de gradul 1!
start citeste a, b, c; daca a = 0 daca b = 0 daca c = 0 scrie Ecuatia are o infinitate de solutii; altfel scrie Ecuatia nu are nici o solutie; altfel { x = -c / b; scrie Ecuatia este de gradul I cu solutia:; scrie x; } altfel { d = b * b + 4 * a * c; daca d < 0 scrie Ecutia nu are radacini reale; altfel daca d = 0 { x= - b / (2 * a); scrie Ecuatia are 2 radacini egale cu : scrie x; } altfel { x1= -b + sqrt( d ) / (2 * a ); x2 = -b sqrt( d ) / (2 * a ); scrie Ecuatia are 2 radacini distincte: scrie x1, x2; } } stop.

Problema 3. S se afieze n ordine cresctoare valorile a 3 variabile a, b i c. Rezolvare: Putem rezolva aceast problem comparnd dou cte dou valorile celor 3 variabile. n cazul n care nu sunt n ordine cresctoare interschimbm valorile lor.
start citeste a,b,c; daca (a > b) { aux = a; a = b; b = aux;} daca (a > c) { aux = a; a = c; c = aux;} daca (b > c) { aux = b; b = c; c = aux;} scrie a,b,c; stop.

O alt variant este urmtoarea, n care valorile variabilelor nu se modific ci doar se afieaz aceste valori n ordine cresctoare:
start citeste a,b,c daca (a < b si b < c) scrie a, b, c; daca (a < c si c < b) scrie a, c, b;

207

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

daca (b daca (b daca (c daca (c stop.

< < < <

a c a b

si si si si

a c a b

< < < <

c) a) b) a)

scrie scrie scrie scrie

b, b, c, c,

a, c, a, b,

c; a; b; a;

Problema 4. S se calculeze i s se afieze suma: S=1+1*2+1*2*3+..+n! Rezolvare: Vom folosi o variabil auxiliar, p, n care vom calcula produsul parial.
start citeste n; s = 0; pentru i de la 1 la n cu pasul 1 { p = 1; pentru j de la 2 la i cu pasul 1 { p = p * j; } s = s + p; } scrie s; stop.

O alt posibilitate de calcul este urmtoarea, n care produsul parial este actualizat la fiecare pas, fr a mai fi calculat de fiecare dat de la 1:
start citeste n; s = 0; p = 1; pentru i de la 1 la n cu pasul 1 { p = p * i; s = s + p; } scrie s; stop.

Problema 5. S se calculeze i s se afieze suma cifrelor unui numr natural n. Rezolvare: Vom folosi o variabil auxiliar c n care vom calcula rnd pe rnd cifrele. Pentru aceasta vom obine ultima cifr a lui n ca rest al mpririi lui n la 10, dup care micorm n mprindu-l la 10, astfel nct nurmtoarea iteraie (pas al calculului repetitiv) s obinem urmtoarea cifr, .a.m.d.
start citeste n; s = 0; atata timp c = n s = s n = n } scrie s; stop.

cat n > 0 { mod 10; + c; / 10;

Problema 6. S se calculeze i s se afieze inversul unui numr natural n. 208

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Rezolvare: Vom folosi o variabil auxiliar c n care vom calcula rnd pe rnd cifrele ca n problema anterioar, i vom construi inversul pe msur ce calculm aceste cifre.
start citeste n; inv = 0; atata timp c = n inv = n = n } scrie inv; stop.

cat n > 0 { mod 10; inv * 10 + c; / 10;

Problema 7. S se afieze un mesaj prin care s se precizeze dac un numr natural dat n este prim sau nu. Rezolvare: Pentru aceasta vom mpri numrul dat, pe rnd, la numerele de la 2 pn la radical din n (este suficient pentru a testa condiia de prim, dup aceast valoare numerele se vor repeta). Dac gsim un numr care s-l mpart exact vom seta o variabil auxiliar b (numit variabila flag, sau indicator) pe 0. Ea are rolul de a indica faptul c s-a gsit un numr care divide exact pe n. Iniial presupunem c nu exist un astfel de numr, i deci, b va avea valoarea 1 iniial.
start citeste n; b = 1; pentru d de la 2 la n cu pasul 1 daca (n mod d = 0) b = 0; daca (b == 1) scrie Numarul este prim; altfel scrie Numarul nu este prim; stop.

Problema 8. S se afieze primele n numere naturale prime. Rezolvare: Folosm algoritmul de la problema anterioar la care adugm o variabil de contorizare /numrare, k.
start citeste n; k = 0; i = 2; atata timp cat k < n { b = 1; pentru d de la 2 la i cu pasul 1 daca (i mod d = 0) b = 0; daca (b == 1) { scrie i; k = k + 1; } i = i + 1; stop.

209

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Problema 9. S se descompun n factori primi un numr dat n. Rezolvare: Pentru aceasta vom mpri numrul pe rnd la numerele ncepnd cu 2. Dac gsim un numr care s-l mpart exact vom mpri pe n de cte ori se poate la numrul gsit, calculnd astfel puterea. n modul acesta nu va mai fi necesar s testm c numerele la care mprim sunt prime!
start citeste n; div = 2; scrie x = ; atata timp cat x != 0 { daca (x mod div = 0) { nr =0; atata timp cat x mod div = 0 { x = x / div; nr = nr + 1; } scrie div,^,nr,+; } div = div + 1; } stop.

Problema 10. S se afieze toate numerele naturale mai mici dect 10000 care se pot descompune n dou moduri diferite ca sum de dou cuburi. Rezolvare: Aceast problem prezint foarte bine avantajul utilizrii unui calculator n rezolvarea anumitor probleme. Calculm, pe rnd, suma de cuburi a perechilor de numere de la 1 la 21 (cel mai apropiat ntreg de radical de ordin 3 din 10000). Cutm apoi, pentru fiecare sum, o a doua pereche a crei sum de cuburi este aceeai. Dac este o pereche diferit de prima, afim numerele.
start pentru a de la 1 la 21 cu pasul 1 pentru b de la 1 la 21 cu pasul 1 { x = a * a * a + b * b * b; pentru c de la 1 la 21 cu pasul 1 pentru d de la 1 la 21 cu pasul 1 y = c * c * c + d * d * d; daca (y = c si a != c si b != d) scrie a, b, c, d; } stop.

Problema 11. S se calculeze valoarea minim, respectiv maxim, dintr-o secven de n numere reale. Rezolvare: Vom utiliza dou variabile, max i min pe care le iniializm cu o valoare foarte mic i respectiv cu o valoare foarte mare. Vom compara pe rnd valorile citite cu max i respectiv cu min, iar dac gsim o valoare mai mare, respectiv mai mic dect acestea modificm max (sau min, dup cum e cazul) la noua valoare maxim, respectiv minim.
start citeste n;

210

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

min = +; max = -; pentru i de la 1 la n cu pasul 1 { citeste x; daca (x > max) max = x; daca (x < min) min = x; } scrie valoarea minima este:,min,valoarea maxima este:,max; stop.

Problema 12. Se d o secven de n numere ntregi pozitive. S se afieze cele mai mari numere de 2 cifre care nu se afl n secvena respectiv. Rezolvare: n cadrul acestei probleme i a urmtoarei vom folosi o structur numit vector. Aceasta este util atunci cnd trebuie s memorm ca date mai multe valori de acelai tip. De exemplu, dac avem mai multe valori ntregi, putem folosi un vector de numere ntregi. Numrul acestor valori poate fi variabil iar ele vor fi pstrate sub un acelai nume, la adrese consecutive de memorie. Accesarea unei anumite valori din structura vector se face folosind un index. Dac numele vectorului este v, atunci pentru a accesa prima valoare din vector vom folosi notaia v[0], pentru a doua valoare v[1], .a.m.d. n rezolvarea acestei probleme vom folosi un vector ca variabil auxiliar, n care vom memora dac un numr de dou cifre a fost citit de la tastatur sau nu (v[nr] este 0 dac nr nu a fost citit i v[nr] devine 1 dac nr a fost citit de la tastatur). Iniial, toate valorile din vector sunt 0. Pentru a afla cele mai mari numere de dou cifre care nu sunt n secvena citit vom parcurge vectorul v de la sfrit (indexul 99) la nceput, pn ntlnim dou valori zero. Atenie! n cazul n care nu exist una sau dou valori de dou cifre care s nu aparin secvenei citite nu se va afia nimic!
start citeste n; pentru i de la 10 la 99 cu pasul 1 v[i] = 0; pentru i de la 1 la n cu pasul 1 citeste nr; dac (nr > 9 si nr < 100) v[nr] = 1; i = 99; atata timp cat(v[i] != 0 si i > 0) i = i 1; dac (i > 9) atunci scrie i, " "; i = i 1; atata timp cat (v[i] != 0 si i > 0) i = i 1; daca (i > 9) atunci scrie i, " "; stop.

Problema 13. Se d o secven de n numere ntregi, ale cror valori sunt cuprinse n intervalul 0 100. S se afieze valorile care apar cel mai des. Rezolvare: Vom utiliza de asemenea un vector n care vom memora de cte ori a aprut fiecare valoare citit: v[nr] memoreaz de cte ori a fost citit nr. Iniial toate valorile din vector sunt 0. Vom 211

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

determina apoi valoarea maxim din acest vector, dup care, pentru a afia toate numerele cu numr maxim de apariii mai parcurgem nc o dat vectorul i afim indicii pentru care gsim valoarea maxim. start citeste n; pentru i de la 0 la 100 cu pasul 1 v[i] = 0; pentru i de la 1 la n cu pasul 1 citeste nr; v[nr] = v[nr] + 1; max=0; pentru i de la 0 la 100 cu pasul 1 dac (v[i] > max) atunci max = v[i]; pentru i de la 0 la 100 cu pasul 1 dac (v[i] == max) atunci scrie i; stop.

Capitol IB.02. Introducere n limbajul C. Elemente de baz ale limbajului Probleme propuse i rezolvate 1. Scriei cele dou constante ir de caractere care conin caracterele de mai jos: a. Informatii 100% corecte: I.Ionescu / 24 ani \ zis "a lu' Vasile" b. slash /; backslash \; procent%; ghilimele "; apostrof '. Rezolvare: a. "Informatii 100%% corecte:\n\I.Ionescu / 24 ani \\ zis \"a lu' Vasile\"" 2. Care este spaiul de memorie ocupat de constanta caracter i cea ir: 'z' i "z"? Rspuns: 1 octet, respectiv 2 octei 3. Cum se definete corect o variabil suma de tip ntreg? Rspuns: int suma; 4. Cum se definete o constant simbolic avnd numele TRUE i valoarea 1? Rspuns: #define TRUE 1 5. Folosind operatorul condiional, sa se determine maximul dintre a,b, relatia dintre a,b i maximul dintre a,b,c. 212

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Rezolvare: max_a_b=a>b?a:b; char max_a_b_c=a>b?(a>c?a:c):(b>c?b:c);

c=a>b?'>':a<b?'<':'=';

6. Adugai comentariile legate de conversiile implicite i explicite pentru secvena urmtoare: char c='a',cc; int i=4; float f=5.95; i=f; f=i+100000; i=-99.001; f='a'; c=0x3239; cc=-i; float r1=5/2, r2=(float)5/2, r3=(float)(5/2), r4=5/(float)2, r5=(float)5/(float)2; Rezolvare: char c='a', cc; int i=4; float f=5.95; i=f; // conversie implicita, trunchiere f=i+100000; // conversie implicita a rezultatului expresiei i=-99.001; // conversie implicita, trunchiere f='a'; c=0x3239; cc=-i; float r1=5/2, r2=(float)5/2, // conversie explicit r3=(float)(5/2), // conversie explicit r4=5/(float)2, // conversie explicit r5=(float)5/(float)2; // conversie explicit Probleme propuse 1. Care este valoarea variabilei real definit double real=26/4? 2. Dac avem urmtoarea secven: int a=29,b=7; a%=b--; care va fi valoarea lui a? 3. Dac avem urmtoarea secven: int i=1; char c='1'; care va fi valoarea expresiei i<c? 4. Dac avem urmtoarea secven: int x=-1,y; y=++x?5:7; 213

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

care va fi valoarea lui y? 5. Gsii valorile expresiilor de mai jos. a. sizeof(int) b. sizeof(double) c. sizeof(long double) d. sizeof('a') e. sizeof((char)'a') f. sizeof(33000) g. sizeof(1.3+'a') h. sizeof(1.3f) i. sizeof(1.3) j. sizeof(1.3l) k. sizeof(5/2) l. sizeof((float)5/2) 6. Spunei care sunt valorile variabilelor n timpul execuiei urmtoarei secvene: a. int i=20000,j=15000,k; float r; k=i+j; r=i+j; r=(float)i+j; r=(long)i+j; r=i/j; k=i/j; r=(float)i/j; k=i%j; k=+ - - -i; k=+ - --i; k=- +j--; i=k/(j-j);

b. int a,b,c; float z; a=25000;b=20000; c=a+b; z=a+b; c=(float)a+b; z=(float)a+b; c=a/b; c=a%b; c=a>b; c=a==b; a=3;b=11; c=a++ + ++b; c=a&b; c=a|b; c=a^b; c=b<<2; c=-a>>3; c=~a; 214

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

a=0; c=a&&b; c=a||b; c=!a; a=4; c=a&&b; c=a||b; c=!a; a=2;c=3; c*=a; b=5; c=(a>b)?a:b; 7. S se scrie o secven care determin maximul dintre a i b, iar daca a<b, le interschimb, astfel nct secvena a i b s fie descresctoare, utiliznd operatorul condiional. Rspuns: max_a_b=a<b?(t=b,b=a,a=t):a; 8. Pentru un n dat s se scrie secvena care calculeaz valorile mai mari/mai mici decat n de 2,4,8,16 ori obinute prin nmuliri/mpriri, respectiv deplasri. 9. Se citesc ntregii x, y, a, b ( 0<=a, b<16). Se cere s se scrie secvenele care calculeaz: bitul (a+b) din x a. valoarea obinut prin setarea biilor a i b din x b. valoarea obinut prin stergerea biilor a i b din x c. valoarea obinut prin inversarea biilor a i b in x d. valoarea obinut prin negarea, respectiv complementarea lui y e. valorile obtinute prin aplicarea lui x i y a operatorilor i logic, sau logic, i pe bii, sau pe bii, sau exclusiv pe bii. 10. Fiind date urmtoarele definiii: int i = 3, j = 5,c1,c2,c3,c4; determinai valorile tuturor variabilelor, dup execuia secvenei: c1=(i/2) + 'b' + i-- - - - 'c'; c2=(j%8) * i; c3=(i++) - (--j); c4= j = (i += 2); 11. Fiind date definiiile: int a=2, b=2, c=1, d=0, e=4, i = 2, j = 4; determinai valorile urmtoarelor expresii: a. a++ / ++c * --e b. --b * c++ -a c. -b - --c d. e / --a * b++ /c++ e. e / --a * b++ / c++ f. a %= b = d = 1 + e /2 g. j = (i++ , i -j) 12. Se citesc 2 numere ntregi x i n unde n este ntre 0 i 15. S se afieze: a. bitul n din x b. numrul x n care se seteaz pe 1 bitul n c. numrul x n care se terge bitul n. 215

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Aplicaii module 3-12 Pentru fiecare din urmtoarele laboratoare exist Probleme propuse Rezolvrile problemelor propuse Evaluator al soluiilor trimise de student n plus, a fost necesar crearea unui tutorial de utilizare a site-ului i a evaluatorului: http://elf.cs.pub.ro/programare/ Capitol IB.03. Funcii de intrare/ieire n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-02 Capitol IB.04. Instruciunile limbajului C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-03 Capitol IB.05. Tablouri. Definire i utilizare n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-04 Capitol IB.06. Funcii. Definire i utilizare n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-05 http://elf.cs.pub.ro/programare/runda/lab-06 Capitol IB.07. Pointeri. Pointeri i tablouri. Pointeri i funcii Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-07 Capitol IB.08. iruri de caractere. Biblioteci standard Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-07 Capitol IB.09. Structuri de date. Definire i utilizare n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-09 Capitol IB.10. Alocarea memoriei n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-08 Capitol IB.11. Operaii cu fiiere n limbajul C Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-011 http://elf.cs.pub.ro/programare/runda/lab-012 Capitol IB.12. Convenii i stil de programare Probleme propuse i rezolvate http://elf.cs.pub.ro/programare/runda/lab-10 http://elf.cs.pub.ro/programare/runda/lab-13 216

INFORMATIC*I*

IB. INTRODUCERE IN PROGRAMAREA CALCULATOARELOR

Bibliografie
[B01] Gary J. Bronson, Program Development and Design using C++ [D01] Deitel & Deitel, C++ How to Program [K01] Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language [M01] Moraru F., Programarea Calculatoarelor, editura Bren, 2005 [M02] Moraru F., Programarea Calculatoarelor (culegere), editura Bren, 2005 [N01] Negrescu L, Limbajele C i C++ pentru nceptori, volumul 1: Limbajul C, Ed. Albastr, Cluj-Napoca, 2002 [P01] Plauger, The Standard C Library, PrenticeHall, 1992 [S01] Stroustrup B., The C++ Programming Language [S02] Stroustrup B., The Design and Evolution of C++ [W01] http://www.eskimo.com/~scs/cclass/notes/top.html [W02] http://www.eskimo.com/~scs/cclass/int/top.html [W03] http://cermics.enpc.fr/~ts/C/cref.html [W04] http://www.cs.bath.ac.uk/~pjw/NOTES/ansi_c/ [W05] http://www.chris-lott.org/resources/cstyle/ [W06] http://www.infoiasi.ro/fcs/absolvire4info.html [W07] http://www.timsoft.ro/aux/module.shtml [W08] http://www3.ntu.edu.sg/home/ehchua/programming/#Cpp [W09] http://www.cplusplus.com, C++ documents, tutorials, library references [W10] curs.cs.pub.ro, Programarea Calculatoarelor, seria 1CC

217

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