Sunteți pe pagina 1din 204

PROGRAMAREA CALCULATOARELOR

ŞI
LIMBAJE DE PROGRAMARE 1
Titular : prof.dr.ing. Adriana Sîrbu

Situri:

www.etti.legacy.tuiasi.ro/pclp - Password: Kon-Tiki

edu.etti.tuiasi.ro (Moodle)

E-mail: asirbu@etti.tuiasi.ro

etti_pclp@yahoo.com √

Bibliografie :

 G. Brookshear – Introducere în informatică, Editura Teora 1998


Computer Science: An Overview, Pearson, 13th edition (2019)
 L. Negrescu – Limbajele C si C++ pentru începători - Limbajul C (vol. I –
părţile 1 şi 2) (ediţia a XI-a), Editura Albastră 2005
 H. Schildt - C Manual complet, Editura Teora, 1997.
 V. Cristea,s.a. - Limbajul C standard, Editura Teora, Bucuresti, 1992.
 A. Sîrbu - Limbajul C - Tehnici de programare, Editura "Gh. Asachi", Iaşi,
2000
• Al Aho and Jeff Ullman - Foundations of Computer Science

http://infolab.stanford.edu/~ullman/focs.html

• I. Wienand Computer Science from the Bottom Up, 2019 –

http://www.bottomupcs.com/

• https://github.com/EbookFoundation/free-programming-
books/blob/master/free-programming-books.md#c.

• V. Eijkhout - Introduction to High Performance Scientific Computing

 https://www.semanticscholar.org/paper/Introduction-to-High-
Performance-Scientific-
Eijkhout/a301a69a29776cbace06a3e3ba232f1d969dbc82

Resurse web :

https://learn.saylor.org/course/view.php?id=71 CS301: Computer Architecture

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

Generaţia a II-a (1957-1963)


- tranzistoare şi diode semiconductoare
- memorii din ferite,
- viteza de lucru - 200.000 de operaţii pe secundă
- capacitatea memoriei interne - aproximativ 32KO
- primele limbaje de nivel înalt (Fortran, Cobol)
- epoca informaticii de gestiune

Generaţia a III-a (1964-1970; 1971-1981)


- circuite integrate
- memorii cu circuite semiconductoare
- memorie externă - discuri magnetice
- viteza de lucru - 5 milioane de operaţii pe secundă
- capacitatea memoriei interne - aproximativ 2MO

Generaţia a IV-a (1982-1989)


- circuite integrate pe scară largă (LSI – Large Scale Integration) şi foarte largă (VLSI – Very Large
Scale Integration) (echivalentul a 50.000 de tranzistoare pe chip)
- capacitatea memoriei interne - aproximativ 4GO
- viteza de lucru - 30.000.000 de operaţii pe secundă
- memorie externă - discurile optice
- noi paradigme de programare: programarea orientată pe obiecte

Generaţia a V-a (1990-)


- generaţia inteligenţei artificiale
- bazată pe prelucrarea cunoştintelor (Knowledge Information Processing System - KIPS)
Momente importante :

- 1971 - apare primului microprocesor - circuitul Intel 8004 pe 4 biţi

- 1972 - apare microprocesorul Intel 8008 pe 8 biţi

- 1975 - apare primul calculator personal comercial Apple I

- 1978 - apare microprocesorul Intel 8086 pe 16 biţi

- 1981 - IBM produce primul calculator personal IBM PC XT

- 1982 - apare microprocesorul 80286 - 16 biţi

- 1984 - Apple produce calculatorul Macintosh

- 1984 - IBM produce primul PC AT

- 1985 – apare microprocesorul 80386 - 32 biţi

- 1993 – a cincea generaţie de microprocesoare – Pentium

- 2003 – apare primul processor pe 64 biţi, AMD Athlon 64

- 2006 - apare microprocesorul Core 2 Duo - 64 biţi


Capitolul II. Structura sistemelor de calcul
Calculatorul – definiţii alternative

- sistem complex ce rezolvă la un anumit moment dat o problemă/aplicaţie - denumit de aceea şi


sistem de calcul
- dispozitiv capabil să efectueze calcule şi decizii logice, procesând datele sub controlul unui program

Caracteristici ale calculatoarelor


 programabile
 automate
 universale
 electronice
 numerice (digitale)

Structura unui sistem de calcul


 echipamente – hardware
 programe – software
Structura hardware a unui sistem de calcul
Arhitectura von Neumann
- este un model de structură pentru calculatoarele cu program memorat ce utilizează o unitate de
procesare şi o singură unitate de memorie ce stochează atât date cât şi programe, funcţionarea fiind
secvenţială.
- adresa instrucţiunii ce se execută este memorată într-un registru (PC - program counter ) al cărui
conţinut este actualizat după execuţia unei instrucţiuni. La un moment dat, o singură instrucţiune
este încărcată pentru execuţie.

Principiile enunţate de von Neumann :


1. Un calculator trebuie să aibă un mediu de intrare, prin intermediul căruia să se poată introduce un
numar nelimitat de instrucţiuni şi date .
2. Un calculator trebuie să aibă o memorie, din care să se citească instrucţiunile şi datele şi în care să se
poată memora rezultatele.
3. Un calculator trebuie să aibă o secţiune de calcul, capabilă să efectueze operaţii aritmetice şi logice
asupra datelor/operanzilor din memorie.
4. Un calculator trebuie să aibă un mediu de ieşire, prin intermediul căruia un numar nelimitat de rezultate
să poată fi obţinute de către utilizator.
5. Un calculator trebuie să aibă o unitate de comandă, capabilă să interpreteze instrucţiunile obţinute din
memorie şi capabilă să selecteze diferite moduri de desfăşurare a activităţii calculatorului pe baza
rezultatelor calculelor .
date si
date si instructiuni date
instructiuni rezultate
Unitate Unitate
Memorie
de intrare de iesire

date
instructiuni
Unitate de calcul
logico-aritmetica

Unitate de comanda si control


