Sunteți pe pagina 1din 18

Echipamente programabile – Laborator 7 & 8

George Mois
November 12, 2021

Abstract
Procesul ”build”, analiza codului, numere reale s, i preluarea de date
de la utilizator.

1 Introducere
Un compilator este un program pe calculator care transformă codul sursă (scris
ı̂ntr-un anumit limbaj sursă) ı̂ntr-un alt limbaj numit limbaj t, intă. De obicei,
dar nu ı̂ntotdeauna, limbajul sursă este un limbaj de programare pe care ı̂l
ı̂nt, eleg oamenii (programatorii), cum sunt Java, C, Scala, sau altele. De obicei,
dar nu ı̂ntotdeauna, limbajul t, intă constă ı̂n cod obiect care poate fi executat de
către o platformă hardware (ex. x86-32, x86-64, ARM7, powerpc, etc.). Cu alte
cuvinte, un compilator traduce dintr-un limbaj ı̂ntr-un altul. Un compilator nu
este un obiect magic, ci este un program ca s, i oricare altul s, i este scris ı̂ntr-un
anumit limbaj de programare (limbajul de implementare) de către programatori.
O problemă la care s-ar putea gândi cineva este modul ı̂n care am putea scrie
un compilator fără a avea la ı̂ndemâna un compilator. Acest lucru este posibil,
dar ı̂n ziua de astăzi avem o gamă largă de compilatoare pe care le putem folosi.
Compilatoarele au un set comun de componente:

• front-end
– partea care se ocupă de corectitudinea programului ı̂n ceea ce prives, te
semantica
– are ca rezultat o reprezentare intermediară a programului de intrare
– lexical checking, syntax checking, type checking, etc.
• middle-end
– optimizarea programului prin rescrierea reprezentării intermediare
– ex. dead code removal, constant propagation, etc.
• back-end
– translatarea reprezentării intermediare la limbajul t, intă adăugând
optimizări specifice platformei folosite
– ex. limbaj de asamblare pentru platforma t, intă

