Documente Academic
Documente Profesional
Documente Cultură
ŞI
LIMBAJE DE PROGRAMARE 1
Titular : prof.dr.ing. Adriana Sîrbu
Situri:
edu.etti.tuiasi.ro (Moodle)
E-mail: asirbu@etti.tuiasi.ro
etti_pclp@yahoo.com √
Bibliografie :
http://infolab.stanford.edu/~ullman/focs.html
http://www.bottomupcs.com/
• https://github.com/EbookFoundation/free-programming-
books/blob/master/free-programming-books.md#c.
https://www.semanticscholar.org/paper/Introduction-to-High-
Performance-Scientific-
Eijkhout/a301a69a29776cbace06a3e3ba232f1d969dbc82
Resurse web :
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-
087-practical-programming-in-c-january-iap-2010/
http://users.cs.cf.ac.uk/Dave.Marshall/C/
Capitolul I. Introducere
Informaţia – un mesaj (de orice natură) care are o anumită relevanţă într-un context dat (care aduce o
precizare într-o problemă ce comportă un anumit grad de incertitudine).
Calculatorul – sistem capabil să proceseze informaţii pe baza unor metode/proceduri bine definite.
Data - informaţia prelucrată de calculator.
Informatica (termenul englezesc corespunzător este Computer Science) –activitate pluridisciplinară,
având ca scop iniţial elaborarea de metode şi dispozitive automate pentru prelucrarea informaţiei
tehnico-ştiinţifice, urmărind în prezent dezvoltarea unor tehnici şi sisteme pentru organizarea,
memorarea, prelucarea şi distribuirea mai eficientă a informaţiei de cea mai diversă natură. În
accepţiunea curentă, cuprinde toate activităţile legate de proiectarea şi exploatarea sistemelor de
prelucare a informaţiei.
Tehnologia informaţiei (termenul englezesc corespunzător este Information Technology, abreviat
IT) – tehnologia necesară pentru crearea, memorarea, prelucrarea, distribuirea şi utilizarea informaţiei,
în formele sale diverse (date tehnico-ştiinţifice, date economice, voce, imagini, filme, prezentări
multimedia); include atât tehnologiile legate de sistemele de calcul, cât şi cele de conversie, comunicaţii,
de transport şi distribuire a informaţiei.
Bit - unitatea de măsură pentru cantitatea de informaţie. Un bit este cantitatea de informaţie necesară
reducerii la jumatăte a incertitudinii. Termenul este o prescurtare combinată a cuvintelor englezeşti
binary digit (cifră binară). Un bit mai are şi semnificaţia unei celule de memorie a unui calculator care
poate conţine doar una din două variante posibile: 0 sau 1. De aceea, cantitatea de informaţie stocată
într-un bit de calculator este chiar de 1 bit.
Algoritm – set ordonat, finit, de paşi executabili, descrişi fără echivoc, necesari pentru rezolvarea unei
categorii de probleme. Denumirea provine de la numele unui faimos matematician persan - Muhammad
ibn Musa al-Khwarizmi.
Scurt istoric
Calculatoare mecanice
- 1642 - Blaise Pascal - prima masină de calculat mecanică (adunări/scăderi pe 6 cifre)
- 1668 - Gottfried Wilhelm von Leibniz (+ înmulţiri)
- 1830 - Charles Babbage - maşină de calcul cu program de control
Calculatoare electromecanice
- 1944 - primul calculator electromecanic - Mark I (alcătuit din comutatoare şi relee)
Calculatoare electronice
- 1946 - primul calculator electronic - ENIAC (Electronic Numerical Integrator and
Computer)
17.468 tuburi electronice, 70.000 rezistenţe, 10.000 condensatoare, 1.500 relee,
6.000 comutatoare manuale şi 5 millioane lipituri
acoperea o suprafaţă de 67 metri pătraţi şi cântărea 30t
5.000 adunări, 357 înmulţiri sau 38 împărţiri pe secundă
- 1946 John von Neumann publică lucrarea intitulata “Preliminary Discussions of the
Logical Design of an Electronic Computer”, lucrare din care derivă marea majoritate a
conceptelor moderne de arhitectură a calculatoarelor – în special aşa-numita arhitectură
von Neumann.
Generaţii de calculatoare electronice
Generaţia a I-a (1943-1956)
- tuburile electronice
- memorie cu tambur magnetic
- viteza de lucru - 50-30.000 operaţii pe secundă
- capacitatea memoriei interne - aproximativ 2KO
- limbaje de asamblare
- epoca informaticii ştiinţifice
date
instructiuni
Unitate de calcul
logico-aritmetica
semnale de
comanda si control
instructiuni
Unitatea de calcul logico- artimetică (ALU) împreună cu cea de comandă şi control formează aşa-numita
unitate centrală (UC sau CPU).
2. Unitatea de calcul
- realizează prelucrarea informaţiilor (datelor) prin efectuarea de operaţii:
- aritmetice (+,-,*,/,%),
- logice (negaţie, conjuncţie, disjuncţie),
- comparări (=,#,>,<,>=,<=)
şi poate lua decizii în funcţie de valoarea rezultatelor.
3. Unitatea de intrare
Asigură conversia informaţiei de la nivelul accesibil utilizatorului la cel intern, specific calculatorului, permiţând astfel
introducerea informaţiei în memoria calculatorului (de exemplu: cititor de cartele, tastatura).
4. Unitatea de ieşire
Asigură conversia inversă a informaţiei spre periferice (monitor - display, imprimantă).
5. Memoria
Are rol de înregistrare şi păstrare a informaţiei în vederea prelucrării ulterioare, punând-o la dispoziţia celorlalte unităţi
implicate în procesare. Informaţiile memorate pot fi atât programe ce se vor executa , cât şi date ce se vor prelucra.
Magistrala de date
Unitati
Unitatea Unitatea
de intrare/
centrala de memorie
iesire
Magistrala de Magistrala de
control control
Magistrala de adrese
Bus
Exemple de limbaje de asamblare : pentru microprocesoarele Intel (8086, 80284, 80386), pentru
microprocesorul Z80, pentru familia de uP Motorola 6800
Exemple de limbaje de nivel înalt : FORTRAN, BASIC, Cobol, Pascal, C, C++, C#, Java, Ada, Prolog, Lisp,
Python.
Scurt istoric
Asambloare - programele care realizeaza translarea (traducerea) programelor sursă scrise în limbaje de
asamblare
Compilatoare - programele care realizeaza translarea (traducerea) programelor sursă scrise în limbaje de
nivel înalt
Editoare de legaturi - Modulele obiect rezultate fie în urma asamblării, fie în urma compilării sunt reunite
(legate) într-un singur modul în aşa-numita fază de editare de legături (link editare), rezultând astfel
programul executabil, care odată încărcat în memorie, poate fi executat de către unitatea centrală.
În cazul în care compilatorul sau editorul de legături semnalează erori, programul sursă trebuie modificat şi
procesul de compilare/editare de legături reluat.
În concluzie :
• Un program executabil este reprezentat de un set de instrucţiuni maşină, care, la rândul lor, sunt
reprezentate prin secvenţe de biţi
• Pentru a putea fi rulat, un program executabil trebuie încărcat în memoria internă
• La orice moment de timp se spune că un calculator se află într-o anumită stare. Starea este caracterizată
de un aşa numit pointer de instrucţiune care indică (pointează) spre instrucţiunea care se execută.
Practic este vorba despre adresa instrucţiunii care se execută şi care este memorată în registrul Program
Counter.
• Modalitatea de execuţie a unui grup de instrucţiuni este descrisă de o aşa-numită structură de control
Biblioteci si/sau
alte module
obiect
Compilator Editor de
Editor
legaturi
(creaza si modifica (creaza cod
obiect) (creaza programul
programul sursa)
executabil) Fisier executabil
Exemple de sisteme de operare: DOS, PC-DOS, MS-DOS, Linux, Mac OS , Solaris, UNIX ,Windows, Android
SO multi-utilizator – permit mai multor utilizatori să utilizeze acelaşi computer în acelaşi timp sau
la momente de timp diferite
- Linux
- Unix
- Windows 2000
SO multithreading - permit ca diferite părţi ale unui program să ruleze concurent (în acelaşi timp)
- Linux
- Unix
- Windows 2000
Sistemul de programare conţine :
- (macro) asambloare
- procesoare de limbaj (compilatoare, interpretoare)
- biblioteci de programe sau proceduri (biblioteci matematice, grafice)
- editoare de legături
- încărcătoare
- depanatoare
- programe pentru gestiunea fişierelor
- medii de programare
Visual C++
(http://msdn.microsoft.com/visualc)
C++ Builder
(http://www.borland.com/us/products/cbuilder/index.html)
Dev-C++ (free)
(http://www.bloodshed.net/devcpp.html)
Code::Block (free)
(http://www.codeblocks.org)
Eclipse(CDT)/EasyEclipse (free)
(http://www.easyeclipse.org/site/home/)
Sumarul Capitolului II
Proprietăţile algoritmilor
- precizie
- claritate
- conciziune
- interpretabilitate
- generalitate
- eficacitate (finitudine)
Clasificarea datelor (după posibilităţile de modificare pe parcursul execuţiei sau de la o execuţie la alta)
- constante
- variabile
Soluţionarea unei probleme folosind un sistem de calcul
Salut Salut
camp curent camp curent camp curent
-1.234 -1.234
A
1000 #
Electronica
a) b) c)
nume variabila
date de intrare
a valoare viteza
-123 0.95
F
nume variabila
valoare
nume caracter
rezultate
Ioan @
d) e)
Figura 2. Modele pentru unităţile funcţionale ale unui sistem de calcul :
a) unitate de intrare, b) unitate de ieşire, c) unitate de memorie externă, d) unitate de memorie internă, e) unitate de calcul
Figura 3.
REPREZENTAREA STRUCTURILOR SIMPLE
SI A BLOCULUI DE EVALUARE
START start
STOP stop
<-- atribuie
citeste
CITESTE
scrie
SCRIE
(bloc de evaluare)
DA NU
corpul ciclului
* Secventa
DA expresie_conditie NU
* Secventa_1 * Secventa_2 NU DA
expresie_conditie
corpul ciclului
NU expresie_conditie DA
NU contor <= corpul ciclului
valoare_finala DA
* Secventa
* Secventa
1. Secvenţa
Enunţ 1: Dându-se valorile a,b,c, preluate de la consolă, se cere să se calculeze şi să se afişeze valorile expresiilor :
b 2 ac
E1
2
b 2 ac
E2 ab
2
Soluţie :
start
citeşte a,b,c
atribuie E1 ← (b*b – a*c)/2
atribuie E2 ← E1- a*b
scrie E1,E2
stop
DEFINIŢIE : Secvenţa reprezintă un şir de acţiuni care se execută una după alta, în ordinea în care
au fost scrise, efectul fiecărei acţiuni cumulându-se efectului global al acţiunilor ce o preced.
In concluzie, secvenţa poate cuprinde o înşiruire de structuri simple şi sau alte structuri de control, formând astfel
ceea ce se numeşte o structură compusă.
2. Decizia
Enunţ 2.1: Dându-se valorile a,b, preluate de la consolă, se cere să se calculeze şi să se afişeze valoare maximă dintre
acestea.
Descrierea deciziei ca structură de control este următoarea :
Decizia se execută astfel : în cazul în care condiţia este îndeplinită, se execută secvenţa de acţiuni marcată prin
enunţul nestandard *secvenţă_1, ce reprezintă practic o structură compusă, şi nu se execută *secvenţă_2. În cazul în
care condiţia nu este îndeplinită, se execută secvenţa de acţiuni marcată prin enunţul nestandard *secvenţă_2, şi nu
se execută *secvenţă_1 .
În cazul în care una din alternative este vidă se poate folosi o
formă prescurtată în care este prezentă doar alternativa dacă :
DA expresie_conditie NU
DEFINIŢIE : Decizia reprezintă alegerea unei alternative dintre două posibile, pe baza evaluării unei
condiţii.
Enunţ 2.2: Dându-se valorile a,b,c preluate de la consolă, se cere să se calculeze şi să se afişeze valoarea maximă
dintre acestea.
Soluţie :
start
citeşte a,b,c
dacă a>b atunci
| dacă a>c atunci
| | scrie a
| | altfel
| | scrie c
| |_□
| altfel
| dacă b>c atunci
| | scrie b
| | altfel
| | scrie c
| |_□
|_□
stop
Evident această soluţie este greu generalizabilă, de aceea se preferă varianta de mai jos:
start
citeşte a,b,c
atribuie max a
dacă b > max atunci
| atribuie max b
|_ □
dacă c > max atunci
| atribuie max c
|_□
scrie max
stop
Temă :
1. Dându-se valorile a, b, c, d, e preluate de la consolă, se cere să se calculeze şi să se afişeze valoarea maximă dintre
valorile negative introduse (se va semnala cazul în care nici una dintre valorile introduse nu este negativă).
2. Dându-se valorile a, b, c, d, e preluate de la consolă, se cere să se calculeze şi să se afişeze valoarea maximă dintre
valorile pare introduse, precum şi numărul valorilor pare introduse.
3. Structuri de control ciclice
DEFINIŢIE : Structurile de control în care un grup de operaţii se execută în mod repetat se numesc
structuri ciclice sau iterative.
Enunţ 3.1.1 : Programatorul alege un număr (între 1 şi 50) şi cere utilizatorului acelui program să îl ghicească.
Obs. În condiţiile în care intervalul de alegere este extins, se poate limita numărul de încercări (temă de rezolvat!) .
Structura de tip ciclu cu test iniţial se descrie în general astfel :
corpul ciclului
NU expresie_conditie DA cât timp expresie_condiţie execută
| *secvenţă
|_□
* Secventa
unde: expresie_condiţie reprezintă relaţia de condiţionare a ciclului, iar secvenţă, corpul ciclului.
Observaţii :
- dacă de la început, deci la intrarea în execuţie a ciclului, condiţia nu este îndeplinită, corpul ciclului nu se
execută niciodată;
- corpul ciclului trebuie să acţioneze asupra condiţiei, modificând-o în sensul în care, la un moment dat aceasta
să nu mai fie îndeplinită, pentru a asigura un număr finit de execuţii ale corpului ciclului.
- structura se pretează acelor aplicaţii în care numărul de iteraţii nu este cunoscut a priori şi nu există garanţia
faptului că secvenţa ce reprezintă corpul ciclului de execută cel puţin o dată.
Soluţie 3.1.1 :
start
atribuie nr_secret 12
scrie “Ghiceste numarul !”
citeşte n
cât timp n ≠ nr_secret execută
| scrie “ N-ai ghicit !!! Incearca din nou !”
| citeşte n
|_□
scrie “Felicitari, ai ghicit !!!”
stop
Temă – Introduceţi în soluţie restricţia ca utilizatorul să ghicească numărul din maximum 25 de încercări .
Capitolul III. Proiectarea algoritmilor
Concepte de bază ale Programării Structurate - recapitulare
Programarea structurată - este o paradigmă a programării informatice apărută ca un model nou în
scopul de a crea noi tehnici capabile să dezvolte în mod eficient programe. Conceptele de bază au fost
enunţate în 1966 prin aşa-numita Teoremă de structură, publicată într-o lucrare de Böhm and
Jacopini.
Modelul simplificat al unui sistem ce calcul presupune operarea cu o maşină capabilă să execute doar 5
operaţii elementare:
- pornirea
- citirea
- atribuirea
- scrierea
- oprirea
Descrierea acestor operaţii într-un algoritm se face folosind corespunzător 5 structuri simple
(elementare).
Ordinea în care se execută acţiunile (operaţiile) într-un sistem de calcul este descrisă cu ajutorul aşa-
numitelor structuri de control.
Principiul programării structurate : orice algoritm având o singură intrare şi o singură ieşire
poate fi reprezentat ca o combinaţie de structuri simple şi de trei structuri de control :
- secvenţa
- decizia
- ciclul cu test iniţial
Fără a contraveni principiilor programării structurate, se tolerează si următoarele structuri de control
(ce asigură o descriere mai compactă a algoritmilor)
- ciclul cu test final
- ciclul cu contor
- selecţia
3. Structuri de control ciclice
corpul ciclului
NU expresie_conditie DA cât timp expresie_condiţie execută
| *secvenţă
|_□
* Secventa
unde: expresie_condiţie reprezintă relaţia de condiţionare a ciclului, iar secvenţă, corpul ciclului.
Observaţii :
- dacă de la început, deci la intrarea în execuţie a ciclului, condiţia nu este îndeplinită, corpul ciclului
nu se execută niciodată;
- corpul ciclului trebuie să acţioneze asupra condiţiei, modificând-o în sensul în care, la un moment
dat aceasta să nu mai fie îndeplinită, pentru a asigura un număr finit de execuţii ale corpului ciclului.
- structura se pretează acelor aplicaţii în care numărul de iteraţii nu este cunoscut a priori şi nu există
garanţia faptului că secvenţa ce reprezintă corpul ciclului de execută cel puţin o dată.
Enunţ 3.1.2: Pentru un număr n precizat de valori preluate de la consolă se cere să se calculeze şi să se
afişeze suma şi produsul acestora (nu este necesară păstrarea în memorie a tuturor termenilor introduşi).
Soluţie 3.1.2.a:
start
citeşte n
atribuie s 0, p 1
atribuie i 1
cât timp i <= n execută
| citeşte x
| atribuie s s + x
| atribuie p p * x
| atribuie i i + 1
|_□
scrie s,p
stop
Temă : Completaţi soluţia astfel încât să se calculeze şi să se afişeze media aritmetică a termenilor nenuli
preluaţi de la consolă.
3.2 Ciclul cu contor
Modul de execuţie al acestei structuri rezultă din echivalenţa ce se poate stabili cu secvenţa de mai jos,
care confirmă neîncălcarea principiilor programării structurate :
atribuie contor val_in
cât timp contor <= val_fin execută
| *secvenţă
| atribuie contor contor + pas
|_□
Observaţii :
- dacă de la început, deci de la intrarea în execuţie a ciclului, valoarea iniţială a contorului este strict
mai mare decât valoarea finală a acestuia, corpul ciclului nu se execută niciodată
- în cazul folosirii ciclului cu contor, iniţializarea contorului, testul asupra valorii finale a acestuia,
precum şi actualizarea contorului sunt incluse implicit în cuvintele cheie ale structurii.
- structura se pretează acelor aplicaţii în care numărul de iteraţii este cunoscut a priori.
- structura de tip ciclu cu contor este o structura tolerată, ea asigurând o descriere mai compactă a
algoritmilor.
Soluţie 3.1.2.b:
start
citeşte n
atribuie s 0, p 1
pentru i = 1, n execută
| citeşte x
| atribuie s s + x
| atribuie p p * x
|_□
scrie s,p
stop
Enunţ 3.1.3: Se preia de la consolă un şir de valori strict pozitive. Se cere să se calculeze şi să se afişeze
suma şi produsul acestora, precum şi numărul termenilor astfel introduşi. Sfârşitul sesiunii este marcat prin
introducerea unui număr negativ.
Soluţie 3.1.3 a:
start
atribuie s 0
atribuie nrp 0
citeşte x
cât timp x > 0 execută
| atribuie s s + x
| atribuie nrp nrp + 1
| citeşte x
|_□
scrie s, nrp
stop
Temă : Completaţi soluţia astfel încât să se calculeze şi să se afişeze produsul termenilor pari preluaţi de la
consolă.
3.3 Ciclul cu test final (condiţionat posterior)
corpul ciclului
* Secventa
repetă
*secvenţă
cât timp expresie_condiţie
NU DA
expresie_conditie
unde: expresie_condiţie reprezintă relaţia de condiţionare a ciclului, iar secvenţă, corpul ciclului.
Observaţii :
- corpul ciclului se execută cel puţin o dată ;
- corpul ciclului trebuie să acţioneze asupra condiţiei, modificând-o în sensul în care, la un moment
dat aceasta să nu mai fie îndeplinită, pentru a asigura un număr finit de execuţii ale corpului ciclului.
- structura se pretează acelor aplicaţii în care numărul de iteraţii nu este cunoscut a priori şi există
garanţia faptului că secvenţa ce reprezintă corpul ciclului de execută cel puţin o dată
- structura de tip ciclu cu test final este o structura tolerată, ea asigurând o scriere mai compactă a
algoritmilor, ea putând fi descrisă echivalent :
*secvenţă
cât timp expresie_condiţie execută
| *secvenţă
|_□
- implementarea standard în programarea structurată a structurii de tip ciclu cu test final se descrie în
general în pseudocod astfel :
repetă
*secvenţă
până când expresie_condiţie
Se observă cu uşurinţă faptul că , în cazul structurii modificate, relaţia de condiţionare a ciclului este negata
expresiei care apare în structura standard, astfel încât repetarea corpului ciclului se face cât timp condiţia
este îndeplinită şi se încheie în momentul în care condiţia nu mai este îndeplinită .
Vom folosi în cele ce urmează structura modificată deoarece aceasta corespunde implementării în limbajul C
a instrucţiunii aferente.
Enunţ 3.3.1 : Proiectaţi un algoritm care afişează mesajul ”Continuati ?(D/N)“ cât timp nu s-au tastat
literele N or n de la consolă. (Temă– Descrieţi soluţia solosind ciclul cu test iniţial.)
Soluţie 3.3.1
start
repetă
scrie ”Continuati ?(D/N)“
citeşte raspuns
cât timp raspuns ≠ N şi raspuns ≠ n
scrie ”Sfarsit !!!“
stop
Enunţ 3.3.2 : Se cere să se scrie un algoritm care preia de la consolă un text, incheiat cu caracterul punct.
Soluţie 3.3.2
start
scrie ”Introduceti un text. Tastati “.” pentru a incheia“
repetă
citeşte caracter
scrie caracter
cât timp caracter ≠ .
stop
Temă : Completaţi soluţia astfel încât să se calculeze şi să se afişeze numărul de caractere ale propoziţiei
citite de la consolă.
Enunţ 3.3.3 : Se preia de la consolă un şir de valori strict pozitive. Se cere să se calculeze şi să se afişeze
suma şi produsul acestora, precum şi numărul termenilor astfel introduşi. Sfârşitul sesiunii este marcat
prin introducerea unui număr negativ. (Să se implementeze soluţia folosind ciclul cu test final.)
Soluţie 3.3.3 :
start
atribuie s 0
atribuie nrp -1
atribuie x 0
repetă
| atribuie s s + x
| atribuie nrp nrp + 1
| citeşte x
| cât timp x > 0
scrie s, nrp
stop
Enunţ 3.1.4. : Pentru un număr întreg n, diferit de zero, preluat de la consolă, se cere să se calculeze şi să
se afişeze suma şi numărul cifrelor sale în baza 10.
Soluţie 3.1.4. :
start
citeşte n
atribuie n |n|
atribuie suma_cifre 0
atribuie număr_cifre 0
cât timp n ≠ 0 execută
| atribuie cifra_curentă *restul împărţirii lui n la 10
| atribuie suma_cifre suma_cifre + cifra_curentă
| atribuie număr_cifre număr_cifre + 1
| atribuie n *câtul împărţirii lui n la 10
|_□
scrie suma_cifre, număr_cifre
stop
Temă : Completaţi soluţia astfel încât afişarea numărului de cifre să fie corectă inclusiv pentru cazul n=0.
Enunţ 3.3.4 : Se cere ca, pentru o valoare x preluată de la consolă , |x|≤½, să se calculeze valoarea
aproximativă a valorii funcţiei ex , cu o eroare inferioară unui ε precizat. (e este numărul lui Euler)
xk n xk
e
x
t k R n ( x ), tk , R n (x) t k
k 0 k! k 0 k! k n 1
Să notăm cu e_aprox valoarea aproximativă a lui ex. Evident modulul erorii de aproximare care se face
trunchiind suma la n+1 termeni este :
Observaţii :
1. Condiţia suplimentară impusă ca |x| ½ face ca majorarea de mai sus să fie valabilă începând
chiar de la n = 1.
2. Calculul termenilor sumei ca raport între o putere a lui x şi un factorial este ineficient din punct de
vedere numeric, de aceea se caută o relaţie de recurenţă.
Soluţie 3.3.4. :
start
citeşte x, ε
dacă |x| <=0.5 atunci
| atribuie t 1
| atribuie k 1
| atribuie e_aprox 1
| repetă
| atribuie t t*x/k
| atribuie e_aprox e_aprox + t
| atribuie k k + 1
| cât timp | t | ≥ ε
| scrie e_aprox
| altfel
| scrie ”Date eronate ! ”
|_□
stop
Temă :
1. Completaţi tabloul de evoluţie a valorilor variabilelor algoritmului pentru cazul x=0.4 şi ε=0.0001
2. Scrieţi soluţia completă a algoritmului de calcul a valorii aproximative a valorii ex (inclusiv pentru
cazul |x|>½).
3. Modificaţi soluţia astfel încât să se limiteze numărul maxim de iteraţii în care se calculează
aproximarea la o valoare impusă, MAX _ITER = 1000.
Capitolul III. Proiectarea algoritmilor (continuare)
4. Date structurate
Un tip de date defineşte mulţimea valorilor pe care le pot lua datele respective precum şi mulţimea
operaţiilor care se pot executa asupra datelor respective
Datele care sunt indivizibile în raport cu operaţiile ce se execută asupra lor, se numesc date simple. O
dată simplă poate memora o singură valoare - un număr unic, întreg sau real, o valoare booleană unică sau
un caracter unic. Datele simple aparţin unor tipuri de date simple
Tipurile de date rezultate prin organizarea altor tipuri de date, în particular a celor simple, se numesc
tipuri structurate, iar datele ce aparţin unor astfel de tipuri se numesc date structurate
Pentru a caracteriza o dată aparţinând unui tip structurat e necesar sa se considere :
- numărul şi tipul elementelor din care poate fi compusă data respectivă
- modul de acces la elementele componente
- posibilitatea adăugării ulterioare de noi componente.
tabloul este o structură omogenă, ce constă dintr-o mulţime cu un număr fixat de componente de acelaşi
tip, numit tip de bază
fiecare element al unui tablou este accesibil pe baza poziţiei sale în structură, poziţie precizată prin
intermediul unor valori numite indici (indecşi)
dimensiunea unui tablou este dată de numărul de indici necesari pentru a specifica poziţia uni element
din structură
după dimensiune, tablourile se clasifică în :
- tablouri unidimensionale (vectori, şiruri)
- tablouri bidimensionale (matrici)
- tablouri multidimensionale
referirea la elementele unui tablou se face prin intermediul unei aşa-numite variabile indexate.
Observaţie : În teoria algoritmilor nu există actualmente un standard privitor la modul în care se construieşte numele
unei variabile indexate. De aceea vom folosi o notaţie specifică limbajului C. Astfel numele unei date indexate se
formează plecând de la numele tabloului, urmat, între paranteze drepte (croşete), de expresiile indiciale adecvate.
Suplimentar, se va folosi convenţia din C, în conformitate cu care indicele tablourilor are ca valoare de start
valoarea zero.
Enunţ 4.1.: Pentru un vector x, se preiau de la consolă, numărul n al elementelor sale, precum şi valorile acestora. Se
cere să se afişeze elementele vectorului şi să se determine şi afişeze elementul maxim şi poziţia acestuia.
Soluţie 4.1. :
start
citeşte n
pentru i = 0, n-1 execută
| citeşte x[i]
|_□
atribuie max x[0]
atribuie poz_max 0
pentru i = 1,n-1 execută
| dacă x[i] > max atunci
| | atribuie max x[i]
| | atribuie poz_max i
| |_□
|_□
pentru i = 0 , n-1 execută
| scrie x[i]
|_□
scrie max
scrie poz_max
stop
Enunţ 4.2 : Pentru un vector v cu n elemente să se ordoneze crescător elementele sale folosind sortarea prin
interschimbare.
Indicaţie : Metoda presupune compararea fiecărui element al şirului, de la primul, la penultimul, cu toate elementele
care îi urmează, interschmbând elementele ori de câte ori acestea nu respectă relaţia de ordonare.
Soluţie 4.2. :
start
citeşte n
pentru i = 0 , n - 1 execută
| citeşte v[i]
|_□
pentru i = 0 , n - 2 execută
| pentru j = i +1, n-1 execută
| | dacă v[i] > v[j] atunci
| | | atribuie aux v[i]
| | | atribuie v[i] v[j]
| | | atribuie v[j] aux
| | |_□
| |_□
|_□
pentru i = 0 , n - 1 execută
| scrie v[i]
|_□
stop
Enunţ 4.3 Fie un vector x cu n elemente. Să se construiască în memorie şi apoi să se afişeze un nou vector y care să
conţină doar elementele pare din vectorul x.
Soluţie 4.3. :
start
citeşte n
pentru i = 0, n-1 execută
| citeşte x[i]
|_□
atribuie j 0
pentru i = 0, n-1 execută
| dacă * restul împărţirii lui x[i] la 2 = 0 atunci
| | atribuie y[j] x[i]
| | atribuie j j + 1
| |_□
|_□
dacă j ≠ 0 atunci
| pentru i = 0 , j-1 execută
| | scrie y[i]
| |_□
altfel
| scrie “Nu sunt elemente pare !”
|_□
stop
Enunţ 4.4 : Fie o matrice A, cu m linii şi n coloane, m şi n preluate de la consolă. Să se citească elementele matricii,
să se afişeze matricea, să se calculeze şi afişeze suma şi produsul elementelor matricii .
Soluţie 4.4. :
start
citeşte m,n
pentru i = 0 , m-1 execută
| pentru j = 0 , n-1 execută
| | citeşte a[i][j]
| |_□
|_□
atribuie s 0
atribuie p 1
pentru i = 0 , m-1 execută
| pentru j = 0 , n-1 execută
| | atribuie s s + a[i][j]
| | atribuie p p * a[i][j]
| |_□
|_□
pentru i = 0 , m-1 execută
| pentru j = 0 , n-1 execută
| | scrie a[i][j]
| |_□
|_□
scrie s , p
stop
5. Subprograme
un subprogram este alcătuit prin gruparea unei secvenţe de acţiuni într-o descriere separată,
căreia i se atribuie un nume, numele subprogramului
utilizarea subprogramelor este legată de conceptul de abstractizare procedurală ce
defineşte posibilitatea atribuirii unui nume unei secvenţe de acţiuni, urmând ca acest nume să
fie folosit pentru a determina execuţia secvenţei respective
invocarea numelui subprogramului în vederea execuţiei acestuia se numeşte apel de
subprogram
secţiunea care realizează apelul se numeşte secţiune apelantă, iar subprogramul devine în
acest context, secţiune apelată.
pentru a creşte generalitatea şi flexibilitatea subprogramelor, acestea pot comunica cu
exteriorul prin intermediul unor aşa-numiţi parametri.
în funcţie de sensul transferului de informaţii, parametrii unui subprogram pot fi :
- parametrii de intrare, sensul transferului de informaţii fiind în acest caz dinspre
secţiunea apelantă spre secţiunea apelată
- parametrii de ieşire, sensul transferului de informaţii fiind în acest caz dinspre
secţiunea apelată spre secţiunea apelantă.
în funcţie de locul în care se folosesc într-un algoritm, parametrii se clasifică în :
- parametri formali - folosiţi pentru descrierea acţiunilor pe care le execută
subprogramul
- parametri efectivi (sau actuali) reprezintă acele valori cu care se va executa efectiv
subprogramul şi care vor substitui parametrii formali în momentul apelului
mecanismul de înlocuire (substituţie) a parametrilor formali de către parametrii efectivi se
numeşte transfer al parametrilor
între parametrii formali şi cei efectivi există o corespondenţă biunivocă
corespondenţa dintre parametrii formali şi parametrii efectivi este poziţională : parametrul ce
ocupă poziţia p în lista parametrilor efectivi va înlocui parametrul formal ce ocupă aceeaşi
poziţie, p, în lista parametrilor formali, coincizând ca tip şi evident semnificaţie.
în teoria algoritmilor se definesc două tipuri de subprograme :
- funcţii - subprograme ce returnează o valoare, prin intermediul numelui lor, secţiunii
apelante. În principiu se recomandă ca funcţiile să aibă numai parametrii de intrare.
Fiind purtătoare de valori, numele funcţiilor pot apare ca operanzi în expresii.
- proceduri - subprograme ce nu returnează valori prin intermediul numelui lor,
comunicarea cu secţiunea apelantă făcându-se numai prin intermediul parametrilor,
care, în acest caz, pot fi atât parametrii de intrare cât şi parametrii de ieşire.
Descrierea subprogramelor în pseudocod se face astfel :
respectiv :
Se observă că ar fi extrem de utilă proiectarea unei funcţii care să calculeze şi să returneze valoarea
factorialului argumentului. Fie numele acestei funcţii fact.
funcţie fact(m)
atribuie f 1
pentru i = 2, m execută
atribuie f f * i
returnează f
sfârşit
Algoritmul care descrie calculul combinărilor făcând apel la această funcţie este prezentat în cele ce
urmează.
start
citeşte n,k
atribuie comb fact(n)/fact(k)/fact(n-k)
scrie comb
stop
Indicaţie : Funcţia care calculează cel mai mare divizor comun se proiectează în conformitate cu
algoritmul lui Euclid. Astfel pentru a calcula cel mai mare divizor al două numere naturale, se împart
cele două numere. Dacă restul împărţirii este nul, împărţitorul divide deîmpărţitul. În caz contrar
procesul împărţirilor continuă, la fiecare pas, vechiul împărţitor devenind deîmpărţit, iar noul
împărţitor fiind restul împărţirii anterioare. Ultimul rest nenul este cel mai mare divizor al celor două
numere.
Soluţie 5.2:
Funcţia care calculează cel mai mare divizor comun al parametrilor m şi n este :
funcţie cmmdc(m , n)
cât timp n0 execută
| atribuie r *restul împărţirii lui m la n
| atribuie m n
| atribuie n r
|_□
returnează m
sfârşit
start
citeşte NR_MAX
pentru i = 1 , NR_MAX-1 execută
| pentru j = i +1, NR_MAX execută
| | scrie cmmdc(i,j)
| |_□
|_□
stop
Temă. Pentru 2 vectori, u,v, cu p elemente de tip întreg, să se construiască un nou vector, w, conţinând cmmdc al
elementelor de acelaşi rang (indice) .
Indicaţie: w[i]=cmmdc(u[i],v[i]), i=0,p-1.
Enunţ 5.3 : Fie un vector V, cu n elemente. Se cere să se ordoneze descrescător elementele vectorului,
utilizând metoda sortării prin selecţie.
Indicaţie : metoda sortării prin selecţie constă în execuţia următoarelor operaţii : pentru fiecare poziţie
a vectorului se caută poziţia maximului dintre elementele ce urmează poziţiei curente, inclusiv. Se
interschimbă apoi elementul de pe poziţia curentă cu elementul maxim astfel găsit şi se continuă
procedeul până la penultima poziţie.
Observaţie : metoda propusă nu este cea mai eficientă metodă de ordonare, dar este ilustrativă pentru
conceptele prezentate.
Soluţie 5.3:
Vom construi o funcţie pentru localizarea maximului, cu numele LocMax. Aceasta va avea ca
parametri, vectorul X în care se face căutarea, poziţia de la care începe căutarea, prim şi poziţia la care
se încheie căutarea, ultim.
Vom proiecta o procedură cu numele Schimba, ce va avea ca parametri valorile care se interschimbă.
procedură Schimba(a, b)
atribuie aux a
atribuie a b
atribuie b aux
sfârşit
Soluţia finală a problemei este :
start
citeşte n
pentru i = 0 , n - 1 execută
| citeşte V [i]
|_□
pentru i = 0 , n - 2 execută
| atribuie poz LocMax(V, i, n - 1)
| Schimba (v[poz], v[i])
|_□
pentru i = 0 , n - 1 execută
| scrie V [i]
|_□
stop
Temă :
1. Să se proiecteze şi să se apeleze adecvat o procedură care să citească un vector cu un număr specificat de
elemente.
2. Să se proiecteze şi să se apeleze adecvat o procedură care să afişeze un vector cu un număr specificat de
elemente.
Sumarul Capitolului III - Proiectarea algoritmilor
Definiţia algoritmului
Proprietăţile algoritmilor
Definiţia datelor
Clasificarea datelor (după tip, după poziţia în lanţul informatic, după posibilităţile de modificare pe parcursul
execuţiei sau de la o execuţie la alta)
Principalele etape necesare pentru rezolvarea unei aplicaţii cu ajutorul unui sistem de calcul
Modalităţi de descriere a algoritmilor (scheme logice şi limbajul pseudocod)
Conceptul de programare structurată - enunţul Teoremei de structură
Definiţia, descrierea (cu scheme logice sau în limbajul pseudocod), caracteristicile şi condiţiile de utilizare ale
structurilor de control din programarea structurată:
- secvenţa
- decizia
- ciclul cu test iniţial
- ciclul cu test final
- ciclul cu contor
- selecţia
Conceptul de dată structurată
Definiţia şi caracteristicile tipului tablou (tipul şi numărul componentelor, modalitatea de acces la elementele
componente)
Conceptul de subprogram
Definiţia şi clasificarea parametrilor unui subprogram
Definiţia, descrierea, caracteristicile şi condiţiile de utilizare ale procedurilor şi funcţiilor
Capitolul IV. Programarea în limbajul C
1. Scurt istoric
„Părintele” limbajului C este Dennis Ritchie (Bell Laboratories)
Limbajul a fost proiectat în 1972 pentru implementarea unui sistem de operare pentru calculatoarele PDP-11,
ulterior fiind folosit pentru implementarea portabilă a sistemului de operare UNIX
În 1978, Dennis Ritchie şi Brian Kernighan au publicat prima ediţie a cărţii Limbajul de programare C (The C
Programming Language), versiunea limbajului C descrisă fiind cunoscută sub numele K&R C.
În 1983, American National Standards Institute (ANSI) a format un comitet pentru a stabili specificaţiile unui
limbaj C standard. Standardul a fost terminat în 1989 şi ratificat ca ANSI X3.159-1989 "Programming Language
C". Această versiune a limbajului este cunoscută sub numele ANSI C.
În 1990, standardul ANSI C (cu mici modificări) a fost adoptat de International Organization for
Standardization (ISO) ca ISO/IEC 9899:1990. Acest standard este cunoscut actualmente sub numele de C99.
Revizia curentă este ISO/IEC 9899:2018 – cunoscută sub numele de C17, publicată în iunie 2018, înlocuind
standardul C11 (standard ISO/IEC 9899:2011).
Cele mai utilizate medii de dezvoltare pentru limbajul C sunt:Visual C++, Borland C++, Dev-C++, Code::Blocks.
Limbajul C este considerat ca un limbaj de nivel mediu (intermediar), oferind programatorilor atât posibilitatea
de a utiliza conceptele programării structurate, cât şi facilităţi specifice limbajelor de asamblare
#include <stdio.h>
int main(void)
{
printf("Hello, World!\n");
return 0;
}
2. Noţiuni introductive
Un limbaj de programare descrie, prin directive şi instrucţiuni, prelucrările de date ce vor trebui efectuate de
către un sistem de calcul
În general, limbajele de programare se definesc prin :
- sintaxă - mulţimea de reguli ce descriu modalităţile de scriere corectă a programelor în limbajul respectiv;
- semantică - mulţimea de reguli ce definesc înţelesul unui program şi efectul execuţiei sale asupra unui
calculator real sau virtual;
- pragmatică - mulţimea de reguli ce definesc detaliile de implementare a limbajului pe un anumit tip de
calculator.
Numai sintaxa dispune de o modalitate sistematică de prezentare şi anume fie folosind aşa-numita notaţia
BNF (Backus-Naur Form), fie folosind diagramele sintactice (Conway).
Descrierea BNF este un şir de aşa-numite producţii ce definesc tipurile de elemente componente ale limbajului
cu ajutorul unor simboluri de metalimbaj.
Diagramele sintactice reprezintă o modalitate grafică de descriere a regulilor sintactice, fiind uşor de înţeles şi
extrem de sugestive.
Vom prezenta în cele ce urmează noţiunile principale privitoare la diagramele sintactice, care să permită
ulterior descrierea sistematică a regulilor de sintaxă ale limbajului C.
2.1. Diagrame sintactice
O diagramă sintactică este un graf orientat, cu un singur nod iniţial (în care nu intră nici un arc) şi un singur
nod final (din care nu iese nici un arc).
Orice drum posibil de la nodul iniţial la nodul final descrie o construcţie corectă din punct de vedere sintactic.
Nodului inţial i se asociază un nume, care va fi interpretat ca numele diagramei şi implicit al construcţiei
sintactice descrise de aceasta. Nodul final nu se etichetează (nu i se asociază un nume).
Celelalte noduri ale diagramei se împart în două categorii :
- noduri cu o singură intrare şi o singură ieşire. Astfel de noduri sunt marcate în două moduri :
- prin elemente ale vocabularului limbajului care vor fi reprezentate grafic prin cercuri sau
dreptunghiuri cu colţurile rotunjite;
- prin nume ale altor diagrame sintactice. Astfel de denumiri se vor reprezenta încadrate în
dreptunghiuri. La întâlnirea unui astfel de nod se subînţelege că diagrama sintactică cu numele
menţionat substituie nodul;
- noduri cu o intrare şi mai multe ieşiri, numite bifurcaţii, care nu se marchează;
- noduri cu mai multe intrări şi o ieşire, numite confluenţe, care de asemenea nu se marchează.
3. Elementele limbajului C
Un program C poate fi considerat în ultimă instanţă un text, format din caractere, grupate în unităţi lexicale,
echivalente cuvintelor textului, ce pot alcătui expresii şi în final instrucţiuni, echivalente frazelor textului.
Elementele componente ale limbajului se pot prezenta sintetic astfel :
- alfabetul;
- unităţile lexicale;
- expresiile;
- instrucţiunile.
3.1. Alfabetul - reprezintă setul de caractere din care poate fi alcătuit un program C. La scrierea programelor în
limbajul C se folosesc caracterele codului ASCII.
3.2. Unităţi lexicale - reprezintă grupuri de caractere cu o semnificaţie de sine stătătoare. În categoria
unităţilor lexicale se includ :
- cuvinte cheie (keywords) ;
- identificatori (nume) ;
- operatori ;
- separatori ;
- constante ;
- comentarii.
3.2.1. Cuvinte rezervate/cheie (keywords) - sunt şiruri de caractere ce reprezintă cuvinte preluate
din limba engleză, ce au un înţeles predefinit în cadrul limbajului. Utilizarea lor nu este permisă în alt context
decât cel cel precizat la definirea limbajului.
Tabelul 3.1. Cuvintele cheie ale limbajului C :
3.2.2. Identificatori (sau nume) - reprezintă denumiri asociate de programator diverselor entităţi
utilizate într-un program.
- din punct de vedere sintactic, identificatorii sunt şiruri de litere şi cifre ce pot include şi caracterul de
subliniere( _ ).
- singurele restricţii sunt ca un identificator să înceapă cu o literă sau cu caracterul de subliniere( _ ) şi să
nu coincidă cu un cuvânt rezervat.
cifra
0 1 2 3 4 5 6 7 8 9
litera
identificator
litera
_ cifra
( ) [ ] . ->
- + * & ! ~ ++ -- s iz e o f
* / %
+ -
<< >>
< <= >= >
== !=
&
^
|
&&
||
? :
= += -= *= /= %= <<= >>= &= ^= |=
,
3.2.4. Separatori - sunt caractere sau şiruri de caractere cu rol de separare a celorlalte unităţi lexicale .
- în limbajul C pot avea rol de separatori următoarele caractere sau grupuri de caractere :
- punctul şi virgula (;)
- trei puncte (...)
- spaţii albe (white spaces)
- spaţiul (caracterul blank)
- tabulatorul orizontal
- caracterul linie nouă
3.2.5. Constante - reprezintă mărimi ale căror valori nu se pot modifica pe parcursul execuţiei unui
program sau de la o execuţie la alta.
- orice constantă are un tip şi o valoare.
- atât tipul, cât şi valoarea unei constante se definesc implicit prin caracterele care compun constanta
respectivă.
- în C sunt definite următoarele tipuri de constante :
- constante întregi;
- constante reale;
- constante caracter;
- constante şiruri de caractere;
- o constantă reală reprezintă un număr raţional şi se consideră implicit ca fiind de tip dublă
precizie şi reprezentată intern pe 64 de biţi, în format virgulă mobilă. În cazul în care se
doreşte reprezentarea internă pe 32 de biţi, în simplă precizie, se foloseşte sufixul f sau F.
Dacă se doreşte reprezentarea ca long double, pe 80 de biţi, se foloseşte sufixul L sau l .
- constantele reale se pot scrie în două moduri :
- în format fără exponent - caz în care ele se scriu ca parte întreagă, care poate fi şi
vidă, prefixată sau nu de semn, şi parte fracţionara, ce poate fi vidă (dar nu şi dacă partea
întreagă este vidă), separate între ele printr-un punct zecimal;
- în format cu exponent - caz în care o constantă reală fără exponent sau o constantă
întreagă zecimală sunt urmate de un aşa numit exponent.
- exponentul este un şir de caractere ce începe cu litera e sau E, după care urmează
opţional un semn şi care se încheie cu un şir de cifre zecimale.
- semnificaţia notaţiei cu exponent este aceea că numărul real/întreg care precede
exponentul se înmulţeşte cu 10 la o putere egală cu numărul ce urmează după litera
exponentului.
3.2.5.3. Constante caracter
- o constantă caracter reprezintă un caracter şi are ca valoare codul ASCII extins al
caracterului respectiv.
- constantele caracter se pot clasifica în :
- constante neprintabile (de control), cu codurile ASCII cuprinse între [0, 31], plus
caracterul DEL (şterge), cu codul ASCII 127;
- spaţiu - codul ASCII 32;
- constante printabile, cu codurile ASCII cuprinse în intervalele [33, 126].
- constante grafice, cu codurile cuprinse în intervalul [128, 255]
- o constantă caracter corespunzătoare unui caracter printabil sau spaţiu, se scrie în C prin
încadrarea caracterului respectiv între apostroafe.
- excepţie fac caracterele apostrof ('), ghilimele (") şi backslash (\), care se reprezintă prin
prefixarea lor cu caracterul backslash, şi anume :
'\\' - reprezintă constanta caracter \;
'\'' - reprezintă constanta caracter ' şi
'\"' - reprezintă constanta caracter " .
- anumite constante negrafice se pot reprezenta prin secvenţe speciale şi anume :
'\t' tabulator orizontal;
'\n' rând nou;
'\b’ semnal sonor.
- în general, caracterul backslash se utilizează pentru a defini orice constantă caracter,
obişnuindu-se a se spune că backslash-ul introduce o aşa-numită secvenţă escape.Secvenţa escape
foloseşte codul ASCII al caracterului ce se doreşte a fi reprezentat, construcţia :'\ooo', unde o este o
cifră octală, reprezintă constanta caracter al cărei cod ASCII scris în octal are valoarea ooo.
- constanta cu codul ASCII zero, '\0', se mai numeşte şi N u ll.
3.2.5.4. Constante şiruri de caractere
- o succesiune de zero sau mai multe caractere incluse între ghilimele formează o constantă şir
de caractere.
- la scrierea caracterelor din compunerea unui şir se pot folosi secvenţele escape.
- un şir poate fi continuat pe rândul următor folosind caracterul backslash (\). Caracterul ce
precede caracterul backslash se va concatena cu caracterul scris pe rândul următor.
- caracterele ce compun un şir de caractere se stochează în memorie într-o zonă contiguă,
memorându-se în octeţi consecutivi codurile ASCII ale acestora. După ultimul caracter al şirului se
memorează caracterul N u ll, ce joacă rolul de marcaj al sfârşitului oricărui şir de caractere. Din
această cauză reprezentarea unui caracter ca şi constantă caracter sau ca şir de caractere, conduce la
reprezentări interne diferite, în cel de al doilea caz, folosindu-se doi octeţi (codul ASCII al
caracterului respectiv urmat de codul ASCII 0).
3.2.6. Comentarii
sunt texte ce explicitează un program, documentându-l.
- un comentariu se poate insera oriunde într-un program în locurile în care este permisă folosirea
spaţiului, a tabulatorului orizontal sau a unui rând nou.
- un comentariu începe cu succesiunea de caractere /* şi se termină cu succesiunea */. În C++ s-a
introdus o convenţie suplimentară pentru comentariile care ocupă un singur rând şi anume prefixarea
acestuia cu succesiunea de caractere //.
4. Structura programelor C
Un program scris în limbajul C este format dintr-o succesiune de instrucţiuni înlănţuite în conformitate cu
anumite reguli sintactice. Formatul de redactare este liber (neimpus).
Pentru prezentarea şi învăţarea sistematică a regulilor de sintaxă se obişnuieşte împărţirea logică a programului
în componente, urmată de descrierea formală a acestora şi a legăturilor dintre ele, folosind de exemplu
diagramele sintactice.
Un program C se compune din una sau mai multe funcţii, aşa cum rezultă din diagrama sintactică de mai jos:
program_C
functie
functie
Fiecare funcţie are un nume, iar dintre aceste funcţii, una este în mod obligatoriu funcţia principală, ce poartă
numele m a in .
Funcţia m a in defineşte adresa de lansare în execuţie a programului.
Programul se memorează sub forma unuia sau a mai multor fişiere sursă pe disc, fişiere ce au extensia C, pentru
limbajul C şi CPP, pentru limbajul C++.
Prin compilarea fişierelor sursă se obţin fişiere obiect, ce au extensii OBJ sau o, în funcţie de compilator.
Fişierele obiect astfel obţinute se reunesc într-un program executabil, prin editarea de legături, rezultând un
fişier executabil, cu extensia EXE.
4.1. Definiţia unei funcţii în limbajul C
definitie_functie
antet corp
tip
antet
identificator ( lista_parametrii_formali )
4.2. Preprocesarea
#include <specificator_fişier>
#include "specificator_fişier "
Specificatorul de fişier depinde de sistemul de operare. El defineşte un fişier cu text sursă memorat pe
disc. În faza de preprocesare, construcţia # include este substituită de textul fişierului menţionat. În
felul acesta textul fişierului respectiv participă la compilare împreună cu textul în care a fost inclus.
Formatul cu paranteze unghiulare se utilizează la includerea fişierelor standard, de exemplu fişierele
ce conţin prototipurile funcţiilor din bibliotecile standard, căutarea acestor fişiere făcându-se în
directoare speciale, precizate de configurările specifice mediului de programare în care se operează.
Formatul în care se utilizează ghilimelele indică faptul că se va face căutarea fişierului menţionat pe
calea precizată de specificatorul de fişier sau în directorul curent.
4.2.2. Definiţii şi apeluri de macrouri
# u n d e fin e nume
La întâlnirea ei se dezactivează substituţia numelui nume prin succesiunea de caractere ce i-a fost
asociată prin directiva pereche #d e fin e
Macrodefiniţiile pot avea şi parametri :
#define nume(arg1,arg2, ...) succesiune_de _caractere
unde:
Macrodefiniţiile sunt expandate în textul sursă înainte de compilare (în faza de preprocesare) în doua etape:
declaratie_variabila_simpla
tip identificator ;
În diagrama din figura 6.1, tip reprezintă tipul variabilei identificator şi poate fi un tip de date simplu,
predefinit, un tip de date structurat sau un tip de date definit de utilizator, aşa cum se va arăta în capitolele
următoare.
Practic, prin intermediul identificatorului ce reprezintă numele variabilei, referim valoarea conţinută în
zona de memorie alocată variabilei.
7. Citirea şi scrierea datelor în C. Operaţii de intrare/ieşire standard
Prin termenul de operaţii de intrare/ieşire se înţelege un set de acţiuni care permit schimbul de date
între un program şi un periferic.
Operaţia de introducere a datelor de la un periferic se numeşte citire, iar cea de extragere a datelor
către un periferic se numeşte scriere.
Operaţiile de intrare/ieşire se realizează prin apeluri de funcţii din bibliotecile limbajului şi nu prin
intermediul unor instrucţiuni de intrare/ieşire, ceea ce asigură flexibilitate şi portabilitate programelor,
deoarece astfel de operaţii sunt puternic dependente de particularităţile hardware-ului.
Un prim set de astfel de funcţii asigură interfaţa cu terminalul de la care s-a lansat programul, care se
numeşte terminal standard. De obicei, terminalul standard este consola. Operaţiile de intrare/ieşire ce
implică terminalul consolă se numesc operaţii de intrare/ieşire standard.
Într-un program C datele de intrare/ieşire se presupun organizate în fişiere.
Un fişier este o colecţie de informaţii organizate pe un anumit suport.
Unui program C i se ataşează automat următoarele fişiere :
- s t d in - intrare standard;
- s t d o u t - ieşire standard;
- s t d e r r - ieşire standard pentru erori;
- s t d p r n - ieşire la imprimantă;
- s t d a u x - intrare/ieşire serială.
În cele ce urmează ne vom referi la funcţiile care în mod implicit se referă la primele două fişiere, şi
anume:
- pentru scriere: p u t ch , p u t s şi p r in t f
- pentru citire : g e t ch , g e t ch e , g e t s , s ca n f.
Fişierele s t d in şi s t d o u t sunt fişiere de tip text ce conţin exclusiv şiruri de caractere organizate pe linii.
Fiecare linie se termină printr-un caracter special, numit “sfârşit de linie” sau “linie nouă” (simbolizat
uneori şi ca e o ln , acronim de la termenul englezesc end of line).
Sfârşitul fişierului este marcat şi el printr-un caracter special, numit sfârşit de fişier, referit prin
constanta simbolică predefinită EOF (acronim de la termenul englezesc end of file).
Citirea şi scrierea acestor fişiere se poate face caracter cu caracter, folosind funcţii speciale de
bibliotecă: g e t ch , g e t ch e , respectiv p u t ch şi macrourile g e t ch a r şi p u t ch a r .
Funcţiile p r in t f şi s ca n f realizează operaţii mai complexe. Astfel diversele tipuri de date ce se pot citi
de la, respectiv scrie la consolă sunt reprezentate în fişierele s t d in , s t d o u t sub forma unor şiruri de
caractere (format extern), în timp ce formatul lor de reprezentare în memoria internă a sistemului de
calcul (format intern) este complet diferit. De aceea citirea şi scrierea datelor numerice implică operaţii
de conversie dintr-un format în altul.
Funcţiile p r in t f şi s ca n f sunt extinse în sensul facilitării analizei respectiv formatării fişierelor text
prin executarea implicită a unor asemenea operaţii.
Un alt aspect extrem de important de reţinut este acela că cele două fişiere sunt accesate strict
secvenţial : în mod uzual nu poate avea loc o revenire pe linia anterior scrisă sau citită.
7.1. Funcţile g e t ch şi g e t ch e
Aceste funcţii sunt dependente de implementare.
Functia g e t ch citeşte de la tastatura fără ecou, cu alte cuvinte caracterul tastat de la tastatură nu se
afişează (automat) şi pe ecranul consolei.
Funcţia g e t ch poate fi apelată astfel :
g e t ch ()
Ea permite citirea de la tastatură atât a caracterelor corespunzatoare codului ASCII, cât şi a celor
corespunzatoare unor funcţii speciale.
La citirea unui caracter al codului ASCII, funcţia returnează codul ASCII al caracterului respectiv.
În cazul în care se acţionează o tastă care nu corespunde codului ASCII, funcţia g e t ch se apelează de doua
ori : la primul apel funcţia returnează valoarea zero, iar la cel de al doilea apel se returnează o valoare specifică
tastei acţionate.
Functia g e t ch e este analogă funcţiei g e t ch , cu singura diferenţă că ea realizează citirea cu ecou a
caracterului tastat. Aceasta înseamnă că se afişează automat pe ecranul terminalului caracterul tastat.
Prototipurile funcţiilor g e t ch şi g e t ch e se află în fişierul co n io .h
Ambele funcţii nu au parametri şi se pot apela şi ca operanzi în expresii.
7.2. Funcţia p u t ch
Aceasta funcţie afişează un caracter pe ecranul terminalului standard. Ea are ca parametru caracterul ce
urmează a fi afişat la terminal. Prototipul functiei p u t ch se află în fişierul co n io .h
Funcţia p u t ch poate fi apelată astfel :
p u t ch (expresie)
Valoarea expresie se interpretează ca fiind codul ASCII al caracterului care se afişează.
Dacă valoarea expresiei se afla în intervalul [32,126], atunci se afişează un caracter grafic al codului ASCII.
Dacă valoarea respectivă este în afara acestui interval, atunci se afişează corespunzător anumite simboluri sau
nu se afişează nimic.
Funcţia p u t ch returnează codul caracterului afişat (valoarea parametrului de apel).
7.3. Macro-urile g e t ch a r şi p u t ch a r
Aceste macrouri sunt definite în fişierul s t d io .h . Ele se apeleaza la fel ca şi funcţiile.
Macroul g e t ch a r permite citirea cu ecou a caracterelor de la terminalul standard. Se pot citi numai
caractere ale codului ASCII, nu şi caractere corespunzatoare tastelor speciale.
Prin intermediul acestui macro, caracterele nu se citesc direct de la tastatură. Caracterele tastate se
introduc într-o zonă tampon până la acţionarea tastei <Enter>. În acest moment, în zona tampon se introduce
caracterul de rând nou şi se continuă execuţia macro-ului g e t ch a r . Se revine din macro returnându-se codul
ASCII al caracterului curent din zona tampon. La un nou apel se revine cu codul ASCII al caracterului următor
din zona tampon. La epuizarea tuturor caracterelor din zona tampon, apelul lui g e t ch a r implică tastarea la
terminal a unui nou set de caractere care se stochează în zona tampon.
Macro-ul se apelează fară parametri, putând fi şi un operand al unei expresii, sub forma :
g e t ch a r ()
Observaţie : citirea caracterelor prin intermediul zonelor tampon are avantajul de a permite corectarea
erorilor la tastare (înainte de acţionarea tastei <Enter>).
Macro-ul returnează codul ASCII al caracterului citit sau, dacă de la tastatură s-a tastat sfârşit de fişier de
la consolă, se returnează constanta EOF ( în majoritatea mediilor de programare definită ca fiind -1).
Macroul p u t ch a r afişeaza un caracter al codului ASCII. El returnează codul caracterului afişat sau
valoarea -1 în caz de eroare.
Macroul p u t ch a r se poate apela prin formatul :
p u t ch a r (expresie)
unde valoarea expresiei reprezintă codul ASCII al caracterului care se afişează.
7.4. Funcţia p u t s
Funcţia p u t s are prototipul în fişierul s t d io .h şi ea afişează la terminalul standard un şir de caractere.
După afişarea şirului respectiv cursorul trece automat în coloana întâi de pe linia următoare. Funcţia are ca
parametru adresa de început a zonei de memorie care conţine caracterele de afişat şi se apelează sub forma :
p u t s (adresă_şir_caractere)
Funcţia poate avea ca parametru şi o constantă şir de caractere.
p u t s (constantă_şir_caractere)
Funcţia p u t s returnează codul ASCII al ultimului caracter al şirului afişat sau -1 în caz de eroare.
7.5. Funcţia g e t s
Funcţia g e t s are prototipul în fişierul s t d io .h şi ea citeşte de la terminalul standard un şir de caractere.
Funcţia are ca parametru adresa de început a zonei de memorie care va conţine caracterele citite şi se apelează
sub forma :
g e t s (adresă_şir_caractere)
Memorarea şirului se face cu începere de la adresa adresă_şir_caractere (spaţiul necesar memorării
şirului de caractere trebuie rezervat explicit).
Caracterul “linie nouă” se înlocuieşte în memorie cu caracterul N U L ( cod ASCII 0).
Funcţia g e t s returnează adresa de început a zonei de memorie în care se stochează caractererele citite sau
0 (N U LL – pointerul nul) la întâlnirea sfârşitului de fişier.
7.6. Funcţia p r in t f
Funcţia p r in t f afişează date pe ecranul terminalului standard sub controlul unor formate.
Sintaxa apelului este :
apel_printf
printf ( control )
expresie ,
unde expresie reprezintă valoarea ce se va scrie conform specificatorilor de format prezenţi în parametrul de
control
- opţional : un caracter minus - implicit datele se cadrează la dreapta câmpului în care se scriu. În
prezenţa minusului, datele se cadrează la stânga câmpului în care se scriu;
- opţional : un şir de cifre zecimale - acesta defineşte numărul minim de poziţii ale câmpului în care
se afişează caracterele ce intră în compunerea formatului extern al datei. În cazul în care afişarea datei necesită
un câmp de lungime mai mare decat cel precizat, se face automat extensia câmpului la numărul necesar de
poziţii. În cazul în care data necesită un câmp mai mic, ea se va scrie în câmpul respectiv în stanga sau în
dreapta, după cum este prezent sau nu semnul minus în specificatorul de format corespunzator ei. De
asemenea, în acest caz, câmpul se completează cu caractere nesemnificative; implicit, caracterele
nesemnificative sunt spaţii. Caracterele nesemnificative vor fi zerouri daca numărul ce indică dimensiunea
minimă a câmpului începe cu un zerou nesemnificativ.
- opţional : un punct urmat de un şir de cifre zecimale - şirul de cifre zecimale specifică precizia datei
care se scrie sub controlul specificatorului respectiv. În cazul în care data care se scrie este un număr real,
precizia defineşte numărul de zecimale care se scriu. În cazul în care data este un şir de caractere, precizia
indică numarul de caractere care se scriu.
- una sau două litere - acestea definesc tipul de conversie aplicat datei care se scrie.
Semnificaţia ultimei litere a specificatorului de format la scriere :
#include <stdio.h>
#define A 47.389
#define X -123.5e20
int main(void)
{
printf("caracterul A =%c\n", 'A'); caracterul A =A
Funcţia s ca n f preia date tastate de la terminalul standard sub controlul unor formate. Sintaxa
apelului este :
apel_scanf
- adresa - reprezintă adresa zonei de memorie în care se păstrează datele citite după ce au fost convertite
din formatele lor externe în formate interne corespunzătoare. Adresa unei zone de memorie se exprimă
frecvent folosind operatorul unar &;
- control - este un şir de caractere care defineşte formatul datelor şi al eventualelor texte aflate la intrarea
de la tastatură;
Caracterele albe din compunerea parametrului de control sunt neglijate. Restul caracterelor, care nu
fac parte dintr-un specificator de format, trebuie să existe la intrare în poziţii corespunzătoare (acestea se
folosesc în vederea efectuării de controale asupra datelor citite).
Specificatorii de format încep cu caracterul % şi se termină cu 1-2 litere.
Literele definesc tipul de conversie aplicat datei care se scrie.
Între procent şi litere într-un specificator de format se mai pot utiliza:
- opţional : un caracter asterisc - caz în care data din câmpul respectiv va fi prezentă la intrare,
dar ea nu se atribuie nici unei variabile şi deci nu-i va corespunde un parametru;
- opţional : un şir de cifre care defineşte lungimea maxima a câmpului din care se citeşte data sub
controlul formatului respectiv;
Câmpul controlat de un specificator de format începe cu primul caracter care nu este alb şi se termină:
- fie la caracterul după care urmează un caracter alb;
- fie la caracterul după care urmează un caracter care nu corespunde specificatorul de format
care controleaza acel câmp;
- fie la caracterul prin care se ajunge la lungimea maximă a câmpului indicată în specificatorul de
format (doar dacă în specificatorul de format este indicată lungimea maximă).
Funcţia s ca n f citeşte toate câmpurile care corespund specificatorilor de format, inclusiv eventualele
texte prezentate în parametrul control.
În cazul unei erori, citirea se întrerupe în locul în care s-a întalnit eroarea. Eroarea poate proveni :
- din necorespondenţa textului curent în parametrul de control cu cel din fişierul de intrare;
- din neconcordanţa dintre data din câmp şi specificatorul de format sub controlul căruia se face
citirea.
Apariţia unei erori poate fi pusă uşor în evidenţă deoarece funcţia s ca n f returnează, la
revenirea din ea, numărul câmpurilor citite corect.
Funcţia s ca n f citeşte date din zona tampon ataşata tastaturii, la fel ca şi g e t ch a r . De aceea, datele se
citesc efectiv după ce s-a acţionat tasta Enter.
Semnificatia ultimei litere a specificatorului de format la citire :
d - data din câmpul de intrare este un şir de cifre zecimale, precedat eventual de un semn şi se converteşte din zecimal
în binar de tip int;
o - ca şi în cazul literei d, cu deosebirea ca şirul de cifre este considerat ca formeaza un numar octal;
x - ca şi în cazul literei d, cu deosebirea ca şirul de cifre este considerat ca formeaza un numar
hexazecimal; se utilizeaza literele mici a-f sau mari A-F pentru a scrie cifrele peste 9;
X - ca şi în cazul literei x;
u - data de intrare este un şir de cifre zecimale care formează un numar întreg fără semn şi se converteşte în binar tipul
unsigned;
c - data de intrare se consideră formată din caracterul curent de la intrare şi parametrului corespunzator i se atribuie
codul ASCII al acestui caracter; în acest caz nu se face avans peste caracterele albe, ca în cazul celorlalţi specificatori;
s - data se consideră că este un şir de caractere; şirul incepe, conform regulilor generale, cu primul caracter care nu
este alb şi se termina la caracterul după care urmează un caracter alb sau când s-au citit atâtea caractere câte indică
dimensiunea maximă din specificatorul de format;
f - data de intrare reprezintă un numar flotant, eventual precedat de un semn; se converteşte în virgula flotanta simplă
precizie; data care se citeşte poate fi scrisă atât în formatul cu exponent, cât şi în formatul fără exponent.
Literele d,o,x şi u pot fi precedate de litera h şi în acest caz conversia se realizează spre s h o r t in t , de litera l şi în acest caz
conversia se realizează spre lo n g în loc de in t , sau de perechea ll şi în acest caz conversia se realizează spre lo n g lo n g .
Literele f,e,E,g şi G vor fi precedate de litera l pentru a face conversia spre formatul virgula flotanta dubla precizie, deci
spre tipul d o u b le .
Literele f,e,E,g şi G pot fi precedate de litera L şi în acest caz conversia se realizează spre lo n g d o u b le .
Exemple de utilizare a specificatorilor de format la citire (codul sursă şi textul afişat pe ecran)
#include <stdio.h>
#include <conio.h>
int main(void)
{
int a;
float b;
double c;
char d;
printf("Introduceti un numar intreg, un numar real simpla precizie, un numar real dubla precizie si un
caracter\n");
if(scanf("%d%f%lf%c",&a,&b,&c,&d)!=4) // instructiunea if implementeaza structura de decizie
printf("Date eronate !\n");
else
printf("Ati introdus : a=%d, b=%.14g, c=%.14g, d=%c\n",a,b,c,d);
getch();
return 0;
}
Rezultatul execuţiei :
Introduceti un numar intreg, un numar real simpla precizie, un numar real dubla precizie si un caracter
10
1.23456789012345
1.23456789012345#
Ati introdus : a=10, b=1.2345678806305, c=1.2345678901235, d=#
Observaţii :
- tipul de date float poate reprezenta mai puţine cifre semnificative exacte decât tipul double (a se vedea
paragraful 5.2)
- citirea datelor se face în flux continuu, iar în cazul citirii caracterelor NU există separatori
#include <stdio.h>
#include <conio.h>
int main(void)
{
int a;
double b;
char c;
printf("Introduceti un numar intreg, un numar real dubla precizie si un caracter\n");
printf("a=");
if(scanf("%d",&a)!=1)
printf("Date eronate !\n");
else
{
printf("b=");
if(scanf("%lf",&b)!=1)
printf("Date eronate !\n");
else
{
printf("c=");
fflush(stdin); // curata zona de memorie tampon asociata tastaturii
scanf("%c",&c);
printf("Ati introdus : a=%d, b=%.10g, c=%c\n",a,b,c);
}
}
getch();
return 0;
}
Rezultatul execuţiei :
Introduceti un numar intreg, un numar real dubla precizie si un caracter
a=10
b=5.5e-9
c=$
Ati introdus : a=10, b=5.5e-009, c=$
9. Operatori în limbajul C
Operatorii sunt unităţi lexicale, formate din unul sau mai multe caractere ale alfabetului, ce
simbolizează diverse operaţii ce se pot efectua asupra datelor în limbajul C.
Vom prezenta, pentru început, principalii operatori ai limbajului C, urmând ca, pe parcursul
prelegerilor următoare, să introducem şi ceilalţi operatori
Operatorii ce apar într-o expresie pot fi :
operatori unari, ce se aplică unui singur operand
operatori binari, ce leagă doi operanzi
operatori ternari, ce leagă trei operanzi.
Operatorii se pot clasifica în funcţie de clasa de operaţii pe care o implementează, sau în
funcţie de prioritatea atribuită.
La scrierea unei expresii se pot folosi operatori din aceeaşi clasă sau din clase diferite.
La evaluarea unei expresii se ţine seama de :
priorităţile operatorilor ce aparţin unor clase diferite
asociativitatea operatorilor de aceeaşi prioritate
regula conversiilor implicite.
Tabelul 9.1. Operatori în limbajul C, în ordinea priorităţilor acestora :
( ) [ ] . ->
- + * & ! ~ ++ -- sizeof
* / %
+ -
<< >>
< <= >= >
== !=
&
^
|
&&
||
? :
= += -= *= /= %= <<= >>= &= ^= |=
,
Tabelul 9.2. Asociativitatea operatorilor în C
Simbol Tipul operaţiei Asociativitatea
Dacă operatorul curent se aplică unor operanzi de acelaşi tip, se execută operaţia respectivă, iar tipul
rezultatului este tipul comun al operanzilor; dacă operatorul curent se aplică unor operanzi de tipuri diferite,
operandul de tip aşa-numit “inferior” se converteşte spre tipul aşa-numit “superior” al celuilalt operand, se
execută operaţia şi tipul rezultatului este tipul “superior”.
Concret :
- în primul rând se convertesc operanzii de tip char la tipul int ;
- dacă unul din operanzi este de tip long double atunci celălalt operand se converteşte la tipul long
double şi rezultatul operaţiei este de tip long double ;
- dacă unul din operanzi este de tip double atunci celălalt operand se converteşte la tipul double şi
rezultatul operaţiei este de tip double ;
- dacă unul din operanzi este de tip float atunci celălalt operand se converteşte la tipul float şi
rezultatul operaţiei este de tip float ;
- dacă unul din operanzi este de tip unsigned long atunci celălalt operand se converteşte la tipul
unsigned long şi rezultatul operaţiei este de tip unsigned long ;
- dacă unul din operanzi este de tip long int atunci celălalt operand se converteşte la tipul long int şi
rezultatul operaţiei este de tip long int ;
- dacă unul din operanzi este de tip unsigned int atunci celălalt operand se converteşte la tipul
unsigned int şi rezultatul operaţiei este de tip unsigned int.
Aplicând regula de mai sus în procesul de evaluare a unei expresii pe rând fiecărui operator, se determină
în final tipul şi valoarea expresiei respective.
9.3. Operatori de relaţie
Facem observaţia că în limbajul C nu există definit tipul de date logic. Se consideră că valoarea de adevăr “fals”
este reprezentată de valoarea 0 (zero), iar orice valoare diferită de zero este interpretată ca valoare de adevăr
“adevărat”.
O expresie de forma :
expresie_ 1 operator_relaţie expresie_ 2 ,
unde expresie_ 1 şi expresie_ 2 sunt expresii valide în C, iar operator_relaţie este unul din operatorii de relaţie, se
numeşte expresie de relaţie şi este de tip int. Valoarea unei astfel de expresii este 1 (adevărat) dacă valorile celor
două expresii expresie_1 şi expresie_ 2 satisfac relaţia specificată de operatorul în cauză şi 0 (fals) în caz contrar.
O expresie de forma :
expresie_ 1 operator_egalitate expresie_ 2 ,
unde expresie_ 1 şi expresie_ 2 sunt expresii valide în C, iar operator_egalitate este unul din operatorii de
egalitate, se numeşte expresie de egalitate şi este de tip int. Valoarea unei astfel de expresii este 1 dacă valorile celor
două expresii expresie_ 1 şi expresie_ 2 satisfac relaţia specificată de operatorul în cauză şi 0 în caz contrar.
9.5. Operatori logici
O expresie de forma :
expresie_ 1 operator_logic expresie_ 2 ,
unde expresie_ 1 şi expresie_ 2 sunt expresii valide C, iar operator_logic este unul din operatorii logici binari, se
numeşte expresie logică şi este de tip int. Valoarea unei astfel de expresii este 1 dacă valorile celor două expresii E1 şi
E2 satisfac relaţia specificată de operatorul în cauză şi 0 în caz contrar.
Observaţie : Pentru operatorii logici este garantată evaluarea operanzilor de la stânga la dreapta. Cu toate acestea,
întotdeauna se evaluează numărul minim de operanzi necesari pentru a determina valoarea expresiei. Această
abordare se mai numeşte "evaluarea în scurt circuit (short-circuit evaluation)". Astfel, în anumite situaţii, anumiţi
operanzi ar putea rămâne neevaluaţi.
O expresie de forma:
v = (expresie)
unde v este o variabilă simplă, elementul unui tablou sau al unei structuri, iar expresie este o expresie validă C, se
numeşte expresie de atribuire. Cum operatorul de atribuire este unul dintre cei mai puţin prioritari, parantezele pot
lipsi în cele mai multe cazuri.
Expresia de atribuire se evaluează astfel : se evaluează întâi expresie, iar rezultatul obţinut se converteşte dacă
este cazul la tipul lui v, după care i se atribuie lui v. Ea are ca tip, tipul membrului stâng şi ca valoare, valoarea
expresiei rezultată în urma conversiei.
O construcţie de forma :
v = (v1 = (expresie))
este corectă şi reprezintă tot o expresie de atribuire, lui v atribuindu-i-se valoarea lui v1, eventual după o conversie de
tip dacă este cazul.
Deoarece operatorul de atribuire se asociază de la dreapta la stânga, expresia se poate scrie în general :
v = v1 = v2= ......= vn = (expresie)
În limbajul C, pentru a prescurta scrierea unei expresii de forma:
v = v operator (expresie)
unde operator este unul din operatorii : / % * - +, este permisă scrierea echivalentă :
v operator = expresie
9.7. Operatori de incrementare şi de decrementare
Acest operator ternar permite construirea unei expresii a cărei valoare depinde de valoarea unei condiţii şi el
este simbolizat prin caracterele :
?:
asociindu-se de la dreapta la stânga.
O expresie care foloseşte acest operator se numeşte expresie condiţională şi ea se construieşte astfel :
expresie_ 1? expresie_ 2 : expresie_ 3
unde expresie_ 1, expresie_ 2, expresie_ 3 sunt expresii valide în limbajul C.
O expresie condiţională se evaluează astfel :
- se calculează valoarea expresiei expresie_ 1.
- dacă rezultatul obţinut este diferit de zero, atunci tipul şi valoarea expresiei coincid cu tipul şi valoarea
expresiei expresie_ 2.
- în caz contrar, tipul şi valoarea expresiei coincid cu tipul şi valoarea expresiei expresie_ 3.
9.12. Operatorul virgulă
Operator Descriere
Operatorul NOT pe biţi (complement) transformă fiecare bit din reprezentarea
~ operandului în complementul său (0 devine 1, iar 1 devine 0) ; furnizează aşa numitul
complement faţă de 1.
Operatorul ŞI pe biţi compară fiecare bit al primului operand cu bitul corespunzător al
& celui de al doileae operand. Dacă ambii biţi au valoarea 1, bitul corespunzător al
rezultatului este 1. Altfel bitul corespunzător al rezultatului este 0.
Operatorul SAU EXCLUSIV pe biţi compară fiecare bit al primului operand cu bitul
corespunzător al celui de al doileae operand. Dacă cei doi biţi sunt diferiţi, bitul
^
corespunzător al rezultatului este 1. Dacă cei doi biţi sunt identici, bitul corespunzător al
rezultatului este 0.
Operatorul SAU pe biţi compară fiecare bit al primului operand cu bitul corespunzător
| al celui de al doileae operand. Dacă ambii biţi au valoarea 0, bitul corespunzător al
rezultatului este 0. Altfel bitul corespunzător al rezultatului este 1.
Concret:
b1 b2 ~ b1 b1&b2 b1^b2 b1|b2
0 0 1 0 0 0
0 1 1 0 1 1
1 0 0 0 1 1
1 1 0 1 0 1
În cazul operatorilor de deplasare, care sunt binari, primul operand este cel al cărui biţi sunt deplasaţi, iar al
doilea indică numărul de biţi cu care se face deplasarea.
La deplasarea la stânga cu o poziţie, bitul cel mai semnificativ se pierde, iar în dreapta se completează cu bitul 0.
La deplasarea la dreapta cu o poziţie, bitul cel mai puţin semnificativ se pierde, iar in stânga se completează în
principiu cu un bit identic cu cel de semn (poate fi dependent de implementare).
Observaţii:
- Cu excepţia cazurilor când se produce depăşire, deplasarea la stânga cu n biţi echivalează cu înmulţirea
cu 2 la puterea n.
- Analog, deplasarea la dreapta cu n biţi echivalează cu împărţirea cu 2 la puterea n.
- Este indicată realizarea înmulţirilor şi împărţirilor cu puteri ale lui 2 prin deplasări, ultimele
realizându-se într-un timp mult mai scurt.
În tabelul următor apar valorile obţinute ( în binar şi zecimal ) prin aplicarea operatorilor de deplasare unui
operand i, de tip char (reprezentat pe un octet):
Cele trei secvenţe din tabelul de mai jos conduc la aceleaşi rezultate pentru o variabilă întreagă i:
i*=8; i<<=3;
i/=4; i>>=2;
i*=10; i=(i<<3)+(i<<1);
Exemplu : Secvenţa de mai jos poate fi folosită pentru afişarea reprezentării binare, pe 4 octeţi, a unui număr întreg,
val
int val;
.........................
for(int i=31;i>=0;i--)
if(val&(1<<i))
printf("%c",'1');
else
printf("%c",'0');
Tehnici de testare şi depanare a programelor
In programele mari se pune problema localizarii erorilor, a determinarii zonei in care acestea se produc. Acest
obiectiv se atinge iterativ, restrangand, treptat, zona de interes, prin simplificarea contextului in care se manifesta
problema.
Se consideră că procesul de localizare a erorii reprezintă 95% din activitatea de depanare!
Figura 1. Procesul de depanare
Procesul de Depanare
Erori de sintaxă
Erori logice
Erori matematice
• Împărţire la zero
• Depăşire aritmetică (Arithmetic overflow or underflow)
Erori de resurse
• Variabile neiniţializate
Metode de depanare
I. Metoda Forţei Brute (Brute Force).
II. Depanarea prin inducţie
III. Depanarea prin deducţie
IV. Depanarea prin Trasare inversă (Backtracking)
V. Depanarea prin Testare
Cea mai populară metodă este Metoda Forţei Brute (Brute Force).
Există trei submetode în această categorie:
I.1. Depanarea cu afişarea conţinutului memoriei (storage dump).
I.2. Depanarea cu inserarea de comenzi de afişare (“scatter print statements throughout your program”)
I.3. Depanarea cu unelte automate de depanare.
Amintim cateva elemente din terminologia specifica, ce se regasesc si in depanatorul integrat al mediului DevC++:
- breakpoint: punctul in care execuţia programului va fi suspendata, pentru analiza detaliata.
- stepping (execuţia pas cu pas) : execuţia instructiunii curente, in scopul evaluarii individuale. Daca
aceasta este un apel de functie, se poate face:
step into: se va sari la prima instructiune din corpul functiei
step over: se va considera apelul ca fiind atomic, si se va trece la urmatoarea instrucţiune.
- watch: mecanismul de urmarire a valorii unor variabile/expresii, pe masură ce acestea se modifică
Editorul
de
text
Secțiunea
Project Manager
a ferestrei
de
Explorare
Fereastra
de
mesaje
Bara de stare
6) vizualizarea în dinamică a valorilor variabilelor de interes se face progresând cu codul programului în următoarele
moduri controlate pe care le oferă butoanele din secțiunea Debug (vezi fig. 3):
Fereastra
de
editare
Secțiunea
Debug a ferestrei
Report
Figura 5. Fereastrele de editare, de depanare (Debug) și fereastra Project Manager cu secțiunea sa Debug.
În orice moment al execuției programului în modul depanare se pot adăuga și sterge atât breakpoint-uri cât și
variabile de interes în și din lista variabilelor urmărite în secțiunea Debug a ferestrei Explorer.
Următoarele două butoane din secțiunea Debug sunt dedicate depanării programului în limbaj de asamblare.
Pentru aceasta se va activa mai întâi fereastra CPU folosind butonul View CPU window din fereastra Debug.
Funcțiile de depanare corespunzătoare sunt:
b)
int main(void)
{
int a;
float b;
double c;
char d,e;
printf("Introduceti un numar intreg, un numar real simpla precizie si un numar real dubla precizie\n");
if(scanf("%d%f%lf",&a,&b,&c)!=3);
printf("Date eronate !\n");
exit(1);
printf("Introduceti doua caractere\n");
fflush(stdin);
scanf("%c%c",&d,&e);
printf("Ati introdus : a=%d, b=%g, c=%g, d=%c, e=%c\n",a,b,c,d,e);
getch();
return 0;
}
/* Exemplul 2
Programul de mai jos trebuie sa citeasca corect un real dp x(|x|>=0.5). Executia va continua pana cand utilizatorul va raspunde
la intrebarea "Doriti sa continuati ?(D/N)" cu n sau N. */
#include <stdio.h>
#include <math.h>
int main(void)
{
double x;
char car;
do
{
do
{
printf("x=");
//fflush(stdin);
} while (scanf("%lf",&x) !=1 , (fabs(x)<0.5));
printf("URAAAAAAAAAAA, ati introdus corect! x=%d\n",x);
printf("Aici se executa o secventa ... complexa!\n");
x=x%10;
printf("Doriti sa continuati ?(D/N)");
//fflush(stdin);
scanf("%c",&car);
} while (car!='n' || car!='N' );
return 0;
}
/* Exemplul 3
Programul de mai jos trebuie sa testeze egalitatea cu zero a unui intreg i. */
int main(void)
{
int i=9;
if(i=0)
printf("%d este egal cu 0",i);
else
printf("%d NU este egal cu 0",i);
getch();
return 0;
}
/* Exemplul 4
Programul de mai jos trebuie sa determine daca x este >8,1. */
int main(void)
{
double x=9;
if(x<8,1)
printf("%g este mai mic decat 8",x);
else
printf("%g este mai mare decat 8,1",x);
getch();
return 0;
}
/* Exemplul 5.
Să se depaneze programul de mai jos care implementează soluţia problemei :
Stiind ca dezvoltarea in serie ce poate aproxima numarul PI este:
PI=4*(1-1/3+1/5-1/7+..........),
sa se calculeze valoarea aproximativa a lui PI, cu o eroare mai mica decat un epsilon precizat.
INDICATIE: eroarea este inferioara lui epsilon cand modulul termenului ce urmeaza a fi adaugat in suma este inferior lui epsilon. Se vor folosi pentru
epsilon valori de ordinul 1e-2, 1e-3, 1e-4.
#include<stdio.h>
#include<math.h>
int main(void)
{
int k=3;
int i=0;
double piapr, t, eps=1e-4;
int semn=-1;
t=1; k=3; i=0;
do
{
piapr=piapr+t;
t=semn/k;
semn=-semn;
k+=2; i++;
} while(abs(t)>eps);
piapr=4*piapr;
printf("piapr =%.14g\t",piapr);
printf("err =%e i=%d\n", fabs(piapr-M_PI),i);
return 0;
}
Capitolul IV. Programarea în limbajul C (continuare)
10. Reprezentarea informaţiei într-un sistem de calcul
Din raţiuni tehnice (imunitate crescută la zgomot şi fiabilitate), circuitele electronice din structura
calculatoarelor numerice tratează informaţia în forma cea mai elementară, reducând-o la un şir de valori binare
(biţi), ce nu pot lua deci decât două valori : 0 sau 1. Aceste şiruri de biţi pot reprezenta numere, caractere (cifre,
litere, semne speciale) sau instrucţiuni, în conformitate cu anumite legi de codare ce fac să corespundă un număr,
un caracter sau o instrucţiune fiecărei combinaţii posibile de biţi, de o lungime precizată. Legile de codare pot
diferi de la un sistem la altul sau de la un limbaj de programare la altul.
Rezultă deci că în orice sistem de calcul există două categorii de informaţii a căror reprezentare internă
urmează a fi stabilită : instrucţiuni (comenzi date calculatorului în limbaj maşină) şi date.
i
i
= bn - 1
Rezultă deci că, în baza b , folosind doar n cifre, se pot reprezenta numai numere mai mici sau egale cu b n - 1.
Este specific faptul că, pentru a exploata la maximum posibilităţile calculatoarelor numerice, să se modifice
reprezentarea informaţiei numerice în funcţie de poziţia acesteia în lanţul informatic. Astfel, de exemplu,
operatorul va manevra informaţia numerică reprezentată în sistemul zecimal, iar sistemul de calcul e conceput
pentru tratarea informaţiei binare.
În programarea calculatoarelor se folosesc în mod uzual în reprezentarea datelor patru baze de numeraţie
diferite:
- reprezentare binară - în care se utilizează baza 2
- reprezentare octală - în care se utilizează baza 8
- reprezentare hexazecimală- în care se utilizează baza 16
- reprezentare zecimală - în care se utilizează baza 10
Există situaţii în care se consideră ca fiind avantajoasă o reprezentare hibridă : zecimal codificat binar BCD
(Binary Coded Decimal). În acest caz, fiecare cifră zecimală ak a reprezentării în baza 10 este codificată binar
folosind patru cifre binare :
an-1,3 an-1,2 an-1,1 an-1,0 an-2,3 an-2,2 an-2,1 an-2,0 ..... a1,3 a1,2 a1,1 a1,0 a0,3 a0,2 a0,1 a0,0
unde
3
a k a k,i bi , k=0,n -1
i 0
- 2 -1 A 2 -1
n 1 n 1
i 0
Cu alte cuvinte, complementul faţă de 1 al unui număr întreg strict negativ se calculează prin
negarea tuturor biţilor din reprezentarea modulului numărului în binar.
Pentru a nu exista confuzie între numerele pozitive şi cele negative trebuie ca modulul numerelor ce se
reprezintă astfel să respecte relaţia :
0 |A| 2n - 1 - 1
unde s-a ţinut cont de faptul că un bit este bit de semn şi rămân n -1 biţi pentru reprezentarea modulului.
Rezultă deci că, implicit, bitul cel mai semnificativ al unei astfel de reprezentări este marca semnului : 0
pentru numere pozitive şi 1 pentru numere negative.
În cazul operaţiei de scădere la care am făcut referire, A>0, B 0, aceasta se poate scrie:
B-A=B+ Ã + 1
Metoda complementului faţă de 1 înlocuieşte scăderea a două numere prin adunarea dintre descăzut
(presupus pozitiv) şi complementul faţă de 1 al scăzătorului, urmată de adunarea unei unităţi ( deoarece 2n nu
este reprezentabil pe numărul de biţi cu care se lucrează (n ), scăderea sa efectivă nu este necesar a fi executată).
b. Reprezentarea în cod complement faţă de 2.
Reprezentarea întregilor în cod complement faţă de 2 este cea mai răspândită metodă folosită actualmente
în sistemele de calcul. Aceasta se bazează pe acelaşi principiu general, numai că, în acest caz, valoarea lui R este
dată de relaţia :
R = 2n
Complementul faţă de 2 al unui număr negativ -A, A>0, notat Â, se calculează cu relaţia:
 = 2n - A
Complementul lui A faţă de 2, notat cu  devine :
n 1 n 1 n 1
 2
i 0
1i
i
a 2 1
i 0
i 1 Ã+1
(1
i
a )2 i
i 0
Cu alte cuvinte, complementul faţă de 2 al unui număr întreg strict negativ se calculează prin
negarea tuturor biţilor din reprezentarea modulului numărului în binar, urmată de adunarea
unei unităţi, sau, echivalent, prin adăugarea unei unităţi la complementul faţă de 1 al numărului.
Această relaţie poate fi transpusă într-o regulă mai simplă de calcul al complementului faţă de 2 al unui
număr negativ -A şi anume : acesta se obţine negând toţi biţii reprezentării binare a opusului (modulului) său,
aflaţi la stânga primului 1 întâlnit în parcurgerea reprezentării de la dreapta la stânga.
Pentru a nu exista ambiguitate, domeniul de reprezentare pentru modulul numerelor întregi trebuie să
satisfacă relaţia :
0 |A| 2n – 1 -1
Cel mai semnificativ bit este în acest fel o marcă a semnului numărului : 0 pentru numere pozitive şi 1 pentru
numere negative. Se observă că, în acest caz rămâne disponibilă o combinaţie binară, care într-o scriere
poziţională se reprezintă prin combinaţia : 100 ... 000 (1 urmat de n -1 biţi de 1)
Conform convenţiilor de calcul ale complementului faţă de 2, este naturală asocierea acestei combinaţii
valorii - 2n - 1.
Rezultă deci că domeniul de reprezentare a întregilor în cod complement faţă de 2 este:
- 2n - 1 A 2n – 1 - 1
unde n este numărul de biţi pe care se face reprezentarea.
Ţinând cont de cele de mai sus, se pot deduce cu uşurinţă valorile menţionate în tabelul referitor la tipurile
de date întregi în C.
Er _ med = 0.
1 z
b
Er _ min_ vf 2 n b z n ,
b
iar eroarea relativă maximă se obţine pentru valoarea minimă din domeniul de reprezentare
1 z
b
1
Er _ max_ vf 2 z
b 2
ceea ce înseamnă că eroarea relativă poate ajunge în acest caz şi la 50%, ceea ce este inacceptabil.
X = ( 1) M BE
S
unde S este 0 pentru numere pozitive şi 1 pentru numere negative, M se numeşte mantisă, E este exponentul iar B
este baza reprezentării, cel mai adesea B=2.
Acest format se poate stoca într-o structură cu 3 câmpuri, baza fiind constantă şi implicită (de obicei 2), nu
trebuie stocată explicit.
Aşa cum se va arăta în cele ce urmează, numărul de biţi folosiţi pentru reprezentarea exponentului
determină plaja de valori reprezentabile în acest format, iar numărul de biţi folosiţi pentru reprezentarea mantisei
determină precizia reprezentării. Astfel, în alegerea unui format în virgulă mobilă trebuie să se facă un compromis
între dimensiunea mantisei şi cea a exponentului. Creşterea dimensiunii mantisei duce la creşterea preciziei iar
creşterea dimensiunii exponentului duce la creşterea domeniului. Pentru a creşte atât precizia, cât şi domeniul, se
utilizează un număr mai mare de biţi.
De obicei nu se reprezintă exponentul real ci o caracteristică sau un aşa numit exponent deplasat, folosind
reprezentarea în cod exces-deplasament :
C = E + deplasament,
unde deplasamentul se alege astfel încât caracteristica sa fie mereu pozitivă în domeniul de valori
reprezentabile.
Unul din avantajele acestei notaţii este simplificarea operaţiilor ce se efectuează asupra exponenţilor.
Ca dezavantaj menţionăm complicarea operaţiei de adunare, fiind necesară scăderea deplasamentului din
suma exponenţilor.
De exemplu, în cazul unei reprezentări pe 8 biţi, caracteristica C [0,255], deplasamentul poate fi 127(7FH),
iar exponentul real va fi: E [-127,128]. De unde rezultă că exponentul va fi negativ, dacă C < 127 , pozitiv, dacă
C>127 şi nul, dacă C = 127. Se mai spune că exponentul este reprezentat în exces 127.
Pentru a asigura o reprezentare unică în format virgulă mobilă, mantisa se reprezintă sub formă
normalizată. În conformitate cu standardul IEEE 754, normalizarea presupune reprezentarea mantisei ca având o
singură cifră la partea întreagă, şi cum baza reprezentării este 2, aceasta cifră nu poate fi decât 1, şi de aceea, nici
nu se mai reprezintă explicit. Rezultă că, în acest caz, mantisa poate lua valori în intervalul [1, 2) .
O alta variantă a fost aceea de a reprezenta mantisa normalizată ca un număr pur fracţionar, având prima
cifră de după virgulă, nenulă. Pentru o astfel de reprezentare, în cazul caracteristicii de 8 biţi se utiliza
reprezentarea în exces 128.
În reprezentarea în virgulă mobilă, eroarea relativă maximă a reprezentării (care se obţine pentru valoarea
minimă a mantisei, adică valoarea 1 ) este constantă în toată gama ;
1 z E
b B
2 1 z
Er _ max_ vm b
1 B E
2
unde z reprezintă numărul de cifre cu care se face reprezentarea mantisei.
Rezultă deci că precizia reprezentării este dată de numărul de cifre cu care se face reprezentarea mantisei.
În funcţie de numărul de biţi folosiţi pentru reprezentarea mantisei se disting mai multe tipuri de
reprezentări (pentru toate formatele, baza implicită este doi) :
- reprezentare în simplă precizie
- reprezentare în dublă precizie
- reprezentare în precizie extinsă
Structura reprezentărilor în conformitate cu standardul IEEE 754 este redata în următoarea figură:
31 30 23 22 0
S Caracteristică Mantisă
1 8 23
a.) Formatul cu precizie simplă
63 62 52 51 0
S Caracteristică Mantisă
1 11 52
b) Formatul cu precizie dublă
79 78 64 63 0
S Caracteristică Mantisă
1 15 64
c.) Formatul cu precizie extinsă
Figura 1. Formatele standardului IEEE 754
C 1023
N D = (1) 1, M 2
S
C 16383
N E = (1) M 2
S
.
În ceea ce priveşte precizia reprezentării, se ţine cont, aşa cum s-a arătat, de numărul de cifre reprezentate în
fiecare caz şi se aplică formula de calcul pentru Er _ max_ vm .
Astfel :
- pentru simplă precizie se reprezintă 23 de cifre, z = 23, deci Er _ max_ vm = 5.96e-08
- pentru dublă precizie se reprezintă 52 de cifre, z = 52, deci Er _ max_ vm = 1.11e-16
- pentru precizie extinsă se reprezintă 64 de cifre, dar z = 63, deci Er _ max_ vm = 5.42e-20
Pentru a regăsi numărul de cifre zecimale exacte ale reprezentărilor se poate ţine cont şi de faptul că pentru
a reprezenta exact d cifre zecimale este nevoie de aproximativ de 3,33 ori mai multe cifre binare.
Ţinând cont de cele de mai sus se pot deduce cu uşurinţă valorile menţionate în tabelul referitor la tipurile
de date reale în C (micile diferenţe apar şi din precizia cu care se implementează de către diverse sisteme conversia
din zecimal în binar şi invers).
Standardul permite şi reprezentarea unor valori speciale. Pentru acestea sunt rezervate valoarea 0 şi
valoarea maximă a exponentului.
Pentru valoarea zero trebuie ca C = 0 şi M = 0. Deoarece bitul semnului poate lua valoarea 0 sau 1 vom avea
două reprezentări pentru valoarea 0: +0, -0. Valoarea infinit se obţine când C ia valoarea maximă, iar M = 0,
având şi în acest caz două posibilităţi: + şi - .
Un alt caz special este dat de valorile denumite NaN (Not a Number). Această valoare este folosită pentru a
reprezenta o dată ce nu este număr real. NaN apare când C ia valoarea maximă (toţi biţii sunt 1) şi mantisa M este
diferită de 0. Există o clasă întreagă de valori NaN. Atunci când argumentul unei operaţii este NaN, rezultatul
trebuie să fie NaN.
Un alt aspect al acestui standard este dat de introducerea aşa numitelor valori denormalizate. Acestea apar
atunci când C=0 şi M 0. Un număr denormalizat este generat printr-o tehnică specială, numită depăşire
inferioară graduală, şi permite extinderea reprezentării spre valori mai mici decât cele normalizate.
Particularizând pentru cazul formatelor cu precizie simplă şi dublă avem următoarele situaţii:
a. Formatul cu precizie simplă
Dat un număr în acest format, reprezentat pe 32 biţi, atunci valoarea X ce corespunde acestuia se
caracterizează astfel:
1 . 5707964 x 2 3 . 1415928
Eroarea absolută de aproximare în acest caz este de aproximativ 1.464 e-7 (asta înseamnă 7 cifre exacte !)
Folosind formatul de reprezentare IEEE 754, simplă precizie, cei 24 de biţi determină mantisa nenormalizată
M_nenormalizată, cu E = 1. În reprezentarea mantisei se ţine cont de normalizare, primul bit fiind 1nu se mai reprezintă,
câştigându-se astfel încă un bit de precizie.
Pentru a găsi reprezentarea (aproximativă) a numărului π în memoria internă a unui sistem de calcul se pleacă de la
reprezentarea valorii sale binare pe 24 de biţi:
s = 0 ; E = 1 ; M_nenormalizată = 110010010000111111011011 (incluzând bitul de la partea întreagă)
Suma dintre deplasament (127) şi exponent (1) este 128, astfel încât reprezentarea în format virgulă mobilă simplă
precizie în conformitate cu standardul IEEE 754 este (excluzând bitul de la partea întreagă, care nu se reprezintă după
normalizare z=23) :
0 10000000 10010010000111111011011
s C M
Grupând corespunzător biţii obţinem :
0100 0000 0100 1001 0000 1111 1101 1011
ceea ce se scrie în sistem hexazecimal
4 0 4 9 0 F D B
b. Formatul cu precizie dublă
Dat un număr în acest format, de dimensiune 64 biţi, atunci valoarea v ce corespunde acestuia se
caracterizează astfel:
- dacă C = 2047 şi M 0 atunci v este NaN indiferent de valoarea lui s;
În mod similar se deduc intervalele pentru reprezentarea în format precizie extinsă (TEMĂ !)
Avantajul principal al standardului IEEE 754 este faptul că facilitează scrierea unor programe portabile.
În concluzie :
În reprezentarea în format virgulă fixă :
domeniul valorilor reprezentabile este foarte limitat;
eroarea relativă maximă a reprezentării este variabilă în gama de reprezentare.
În reprezentarea în virgulă mobilă :
domeniul valorilor reprezentabile este mult mai mare, în comparaţie cu reprezentarea pe
acelaşi număr de octeţi în format virgulă fixă; acesta depinde de numărul de biţi rezervaţi
pentru caracteristică (exponent);
precizia reprezentării depinde de numărul de biţi rezervaţi pentru mantisă;
eroarea relativă maximă a reprezentării este constantă în toată gama.
Exemplu :
Declararea tipului unei date este extrem de importantă deoarece în acest mod se specifică modalitatea de
reprezentare a datei respective în memoria internă a sistemului de calcul.
În cele de mai jos prezentăm diverse moduri de reprezentare în memoria internă a unui sistem de calcul
pentru -1.
Şirul de caractere -1 se va repezenta în memorie ca o succesiune de coduri ASCII (3 octeţi) : codul ASCII
pentru simbolul – (2D - hexa), cel pentru cifra 1 (31- hexa) şi teminatorul N U L, cod ASCII 0 :
0010 1101 0011 0001 0000 0000 BINAR
2 D 3 1 0 0 HEXAZECIMAL
Numărul întreg -1 se va reprezenta în cod complementar faţă de 2, pe 4 octeţi :
1111 1111 1111 11111 1111 1111 BINAR
F F F F F F HEXAZECIMAL
Numărul real -1 se va reprezenta în virgulă mobilă, dublă precizie (8 octeţi) astfel : primul bit este semnul
s=1, exponentul E = 0 ; M_nenormalizată = 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 (pe 53 de biţi)
Suma dintre deplasament (127) şi exponent (0) este 127, astfel încât reprezentarea în format virgulă mobilă dublă
precizie în conformitate cu standardul IEEE 754 este (excluzând bitul de la partea întreagă, care nu se reprezintă după
normalizare, z=52) :
1 01111111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
Grupând pe câte 4 biţi :
1011 1111 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
ceea ce se scrie în sistem hexazecimal:
B F 8 0 0 0 0 0 0 0 0 0 0 0 0 0
10.2.3. Reprezentarea caracterelor
Reprezentarea caracterelor în sistemele de calcul utilizează codul ASCII. Denumirea codului provine de la
iniţialele denumirii sale în limba engleză, American Standard Code for Information Interchange.
Aşa cum s-a menţionat, în general, printr-o operaţie de codare se realizează o mapare a unui set de simboluri
pe o mulţime de reprezentări convenabil alese. În cazul codului ASCII se realizează o corespondenţă biunivocă
între o submulţime a caracterelor (litere, cifre, semne speciale) şi secvenţele binare de lungime n = 7. Evident se
pot coda/reprezenta în acest mod 128 de simboluri.
Iniţial codul a fost proiectat pentru exploatarea eficientă a maşinilor de tip telex, de aceea o serie de
simboluri, cele destinate unor comenzi, au o descriere ce pare astăzi obscură.
In tabelul 1 se prezintă codurile ASCII precizând descrierea primelor 32 caractere neimprimabile.
În ciuda faptului că reprezentarea setului de caractere în cod ASCII nu se bazează pe relaţii matematice (de
fapt, într-o oarecare măsură, stabilirea corespondenţelor este arbitrară), există câteva proprietăţi importante ce
asigură o exploatare eficientă a reprezentării.
Astfel, reprezentările pentru cifre, pentru litere mari şi litere mici sunt contigue, respectându-se relaţiile de
ordonare naturale:
- cifrele '0' - '9' se mapează pe intervalul (exprimat în zecimal) 4810 - 5710
- literele mari 'A' - 'Z' se mapează pe intervalul (exprimat în zecimal) 6510 - 9010
- literele mici 'a' - 'z' se mapează pe intervalul (exprimat în zecimal) 9710 - 12210
Datorită acestor proprietăţi este extrem de uşor să se verifice dacă o variabilă de tip caracter este literă (mare
sau mică) sau cifră.
De asemenea se poate observa faptul că maparea nu alocă secvenţe consecutive literelor mari şi mici.
Deoarece se doreşte conversia simplă între cele două tipuri de litere, diferenţa dintre codurile alocate este de
3210 (2016 ).
În consecinţă, reprezentările respective diferă printr-un singur bit.
Tabelul 1. Codul ASCII
Iniţial, cel de-al optulea bit al octetului ce conţinea codul ASCII era folosit pentru detecţia posibilelor erori.
Actualmente, datorită creşterii fiabilităţii sistemelor de calcul, această măsură de precauţie nu mai este
necesară şi bitul se poate folosi efectiv pentru codare. A fost creat astfel ceea ce se numeşte codul ASCII extins.
In tabelul 2 se prezintă caracterele suplimentare reprezentabile utilizând codul ASCII extins.
program_C
deinitie_functie
functie antet corp
functie
Figura 11.1. Diagrama sintactică a unui program în C Figura 11.2. Diagrama sintactică a definiţiei unei funcţii în C
tip
antet
identificator ( lista_parametri_formali )
{ }
declaratii instructiuni
instructiuni
corp_C++
{ }
declaratii
declaratie instructiune
declaratii instructiuni
tip identificator ;
Declaraţiile permit programatorului să definească noi tipuri de date precum şi date de diferite tipuri,
predefinite sau definite de utilizator. Datele pot fi de asemenea iniţializate în secţiunea declarativă.
În limbajul C++ a dispărut restricţia ca toate declaraţiile unei funcţii să preceadă instrucţiunile utilizate în
corpul funcţiei, ceea ce permite creşterea flexibilităţii programării.
Instrucţiunile limbajului permit descrierea modalităţilor de prelucrare a informaţiei într-un program.
Ordinea în care se execută instrucţiunile unui program defineşte aşa-numita structură de control a
programului.
Prin program structurat se înţelege un program ce are o structură de control ce respectă principiile
programării structurate. Programarea structurată este un stil în programare care contribuie la realizarea de
programe cu o structură clară, ce pot fi uşor depanate şi întreţinute.
Limbajul C pune la dispoziţia programatorului instrucţiuni care permit implementarea imediată a structurilor
de control impuse de programarea structurată şi în plus o serie de instrucţiuni, care, deşi contravin principiilor
programării structurate, conferă facilităţi de optimizare a codului generat.
Vom prezenta în cele ce urmează instrucţiunile limbajului C într-o ordine care să permită construcţia unor
exemple consistente şi coerente, care să ilustreze utilizarea acestora.
11.1. Instrucţiunea vidă
Instrucţiunea se scrie ca un şir vid de caractere. Execuţia sa nu are nici un efect. Ea se utilizează în construcţiile
în care sintaxa cere prezenţa unei instrucţiuni, dar nu trebuie să se execute nimic în punctul respectiv (o aplicaţie va fi
prezentată la exemplificarea utilizării instrucţiunii s w it ch ).
void
lista_parametri_formali
declaratie_
,
parametru_formal
declaratie_
parametru_formal
declaratie_parametru_formal
tip identificator
apel_functie
identificator ( lista_parametri_efectivi )
unde identificator este numele funcţiei şi lista_parametri_efectivi are diagrama sintactică din Figura 11.8
lista_parametri_efectivi
expresie ,
expresie
Reamintim faptul că parametrii efectivi reprezintă valorile cu care efectiv se va executa funcţia şi că este
obligatoriu ca ei sa coincidă ca număr şi ordine (semnificaţie) cu parametrii formali cărora le corespund.
O funcţie poate fi apelată doar dacă ea este definită (cunoscută) în fişierul sursă înaintea apelului. Cum nu
întotdeauna posibil ca definiţia funcţiei (antet +corp) să preceadă apelul, în astfel de cazuri, apelul funcţiei
trebuie precedat de prototipul acesteia.
Prototipul unei funcţii are ca scop să informeze compilatorul despre : tipul valorii returnate de funcţie, numele
funcţiei, numărul şi tipul parametrilor.
void
prototip
tip nume_functie ( ) ;
element_lista ,
element_lista
element_lista
declaratie_parametru_formal
tip
În general mediile de dezvoltare pun la dispoziţia utilizatorului un set de funcţii standard care pot fi apelate
pentru rezolvarea unei game extrem de largi de aplicaţii. Apelurile unor astfel de funcţii implică includerea de
fişiere header, cu extensia h, care conţin prototipurile acestor funcţii.
Principalele funcţii matematice aflate în bibilioteca limbajului C
instructiuni
instructiune_compusa
{ }
declaratii
Se observă de asemenea, prin comparaţie cu diagrama sintactică a corpului unei funcţii, prezentată în figura 11.1
ca acesta din urmă nu este altceva decât o instrucţiune compusă.
Dacă instrucţiunea compusă include şi declaraţii, atunci acestea definesc entităţi care sunt cunoscute (valabile)
şi există atâta timp cât controlul execuţiei programului este preluat de respectiva instrucţiune compusă.
11.4. Instrucţiunea if
Instrucţiunea if implementează structura de decizie din programarea structurată. Ea permite selectarea pentru
execuţie a uneia dintre două alternative, în urma testării valorii unei expresii ce implementează relaţia de
condiţionare a deciziei.
Sintaxa instrucţiunii este:
else instructiune_2
instructiune_if
if ( expresie ) instructiune_1
unde expresie este o expresie C validă, iar instrucţiune_1 şi instrucţiune_2 reprezintă respectiv câte o
instrucţiune a limbajului C. În cazul în care alternativele deciziei trebuie descrise prin mai multe instrucţiuni, acestea
se grupează într-o instrucţiune compusă. Pentru claritatea redactării (lizibilitate) se recomandă scrierea indentată
care pune mai bine în evidenţă cele două alternative.
Modul de execuţie al instrucţiunii este următorul : se evaluează expresia expresie . Dacă aceasta are o valoare
diferită de zero, se execută instrucţiunea instrucţiune_1 şi nu se execută instrucţiunea instrucţiune_2. Altfel,
deci dacă expresie are o valoare egală cu zero, se execută instrucţiunea instrucţiune_2 şi nu se execută
instrucţiunea instrucţiune_1.
Sintaxa permite ca instrucţiunea instrucţiune_2 să fie vidă, caz în care se omite şi cuvântul rezervat e ls e .
Scrierea neglijentă a separatorului ; marchează sfârşitul instrucţiunii if (interpretat de compilator ca fiind cu o
singură alternativă, descrisă prin instrucţiunea vidă). Compilatorul nu va semnala eroare, instrucţiunea compusă se
va executa necondiţionat, incorect deci.
Dacă instrucţiune_1 este la rândul ei o decizie, o construcţie de forma :
if (expresie_1) if (expresie_2) instrucţiune_1 else instrucţiune_2
ar putea fi interpretată ambiguu, în două moduri, şi anume :
Varianta 1 : if (expresie_1)
if (expresie_2)
instrucţiune_1
else
instrucţiune_2
caz în care instrucţiune_2 este alternativa celui de al doilea if.
Varianta 2 : if (expresie_1)
if (expresie_2)
instrucţiune_1
else
instrucţiune_2
caz în care instrucţiune_2 este alternativa primului if.
Evident, datorită faptului că formatul de redactare a unui program în limbajul C este liber, scrierea indentată nu
face decât să sugereze cele două posibile modalităţi de interpretare, ele fiind de fapt echivalente.
Compilatorul rezolvă această ambiguitate, asociind construcţia e ls e ultimului if care o precede în textul sursă
căruia nu i s-a asociat deja un e ls e şi nu se găseşte în interiorul unei instrucţiuni compuse. Ca urmare compilatorul va
interpreta construcţia prezentată în varianta 1, caz în care alternativa e ls e se asociază celui de al doilea if.
Pentru a implementa în C varianta a doua, scrierea corectă este :
if (expresie_1)
{
if (expresie_2)
instrucţiune_1
}
else
instrucţiune_2
11. Instrucţiunile limbajului C (continuare)
Instrucţiunea implementează structura de ciclu cu test iniţial (condiţionat anterior) din programarea structurată
şi are sintaxa :
instructiune_while
while ( expresie ) instructiune
unde expresie este orice expresie validă în limbajul C şi implementează relaţia de condiţionare a ciclului şi
instrucţiune este o singură instrucţiune C . În cazul în care corpul ciclului ar trebui descris prin mai multe
instrucţiuni, acestea se pot grupa într-o singură instrucţiune, constituind astfel o instrucţiune compusă.
Efectul execuţiei acestei instrucţiuni este următorul :
- 1- se evaluează expresia expresie. În cazul în care valoarea expresiei este nenulă se continuă cu pasul
2, în caz contrar execuţia se încheie, situaţie în care se continuă cu execuţia instrucţiunii imediat următoare
instrucţiunii w h ile ;
- 2- se execută instrucţiune (corpul ciclului) şi apoi se revine la pasul 1.
O descriere echivalentă a modului de execuţie : atâta timp cât valoarea expresiei expresie este nenulă se
execută în mod repetat instrucţiunea instrucţiune. Execuţia ciclului se încheie în momentul în care expresia are
valoarea zero, situaţie în care se continuă cu execuţia instrucţiunii imediat următoare instrucţiunii w h ile .
Observaţii :
- corpul ciclului poate să nu se execute niciodată dacă expresia are valoarea zero chiar la intrarea în ciclu;
- evident contextul în care se execută instrucţiunea ce constituie corpul ciclului sau chiar elementele variabile ce
intervin în expresie trebuie să acţioneze asupra expresiei, modificând-o, astfel încât, la un moment dat, valoarea
acesteia să devină nulă, asigurând astfel finitudine numărului de execuţii ale corpului ciclului;
- instrucţiunea care constituie corpul ciclului poate să definească un alt mod de execuţie a instrucţiunii w h ile
decât cel indicat mai sus. Astfel ea poate realiza încheierea instrucţiunii w h ile fără a se evalua expresia ce constituie
relaţia de condiţionare, prin intermediul unei aşa numite ieşiri forţate din ciclu (de exemplu corpul ciclului poate
conţine un apel al funcţiei e xit ).
Reamintim descrierea structurii de ciclu cu test iniţial din programarea structurată :
relatia_de_conditionare
cât timp expresie_condiţie execută
corpul ciclului
NU expresie_conditie DA
| *secvenţă
|_□
* Secventa
Exemple
1. Se preia de la consolă o secvenţă de întregi. Să se calculeze produsul valorilor introduse. Sesiunea se încheie când
nu se introduce un întreg de la tastatură (când se tastează un caracter ce nu este cifră zecimală, + sau - ).
1 1 1
3. Să se calculeze în diferite moduri suma 1 (n ≥ 100000)
2 3 n
4. Să se calculeze şi să se afişeze cel mai mare divizor comun pentru două numere m si n
1. Se preia de la consolă o secvenţă de întregi. Să se calculeze produsul valorilor introduse. Sesiunea se încheie când
nu se introduce un întreg de la tastatură (când se tastează un caracter ce nu este cifră zecimală, + sau - ).
#include <stdio.h>
int main(void)
{
int x;
long prod;
prod=1L;
while (printf("x="),scanf("%d",&x)==1)
prod*=x;
printf("prod = %ld\n",prod);
return 0;
}
2. Să se calculeze suma cifrelor unui număr dat n, pozitiv.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
long n;
int sum=0;
printf("n=(n>0) =");
if(scanf("%ld",&n)!=1 || n<0)
{
printf("Date eronate\n");
exit(1);
}
printf("Suma cifrelor lui %ld ",n);
while(n>0)
{
sum+=n%10;
n/=10;
}
printf(" = %d\n",sum);
return 0;
}
1 1 1
3. Să se calculeze în diferite moduri suma 1 (n ≥ 100000)
2 3 n
#include <stdio.h>
int main(void)
{
long i,n;
float h;
n=100000L;
h=0.0f;
i=1;
while (i<=n)
{
h=h+1.0f/i;
i++;
}
printf("h1=%.6f\n",h);
h=0.0f;
i=n;
while (i>=1)
{
h=h+1.0f/i;
i-- ;
}
printf("h2=%.6f\n",h);
h=0.0f; i=0;
while (++i<=n)
h=h+1.0f/i;
printf("h3=%.6f\n",h);
return 0;
} // h1=12.090851 h2=12.090152 h3=12.090851
4. Să se calculeze şi să se afişeze cel mai mare divizor comun pentru două numere m si n
#include <stdio.h>
int main(void)
{
int m,n,r;
printf("m,n =");
if(scanf("%d%d",&m,&n)!=2)
printf("Date eronate\n");
else
{
printf("cmmdc al numerelor %d si %d = ",m,n);
while (n!=0)
{
r=m%n;
m=n;
n=r;
}
printf("%d\n",m);
}
return 0;
}
11.5.2. Instrucţiunea do - while
Instrucţiunea implementează structura de ciclu cu test final (condiţionat posterior) din programarea structurată
şi are sintaxa :
instructiune_do-while
do instructiune while ( expresie )
unde expresie este orice expresie validă în limbajul C şi implementează relaţia de condiţionare a ciclului, iar
instrucţiune este o singură instrucţiune C (în cazul în care corpul ciclului ar trebui descris prin mai multe
instrucţiuni, acestea se grupează într-o instrucţiune compusă).
Efectul execuţiei acestei instrucţiuni este următorul :
- 1 - se execută instrucţiune (corpul ciclului).;
- 2- se evaluează expresia expresie. În cazul în care expresia este nenulă se revine la pasul 1, în caz
contrar execuţia se încheie, situaţie în care se continuă cu execuţia instrucţiunii imediat următoare instrucţiunii d o -
w h ile .
O descriere echivalentă a modului de execuţie : se repetă execuţia instrucţiunii instrucţiune atâta timp
cât valoarea expresiei expresie este nenulă. Execuţia ciclului se încheie în momentul în care expresia are valoarea
zero, situaţie în care se continuă cu execuţia instrucţiunii imediat următoare instrucţiunii d o -w h ile .
Observaţii :
- corpul ciclului se execută cel puţin o dată, spre deosebire de cazul ciclului w h ile ;
- evident contextul în care se execută instrucţiunea ce constituie corpul ciclului sau chiar elementele variabile ce
intervin în expresie trebuie să acţioneze asupra expresiei, modificând-o, astfel încât, la un moment dat, valoarea
acesteia să devină nulă, asigurând astfel finitudine numărului de execuţii ale corpului ciclului;
- instrucţiunea care constituie corpul ciclului poate să definească un alt mod de execuţie a instrucţiunii d o -
w h ile decât cel indicat mai sus. Astfel ea poate realiza încheierea instrucţiunii d o - w h ile fără a se evalua expresia ce
constituie relaţia de condiţionare, prin intermediul unei aşa numite ieşiri forţate din ciclu (de exemplu corpul ciclului
poate conţine un apel al funcţiei e xit ).
Reamintim descrierea structurii de ciclu cu test final din programarea structurată :
corpul ciclului
repetă
* Secventa *secvenţă
cât timp expresie_condiţie
NU DA
expresie_conditie
relatia_de_conditionare
Având în vedere diferenţele menţionate în privinţa momentului evaluării condiţiei, înainte pentru w h ile şi
respectiv după execuţia corpului ciclului, pentru d o -w h ile , pentru aplicaţiile în care corpul ciclului se execută cel
puţin odată, ciclul d o -w h ile se poate folosi cu acelaşi corp în locul ciclului w h ile şi reciproc.
In general se poate scrie :
do instrucţiune;
instrucţiune; while (expresie)
while (expresie); instrucţiune;
Exemple.
5. Sã se calculeze valoarea aproximativă a lui ex cu o eroare mai mică în modul decât epsilon, pentru un x preluat de la
consola (|x|<=0.5).
Se ştie că :
x x x 2 x3
e =1+ + + +
1! 2! 3!
şi modulul erorii de aproximare este < epsilon când modulul ultimului termen adăugat în sumă este < epsilon.
5.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(void)
{
double x,t,e;
int k;
char car;
do
{
do
{
printf("x=");
fflush(stdin);
} while (scanf("%lf",&x) !=1 || (fabs(x)> 0.5));
t=1.0;
e=1.0;
k=1;
do
{
t=x*t/k;
e=e+t;
k++;
}while (fabs(t) >EPS);
Intrucţiunea implementează o structura de ciclu cu test inţial mai complex, asemănătoare structurii de ciclu cu
contor din programarea structurată şi are sintaxa :
for ( ; ; ) instructiune
unde expresie_1, expresie_2, expresie_3 reprezintă expresii valide în limbajul C şi instrucţiune reprezintă o
singură instrucţiune C (în cazul în care corpul ciclului ar trebui descris prin mai multe instrucţiuni, acestea se
grupează într-o instrucţiune compusă).
Terminologie:
- expresie_1 se numeşte expresie de iniţializare a ciclului;
- expresie_2 implementează relaţia de condiţionare a ciclului;
- expresie_3 implementează partea de reiniţializare a ciclului sau de actualizare a indexului.
Efectul execuţiei acestei instrucţiuni este următorul :
- 1- se evaluează expresie_1;
- 2- se evaluează expresie_2. În cazul în care expresia expresie_2 este nenulă se continuă execuţia
ciclului cu pasul 3, în caz contrar execuţia se încheie, situaţie în care se continuă cu execuţia instrucţiunii imediat
următoare instrucţiunii fo r ;
- 3- se execută instrucţiune (corpul ciclului);
- 4- se evaluează expresie_3 şi se continuă execuţia ciclului cu pasul 2.
Descrierea execuţiei instrucţiunii fo r folosind scheme logice este :
expresie_1
instructiune
expresie_3
Observaţii :
- corpul ciclului poate să nu se execute niciodată dacă expresia expresie_2 are valoarea zero chiar la intrarea în
ciclu;
- evident contextul în care se execută instrucţiunea ce constituie corpul ciclului sau elementele variabile ce apar
în expresie_2 sau în expresie_3 trebuie să acţioneze asupra expresiei expresie_2, modificând-o astfel încât, la
un moment dat, valoarea aceasteia să devină nulă, asigurând astfel finitudine numărului de execuţii ale corpului
ciclului;
- instrucţiunea care constituie corpul ciclului poate să definească un alt mod de execuţie a instrucţiunii fo r decât
cel indicat mai sus. Astfel ea poate realiza încheierea instrucţiunii fo r fără a se evalua expresia ce constituie relaţia de
condiţionare, prin intermediul unei aşa numite ieşiri forţate din ciclu (de exemplu corpul ciclului poate conţine un
apel al funcţiei e xit ).
Ciclurile w h ile şi fo r sunt echivalente, cu excepţia situaţiilor în care corpul ciclurilor conţine instrucţiunea
co n t in u e .
Astfel instrucţiunea fo r poate fi scrisă folosind secvenţa :
Evident în acest ultim caz, instrucţiune trebuie să prevadă o modalitate de ieşire forţată din ciclu.
În general se recomandă utilizarea instrucţiunii fo r în ciclurile în care sunt prezente şi părţile de iniţializare şi
actualizare ale ciclului, numite cicluri cu contor.
Reamintim descrierea în pseudocod a formei particulare de ciclu cu contor din programarea structurată :
* Secventa
care devine în C :
1 1 1
1. Să se calculeze în două moduri suma 1 ( 1) N 1 pentru N=4000000
22 32 N2
2. Să se tabeleze din grad în grad (intre 0 si 180) funcţiile trigonometrice sinus şi cosinus.
4. Să se contorizeze şi afişeze numarul caracterelor introduse de la tastatura pana la actionarea combinatiei sfarsit de
fisier de la consola.
5. Să se contorizeze şi afişeze numărul caracterelor de tip cifră introduse consecutiv de la tastatura până la acţionarea
combinaţiei sfârşit de fişier de la consolă. Să se determine câte dintre acestea sunt cifre mai mari ca 5.
1 1 1
1. Să se calculeze în două moduri suma 1 2
2
(1) N 1 2
pentru N=4000000
2 3 N
#include <stdio.h>
#define N 4000000
int main(void)
{
double s;
unsigned long i;
int semn;
s=0.0;
semn=1;
for (i=1uL;i<=N;i++)
{
s+=semn/(double)i/i;
semn=- semn;
}
printf("s1=%.15g\n",s);
s=0.0;
semn=-1;
for (i=N;i>=1uL;i-- )
{
s+=semn/(double)i/i;
semn= -semn;
}
printf("s2=%.15g\n",s);
return 0;
} //s1=0.822467033424046 s2=0.822467033424082 Obs. Mai sunt încă alte două moduri de calcul al sumei !!
2. Să se tabeleze din grad în grad (intre 0 si 180) funcţiile trigonometrice sinus şi cosinus.
#define PI 3.14159265358979
// sau se poate folosi constanta simbolica M_PI definita in math.h
#include <stdio.h>
#include <conio.h>
#include <math.h>
int main(void)
{
int i;
double x;
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
unsigned int num,d;
printf("nr=");
if(scanf("%u",&num)!=1)
{
printf("Date eronate\n");
exit(1);
}
printf("%u ->",num); // afiseaza intai cmps cifra!
for(;num;num/=16)
{
d=num%16;
printf("%c",d<=9?d+'0':d+'A'-10);
}
printf("\t hexazecimal in ordine inversa\n");
return 0;
}
4. Să se contorizeze şi afişeze numarul caracterelor introduse de la tastatura pana la actionarea combinatiei sfarsit de
fisier de la consola.
#include <stdio.h>
int main(void)
{
long n;
for (n=0;getchar()!=EOF;n++)
;
printf("nr. car=%ld\n",n);
return 0;
}
5. Să se contorizeze şi afişeze numărul caracterelor de tip cifră introduse consecutiv de la tastatura până la acţionarea
combinaţiei sfârşit de fişier de la consolă. Să se determine câte dintre acestea sunt cifre mai mari ca 5.
#include <stdio.h>
int main(void)
{
int nc,nc_5;
char c;
for (nc=0,nc_5=0;(c=getchar())!=EOF&& c>='0'&&c<='9';nc++)
if (c>'5')
nc_5++;
printf("nr. cifre consecutive=%d\t nr. cifre >5=%d\n ",nc,nc_5);
return 0;
}