`

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).

Figura 1. Schema bloc funcţională a unui sistem de calcul


Unităţi funcţionale ale unui sistem de calcul
1. Unitatea de comandă şi control
- asigură funcţionarea ansamblului, coordonând execuţia operaţiilor;
- primeşte şi interpreteză instrucţiunile ce se execută, le analizează şi ia decizii asupra activităţii ce trebuie executată.

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.

În principiu, asupra fiecărei locaţii de memorie se pot executa următoarele operaţii:


a) - citirea - obţinerea unei copii a informaţiei conţinute, fără a distruge conţinutul iniţial;
b) - scrierea - înlocuirea conţinutului locaţiei cu un nou conţinut, vechea informaţie fiind ştearsă (se pierde) şi noua
informaţie e înscrisă.

Memoria se clasifică din punct de vedere funcţional în:


- interna (principală) – aflată în comunicaţie directă cu unitatea centrală;
- externă - spre deosebire de cea internă care este folosită în principal pentru a memora numai informaţia ce se
prelucrează în mod curent, memoria externă oferă posibilitatea depozitării permanente a informaţiei. Înainte de a fi
folosită, informaţia din memoria externă trebuie transferată în memoria internă.
Memoria internă poate fi:
- memorie ROM (read only memory) - memorie al cărei conţinut poate fi doar citit. Conţinutul său informaţional
este înscris de către fabricant.
- memorie RAM (random acces memory) - memorie ce poate fi (în)scrisă şi citită de către utilizator.

Memoria este caracterizată în principal de doi parametri: capacitate şi timp de acces.

Magistrala de date

Unitati
Unitatea Unitatea
de intrare/
centrala de memorie
iesire

Magistrala de Magistrala de
control control

Magistrala de adrese

Figura 2. Schema bloc de principiu a unui sistem de calcul


Monitor

Bus

Figura 3. Structura hardware a unui sistem de calcul


Structura software a unui sistem de calcul

Limbaj de programare - mijloc de comunicare între un operator uman şi un sistem de calcul


Program - descrierea operaţiilor necesare rezolvării de către sistemul de calcul a unei aplicaţii într-un
limbaj de programare
Instrucţiune - comandă transmisă calculatorului, ce descrie prelucrările efectuate asupra datelor.
Limbaj de programare - set bine definit de expresii şi reguli (sau tehnici) valide de formulare a
instrucţiunilor pentru un calculator.
Orice limbaj de progamare este caracterizat prin :
- sintaxă
- semantică
- pragmatică.

Clasificarea limbajelor de programare


- limbaje de nivel coborât :
- limbaje maşină - un set de instrucţiuni sau comenzi conţinând numai 0 şi 1, care sunt direct
executate de un sistem de calcul (specifice fiecărui tip de CPU în parte).
- limbaje de asamblare - asociază denumiri (mnemonice) instrucţiunilor în limbaj maşină
(păstrând corespondenţa biunivocă între cele două tipuri de instrucţiuni)
- limbaje de macroasamblare
- limbaje de nivel înalt - instrucţiunile sunt simbolice şi nu se păstrează corespondenţa biunivocă
faţă de instrucţiunile în limbaj maşină.

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

 1940 –limbaje maşină


 1950 – limbaje de asamblare
– prima versiune de Fortran (Formula Translator)
 1960 – diversificarea limbajelor de nivel înalt
- Cobol (1960) Common Bussines Oriented Language
- Lisp (1960) List Processing
- Algol (1963) Algorithmic Language
- Basic (1963) Beginners All-purpose Symbolic Instruction Code
 1970 – apar concepte noi : portabilitate, programare structurată, programare modulară
- Pascal (1971 ) - Niklaus Wirth, ETH Zurich
- C (1972) - Dennis Ritchie, Bell Labs
 1980 – apar conceptele de programare orientată obiect, programare funcţională
- Ada (1980 ) - Jean Ichbiah of CII Honeywell Bull
- C ++(1980) -Bjarne Stroustrup, Bell Labs
 1990 – apare conceptul de programare distribuită
- Java (1996 ) -James Gosling, Sun Microsystems
- C# (1999) - Anders Hejlsberg, Microsoft.
 2000 – apar conceptele de programare genetică precum şi cel de bio-computing
Programe sursă - programe scrise în limbaje de programare simbolice .

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

În urma translatării se obţine un program obiect ce urmează a fi prelucrat în continuare.

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

Figura 4. Etapele prelucrării unui program sursă


Software-ul unui sistem de calcul se poate clasifica în :
- software de sistem (de bază)
- software de aplicaţie

Software-ul de sistem se poate clasifica în :


- sistemul de operare (de exploatare)
- sistemul de programare

Sistemul de operare are următoarele funcţii :


- realizează controlul operaţiilor de intrare-ieşire
- realizează comunicarea om-maşina într-un limbaj convenabil materializat prin comenzi ale
sistemului de operare
- realizează gestiunea resurselor hardware şi software:
- permite executarea programelor existente şi dezvoltarea altora noi
- controlul încărcărilor în memorie
- controlul lansării în execuţie şi al încetării activităţii pentru programele utilizator
- alocă programelor resursele sistemului (memorie, dispozitive periferice)
- asigură tratarea erorilor de funcţionare a sistemului
Figura 5. Sistemul de operare ca interfaţă între utilizator şi sistemul de calcul

Exemple de sisteme de operare: DOS, PC-DOS, MS-DOS, Linux, Mac OS , Solaris, UNIX ,Windows, Android

Sistemul de operare conţine :


- interfaţa
- nucleu (kernel)
Nucleul unui sistem de operare conţine următoarele componente :
- administratorul de fişiere (file manager)
- driverele de dispozitiv (device drivers)
- administratorul de memorie (memory manager)
- secvenţiatorul (sheduler)
- supervizorul (dispatcher)
Clasificarea sistemelor de operare
 SO bazate pe interfaţă grafică GUI - Graphical User Interface
- Windows XP
- Windows 98
- Windows CE

 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 multiprocesor - capabile să utilizeze mai multe procesoare


- Linux
- Unix
- Windows 2000

 SO multitasking – capabile să execute mai multe procese software în acelaşi timp


- 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

Un mediu de programare - IDE ( Integrated Development Environment) este un ansamblu unitar de


programe, de sine stătător, ce asistă utilizatorul în toate etapele necesare scrierii şi execuţiei unui program
(editare, compilare, editare de legături, încărcare, plus programe de manipulare a fişierelor şi depanatoare.

Exemple de medii de dezvoltare pentru C/C++:

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 calculatoarelor electronice


 Structura unui sistem de calcul conform arhitecturii von Neumann
 Caracteristicile de bază ale unităţilor funcţionale
 Definiţia unui limbaj de programare
 Definiţia unui program
 Definiţia unei instrucţiuni
 Clasificarea limbajelor de programare
 Caracteristicile limbajelor de nivel coborât (exemple)
 Caracteristicile limbajelor de nivel înalt (exemple)
 Definiţia unui limbaj simbolic
 Definiţia unui program sursă
 Etapele prelucrării unui program sursă în vederea execuţiei sale
 Funcţiile de bază ale unui sistem de operare
 Structura sistemului de programare
Capitolul III. Proiectarea algoritmilor
Algoritm – descrie clar, concis şi fără ambiguităţi, printr-o secvenţă finită de operaţii elementare, rezolvarea unei categorii de
probleme în cadrul căreia o anumită problemă este inclusă prin setul de date iniţiale care o specifică .

Observaţie - cuvântul algoritm are la origine numele matematicianului persan Al-Khwarizmi

Proprietăţile algoritmilor
- precizie
- claritate
- conciziune
- interpretabilitate
- generalitate
- eficacitate (finitudine)

Procesul de abstractizare implică


- abstractizarea datelor
- abstractizarea procedurală

Clasificarea datelor (după tip)


- numerice
- logice
- alfanumerice

Clasificarea datelor (după poziţia în lanţul informatic)


- de intrare
- de ieşire

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

Problemă Formularea problemei


• foloseşte "limbaje naturale"
• poate fi ambigă, imprecisă
Proiectarea software-ului :
alegerea algoritmului şi a
Algoritm structurii de date Algoritm
• set ordonat, finit, de paşi
executabili, descrişi fără echivoc
• precis, interpretabil, general
Programarea:
Utilizarea unui limbaj de
Program programare pentru Program
• exprImă un algoritm folosind un
implementarea solutiei
limbaj de programare (de nivle
înalt sau de nivel coborât)
Compilare (Asamblare)
/Link editare:
Arhitectura conversia la cod masina
(format executabil)
Arhitectura Setului de instrucţiuni
Instruction Set Architecture (ISA)
Setului de • specifică setul de instrucţiuni
(maşină) pe care le poate executa o
instrucţiuni unitate centrală
• specifică tipurile de date şi modurile
de adresare

Figura 1. Secvenţă sistematică de transformări între diferite nivele de abstractizare


Principalele etape necesare pentru rezolvarea unei aplicaţii cu ajutorul unui sistem de calcul sunt :
- analiza amănunţită a problemei
- alegerea metodei numerice
- algoritmizarea problemei
- descrierea algoritmului
- editarea programului sursă
- compliarea
- editarea de legături
- încărcarea
- execuţia
- testarea
III.1. Noţiuni de programare structurată
 Programarea structurată - este o paradigmă de programare apărută ca un model nou în scopul de a crea noi tehnici
capabile să dezvolte în mod eficient programe, evitându-se apariţia aşa-numitului “cod spagheti”.
 Conceptele de bază au fost enunţate în 1966 prin aşa-numita Teoremă de structură, publicată într-o lucrare de Böhm şi
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
 Avantajele stilului structurat în programare sunt :
- reducerea erorilor de programare;
- lizibilitate sporită a programelor;
- facilităţi suplimentare de depanare a programelor;
- facilităţi suplimentare de mentenanţă a programelor;
- costuri reduse.
În concluzie, descrierea oricărui algoritm va conţine doar structuri de control, structuri simple şi, eventual,
enunţuri nestandard (marcate prin *)

Salut Salut
camp curent camp curent camp curent
-1.234 -1.234
A

1000 #

Electronica

cap de citire cap de scriere cap de citire/scriere

banda de intrare banda de iesire banda de intrare/iesire

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

SCHEME LOGICE PSEUDOCOD

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

a) Decizia c) Ciclul cu test final

contor <-- valoare_initiala

corpul ciclului
NU expresie_conditie DA
NU contor <= corpul ciclului
valoare_finala DA

* Secventa
* Secventa

contor <-- contor + pas

b) Ciclul cu test initial d) Ciclul cu contor


Figura 4. Principalele structuri de control utilizate în programarea structurată
III.2. Principalele structuri de control ilustrate prin intermediul unor exemple sugestive

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 :

DA expresie_conditie NU dacă expresie_condiţie atunci


| *secvenţă_1
| altfel
* Secventa_1 * Secventa_2 | *secvenţă_2
|_□

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

dacă expresie_condiţie atunci


| *secvenţă
* Secventa |_□
Soluţie :
start
citeşte a,b
| dacă a>b atunci
| scrie a
| altfel
| scrie b
|_□
stop

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.

 utilizate pentru implementarea raţionamentelor umane de tip iterativ


 o structură ciclică este caracterizată prin :
- corpul ciclului - secvenţa de acţiuni care se execută în mod repetat
- relaţia de condiţionare a ciclului – expresia a cărei evaluare asigura numărul finit de repetări
(finitudine)
 numărul de repetări poate fi cunoscut sau necunoscut a priori.

3.1 Ciclul cu test iniţial (condiţionat anterior)

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.

Execuţia acestei structuri se face astfel :


1 Se evaluează condiţia exprimată de expresie_condiţie. Dacă aceasta este îndeplinită, sau echivalent, are
valoarea de adevăr adevărat, se continuă cu pasul 2. În caz contrar execuţia ciclului se încheie.
2 Se execută secvenţa ce constituie corpul ciclului, după care se revine la pasul 1.

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

DEFINIŢIE : Structurile de control în care un grup de operaţii se execută în mod repetat se


numesc structuri ciclice sau iterative.

 utilizate pentru implementarea raţionamentelor umane de tip iterativ


 o structură ciclică este caracterizată prin :
- corpul ciclului - secvenţa de acţiuni care se execută în mod repetat
- relaţia de condiţionare a ciclului – expresia a cărei evaluare asigura numărul finit de
repetări (finitudine)
 numărul de repetări poate fi cunoscut sau necunoscut a priori.
3.1. Ciclul cu test iniţial

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.

Execuţia acestei structuri se face astfel :


1 Se evaluează condiţia exprimată de expresie_condiţie. Dacă aceasta este îndeplinită, sau
echivalent, are valoarea de adevăr adevărat, se continuă cu pasul 2. În caz contrar execuţia ciclului se
încheie.
2 Se execută secvenţa ce constituie corpul ciclului, după care se revine la pasul 1.

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

Structura de tip ciclu cu contor se descrie în general astfel :

contor <-- valoare_initiala

NU contor <= corpul ciclului


pentru contor = val_in, val_fin, pas execută
valoare_finala DA
| *secvenţă
|_□
* Secventa

contor <-- contor + pas

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)

Structura de tip ciclu cu test final se descrie în general în pseudocod astfel :

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.

Execuţia acestei structuri se face astfel :


1 Se execută secvenţa ce constituie corpul ciclului
2 Se evaluează condiţia expresie_condiţie. Dacă aceasta este îndeplinită , sau echivalent, are
valoarea de adevăr adevărat, se revine la pasul 1. În caz contrar execuţia ciclului se încheie.

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

Execuţia acestei structuri se face astfel :


1 Se execută secvenţa ce constituie corpul ciclului
2 Se evaluează condiţia expresie_condiţie. Dacă aceasta nu este îndeplinită , sau echivalent,
are valoarea de adevăr fals, se revine la pasul 1. În caz contrar execuţia ciclului se încheie.

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)

Se pleacă de la dezvoltarea în serie a funcţiei ex :

ex = 1 + x/1! + x22! + x3/3! + x4/4! + x5/5! + ... + xk/k! + ...

care se mai poate scrie :

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 :

| err | = | ex – e_aprox| = | Rn(x) |

Se poate demonstra că , pentru orice n natural, dacă :


0 < 2| x |  n
atunci :
| Rn(x) | < | tn |
Rezultă că
| tn | < ε => | Rn(x) | = |err| < ε

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.

Dintre tipurile structurate cele mai utilizate amintim :


- tipul tablou
- tipul structură
- tipul uniune
- tipul fişier
4.1. Tabloul

 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 :

funcţie nume_funcţie(lista_parametrii formali)


* secvenţă // corp_funcţie
returnează valoare
sfârşit

respectiv :

procedură nume_procedură(lista_parametrii formali)


* secvenţă // corp_procedură
sfârşit
Enunţ 5.1. : Pentru două valori, n şi k, preluate de la consolă, să se calculeze şi afişeze combinări de n
luate câte k, notate în cele ce urmează Cnk.

Indicaţie : Se pleacă de la formula combinărilor : Cnk = n!/k!/(n-k)!. Exemplul este doar de uz


didactic, calculul factorialului fiind o operaţie extrem de ineficientă din punct de vedere numeric.!
Corect, calculul combinărilor se face folosind o relaţie de recurenţă.

Temă : Deduceţi relaţia de recurenţă care exprimă Cnk în funcţie de Cn(k-1)


Soluţie 5.1. :

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

TEMA : Introduceţi şi secvenţa de validare a datelor (n,k pozitive şi k≤n)


Enunţ 5.2. : Pentru toate perechile distincte de valori inferioare unui NR_MAX precizat, să se
calculeze şi să se afişeze cel mai mare divizor comun al acestora.

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 n0 execută
| atribuie r  *restul împărţirii lui m la n
| atribuie m  n
| atribuie n  r
|_□
returnează m
sfârşit

Algoritmul ce descrie soluţia problemei propuse este :

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.

funcţie LocMax(X, prim, ultim)


atribuie p  prim
pentru i = p + 1 , ultim execută
| | dacă X[p] < X[i] atunci
| | atribuie p  i
| |_□
|_□
returnează p
sfârşit

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

Exemplu de program scris în limbajul C :

#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 :

auto double int struct


break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

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

Figura 3.1. Diagrama sintactică pentru cifră

litera

identificator
litera

_ cifra

Figura 3.2. Diagrama sintactică pentru identificator


3.2.3. Operatori - 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.

Tabelul operatorilor, în ordinea descrescătoare a priorităţilor acestora :

( ) [ ] . ->
- + * & ! ~ ++ -- 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;

3.2.5.1. Constante întregi

- O constantă întreagă se scrie ca un şir de cifre, precedat eventual de semn.


- constantele întregi se pot scrie în sistemul de numeraţie zecimal, hexazecimal sau octal.
- în absenţa oricărui prefix, constanta se consideră ca fiind scrisă în baza 10 (zecimal).
- pentru a marca scrierea în baza 16 (hexazecimal), şirul de cifre ce reprezintă constanta se
prefixează cu caracterele 0x sau 0X
- pentru a marca scrierea în baza 8 (octal), se foloseşte prefixul 0.
- o constantă întreagă se reprezintă în cod complementar faţă de 2, pe cel puţin 16 biţi. În cazul în
care se doreşte reprezentarea pe cel puţin 32 de biţi (în aşa-numitul format long), şirul de
cifre se postfixează cu caracterul l sau L. În cazul în care se doreşte reprezentarea pe cel puţin
64 de biţi (în aşa-numitul format long long), şirul de cifre se postfixează cu caracterele ll sau
LL.
3.2.5.2. Constante reale

- 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

Figura 4.1. Diagrama sintactică a unui program scris în limbajul C

 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

 Structura unei funcţii în C poate fi descrisă de următoarea diagramă sintactică :

definitie_functie

antet corp

Figura 4.2. Diagrama sintactică a unei funcţii în C

tip

antet

identificator ( lista_parametrii_formali )

Figura 4.3. Diagrama sintactică a antetului unei funcţii în C

 În limbajul C există două categorii de funcţii :


- funcţii care returnează o valoare la revenirea din ele. Tipul acestei valori se precizează în antetul
funcţiei aşa cum se va arăta ulterior;
- funcţii care nu returnează nici o valoare la revenirea din ele. Pentru aceste funcţii se va folosi
cuvântul cheie v o id în calitate de tip în antetul funcţiei.
 Dacă specificaţia de tip este absentă, în limbajul C se presupune că funcţia returnează o valoare de tip int.
 Se recomandă indicarea tipului valorii returnate, deoarece absenţa acestuia poate constitui o potenţială sursă de
erori.
 Pentru a permite transferul de date unei funcţii în momentul apelului acesteia, dar şi comunicarea de către
aceasta a unor date în exterior, se folosesc aşa numiţii parametri, care însoţesc atât definiţia cât şi apelul
funcţiei.
 Transferul datelor prin intermediul parametrilor permite proiectarea de funcţii generale, abstracţie făcând de
valorile concrete ale acestora, valori ce vor fi necesare doar la execuţia funcţiei. În momentul compilării este
necesară doar cunoaşterea tipului parametrilor, cunoaştere posibilă prin intermediul unor declaraţii de tip
specifice
 Antetul unei funcţii precizează următoarele informaţii despre funcţie : tipul valorii returnate, numele funcţiei,
numele şi tipul parametrilor acesteia (implicit şi numărul acestora).
 Parametrii declaraţi în antetul unei funcţii şi care apoi sunt folosiţi în corpul funcţiei se numesc parametri
formali, pentru a sublinia faptul că ei sunt folosiţi doar pentru a descrie acţiunile ce trebuie întreprinse de
funcţie. Valorile lor se vor concretiza la execuţie, prin apelurile funcţiei, caz în care ei vor fi substituiţi de
valorile cu care se execută efectiv funcţia, valori care se numesc parametri efectivi (actuali).
 Lista parametrilor poate fi vidă atunci când nu este necesară comunicaţia cu exteriorul prin intermediul
parametrilor, caz în care se foloseşte în locul listei parametrilor cuvântul rezervat v o id .
 Antetul cel mai simplu folosit pentru funcţia m a in , în cazul în care funcţia nu are parametri şi returnează o
valoare de tip întreg este :
in t m a in ( v o id )
În acest caz funcţia m a in trebuie să conţină cel puţin o instrucţiune r e t u r n (de cele mai multe ori plasată la
sfârşitul funcţiei) care să specifice valoare returnată de aceasta; valoarea 0 (zero) indică terminarea execuţiei
fără erori, în timp ce o valoare diferită de zero semnalează apariţia unor erori.
Observaţie : în anumite medii de dezvoltare se poate folosi şi varianta mai veche de antet :
v o id m a in ( v o id )
caz în care funcţia m a in nu returnează nici o valoare. Această variantă de antet nu mai este admisă de noile
standarde C++.
 Apelurile unei funcţii nu pot fi precedate întotdeauna de definiţia funcţiei. În astfel de cazuri, definiţia funcţiei
apelate este înlocuită printr-un aşa-numit prototip al ei.
 Prototipul unei funcţii are un format asemănător cu antetul ei, precizând următoarele informaţii despre funcţie:
tipul valorii returnate, numele funcţiei şi tipul parametrilor (implicit şi numărul acestora).
 Compilatorul utilizează datele din prototip pentru a verifica tipurile parametrilor efectivi de apel. În cazul în
care un parametru efectiv are un tip diferit de tipul parametrului formal corespunzător, dacă este posibil,
compilatorul converteşte automat valoarea parametrului efectiv la tipul indicat în prototip.
 Utilizatorii limbajului C pot folosi o serie de funcţii care au o utilizare frecventă în diverse aplicaţii, funcţii ce
sunt livrate sub forma unor biblioteci ale limbajului. Ele sunt stocate în fişiere speciale în format obiect
(compilate deci) şi se adaugă la fiecare program care le apelează în faza de editare de legături. Aceste funcţii se
numesc funcţii standard, iar bibliotecile care le conţin, biblioteci standard ale limbajului.
 Apelul unei funcţii din bibliotecă presupune cunoaşterea prealabilă a prototipului acesteia. Pentru a simplifica
inserarea în textul sursă a prototipurilor funcţiilor de bibliotecă s-au construit fişiere cu astfel de prototipuri.
Aceste fişiere au extensia h şi conţin prototipuri ale unor funcţii înrudite. De exemplu fişierul stdio.h conţine
prototipurile funcţiilor de intrare/ieşire.

4.2. Preprocesarea

 Un program în limbajul C poate fi prelucrat suplimentar înainte de a fi compilat. O astfel de prelucrare se


numeşte preprocesare şi se realizează cu ajutorul unui program special, numit preprocesor, program apelat
automat înainte de începerea compilării.
 În principiu, preprocesorul limbajului C realizează substituţii la nivel de text. Prin intermediul lui se pot realiza:
- includeri de texte;
- definiţii şi apeluri de macrouri;
- compilare condiţionată.
 Preprocesarea se realizează prin prelucrarea unor directive specifice, care au ca prim caracter, caracterul #.
4.2.1. Includerea de fişiere sursă
 Un fişier cu text sursă poate fi inclus cu ajutorul directivei #include care poate avea unul din
următoarele formate :

#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

 Substituirea de succesiuni de caractere la preprocesare se face cu ajutorul construcţiei #define


 Un caz particular de definiţie de macro este definiţia constantelor simbolice, al cărei format este:
#define nume succesiune_de _caractere
unde succesiune de caractere începe cu primul caracter care nu este “spaţiu alb” (spaţiul,
caracterul “linie nouă”, tabulatorul orizontal). Ea poate fi continuată pe mai multe linii terminând
rândul care dorim să se continue cu caracterul backslash (\).
 Efectul execuţiei acestei directive este substituirea şirului de caractere nume cu şirul succesiune de
caractere, peste tot în textul sursă care urmează construcţiei #d e fin e respective, exceptând cazul
apariţiei numelui în interiorul unui şir de caractere sau al unui comentariu. De obicei nume se scrie
cu majuscule pentru a sublinia faptul că este o constantă simbolică, respectiv că e definit printr-o
directivă #define .
 Substituţia autorizată printr-o construcţie #define începe din punctul în care este scrisă directiva şi se
termină la sfârşitul fişierului în care este scrisă sau până la întâlnirea unei directive # undefine care o
anulează. Formatul acestei ultime directive este :

# 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:

- nume reprezintă numele macrodefiniţiei;


- arg1,arg2, ...reprezintă lista de parametri;
- succesiune_de _caractere reprezintă corpul macrodefiniţiei

Macrodefiniţiile sunt expandate în textul sursă înainte de compilare (în faza de preprocesare) în doua etape:

1. apelul macrodefiniţiei din codul sursa este înlocuit cu corpul acesteia


2. parametrii macrodefiniţiei sunt înlocuiţi cu valorile primite ca parametri, exact ca în cazul apelurilor de
funcţii clasice.
Capitolul IV. Programarea în limbajul C (continuare)

5. Tipuri de date în C (I) - Tipuri de date simple în C


 O caracteristică importantă a datelor este tipul
 Un tip de date precizează mulţimea valorilor pe care le pot avea datele de tipul respectiv, precum şi
mulţimea operaţiilor ce pot fi efectuate asupra valorilor tipului respectiv
 Datele de un anumit tip pot referi valori singulare (indivizibile în raport cu operaţiile ce se efectuează
asupra lor), caz în care se numesc date simple şi ele se asociază unor aşa-numite tipuri de date
simple, sau pot referi mai multe valori, organizate într-un anumit mod, caz în care se numesc date
structurate şi ele se asociază unor tipuri de date structurate.
 Cele mai multe limbaje de programare oferă programatorului un număr de tipuri de date simple,
numite şi tipuri predefinite, referite prin intermediul unor cuvinte rezervate precum şi instrumente de
construcţie a tipurilor de date structurate .

Limbajul C oferă următoarele grupe de tipuri de date simple :


- tipuri de date întregi;
- tipuri de date reale;
- tipul de date caracter.
5.1.Tipuri de date întregi
- tipurile de date întregi permit reprezentarea unei submulţimi finite a numerelor întregi
- constantele tipului întreg sunt constante întregi ce se construiesc după regulile precizate
Tabelul 5.1. Tipurile de date întregi în C

Numele tipului Dimensiune de Mod de reprezentare Interval de reprezentare


reprezentare internă
(biţi)
s h o r t [in t ]* 16 complement faţă de 2 [-32.768, 32.767]

u n s ig n e d s h o r t [in t ]* 16 întreg fără semn [0, 65.535]

in t 32 complement faţă de 2 [-2.147.483.648, 2.147.483.6487]

u n s ig n e d [in t ]* 32 întreg fără semn [0, 4.294.967.295]

lo n g [in t ]* 32 complement faţă de 2 [-2.147.483.648, 2.147.483.6487]

u n s ig n e d lo n g [in t ]* 32 întreg fără semn [0,4.294.967.295]

lo n g lo n g [in t ]* 64 complement faţă de 2 [-9.223.372.036.854.775.808,


( C9 9 , C11) 9.223.372.036.854.775.807]

u n s ig n e d lo n g lo n g 64 întreg fără semn [0, 18.446.744.073.709.551.615]


[in t ]*
( C9 9 , C11)

* şirul de caractere reprezentat între paranteze drepte este opţional.


5.2. Tipuri de date reale
- tipurile reale permit reprezentarea unei submulţimi finite a mulţimii numerelor raţionale.
- reprezentarea datelor reale într-un sistem de calcul se face în format virgulă mobilă, fiind afectată
de erori
- mai precis, dacă a şi b sunt două numere reale, reprezentate intern ca x şi respectiv y, există
următoarele relaţii:
a=b  x=y
x<y  a<b
a<b  xy
- constantele tipului real sunt constante reale ce se construiesc după regulile precizate
Tabelul 5.2. Tipurile de date reale în C

Dimensiune de Interval de reprezentare Numărul


reprezentare a minim de
Numele tipului Mod de reprezentare internă
(biţi) modulului cifre
semnificative
flo a t 32 virgulă mobilă- simplă precizie [1.17*10-38, 3.4*10+38] 7 (7.2)

d o u b le (lo n g flo a t ) 64 virgulă mobilă - dublă precizie [2.2*10-308, 1.79*10+308] 15 (15.9)

lo n g d o u b le 80 virgulă mobilă - precizie extinsă [3.4*10-4932, 1.1*10+4932] 19 (19,2)

5.3. Tipul de date caracter


- datele tipului caracter reprezintă o mulţime finită şi ordonată de 256 de caractere, reprezentând
simbolurile codului ASCII extins.
- tipul caracter este referit prin intermediul cuvântului rezervat ch a r
- constantele tipului caracter sunt constantele caracter ce se construiesc după regulile precizate
Tabelul 5.3. Codul ASCII
Tabelul 5.4. Caracterele suplimentare introduse de codul ASCII extins
6. Declaraţii de variabile simple
 În limbajul C trebuie definite sau declarate toate denumirile simbolice folosite de programator. Nu
există deci, ca în cazul altor limbaje de programare, declaraţii implicite
 În general o declaraţie asociază unei entităţi un nume
 Declaraţia unei variabile stabileşte legătura dintre numele variabilei şi tipul valorilor pe care le poate
avea variabila respectivă.

declaratie_variabila_simpla

tip identificator ;

Figura 6.1. Diagrama sintactică a declaraţiei de variabilă simplă în C

Î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 ,

Figura 7.1 . Diagrama sintactică a apelului funcţiei p r in t f

unde expresie reprezintă valoarea ce se va scrie conform specificatorilor de format prezenţi în parametrul de
control

Parametrul control este un şir de caractere ce conţine :


- succesiuni de caractere care se afişeaza ca atare;
- specificatori de format care definesc conversiile valorilor expresiilor din format intern în format
extern.
Un specificator de format începe cu caracterul % după care urmează :

- 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 :

Litera Conversia realizata


d - data se converteşte din tipul int în zecimal şi se scriu la ieşire caracterele zecimale ale ei, eventual precedate de semnul -,
daca data este negativă ;
o - data se converteşte din tipul int în octal şi se scriu la iesire caracterele ei octale;
x - data se converteşte din tipul int în hexazecimal şi se scriu la iesire caracterele ei hexazecimale;
X - ca şi în cazul literei x, dar în scrierea hexazecimala se folosesc literele mari (A - F);
u - data se converteşte din tipul unsigned în zecimal întreg fara semn
c - valoarea parametrului care îi corespunde se consideră ca reprezintă codul ASCII al unui caracter şi se scrie caracterul
respectiv;
s - parametrul care îi corespunde se scrie ca un şir de caractere; se consideră că şirul se termină la întalnirea caracterului
NUL ('\0');
f - valoarea parametrului care îi corespunde se converteşte din tipul flo a t sau d o u b le în formatul:
ddddd.....d.dddd...d
(d reprezintă o cifră zecimală), unde numărul de cifre după punctul zecimal este fie cel indicat de precizia specificatorului
de format, fie este egal cu 6. Partea întreagă este precedată de semnul minus dacă numărul este negativ;
e - conversia se realizează din tipul flo a t sau d o u b le în formatul :
d.ddd.....de+/-ddd
unde numărul cifrelor de după punctul zecimal este dat de precizia specificatorului de format sau este egal cu 6, daca
aceasta este absentă. Partea întreagă este precedată de semnul minus dacă numărul este negativ. La exponent se va scrie
una, două sau trei cifre, în funcţie de valoarea numărului;
E - ca şi în cazul literei e, cu deosebirea că litera e se schimbă cu E :
d.ddd.....dE+/-ddd
La exponent se vor scrie una, două sau trei cifre, în funcţie de valoarea numărului;
g - se aplică una din conversiile definite de literele f sau e, alegându-se cea care se reprezintă pe un număr minim de
caractere;
G - ca şi g cu singura deosebire ca se utilizează E în loc de e.
Literele d,o,x şi u pot fi precedate de litera h şi în acest caz conversia se realizează din s h o r t in t , de litera l şi în acest caz
conversia se realizează din lo n g în loc de in t , sau de perechea ll şi în acest caz conversia se realizează din lo n g lo n g .
Literele f,e,E,g şi G pot fi precedate de litera L şi în acest caz conversia se realizează din lo n g d o u b le în loc de float sau
d o u b le .
Exemplu de utilizare a specificatorilor de format la afişare (codul sursă şi textul afişat pe ecran)

#include <stdio.h>

#define A 47.389
#define X -123.5e20

int main(void)
{
printf("caracterul A =%c\n", 'A'); caracterul A =A

printf("A=%f\n", A); A=47.389000


printf("A=%.2f\n", A); A=47.39

printf("X=%e\n", X); X=-1.235000e+022


printf("X=%.2e\n", X); X=-1.24e+022
printf("X=%.1e\n", X); X=-1.2e+022

printf("%20f %20e %10g\n", 1.25,1.25,1.25); 1.250000 1.250000e+000 1.25


printf("%20f %20e %10g\n",123e-3,123e-3,123e-3); 0.123000 1.230000e-001 0.123
printf("%20f %20e %10g\n", 12.3e4,12.3e4,12.3e4); 123000.000000 1.230000e+005 123000
return 0;
}
7.7. Funcţia s ca n f

Funcţia s ca n f preia date tastate de la terminalul standard sub controlul unor formate. Sintaxa
apelului este :
apel_scanf

scanf ( control , adresa )

Figura 7.2. Diagrama sintactică a apelului funcţiei s ca n f

- 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 :

Litera Conversia realizata

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

[ ] ( ) . –> postfix ++ şi postfix –– Expresiii Stânga la dreapta

prefix ++ şi prefix –– sizeof & * + – ~ ! Unari Dreapta la stânga

ty pecasts Unari Dreapta la stânga

*/% Multiplicativi Stânga la dreapta

+– Additivi Stânga la dreapta

<< >> Deplasare pe biţi Stânga la dreapta

< > <= >= Relationali Stânga la dreapta

== != Equalitate Stânga la dreapta

& SI pe biţi Stânga la dreapta

^ SAU exclusive pe biţi Stânga la dreapta

| SAU pe biţi Stânga la dreapta

&& Si logic Stânga la dreapta

|| SAU logic Stânga la dreapta

?: Expresie condiţională Dreapta la stânga

= *= /= %= += –= <<= >>=&= ^= |= Atribuiri Dreapta la stânga

, Evaluare secvenţială Stânga la dreapta


9.1. Operatori paranteză

Operatori paranteză au prioritate maximă şi sunt reprezentaţi prin simbolurile :


 ( ) -paranteze rotunde - se folosesc pentru a modifica ordinea operaţiilor efectuate în vederea
calculului unei expresii, forţând evaluarea cu prioritate a sub expresiei incluse în paranteze.
Ele se utilizează de asemenea şi pentru a marca apelul unei funcţii
 [ ] - paranteze pătrate - includ expresii ce reprezintă indici, fiind numite şi operatori de
indexare.

9.2. Operatori aritmetici

Operatorii aritmetici, prezentaţi în ordine descrescătoare a priorităţilor, sunt :


 operatori unari :
+ (pozitivare)
- (negativare)
 operatori binari multiplicativi :
* (înmulţire)
/ (împărţire)
% (rest) - numai pentru operanzi întregi !
 operatori binari aditivi :
+ (adunare)
- (scădere)

Toţi operatorii unari au aceeaşi prioritate şi se asociază de la dreapta la stânga


Toţi operatorii multiplicativi au aceeaşi prioritate şi se asociază de la stânga la dreapta
Toţi operatorii aditivi au aceeaşi prioritate şi se asociază de la stânga la dreapta.
În general, rezultatul calculelor este incorect dacă se depăşeşte intervalul valorilor reprezentabile pentru fiecare
tip de dată în parte.

Regula conversiilor implicite

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

Operatorii de relaţie sunt operatori binari, au cu toţii aceeaşi prioritate şi ei sunt :


< (mai mic)
<= (mai mic sau egal)
> (mai mare)
>= (mai mare sau egal).

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.

9.4. Operatori de egalitate

Operatorii de egalitate, operatori binari, ce au cu toţii aceeaşi prioritate, sunt :


== (egal)
!= (diferit)

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

Operatorii logici prezentaţi în ordine descrescătoare a priorităţilor sunt :


! (negaţia logică) - operator unar
&& (ŞI logic) - operator binar
|| (SAU logic) - operator binar
Operatorul unar se asociază de la dreapta la stânga, iar operatorii binari se asociază de la stânga la dreapta.

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.

La evaluarea expresiilor logice se folosesc frecvent legile De Morgan:

!(x && y) este echivalent cu !x || !y

!(x || y) este echivalent cu !x && !y


9.6. Operatori de atribuire

Operatorul de atribuire în limbajul C are simbolul :


= (atribuire)

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

Operatorii de incrementare şi de decrementare sunt operatori unari şi au simbolurile :


++ (incrementare)
-- (decrementare)
Aceşti operatori pot fi folosiţi prefixând operandul, caz în care se foloseşte valoarea operandului la care s-a
aplicat în prealabil operatorul respectiv, sau postfixând operandul, caz în care se foloseşte valoarea operandului
dinaintea aplicării operatorului respectiv.

9.8. Operatorul de forţare a tipului sau de conversie explicită (ty pecast)

Acest operator este unar şi se scrie sub forma :


(tip) operand
unde tip este numele unui tip predefinit sau definit de utilizator , iar efectul aplicării unui astfel de operator este
conversia valorii operandului spre tipul tip.

9.9. Operatorul dimensiune

Acest operator este unar şi are simbolul


sizeof (argument)
şi se poate folosi în una din formele de mai jos :
- sizeof data
unde data este numele unei variabile simple, al unei variabile tablou sau al unei structuri;
- sizeof (tip)
unde tip este numele unui tip de data, predefinit sau definit de utilizator.
Operatorul returnează numărul de octeţi cu care se reprezintă argument-ul.
9.10. Operatorul adresă

Operatorul adresă este un operator unar şi are simbolul :


& (adresă)
Acest operator se aplică pentru a determina adresa de început a zonei de memorie alocate unei date (variabile).

9.11. Operatorul condiţional

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ă

Operatorul virgulă este un operator binar cu simbolul :


, (virgulă)
şi se foloseşte pentru a lega două expresii într- una singură, printr-o construcţie de forma :
expresie_ 1 , expresie_ 2
care reprezintă din punct de vedere sintactic o singură expresie, ce are acelaşi tip şi aceeaşi valoare ca şi expresia
expresie_2.
Operatorul virgulă se foloseşte în cazurile în care sintaxa limbajului impune folosirea unei singure expresii, iar
complexitatea calculelor necesită evaluarea mai multor expresii.
9.13. Operatori pe biţi
 Permit manipularea biţilor individuali ai reprezentării unei date
 Se pot aplica doar datelor de tip întreg sau compatibil
 Din această categorie fac parte operatorii următori, care apar în ordinea descrescătoare a priorităţii:
~ complementare – unar
>> deplasare la dreapta – binar
<< deplasare la stânga – binar
& şi – binar
^ sau exclusiv – binar
| sau – binar

Tabelul ude mai jos descrie operatorii logici pe biţi.

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

Din tabelul de mai sus se observă ca aplicând unui bit:

 operatorul & cu 0, bitul este resetat (are valoare 0)


 operatorul & cu 1, bitul rămâne neschimbat
 operatorul | cu 1, bitul este setat (are valoare 1)
 operatorul ^ cu 1, bitul este complementat.

 Î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):

i i<<1 i<<2 i>>1 i>>2


00000001 00000010 00000100 00000000 00000000
1 2 4 0 0
00001110 00011100 00111000 00000111 00000011
14 28 56 7 3
11111111 11111110 11111100 11111111 11111111
-1 -2 -4 -1 -1
11011101 10111010 01110100 11101110 11110111
-35 -70 116(depăşire) -18 -9

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

 Depanarea (Debugging) reprezinta procesul de identificare și înlăturare a erorilor dintr-un program.


 O eroare (bug) este o greşeală într-un program, ce determină execuţia acestuia într-o manieră
neintenţionată/neaşteptată.
 Situaţiile ce pot determina apariţia erorilor sunt determinate de :
- Erori de codare
- Neînţelegerea cerinţelor specificate de beneficiar
- Cerinţe incorrect/incomplete specificate
- Erori de logică
- Lipsa documetării
- Testare insuficientă

 Diferenţa dintre Testare şi Depanare


- Testarea este procesul de rulare a unui program în scopul determinării eventualelor erori
- Depanarea este procesul de corectare a erorilor depistate în timpul testării.

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

 Procesul de depanare poate avea două rezultate:


- Cauza erorii poate fi detectată şi corectată
- Cauza erorii nu poate fi detectată – se poate suspecta o cauză şi se proiectează teste adiţionale pâna la
detectarea şi corectarea erorii.

Figura 2. Generarea seturilor de date de testare


 Raţionamentul logic este cel mai puternic instrument de depanare

 Un depanator (debugger) facilitează observarea comportării unui program prin


– Execuţia pas cu pas (Step over şi/sau Step into functions)
– Examinarea valorilor variabilelor de interes

Tipuri uzuale de erori

 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

Figura 3. Fereastrele mediului DevC++


Programul de depanare - debugger-ul :
 permite programului să ruleze;
 programatorului îi permite să urmărească execuția instrucțiunilor și evoluția variabilelor pe măsura execuției
programului;
 este capabil să oprească temporar execuția programului, la un moment dat, în cursul funcționării acestuia –
operație numită breakpointing.
Breakpoint-urile pot fi setate în dreptul oricărei linii din cadrul programului (inclusiv linie fără cod). În momentul
în care, în cadrul execuției, se ajunge la un breakpoint, depanarea se oprește permițând astfel utilizatorului să
examineze datele curente din program. Adăugarea unui breakpoint se poate face:
 atât prin poziționarea cursorului pe linia de cod dorită și selectarea din meniul principal Execute/Toggle
Breakpoint (alternativ, tasta funcțională F4),
 cât și prin click din butonul stâng al mouse-ului pe “gutter”-ul editorului de text (banda gri din stânga
ferestrei), în dreptul liniei de cod vizate.
Ambele acțiuni au drept consecință evidențierea liniei indicate cu culoarea roșie, așa cum se poate vedea în fig. 8.
Ștergerea unui breakpoint se face repetând pașii de mai sus.
Pentru a depana un program trebuie să:
1) adăugăm un breakpoint (F4) uneia sau mai multor linii de cod, acolo unde dorim să se oprească debugger-ul (în
caz contrar, debugger-ul va executa tot programul!);
2) să compilăm programul (F9);
3) să lansăm debugger-ul fie prin selecția din meniul principal Execute/Debug (alternativ, tasta funcțională F5), fie
dând click pe icoana corespunzătoare din bara de comenzi – ambele soluții vor face activă secțiunea Debug din
fereastra Report; o altă soluție o reprezintă selectarea secțiunii Debug, urmată de apăsarea butonului Debug;
4) o dată cu lansarea debugger-ului, în fereastra Debug devenită activă devin accesibile butoanele ce permit: controlul
execuției programului (Next line, Continue, Next instruction, Into function, Skip function, Into instruction),
deschiderea ferestrei CPU (View CPU window), adăugarea în lista de variabile a căror valori dorim să le
urmărim a variabilelor de interes (Add watch) și oprirea execuției programului (Stop Execution) (vezi fig. 2); Dev-
C++ ne va avertiza că în timpul execuției programului primul breakpoint a fost atins prin faptul că are loc
schimbarea culorii liniei în albastru;
Figura 4. Lansarea debugger-ului.

5) după atingerea breakpoint-ului se pot urmări valorile variabilelor de interes fie:


a. dând click pe butonul Add Watch din fereastra de depanare (fereastra Debug) afișată în partea de jos a
ferestrei Dev-C++, urmată de introducerea numelui variabilei în fereastra de dialog și activarea butonului
OK, fie
b. indicând cu mouse-ul variabila dorită din codul sursă – acțiune ce are drept rezultat adăugarea variabilei în
fereastra watch (Debug) doar în condițiile în care, în meniul Tools/Environment Options a fost activată
în prealabil opțiunea Watch variable under mouse, fie
c. dând click-dreapta din butonul mouse-ului în secțiunea Debug a ferestrei Project Manager, urmată de
selectarea Add watch din meniul pop-up.
Lista variabilelor selectate spre a fi urmărite în evoluția lor poate fi accesată/afișată prin selectarea secțiunii
Debug din fereastra Project Manager (vezi fig. 4).
Eliminarea unei variabile din lista de mai sus se poate face prin selectarea variabilei, urmată fie de apăsarea
tastei Delete, fie de click-dreapta din butonul mouse-ului în secțiunea Debug a ferestrei explorer și
selectarea, apoi, a comenzii Remove watch.
Accesul la valoarea unei variabile se mai poate obține și prin survolarea acesteia în codul programului.

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):

 Next Line (F7) determină execuția tuturor instrucțiunilor de pe linia de cod


următoare (fără să arate și pașii parcurși și, respectiv, fără
trasare în interiorul funcțiilor apelate).
 Continue determină ca execuția programului să continue până la
întâlnirea unui nou breakpoint sau, în lipsa acestuia, până la
sfârșit.
 Into Function (F8) execută instrucțiunea următoare din program; dacă aceasta
conține un apel de funcție se va executa apelul, respectiv,
bara de execuție se mută la începutul funcției respective.
 Skip function determină ieșirea din funcția curentă (renunțarea la trasarea
în interiorul acesteia) și executarea instrucțiunii următoare
din program.
Secțiunea Debug
a ferestrei
Explorer

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:

 Next execută următoarea instrucțiune din


Instruction implementarea în limbaj de asamblare a
programului, fără trasarea în funcții.
 Into execută următoarea instrucțiune din
instruction implementarea în limbaj de asamblare a
programului și în cazul unui apel de funcție intră
în aceasta.
7) evaluarea unei expresii, în cursul depanării unui program, poate fi făcută prin introducerea în caseta de text
Evaluate, din secțiuea Debug (vezi fig. 10), a unei expresii pe care dorim să o evaluăm, urmată apoi de apăsarea
tastei Enter. Trebuie, însă, mare atenție în utilizarea acestui instrument de lucru oferit de mediu; astfel, expresii
de tip variabilă = expresie, unde variabilă reprezintă o variabilă utilizată în program, are drept efect schimbarea
valorii variabilei și, în consecință afectează starea programului. Aceste din urmă expresii mai sunt numite și
expresii cu efecte secundare. O expresie cu efecte secundare este evaluată o singură dată – atunci când se introduce
prima dată. În pașii următori ai depanării evaluările sunt inactivate. Acest comportament poate fi schimbat în mod
manual printr-o reapăsare a tastei Enter în caseta Evaluate. Efecte secundare neașteptate apar frecvent ca
rezultat al evaluării unei funcții.
8) oprirea depanării se face fie din fereastra Debug, prin apăsarea butonului Stop Execution, fie dând click-
stânga pe icoana corespunzătoare din bara de comenzi, fie selectând din bara de meniuri Execute/Stop
Execution (alternativ, tasta F6).
Exemple :
/* Exemplul 1
Programele de mai jos trebuie sa citeasca un numar intreg, un numar real simpla precizie, un numar real dubla precizie si doua caractere. */
a)
int main(void)
{
int a;
float b;
double c;
char d,e
printf("Introduceti un numar intreg, un numar real simpla precizie, un numar real dubla precizie si doua caractere\n");
if(scanf("%d%f%lf%c%c",&a,&b,&c,&d,&e)!=5);
printf("Date eronate !\n");
else
printf("Ati introdus : a=%d, b=%f, c=%g, d=%c, e=%c\n",a,b,c,d,e);
getch();
return 0;
}

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.

Se va compara rezultatul obtinut cu valoarea furnizata de biblioteca M_PI. */

#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.

10.1. Reprezentarea instrucţiunilor


 Fiecare instrucţiune acceptată (recunoscută) de unitatea centrală este reprezentată intern printr-o
succesiune de cifre binare. Codificarea, deci alegerea succesiunii de biţi pentru fiecare instrucţiune (în limbaj
maşină) este impusă de fabricant şi nu poate fi modificată de utilizator. Numărul de instrucţiuni, ce formează
setul caracteristic fiecărei unităţi centrale în parte depinde de tipul unităţii centrale şi, implicit, de lungimea
şirului de biţi prin care se face codificarea.
 Dacă programarea se face într-un limbaj simbolic, utilizatorul nu trebuie să se preocupe de
reprezentarea acestor instrucţiuni, deoarece aceasta se va face întotdeauna corect, în urma acţiunii programului
de translatare (asamblor, interpretor sau compilator). În consecinţă, problema principală ce trebuie să îl
preocupe pe programatorul într-un limbaj de programare simbolic este legată de reprezentarea datelor.
10.2. Reprezentarea datelor
Datele prelucrate de un sistem de calcul pot fi : numerice, logice sau alfanumerice (de tip caracter).
Pentru a evidenţia aspectele particulare legate de reprezentarea datelor întregi se va prezenta separat cazul
datelor întregi pozitive, respectiv cel al datelor întregi negative.

10.2.1.1. Reprezentarea datelor întregi pozitive


Fie A un număr pozitiv. El poate fi reprezentat într-un sistem de numeraţie de bază b printr-o secvenţă de
cifre ai, ai  { 0,1, ... , b-1}, i= 0, n  1 .
Prin convenţie, pentru o bază dată, numărul A este reprezentat în sistem poziţional printr-o scriere de
forma:

an-1 an-2 an-3 ..... a3 a2 a1 a0


unde n este numărul de cifre necesar reprezentării (a0 se numeşte cea mai puţin semnificativă cifră, iar an-1, cea
mai semnificativă cifră).
În cazul reprezentării cu n cifre în baza b , există relaţia :
n 1 n 1
A= 0 a i  b ≤ (b  1)฀0 b
i
i

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

10.2.1.2. Reprezentarea datelor întregi strict negative


În cele ce urmează ne vom referi numai la reprezentarea în binar.
Metoda cea mai naturală de reprezentare a numerelor întregi negative ar părea a consta în reprezentarea
semnului şi a modulului. Evident semnul poate fi codificat printr-un singur bit, de exemplu 0 pentru valori
pozitive şi 1 pentru valori negative.
Într-o astfel de reprezentare de tip semn şi modul, din cele n cifre ale reprezentării, una este rezervată
semnului, pentru reprezentarea modulului, A, rămânând n -1 cifre.
În acest mod, se pot reprezenta valori (atât pozitive cât şi negative) ce satisfac relaţia :

- 2 -1  A  2 -1
n 1 n 1

Evident există o dublă reprezentare pentru numărul zero.


Şi totuşi, în majoritatea cazurilor, nu se foloseşte o astfel de modalitate de reprezentare a întregilor negativi,
deoarece se încearcă evitarea tratării separate a semnului şi modulului în cazul operaţiilor aritmetice de adunare şi
scădere. Cele mai uzuale reprezentări care răspund acestor deziderate sunt aşa-numitele reprezentări în cod
complemetar.
Ideea reprezentării în cod complement provine din încercarea de a găsi o reprezentare a numerelor negative
care să permită tratarea similară a operaţiilor de adunare şi de scădere şi anume de a transforma scăderea a două
numere în adunarea dintre descăzut şi o mărime asociată scăzătorului, numită complement.
În general deci, întregilor negativi li se asociază un aşa numit complement, care trebuie să fie mereu pozitiv
şi să se calculeze relativ simplu.
Astfel, dacă -A este un număr strict negativ, şi notăm cu CA complementul lui A, acesta se defineşte prin
relaţia :
CA = R-A
unde R se alege astfel încât CA să fie pozitiv pentru tot domeniul posibil de valori pe care le poate lua A şi scăderea
lui R să fie o operaţie simplă.
Astfel, dacă B este un număr întreg pozitiv, se poate scrie :
B – A = B + CA-R
scăderea s-a transformat în adunare, iar corecţia care se face prin scăderea lui R va fi o operaţie simplu de
executat.
Într-o astfel de codificare deci reprezentarea numerelor pozitive rămâne neschimbată, iar numerele negative
se înlocuiesc cu complementul lor.
a. Reprezentarea în cod complement faţă de 1.
În cazul complementului faţă de 1, se alege pentru R valoarea :
n 1
R = 2n - 1   2
i 0
i

unde n este numărul de biţi pe care se face reprezentarea.


Complementul lui -A faţă de 1, notat cu à devine :
n 1 n 1 n 1
Ã  i  i
2 
i 0
a i
2  (1  a
i 0
)2 ii

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.

c. Reprezentarea în cod exces-K.


Codul ”în exces” cunoscut şi sub numele de cod cu offset (deplasament) binar sau reprezentare cu
deplasament, foloseşte o valoare specificată, K, drept valoare de deplasament. În mod uzual valoarea K se alege
astfel încât codul rezultat să fie, pe tot domeniul de reprezentare, o valoare pozitivă. Astfel, o valoare A este
reprezentată de un număr considerat fără semn, care este mai mare cu K unităţi faţă de valoarea A.
De exemplu 0 (zero) este reprezentat prin K, în timp ce −K este reprezentat prin 0(zero).
Acest cod poate fi interpretat ca fiind o uşoară modificare a Codului complementar faţă de 2, care poate fi
văzut ca o reprezentare în exces- 2n - 1, cu cel mai semnificativ bit negat.
Exemple:

Reprezentarea în complement faţă de 1 pe 8 biţi

Reprezentarea în complement faţă de 1 pe 8 biţi


Adunarea a două numere întregi
Interpretarea în cod reprezentate în complement faşă de 1 pe 8 biţi
Interpretarea ca
Valoare binară complementar
întreg fără semn
faţă de 1 binar zecimal
00000000 +0 0
11111110 −1
00000001 1 1
+ 00000010 +2
⋮ ⋮ ⋮ _______________ _____
01111101 125 125 1 00000000 0 ← incorect
1 +1 ← se adună bitul
01111110 126 126 de transport
01111111 127 127 _______________ _____
10000000 −127 128 00000001 1 ← corect
10000001 −126 129
10000010 −125 130
⋮ ⋮ ⋮
11111101 −2 253
11111110 −1 254
11111111 −0 255
Reprezentarea în complement faţă de 2 pe 8 biţi

Adunarea a două numere întregi


Reprezentarea în complement faţă de 2 pe 8 biţi reprezentate în complement faţă de 2 pe 8 biţi

Interpretarea în cod binar zecimal


Interpretarea ca
Valoare binară complementar
întreg fără semn
faţă de 1 11111111 −1
00000000 0 0 + 00000010 +2
_______________ _____
00000001 1 1
1 00000001 1 ← corect
⋮ ⋮ ⋮
01111110 126 126
01111111 127 127
10000000 −128 128
10000001 −127 129
10000010 −126 130
⋮ ⋮ ⋮
11111110 −2 254
11111111 −1 255
Reprezentarea în cod Exces-127 pe 8 biţi

Cod exces-127 pe 8 biţi


Valoare Interpretarea în Interpretarea ca întreg
binară cod exces-127 fără semn
00000000 −127 0
00000001 −126 1
⋮ ⋮ ⋮
01111111 0 127
10000000 1 128
⋮ ⋮ ⋮
11111111 +128 255
10.2.2. Reprezentarea datelor reale
Reprezentarea datelor reale se face în format semn (un bit, 0 pentru numere pozitive şi 1, pentru numere
negative) şi modul.
n
Pentru analiza modului de reprezentare a modulului vom considera un număr real pozitiv 0  X< b ce poate
fi reprezentat în baza b , în scriere poziţională. printr-o suită infinită de cifre xi  { 0, 1, ..., b-1} :
xn 1 xn  2 xn 3 ... x2 x1 x0 . x1 x 2 ... xm1 x m x m 1 ...
sau, dacă notăm cu X i partea întreagă şi cu X f partea fracţionară a numărului, putem scrie:

X i = xn 1 xn  2 xn 3 ... x2 x1 x0 , X f =0. x1 x 2 ... xm1 x m x m 1 ...


Reprezentarea numerelor reale pune problema preciziei reprezentării modulului deoarece, în mod evident,
nu pot fi reprezentate toate cifrele părţii fracţionare, impunându-se o limitare a acestora la un număr finit. De
asemenea, se poate arăta că există numere reale cu un număr finit de cifre la partea fracţionară într-o anumită
bază b 1 ce pot avea un număr infinit de cifre la partea fracţionară într-o altă bază b 2, în funcţie de valorile celor
două baze. De exemplu numărul zecimal 0,7 exprimat în baza 2 are un număr infinit de cifre la partea fracţionară.

10.2.2.1. Metode de aproximare a datelor reale


Aproximarea părţii fracţionare cu un număr finit de cifre se poate face în două moduri : prin trunchiere sau
prin rotunjire.
a. Aproximarea prin trunchiere
Într-o astfel de reprezentare a părţii fracţionare a unui număr real, se reţin doar z cifre :

X ftr = 0. x1 x 2 ... xz 1 x z


z
Se poate demonstra faptul că trunchierea produce o eroare absolută pozitivă, inferioară valorii b :

X f =  b k  xk
k  1
z
X ftr =  b k  xk
k  1
Rezultă că eroarea absolută (pozitivă) obţinută prin trunchiere va fi :

Etr = X f - X ftr =  b k  xk < b  z
k   z 1
Valoarea medie a erorii de trunchiere, pentru o repartiţie uniformă a valorii numerelor reale cu care se
operează, este :
b z
Etr _ med =
2
Datorită prezenţei unei erori sistematice, de medie nenulă, această metodă de aproximare este mai puţin
folosită.

b. Aproximarea prin rotunjire


Aproximarea prin rotunjire presupune creşterea cu o unitate a valorii ultimei cifre reţinute în reprezentarea
numărului (corespunzătoare conform notaţiilor introduse rangului -z ) în cazul în care prima cifră care se
neglijează (corespunzătoare conform notaţiilor introduse rangului - z -1) este mai mare sau egală cu jumătate din
valoarea bazei de numeraţie (b ).
Echivalent, aceasta înseamnă aplicarea aproximării prin trunchiere expresiei :
1 1
X f +  b  b  z 1= X f +  b  z
2 2
ceea ce implică
1 z
| Er | +  b < b z
2
şi deci :
1 z
| Er | < b
2
1 z
Rezultă că aproximarea prin rotunjire produce o eroare absolută inferioară în modul valorii b , iar
2
valoarea medie a erorii este nulă, pentru o repartiţie uniformă a valorii numerelor reale cu care se operează :

Er _ med = 0.

10.2.2.2. Reprezentarea datelor reale in format virgulă fixă (fixed point),


În cazul acestui format de reprezentare se presupune cunoscută (fixată) poziţia virgulei (a punctului zecimal
în notaţia anglo-saxonă), făcându-se convenţia ca cel mai semnificativ bit al reprezentării să codifice semnul (0
pentru plus, 1 pentru minus), următoarele poziţii ale reprezentării, până la virgulă să semnifice cifrele părţii
întregi a numărului real, iar ultimele, de după virgulă, cifrele părţii fracţionare (virgule neocupând explicit o
poziţie în această reprezentare).
O succesiune de n + z + 1 biţi interpretată ca semnificând un număr raţional în reprezentare în virgulă fixă, cu
semn, cu n cifre la partea întreagă şi z cifre la partea fracţionară, poate reprezenta valori dintr-o submulţime de
numere raţionale având modulul cuprins în intervalul [b  z , b n )
Domeniul de reprezentare pentru modulul numerelor reale în cazul utilizării formatului virgulă fixă este cel
mai adesea insuficient, în particular pentru aplicaţiile tehnico-ştiinţifice unde se folosesc atât valori foarte mari cât
şi valori foarte mici.
Aceasta modalitate de reprezentare ridică o problemă specială şi în ceea ce priveşte eroarea de reprezentare
care se poate demonstra că nu este constantă pe întreaga plajă de valori reprezentabile.
Astfel eroarea relativă minimă se obţine pentru cea mai mare valoare reprezentabilă

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.

10.2.2.3. Reprezentarea datelor reale in format virgulă mobilă (floating point)


Un format mult mai avantajos de reprezentare este cel cunoscut sub numele de format virgulă mobilă, care
asigură un domeniu de reprezentare mult mai larg, în condiţiile menţinerii valorii maxime a erorii de aproximare
constante în toată plaja de valori.
Menţionăm că de-a lungul timpului au existat diferenţe în modul de reprezentare a datelor în format virgulă
mobilă pentru diferite familii de calculatoare. Societatea de Calculatoare a IEEE (Institute of Electrical and
Electronics Engineers) a elaborat un standard pentru reprezentarea numerelor în virgulă mobilă, standardul
denumit IEEE 754 (IEEE Standard 754 for Binary Floating-Point Arithmetic), actualmente referit ca ISO/IEC/IEEE
60559:2011, care a fost adoptat de majoritatea proiectanţilor de software.
Într-o astfel de reprezentare, unui număr real, X, i se asociază patru valori, semnul S, mantisa M, exponentul
E şi baza B, numărul scriindu-se sub forma:

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

Deplasamentele exponentului pentru cele 3 cazuri ilustrate sunt:


-127 (7F H) - pentru formatul cu precizie simplă;
-1023 (3FFH) - pentru formatul cu precizie dublă ;
-16.383 (3FFFH) - pentru formatul cu precizie dublă extinsă.
În cazul formatelor cu precizie simplă şi precizie dublă mantisa se reprezintă doar partea fracţionară a
mantisei. În formatul cu precizie extinsă utilizat de unităţile de calcul în virgulă mobilă, bitul 63 reprezintă partea
întreagă a mantisei, care se reprezintă explicit.
Valoarea unui număr în precizie simplă ( N S ), în precizie dublă ( N D ) şi în precizie dublă extinsă ( N E ) se
poate calcula cu formulele de mai jos:
C  127
N S = (1) 1, M  2
S

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:

- dacă C = 255 şi M  0 atunci X este NaN indiferent de valoarea lui s;

- dacă C = 255 şi M = 0 , atunci X = ( 1) ;


s

- dacă 0 < C < 255, atunci X = (1) s  2( C 127)  (1, M ) ;


Valoarea maximă reprezentabilă pentru modul este aproximativ 2127 x 2= 2128 =3,4 1038
Valoarea minimă reprezentabilă pentru modul este aproximativ 2-126 x 1= 1,17 10-38

- dacă C = 0 şi M  0, atunci X = (1) s  2( C 126)  (0, M ) - numere denormalizate;


- dacă C = 0 şi M = 0, atunci X = (1) s  0 - zero.
Exemplu : Reprezentarea în simplă precizie a numărului π
Primii 33 biţi din reprezentarea binară a numărului π sunt :
1.1001001 00001111 11011010 10100010 0.
Rotunjind la 24 de biţi obţinem (bitul 24 devine 1 !):
1.1001001 00001111 11011011.
Pentru a calcula valoarea astfel reprezentată pentru π folosim formula:
z
(1   bit
n 1
n x 2  n ) x 2 E  (1  1 x 2 1  0 x 2  2  1 x 2  4  1 x 2  7  ....  1 x 2  23 ) x 2 1 

 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;

- dacă C = 2047 şi M = 0 , atunci X = (1) s   ;


- dacă 0 < C < 2047, atunci X = (1) s  2( C 1023)  (1, M ) ;
Valoarea maximă reprezentabilă pentru modul este aproximativ 21023 x 2= 21024 =1,79 10308
Valoarea minimă reprezentabilă pentru modul este aproximativ 2-1022 x 1= 2,22 10-308

- dacă C = 0 şi M  0, atunci X = ( 1)


s
 2( C 1022)  (0, M ) - numere denormalizate;
- dacă C = 0 şi M =0, atunci X = ( 1)  0 - zero.
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.

Tabelul 2. Caracterele suplimentare introduse de codul ASCII extins.


Din perspectivă istorică, se poate aminti faptul ca, de-a lungul timpului, sau folosit şi alte modalităţi de
codare pentru caractere. Astfel, codul Hollerith a fost utilizat pentru reprezentarea pe cartele perforate, folosind 12
biţi. Codul EBCDIC, Extended Binary Coded Decim al Interchange Code, introdus de firma IBM, nu reprezenta
caracterele într-o modalitate contiguă şi de aceea a şi dispărut.
Actualmente, codul ASCII, orientat pe reprezentări specifice limbii engleze, se dovedeşte inoperant pentru
reprezentarea caracterelor altor limbi. De aceea, în 1991, a apărut codul denumit Unicode, folosit deja de limbajul
Java. Acesta este un cod ce foloseşte 16 biţi, permiţând astfel reprezentarea a 65536 simboluri, fiind astfel adecvat
pentru codarea caracterelor din limba greacă, din limbile asiatice (chineza, japoneza, etc.), arabice, ebraice. În
plus, codul Unicode păstrează semnificaţia reprezentărilor codului ASCII. Astfel, pentru a converti codul ASCII în
Unicode, se adaugă un octet de zerouri, obţinându-se astfel cei 16 biţi ai reprezentării Unicode.
Cel mai important dezavantaj al codului Unicode este acela că dublează dimensiunea fişierelor text. De
asemenea, existenţa a 2 octeţi în reprezentare ridică problema ordinii în care aceştia se stochează în memorie.
Regula după care se face memorarea octeţilor se numeşte în limba engleză endianness, existând convenţii de tip
big-endian şi little-endian.

TEMĂ - documentaţi-va despre endianness folosind ca sursă Internetul.


11. Instrucţiunile limbajului C
 un program C se compune din una sau mai multe funcţii

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 )

Figura 11.3. Diagrama sintactică a antetului unei funcţii în C


 corpul oricărei funcţii are o secţiune ce cuprinde declaraţii şi o secţiune ce conţine instrucţiunile ce compun
funcţia.
corp_C

{ }

declaratii instructiuni

instructiuni

corp_C++
{ }

declaratii

declaratie instructiune

declaratii instructiuni

Figura 11.4. Diagrama sintactică a corpului unei funcţii în C/C++


declaratie_variabila_simpla

tip identificator ;

Figura 11.5. Diagrama sintactică a declaraţiei de variabilă simplă în C

 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 ).

11.2. Instrucţiunea expresie


Instrucţiunea expresie se obţine prin scrierea unei expresii urmată de separatorul punct şi virgulă (;).
În cazul în care expresia este o expresie de atribuire, instrucţiunea se mai numeşte instrucţiune de atribuire.
Un alt caz utilizat frecvent este acela în care expresia este un operand ce reprezintă un apel de funcţie.

 Aşa cum s-a arătat, o funcţie poate avea sau nu parametri.

void

lista_parametri_formali

declaratie_
,
parametru_formal

declaratie_
parametru_formal

declaratie_parametru_formal

tip identificator

Figura 11.6. Diagrama sintactică a listei parametrilor formali ai unei funcţii


 Absenţa parametrilor se recomandă a fi semnalată prin prezenţa cuvântului cheie void.
 Apelul unei funcţii presupune lansarea în execuţie a funcţiei respective după transferul prealabil al
parametrilor. Transferul parametrilor semnifică substituirea parametrilor formali de către parametrii efectivi.

apel_functie

identificator ( lista_parametri_efectivi )

Figura 11.7. Diagrama sintactică a apelului unei funcţii

unde identificator este numele funcţiei şi lista_parametri_efectivi are diagrama sintactică din Figura 11.8

lista_parametri_efectivi

expresie ,

expresie

Figura 11.8. Diagrama sintactică a listei parametrilor efectivi ai unei funcţii

 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

Figura 11.9. Diagrama sintactică a prototipului a unei funcţii

 Î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

abs calculează şi returnează valoarea absolută a unui argument întreg stdlib.h


- int abs(int x) ;
labs calculează şi returnează valoarea absolută a unui argument de tip long stdlib.h
- long labs(long x) ;
fabs calculează şi returnează valoarea absolută a unui numar real math.h
- double fabs(double x) ;
acos implementează funcţia arccos - rezultatul exprimat în radiani math.h
- double acos(double x) ;
asin implementează funcţia arcsin - rezultatul exprimat în radiani math.h
- double asin(double x) ;
atan implementează funcţia arctg - rezultatul exprimat în radiani math.h
- double atan(double x) ;
sin implementează funcţia sinus - argumentul exprimat în radiani math.h
- double sin(double x) ;
cos implementează funcţia cosinus - argumentul exprimat în radiani math.h
- double cos(double x) ;
tan implementează funcţia tangent - argumentul exprimat în radiani math.h
- double tan(double x) ;
exp implementează funcţia exponenăială math.h
- double exp(double x) ;
log calculează şi returnează logaritmul natural al argumentului math.h
- double log(double x) ;
log10 calculează şi returnează logaritmul zecimal al argumentului math.h
- double log10(double x) ;
pow calculează şi returnează primul argument ridicat la o putere egala cu cel de al doilea argument math.h
- double pow(double x, double y) ;
pow10 calculează şi returnează 10 la puterea cel de al doilea argument math.h
- double pow10(double x) ;
sqrt calculează şi returnează radacina pătrată a argumentului math.h
- double sqrt(double x) ;
ceil rotunjeşte valoarea argumentului (număr real)- aproximare prin exces math.h
- double ceil(double x) ;
floor trunchiază valoarea argumentului (număr real)- aproximare prin lipsa math.h
- double floor(double x) ;
fmod returnează restul împărţirii primului argument la cel de al doilea math.h
- double fmod(double x, double y) ;
O funcţie frecvent utilizată este funcţia e xit . Prototipul acestei funcţii se găseşte în fişierul s t d lib .h şi în fişierul
p r o ce s s .h . Prototipul său este :
v o id e xit (in t cod);
unde parametrul acestei funcţii, cod, defineşte starea programului în momentul apelului. Valoarea zero indică
terminarea normală a execuţiei programului (absenţa erorilor), iar o valoare diferită de zero semnalează terminarea
anormală a programului (prezenţa unei erori).
Efectul apelului funcţiei este următorul : se golesc (se videază) zonele tampon (bufferele) fişierelor deschise
pentru scriere, se închid toate fişierele deschise, se întrerupe execuţia programului.

11.3. Instrucţiunea compusă


Această instrucţiune implementează structura de scevenţă din programarea structurată. Diagrama sintactică a
instrucţiunii este :

instructiuni

instructiune_compusa
{ }

declaratii

Figura 11.10. Diagrama sintactică a instrucţiunii compuse în C++

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

Figura 11.11. Diagrama sintactică a instrucţiunii if

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)

11.5. Instrucţiunile ciclice


 Raţionamentul uman de tip iterativ îşi găseşte corespondentul în limbajul C în instrucţiunile ciclice
(repetitive) – ce implementează structurile ciclice din programarea structurată.
 Instrucţiunile ciclice permit execuţia repetată a unei instrucţiuni sau a unei secvenţe de instrucţiuni
înglobate într-o instrucţiune compusă.
 Întreaga construcţie este considerată din punct de vedere sintactic ca o singură instrucţiune şi se poate
utiliza în orice context în care este permisă prezenţa unei instrucţiuni în C.
 Instrucţiunea sau secvenţa de instrucţiuni ce se execută în mod repetat formează corpul ciclului.
 În principiu, orice acţiune repetitivă trebuie să se încheie după un număr finit de paşi (reluări/repetări), ca
urmare a neîndeplinirii unei condiţii(în limbajul C) ce constituie aşa-numita relaţie de condiţionare a
ciclului .
 În limbajul C există trei tipuri de instrucţiuni ciclice :
- instrucţiunea w h ile - ce implementează ciclul cu test iniţial (condiţionat anterior);
- instrucţiunea d o - w h ile - ce implementează ciclul cu test final (condiţionat posterior);
- instrucţiunea fo r - ce implementează ciclul cu contor.
11.5.1. Instrucţiunea while

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 - ).

2. Să se calculeze suma cifrelor unui număr dat n, pozitiv.

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>

#define EPS 1e-10

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);

printf("expapr(%g) = %0.14g in %d iteratii\n", x, e, k - 1);


printf("expcalc(%g) = %0.14g\n ",x,exp(x));
printf("Doriti sa continuati ?(D/N)");
fflush(stdin);
scanf("%c",&car);
} while (car!='n' && car!='N' );
return 0;
}
11. Instrucţiunile limbajului C (continuare)
11.5.3.Instrucţiunea fo r

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 :

expresie_1 expresie_2 expresie_3

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

NU expresie_2 DA corpul ciclului for

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 :

for (expresie_1;expresie_2;expresie_3)  expresie_1 ;


instrucţiune while (expresie_2)
{
instrucţiune
expresie_3 ;
}
şi reciproc, orice instrucţiune w h ile poate fi substituită de o instrucţiune fo r :

while (expresie)  for (; expresie ; )


instrucţiune instrucţiune

Un caz particular de echivalenţă:

while (1)  for (; ;)


instrucţiune instrucţiune

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ă :

contor <-- val_in

pentru contor = val_in, val_fin, pas execută


| *secvenţă
NU contor <= val_fin DA corpul ciclului for |_□

* Secventa

contor <-- contor + pas

care devine în C :

for (contor = val_in; contor <= val_fin; contor=contor +pas)


*secvenţă
Exemple.

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.

3. Să se afiseze în ordine inversă cifrele unui numar întreg în baza 16.

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;

for (i=0;i<=180; i++)


{
x=i*PI/180.0;
printf("sin(%3d) = %13.10f\t\t\tcos(%3d) = %13.10f\n",i,sin(x),i,cos(x));
if ((i+1) % 23 == 0) // if (i%23==22)
{
printf("Actionati o tasta pentru a continua ...\n");
getch();
}
}
return 0;
}
3. Să se afiseze în ordine inversă cifrele unui numar întreg în baza 16

#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;
}

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