1
Înainte s, i după compilare mai sunt necesari o serie de pas, i pentru a ajunge
de la sursă la t, intă. Înainte de compilare s-ar putea să fie nevoie să rulăm codul
ı̂ntr-un preprocesor care pregătes, te codul pentru compilare (ı̂n C, preprocesorul
rezolvă toate directivele preprocesor – #directivă). După compilare este nevoie
să combinăm diferite obiecte compilate cu bibliotecile necesare ı̂ntr-un program
compilat, iar mai apoi să asamblăm (assemble) codul de asamblare ı̂n as, a fel
ı̂ncât el să poată fi rulat de către platforma hardware sau de către sistemul de
operare. Aces, ti din urmă pas, i sunt executat, i de un linker sau un link editor.
As, adar, procesul de build include pas, ii enumerat, i mai sus, s, i anume: pre-
procesare, compilare s, i link editare. Astfel, compilarea este ceea ce se ı̂ntâmplă
ı̂ntre preprocesare s, i link editare, doar că, datorită faptului că ı̂n general se
foloses, te un singur tool pentru a lansa ı̂ntregul proces de build, termenul com-
pilare este de foarte multe ori folosit pentru a reprezenta procesul de build.

1.1 Procesul build ı̂n C


• preprocesorul C transformă codul care include #directive ı̂n cod ı̂n care
acestea au fost executate
• compilatorul C transformă codul fără directive preprocesor ı̂n fis, iere obiect
suportate de către linker

– poate fi folosit pentru a executa s, i ceilalt, i pas, i (preprocesare s, i link


editare)
– poate lansa ı̂ntreg procesul de build
• linkerul preia obiectele compilate s, i le transforma ı̂ntr-un program compi-
lat

Astfel, este posibil să rezumăm build la două trepte (stages):

• compilation stage,
• linking stage.

1.2 Directive preprocesor s, i preprocesorul


Limbajul C suportă un număr de directive meta-limbaj (limbaj folosit pentru
definirea unui alt limbaj) cu sintaxa:
#d i r e c t i v a
Standardul C nu specifică faptul că procesorul trebuie să fie un program
separat, ci doar defines, te pasul al patrulea al translatării codului C (https://
riptutorial.com/c/example/28469/the-translation-phases) ca: ”macro
expansion and directive handling.”
Principalele caracteristici ale C implementate ca s, i directive sunt:

• includerea fis, ierelor – #include,


• macro – #define,

2
• compilare condit, ională – #if,#ifdef,#ifndef s, i altele.

Cel mai des se foloses, te prima dintre acestea, o directivă include referind un
fis, ier extern prin nume. Directiva este ı̂nlocuită de cont, inutul fis, ierului referit ca
s, i cum acesta ar fi fost scris la locat, ia ei. Principalul beneficiu este factorizarea,
care permite reutilizarea cont, inutului unui fis, ier, fiind necesară ment, inerea unei
singure copii a acestuia. Despre ultimele două directive vom vorbi ı̂ntr-unul din
laboratoarele următoare.
Fis, ierul produs de către preprocesor are adnotări intercalate de forma
# nr rand ”fisier” ..., care specifica provenient, a fiecărei linii de cod. Acest
lucru este necesar pentru raportarea erorilor s, i a atent, ionărilor ı̂n cazul ı̂n care
acestea apar după ce compilatorul verifică semantica limbajului din cod. Pentru
a avea un sens pentru programator, locat, iile erorilor s, i a atent, ionărilor trebuie
să indice liniile din fis, ierele pe care acesta le editează.
În cazul STM32CubeIDE, atunci când accesăm comanda Build, codul trece
prin următoarele transformări:

Figure 1: Dissasembly

• preprocesare
– generarea unui fis, ier cu cont, inutul obt, inut după rularea C:\ >gcc -E main.c
• parsare
– verificarea sintaxei instruct, iunilor din fis, ierul generat la preprocesare
(extensia i );
– componenta ”code generator” va transforma fis, ierul .i ı̂ntr-un fis, ier
.s care cont, ine instruct, iuni ı̂n limbaj de asamblare;
– fiecare instruct, iune C va fi transformată ı̂n mnemonicile corespunzătoare
(Fig. 1);
• producerea de fis, iere obiect
– assemblerul va converti menmonicile ı̂n coduri mas, ină – rezultă fis, iere
obiect, cu extensia .o;

3
– fis, ierul obiect este o colect, ie de coduri mas, ină corespunzătoare codu-
lui C scris init, ial;
• link editarea fis, ierelor obiect

– combinarea fis, ierelor obiect ı̂ntr-un fis, ier executabil – producerea ex-
ecutabilului final;
– ı̂n cazul nostru fis, ierul generat este un fisier .elf
∗ executable and link format;
∗ este un fis, ier executabil s, i un fis, ier de depanare (debug);
• postprocesarea executabilului final – opt, ional
– objcopy tool – generarea unui fis, ier .bin – pure binary executable file.

1.2.1 Exemplu

#include<s t d i o . h>

// G l o b a l v a r s
int g d a t a 1 = −4000;
int g d a t a 2 = 2 0 0 ;
int g r e s u l t = 0 ;

int main ( void )


{
// perform a d d i t i o n
g r e s u l t = g data 1 + g data 2 ;

p r i n t f ( ” R e s u l t i s %d\n” , g r e s u l t ) ;

for ( ; ; ) ;
}

4
Figure 2: Rezultat build

În directorul proiectului (subdirectorul Debug) pot fi văzute toate fis, ierele
generate. Fisierele .i si .s nu sunt generate pentru utilizator. Pentru salvarea
lor, trebuie adăugat flag-ul din Figura 3.

Figure 3: Salvare fis, iere intermediare

Fis, ierele .s cont, in mnemonici (instruct, iuni generate pentru fiecare instruct, iune
C) specifice arhitecturii procesorului (Instruction Set Document of ARM Cortex-
M). Fisierele .o sunt generate ı̂n subdirectorul Src, dar cont, in cod mas, ină care
nu poate fi vizualizat cu editoarele text.

5
2 Analiza codului embedded
2.1 Microcontrolere
Un microcontroler este un computer plasat ı̂ntr-un singur cip. Acest lucru face
ca memoria, viteza, interfet, ele externe, s, i altele, să fie mult sub performantele
unui calculator personal. Oricum, aceasta nu este o problemă de vreme ce
microcontrolerele sunt folosite ı̂n aplicat, ii embedded, unde doar o parte din
funct, ionalităt, i sunt de interes, ı̂n funct, ie de cerint, e.
Un microcontroler tipic (Figura 4) este format dintr-un procesor, memorii
volatile s, i nevolatile, pini de intrare s, i de ies, ire, periferice, ceasuri, magistrale,
toate ı̂n aceeas, i capsulă (ı̂n acelas, i cip).

Figure 4: Schema bloc a unui microcontroler 8051

Figura 4 prezintă o arhitectură internă a unui microcontroler simplu, s, i


anume 8051 (https://www.tutorialspoint.com/microprocessor/microcontrollers_
8051_architecture.htm). Odată ce un utilizator a scris un program, prin pro-
gramarea microcontrolerului el este stocat ı̂n memoria de program, un tip de
memorie nevolatilă. Programul este constituit din instruct, iuni, care sunt citite
s, i executate de către CPU (central processing unit). Fiecare instruct, iune are
stocat un număr care reprezintă locat, ia ei ı̂n memoria programului. Adresele
fiecărei instruct, iuni pot fi comunicate altor părt, i ale microprocesorului prin
intermediul magistralei de adrese. Memoria de date, care este volatilă, este
utilizată pentru a stoca temporar unele date. Programele lucrează cu date, iar
acestea se află ı̂n acestă memorie. Utilizarea sa este legată ı̂n principal de timpul
de rulare al programului. Memoria volatilă pierde datele stocate atunci când
alimentarea sistemului este ı̂ntreruptă ı̂n timp ce cea nevolatilă păstrează datele
stocate s, i ı̂n această situat, ie. Datele sunt transmise folosind magistrala de date.

6
2.2 Memoria pentru cod
Programul este format din instruct, iuni s, i din datele constante ale programului.
Există diferite tipuri de memorie pentru stocarea codului.

• ROM – Read Only Memory


– MPROM – Mask Programmable Read Only Memory
∗ programabilă o singură dată
– EPROM – Ultraviolet Erasable Programmable ROM
∗ programabilă o singură dată
∗ teoretic reprogramabilă
∗ proces de s, tergere a datelor de lungă durată s, i ı̂nvechit
∗ s, tergerea presupune expunere la raze ultraviolete
– EEPROM – Electrically Erasable Programmable ROM
∗ stergere s, i scriere (relativ) rapide
• OTP – On Time Programmable
– programabilă o singură dată
• Flash
– stergere s, i scriere (relativ) rapide
• FRAM – Ferroelectric Random Access memory
– scumpă dar rapidă

2.3 Exemplu
Continutul consolei la lansarea comenzii Debug ı̂n cazul STM32CubeIDE.

S T M i c r o e l e c t r o n i c s ST−LINK GDB s e r v e r . V e r s i o n 5 . 5 . 0
Copyright ( c ) 2 0 1 9 , S T M i c r o e l e c t r o n i c s . A l l r i g h t s r e s e r v e d .

S t a r t i n g s e r v e r with t h e f o l l o w i n g options :
P e r s i s t e n t Mode : Disabled
Logging L e v e l : 1
L i s t e n Port Number : 61234
S t a t u s R e f r e s h Delay : 15 s
Verbose Mode : Disabled
SWD Debug : Enabled
InitWhile : Enabled

Waiting f o r debugger c o n n e c t i o n . . .
Debugger c o n n e c t e d

7
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
STM32CubeProgrammer v2 . 4 . 0
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

ST−LINK SN : 066 BFF323532543457245225


ST−LINK FW : V2J36M26
Voltage : 3 . 2 7V
SWD f r e q : 4000 KHz
Connect mode : Under Reset
Reset mode : Hardware r e s e t
D ev ic e ID : 0 x433
D ev ic e name : STM32F401xD/E
Flash s i z e : 512 KBytes
D ev ic e type : MCU
D ev ic e CPU : Cortex−M4

Memory Programming . . .
Opening and p a r s i n g f i l e : ST−LINK GDB server a06772 . s r e c
File : ST−LINK GDB server a06772 . s r e c
Size : 5004 Bytes
Address : 0 x08000000

E r a s i n g memory c o r r e s p o n d i n g t o segment 0 :
E r a s i n g i n t e r n a l memory s e c t o r 0
Download i n P r g o r e s s :

F i l e download c o m p l e t e
Time e l a p s e d d u r i n g download o p e r a t i o n : 0 0 : 0 0 : 0 0 . 8 2 8

Verifying . . .

Download v e r i f i e d s u c c e s s f u l l y

8
2.4 Folosirea Memory Browser
În Memory Browser (Window - Show View - Memory Browser ) va trebui să
tastăm adresa de bază a memoriei program a microcontrolerului folosit (ı̂n man-
ualul de referint, ă a microcontrolerului folosit - sect, iunea referitoare la memoria
Flash).

2.5 Exercit, iu
Care este adresa de bază a memoriei principale ı̂n Flash pentru microcontrolerul
folosit? Vizualizat, i memoria ı̂ncepând de la această locat, ie folosind Memory
Browser. Afis, area valorilor se face cuvânt cu cuvânt, adică ı̂n grupuri de cate
32 de bit, i.

Figure 5: Continutul memoriei Flash

Pentru afis, are octet cu octet folosit, i meniul contextual din Memory Browser :
Columns - 1, Cell Size 1 byte.

2.6 Memoria pentru date


Memoria de date este SRAM.

2.7 Exercit, iu
Găsit, i adresa memoriei SRAM din manualul de referint, ă s, i vizualizat, i memoria
ı̂ncepând de la această adresă. Schimbat, i modul de afis, are al memoriei la Cell
Size 4 bytes s, i Radix la Signed Integer. Ce valoare avet, i la prima locat, ie?

3 Analiza fisierului .elf


Prin programare, codul ajunge ı̂n memoria Flash. Atunci, cum au ajuns valorile
variabilelor noastre ı̂n memoria SRAM?
Pentru a afla răspunsul la ı̂ntrebarea de mai sus navigat, i ı̂n directorul Debug
al proiectului. Deschidet, i cu un editor text fis, ierul cu extensia .list. La ı̂nceputul
acestuia sunt prezentate sect, iunile fis, ierului .elf.
.text este sect, iunea care cont, ine toate codurile mas, ină.

9
.rodata este sect, iunea care cont, ine datele ”read only” ale programului.
.data este sect, iunea care cont, ine datele programului. Aici avem o coloana
care se numes, te LMA (Load Memory Address) s, i una care se numes, te VMA
(Virtual Memory Address). LMA este sursa din Flash, iar VMA este destinat, ia
datelor ı̂n SRAM. Valorile sunt copiate din LMA ı̂n VMA de către codul din
fis, ierul Startup care rulează pe microcontroler, mai exact de către rutina Re-
set Handler (cod de asamblare). Reset Handler este prima rutină executată de
către hardware la ı̂nceputul operării microcontrolerului.

3.1 Exercit, iu
Vizualizat, i cont, inutul memoriei de la adresa LMA. Găsit, i Reset Handler ı̂n
fisierul Startup. Observat, i că la final, această funct, ie apelează funct, ia main.

4 Dezasamblare – Disassembly
Aceasta este funct, ionalitatea IDE-ului care permite traducerea din cod mas, ină
ı̂ntr-un limbaj de nivel mai ı̂nalt. Acest proces este necesar pentru depanarea
la nivel de instruct, iune (care sunt instruct, iunile generate pentru codul scris de
către noi, optimizare).
Selectati Window - Show View - Dissasembly. În fereastra Dissasembly vom
vedea instruct, iunile ARM corespunzătoare codului scris de către noi.

Figure 6: Code disassembly

Pentru Debug putem activa Breakpoint s, i ı̂n Dissasembly View.

10
O discut, ie mai amănunt, ită despre asamblare este ı̂n afara scopului acestui
laborator.

5 Reprezentarea numerelor reale


Numele reale au parte ı̂ntreagă s, i parte zecimală.
Numerele foarte mici sau foarte mari sunt stocate ı̂n memoria calculatoru-
lui conform standardului IEEE 754. Reprezentarea ı̂n virgulă mobilă (floating
point) IEEE 754 este o reprezentare aproximativă a numerelor reale. Aceasta
este folosită pentru reprezentarea numerelor care au o parte fract, ională sau a
celor care nu pot fi reprezentate folosind tipul de date long.

5.1 Exemplu
Numere foarte mici.
Încărcarea unui electron, care are valoarea de -1.60217662 x 10-19 .
Numere foarte mari.
Distante cosmice, ca s, i distanta de la Pământ la Galaxia Andromeda, cea
mai apropiată galaxie de Calea Lactee.
2.5 milioane ani lumina, sau 2.3651826 x 1019 Km
Aceste valori nu pot fi stocate folosind reprezentarea lor binară, ci doar
informat, iile despre ele:

• semn (+ pentru 2.3651826 x 1019 )


• mantisa (2.3651826)
• exponent (19)

5.2 Precizie simplă


Foloses, te 32 de bit, i pentru stocarea datelor:

• semn – 1 bit
• mantisa – 8 bit, i
• exponent (19) – 23 bit, i

5.3 Precizie dublă


Foloses, te 64 de bit, i pentru stocarea datelor, furnizând o reprezentare mai pre-
cisă:

• semn – 1 bit
• mantisa – 11 bit, i
• exponent (19) – 52 bit, i

11
5.4 Numere reale ı̂n C
1. float
• precizie simplă, 32 bit, i
• precizie de până la 6 pozit, ii zecimale
• 1.2 x 10-38 ... 3.4 x 1038
2. double

• precizie dublă, 64 bit, i


• precizie de până la 15 pozit, ii zecimale
• 2.3 x 10-308 ... 1.7 x 10308

Constantele definite cu punct zecimal sunt implicit stocate ca s, i double.

Table 1: Format string


Specificator Descriere Tip de date
de format suportat
%e, sau Scientific notation float, double
%le of float values
%f Floating point float
%lf Floating point double

5.5 Exemplu
Rulat, i codul de mai jos:
#include<s t d i o . h>

int main ( void )


{
// f l o a t number
f l o a t no = 3 2 . 4 3 5 6 7 8 9 8 3 4 9 ;

// p r i n t t h e number i n d i f f e r e n t f o r m a t s
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ”No . i s %f ( 6 d e c i m a l p l a c e s ) . \ n” , no ) ;
// p r i n t up t o 9 d e c i m a l p l a c e s .
p r i n t f ( ”No . i s %0.9 f ( 9 d e c i m a l p l a c e s ) . \ n” , no ) ;
// Loss o f p r e c i s i o n
// p r i n t i n s c i e n t i f i c n o t a t i o n , a p p r o x i m a t e
p r i n t f ( ”No . i s %e ( 6 d e c i m a l p l a c e s ) . \ n” , no ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , approximate , up t o 2 d e c i m a l p o i n t s
p r i n t f ( ”No . i s %0.2 e ( 2 d e c i m a l p l a c e s ) . \ n” , no ) ;

12
// change d a t a t y p e t o d o u b l e
double n o d o u b l e = 3 2 . 4 3 5 6 7 8 9 8 3 4 9 ;
// p r i n t t h e number i n d i f f e r e n t f o r m a t s
// p r i n t up t o 14 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ”No i s %0.14 l f ( double , 14 d e c i m a l p l a c e s ) . \ n” , n o d o u b l e ) ;
//No l o s s o f p r e c i s i o n

// c h a r g e o f an e l e c t r o n , f l o a t number
f l o a t c = −1.60217662 e −19;
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f an e l e c t r o n i s %f ( 6 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 6 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f an e l e c t r o n i s %e ( 6 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t 8 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f an e l e c t r o n i s %0.8 f ( 8 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 8 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f an e l e c t r o n i s %0.8 e ( 8 d e c i m a l p l a c e s ) . \ n” , c ) ;
// Loss o f p r e c i s i o n
// c h a r g e o f e l e c t r o n , change d a t a t y p e t o d o u b l e , s i n c e poor a p p r o x i m a t i o
double c d o u b l e = −1.60217662 e −19;
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f E i s %0.28 l f ( dbl , 28 dec p l a c e s ) . \ n” , c d o u b l e ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 8 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f E i s %0.8 l e ( dbl , 8 dec p l a c e s ) . \ n” , c d o u b l e ) ;

return 0 ;
}

6 Preluarea datelor de la utilizator


6.1 Funct, ia scanf
Funct, ia scanf cites, te datele de intrare din fluxul standard de intrare (stdin)
s, i le interpretează ı̂n funct, ie de formatul furnizat. În cazul nostru, de obicei,
fluxul standard de intrare este tastatura. Datele de intrare sunt convertite
din reprezentările lor externe ı̂n reprezentările interne corespunzătoare s, i sunt
stocate ı̂n variabilele furnizate ca argumente. Sfârs, itul intrării este semnalat
prin apăsarea tastei ENTER.
Prototipul funct, iei scanf este:
int s c a n f ( const char ∗ format [ , &v a r i a b l e 1 ] [ , &v a r i a b l e 2 ] ...);
scanf returnează numărul de elemente citite cu succes.
Formatul pentru scanf este specificat ca un s, ir de caractere cuprins ı̂ntre
ghilimele (”). S, irul de formatare include specificatori de format, care definesc
regulile de conversie s, i au următoarea formă: %[*][width]type, unde:

13
Table 2: Litere de specificatori de format s, i tipurile lor de date corespunzătoare
Letter Data type of input data
c Scans a character (char).
s Scans a character string (char *).
d Scans an integer as a signed decimal number (int).
u Scans an integer as an unsigned decimal number (unsigned int).
o Scans an integer as an octal number.
x, X Scans an integer as a hexadecimal number.
hd, hu Scans an integer as a short decimal number (short int),
unsigned short decimal number (unsigned short int).
ld, lo, lx, lX Scans an integer as long decimal, long octal, long hexadecimal.
f Scans a floating-point number (float).
lf Scans as a double floating-point number (double).
Lf Scans as a long double floating-point number (long double).

– * (opt, ional) – caracter care indică faptul că rezultatul conversiei datelor
de intrare nu va fi atribuit niciunei variabile;
– lăt, ime (opt, ional) – număr zecimal care defines, te lungimea maximă a câmpului
controlat de specificatorul de format;
– tip – una sau două litere care definesc tipul de date al rezultatului con-
versiei.
Literele care definesc tipul datelor de conversie sunt date ı̂n Tabela 2.
Caracterul & ı̂n fat, a numelui fiecărei variabile indică sistemului unde să
stocheze valoarea introdusă. Astfel se oferă adresa variabilei. Nu avem carac-
terul & ı̂n fat, a numelui variabilei dacă variabila este o matrice unidimensională
(ex. sir de caractere).

6.2 Exemplu

#include <s t d i o . h>


int main ( )
{
char t e s t c h a r = ’ \0 ’ ;
char t e s t s t r i n g [ 1 0 ] = ” ” ;
int test integer = 0;
float t e s t f l o a t = 0.0 f ;

p r i n t f ( ” Enter a c h a r a c t e r : ” ) ;
s c a n f ( ”%c ” , &t e s t c h a r ) ;
p r i n t f ( ” C h a r a c t e r e n t e r e d = %c \n” , t e s t c h a r ) ;

p r i n t f ( ” Enter a c h a r a c t e r s t r i n g : ” ) ;
s c a n f ( ”%s ” , t e s t s t r i n g ) ;

14
p r i n t f ( ” C h a r a c t e r s t r i n g e n t e r e d = %s \n” , t e s t s t r i n g ) ;

p r i n t f ( ” Enter an i n t e g e r : ” ) ;
s c a n f ( ”%d” , &t e s t i n t e g e r ) ;
p r i n t f ( ” I n t e g e r v a l u e e n t e r e d = %d\n” , t e s t i n t e g e r ) ;
p r i n t f ( ” O c t a l v a l = %#o , Hex v a l = %#X\n” , t e s t i n t e g e r , t e s t i n t e g e r ) ;

p r i n t f ( ” Enter a f l o a t number : ” ) ;
s c a n f ( ”%f ” , &t e s t f l o a t ) ;
p r i n t f ( ” F l o a t v a l u e e n t e r e d = %f \n” , t e s t f l o a t ) ;
return 0 ;
}
Observat, ie! Dacă folosit, i consola STM32CubeIDE, atunci trebuie sa folosit, i
apeluri de funct, ii fflush(stdout) după fiecare apel al funct, iei printf.
6.3 Citirea de valori multiple ı̂n cadrul aceleias, i funct, ii
scanf

int s c a n f ( ”%d %d %d” ,&a ,&b,& c ) ;


Numerele pot fi introduse unul după altul s, i separate prin ENTER, spat, iu,
sau Tab.
6.4 Exemplu

#include <s t d i o . h>

int main ( )
{
int a , b , c ;

p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;

return 0 ;
}

6.5 Funct, ia getchar


Funct, ia getchar cites, te un caracter din fluxul de intrare standard (stdin) s, i ı̂l
returnează ca un număr ı̂ntreg (cod ASCII). Sfârs, itul intrării este semnalat la
apăsarea tastei ENTER.
Prototipul funct, iei getchar este:
int g e t c h a r ( void ) ;

15
getchar returnează caracterul citit ca o valoare unsigned char convertită
ı̂ntr-o valoare int la execut, ia funct, iei cu succes.
Apelul funct, iei getchar blochează programul până când se apasă o tastă
urmată de apăsarea tastei ENTER. Poate fi folosită pentru blocarea programului
s, i ment, inerea terminalului deschis atunci când se ajunge la finalul funct, iei main.

6.6 Exemplu

#include <s t d i o . h>

int main ( )
{
int a , b , c ;

p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;

p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
getchar ( ) ;
getchar ( ) ;

return 0 ;
}
Observat, ie! Avem nevoie de două instruct, iuni getchar deoarece atunci
când scanf cites, te un număr, caracterul ENTER nu este citit s, i rămâne ı̂n
bufferul de intrare.
O altă variantă pentru blocarea terminalului este cea de mai jos. Aceasta
furnizează o solut, ie generică.
#include <s t d i o . h>

int main ( )
{
int a , b , c ;

p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;

16
p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
while ( g e t c h a r ( ) != ’ \n ’ )
{

}
getchar ( ) ;

return 0 ;
}
6.7 Exercit, iu
Scriet, i un program care calculează media aritmetică a trei numere introduse de
la tastatură.

6.8 Exercit, iu
Scriet, i un program care cites, te 5 caractere de la tastatură s, i afis, ează codul
ASCII corespunzător fiecăruia dintre ele. Scriet, i două variante pentru program,
una care foloses, te scanf s, i una care foloses, te getchar.

6.9 Exemplu
Program pentru aflarea numărului de electroni care generează o anumită sarcină,
folosind notat, ia s, tiint, ifică.
#include <s t d i o . h>

int main ( )
{
double c h a r g e = 0 ;
double chargeE = −1.602 e −19;
double e l e c t r o n s = 0 ;

p r i n t f ( ” Enter t h e c h a r g e : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%l f ” , &c h a r g e ) ;

p r i n t f ( ”The c h a r g e o f an e l e c t r o n i s %l e . \ n” , −1.602 e −19);


f f l u s h ( stdout ) ;

e l e c t r o n s = c h a r g e / chargeE ;

p r i n t f ( ” E l e c t r o n number ( s c i e n t i f i c format ) : %l e . \ n” , e l e c t r o n s ) ;
p r i n t f ( ” E l e c t r o n number : %l f . \ n” , e l e c t r o n s ) ;
f f l u s h ( stdout ) ;

17
p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
while ( g e t c h a r ( ) != ’ \n ’ )
{

}
getchar ( ) ;

return 0 ;
}

6.10 Exercit, iu
Scriet, i un program pentru a calcula s, i afis, a produsul a trei numere ı̂ntregi citite
de la tastatură.

6.11 Exercit, iu
Scriet, i un program pentru a calcula suma, diferent, a s, i produsul unei perechi de
numere reale.

18

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