PC Laborator 3: Diferență între versiuni

De la WikiLabs
(Exerciții)
(Obiective)
 
(Nu s-au afișat 36 de versiuni intermediare efectuate de alți 3 utilizatori)
Linia 1: Linia 1:
== Obiective ==
+
= Obiective =
  
 +
În urma parcurgerii acestui laborator studentul va fi capabil să utilizeze stream-urile standard ale unui proces în Linux și în programe C;
  
Familiarizarea cu noțiunile de bază ale limbajului C: tipuri de date, operatori, dar și folosirea corectă a acestora.
+
= [https://en.wikipedia.org/wiki/Standard_streams Stream-urile standard] ale unui proces =
  
== Tipuri de date ==
+
[[Fișier:standard_streams.png]]
  
Tipurile de date fundamentale ale limbajului C sunt: caractere (char), întregi (int), numere reale
+
După cum se poate observa în schema de mai sus, un proces care rulează într-un sistem de operare dispune de trei stream-uri de intrare-ieșire implicite:
reprezentate în virgulă mobilă cu simplă precizie (float), numere reale reprezentate în virgulă mobilă
+
* Stream-ul standard de intrare ('''stdin''') - este utilizat pentru a introduce date în proces și este implicit atașat tastaturii;
cu dublă precizie (double) şi fără valoare (void).
+
* Stream-ul standard de ieșire ('''stdout''') - este utilizat pentru a extrage rezultate ale execuției procesului și este implicit atașat consolei;
 +
* Stream-ul standard de eroare ('''stderr''') - este tradițional utilizat pentru a extrage informații legate de erorile apărute în execuția procesului și este implicit atașat consolei;
  
{| class="wikitable"
+
Ambele stream-uri de ieșire sunt implicit trimise către consolă, unde utilizatorul procesului le poate vedea. Motivul pentru care sunt două este acela că aceste stream-uri pot fi redirecționate către alte procese sau către fișiere, astfel se poate face o separare simplă și rapidă a mesajelor de eroare, în scopul depanării programului.
! colspan="3" | Tipuri fundamentale
 
|-
 
| style="text-align: center;" |Tip
 
| style="text-align: center;" |Numar de biti
 
| style="text-align: center;" |Domeniu
 
|-
 
| char
 
| style="text-align: center;" | 8
 
| style="text-align: center;" | -127 ... 128
 
|-
 
| int
 
| style="text-align: center" | 16
 
| style="text-align: center;" | -32,768 ... 32,767
 
|-
 
| float
 
| style="text-align: center;" | 32
 
| style="text-align: center;" | Precizie 6 zecimale
 
|-
 
| double
 
| style="text-align: center;" | 64
 
| style="text-align: center;" | Precizie 10 zecimale
 
|-
 
| void
 
| style="text-align: center;" | -
 
| style="text-align: center;" | Fără valoare
 
  
|}
+
Toate aceste stream-uri sunt formatate pe octet (byte). În cele mai multe din cazuri, acești octeți reprezintă caractere codate [https://en.wikipedia.org/wiki/ASCII ASCII].
  
Exceptând tipul void, tipurile fundamentale pot fi precedate de diferiţi modificatori.
+
== Scrierea pe '''stdout''' ==
Un '''modificator''' se utilizează pentru a adapta cât mai convenabil tipul de bază la situație.
 
  
=== Tipuri reale(float,double) și virgulă mobilă ===
+
Pentru a scrie pe stream-ul standard de ieșire se folosește generic funcția <code style="color: green">fprintf</code>:
  
<div class="regula">'''<font color="red"> Observatie:</font>''' Principalul dezavantaj al reprezentării în virgulă fixă este faptul că, dacă un număr nu mai încape în spaţiul de biţi rezervat, se pierd cifrele cele mai semnificative.</div>
+
Funcția <code style="color: green">fprintf</code> este definită în header-ul <code style="color: blue">stdio.h</code> (standard input/output) și este implimentată în biblioteca standard a limbajului C. Pentru scrierea pe stream-ul standard de ieșire, se folosește în felul următor:
  
Un număr este reprezentat în virgulă mobilă printr-un cuvant de n biți,conținând:
+
<syntaxhighlight lang="C">
{| class="wikitable"
+
fprintf(stdout, "[template]", val1, val2, ...);
!  | s
+
</syntaxhighlight>
!  | e
 
!  | m
 
|}
 
Unde s este bitul de semn al numarului, cu aceeași convenție ca pentru numere in virgulă fixa(s=0 numar pozitiv si s=1 numar negativ),
 
m este mantisa numarului, reprezentând cifrele semnificative, iar e este exponentul (puterea la care trebuie ridicata o valoare)
 
  
Valoarea numarului real reprezentat in virgula mobila este data de expresia:
 
  
<math> valoare=(-1)^s*m*(baza)^e </math>  
+
<div class="sfat">'''<font color="blue">Observație:</font>''' Deoarece scrierea pe stream-ul standard de ieșire este atât de utilizată, s-a creat o funcție specială pentru aceasta, numită <code style="color: green">printf</code>:
 +
<syntaxhighlight lang="C">
 +
printf("[template]", val1, val2, ...);
 +
</syntaxhighlight>
 +
</div>
  
<div class="regula">'''<font color="red"> Observatie:</font>''' Cea mai utilizată bază este baza 2. Mantisa însă trebuie să îndeplinească condiția: 1 > m >= 1/baza
+
''Template''-ul reprezintă o secvență de caractere care specifică ce și cum dorim să afișam în consolă. Acest template poate conține două tipuri de caractere:
</div>
+
# Un caracter oarecare, diferit de caracterul ''procent'' (<code>%</code>), se afișează ca atare pe ecran.
 +
# Un ''pattern'', reprezintă o secvență de caractere care începe cu ''procent'' (<code>%</code>) și se termină cu un specificator de format.
  
== Modificatori ==
+
Un ''pattern'' pentru <code style="color: green">fprintf</code>, respectiv <code style="color: green">printf</code> urmează următorul prototip:
  
 +
%[flags][width][.precision][length]specifier
  
* signed
+
unde caracterul ''specifier'' de la sfârșit este componenta cea mai importantă deoarece definește ce caractere sunt extrase din stream, interpretarea lor, și tipul de date corect al argumentului corespunzător:
* unsigned
 
* long
 
* short
 
  
 
{| class="wikitable"
 
{| class="wikitable"
! colspan="3" | Folosirea modificatorilor
+
! ''specifier'' !! Afișare !! Exemplu
 
|-
 
|-
| style="text-align: center;" |Tip
+
|i, d || întreg decimal cu semn || -694
| style="text-align: center;" |Numar de biti
 
| style="text-align: center;" |Domeniu
 
 
|-
 
|-
| unsigned char
+
|u || întreg decimal fără semn || 1659
| style="text-align: center;" | 8
 
| style="text-align: center;" | 0 ... 255
 
 
|-
 
|-
| unsigned long int
+
|o || întreg în baza 8 || 254
| style="text-align: center" | 16
+
|-
| style="text-align: center;" | 0 ... 4,294,967,295
+
|x || întreg în baza 16 (litere mici) || 1af43
 +
|-
 +
|X || întreg în baza 16 (litere mari) || 1AF43
 +
|-
 +
|f || număr în virgulă mobilă || 23.65
 +
|-
 +
|e || număr în virgulă mobilă, notație științifică (mantisa - exponent), litere mici || 3.9265e+2
 +
|-
 +
|E || număr în virgulă mobilă, notație științifică (mantisa - exponent), litere mari || 3.9265E+2
 +
|-
 +
|g || număr în virgulă mobilă, cea mai scurtă reprezentare (%f sau %e) || 23.65
 +
|-
 +
|G || număr în virgulă mobilă, cea mai scurtă reprezentare (%F sau %E) || 23.65
 +
|-
 +
|a || număr în virgulă mobilă în baza 16, litere mici || -0xc.90fep-2
 +
|-
 +
|A || număr în virgulă mobilă în baza 16, litere mari || -0xc.90FEP-2
 +
|-
 +
|c || caracter || a
 +
|-
 +
|s || șir de caracere || castravete
 +
|-
 +
|p || adresă în memorie (pointer) || 00000000b8000000
 +
|-
 +
|n || nu se printează nimic || numărul de caractere citit până în momentul curent este stocat în variabila specificată
 +
|-
 +
|% || % ||Un caracter % urmat de alt caracter % afișează un singur %.
 
|}
 
|}
  
Diferenţa dintre întregii signed şi unsigned constă în interpretarea diferită a bitului semnificativ (adică cel mai din stânga).
+
''Pattern''-ul poate conține și sub-specificatori: ''flags'', ''width'', ''.precision'' și ''modifiers'' (în această ordine), care sunt opționale și respectă următoarele reguli:
Pentru un întreg signed, se va considera bitul semnificativ ca fiind bit de semn.
 
Dacă acest bit este 0 atunci numărul este pozitiv iar dacă este 1 atunci este negativ.
 
  
<div class="regula">'''<font color="red"> Observatie:</font>''' Bit-ul reprezintă cea mai mică unitate de informaţie dintr-un calculator. Denumirea de bit provine de la „binary digit” adică număr binar. Sistemul binar este un sistem de numeraţie unde totul se reprezintă doar cu 1 şi 0.
+
{| class="wikitable"
Simbolul pentru un bit este "b",a nu se confunda cu byte "B" care reprezintă o grupare de 8 biți.</div>
+
! ''flag'' !! Descriere
 +
|-
 +
| - || Aliniere la stânga cu dimensiunea dată de câmpul ''width'' (implicit aliniera este la dreapta). Vezi descrierea lui ''width''.
 +
|-
 +
| + || Forțează printarea unui semn (+ sau -), chiar și pentru valori pozitive (implicit se afișează semnul doar pentru numere negative).
 +
|-
 +
| (spațiu) || Dacă semnul nu se afișează, în locul lui se inserează un spațiu.
 +
|-
 +
| # || Folosit cu o, x, sau X, printează 0, 0x sau respectiv 0X în fața valorii, pentru numere diferite de 0. Folosit cu a, A, e, E, f, g sau G, forțează printarea punctului decimal, chiar dacă după el nu mai urmează alte cifre. Implicit, dacă după punct nu mai urmează alte cifre (numărul este întreg), punctul nu se mai afișează.
 +
|-
 +
| 0 || Când se specifică o dimensiune (''width''), numărul se completează la stânga cu 0 în loc de spațiu. Vezi descrierea lui ''width''.
 +
|}
  
== Operatori ==
+
{| class="wikitable"
 +
! ''width'' !! Descriere
 +
|-
 +
| (număr) || Numărul minim de caractere care trebuie scrise. Dacă lungimea numărului este mai mică decât această valoare, el este completat cu spații. Dacă lungimea este mai mare, numărul este afișat în întregime.
 +
|-
 +
| * || Valoarea ''width'' nu este specificată în string-ul ''format'', ci într-un argument suplimentar al funcției, care precede argumentul ce trebuie formatat.
 +
|}
  
'''Operatorii aritmetici''':
+
{| class="wikitable"
* +  adunarea
+
! ''.precision'' !! Descriere
* - scăderea
+
|-
* *  înmulţirea
+
| (număr) || Pentru valori întregi (d, i, o, u, x, X), ''precision'' specifică numărul minim de cifre care trebuie afișate. Dacă numărul ce trebuie afișat este mai scurt decât această valoare, rezultatul este completat la stânga cu cifra 0. Dacă numărul este mai lung, el este afișat în întregime. O precizie 0 înseamnă că pentru valoarea 0 nu se va afișa nimic. Pentru specificatorii a, A, e, E, f și F, aceasta este numărul de cifre ce a fi afișat după punct (implicit valoarea aceasta este 6). Pentru specificatorii g și G, acesta este numărul maxim de cifre semnificative ce trebuie afișat. Pentru s, acesta este numărul maxim de caractere ce trebuie afișat. Implicit toate caracterele sunt afișate, până la întâlnirea unui terminator de string. Dacă punctul este scris fără o valoare pentru precizie, aceasta se consideră implicit 0.
* /  împărţirea (rezultatul împărţirii pentru numere reale, câtul împărţirii pentru numere întregi)
+
|-
* %  modulo (restul împărţirii a două numere întregi)
+
| * || Valoarea ''precision'' nu este specificată în string-ul ''format'', ci într-un argument suplimentar al funcției, care precede argumentul ce trebuie formatat.
* ++ incrementarea (mărirea unei valori cu o unitate)
+
|}
* -- decrementarea (micşorarea unei valori cu o unitate)
 
  
Operatorii de incrementare/decrementare sunt folosiţi pentru creșterea/micşorarea unei valori cu o unitate.
+
Specificatorul ''length'' modifică dimensunea tipului de date. Tabelul următor arată tipurile folosite pentru a interpreta argumentele corespunzătoare, cu și fără specificatorul ''length'' (dacă un alt tip este folosit, se realizează promovarea sau conversia tipului, dacă este posibil):
a++ - incrementare
+
<table class="wikitable">
--a - decrementare
+
<tr><th></th><th colspan="7">specifiers</th></tr>
De asemenea se pot folosi instrucțiuni de pre sau post incrementare/decrementare.
+
<tr><th><i>length</i></th><th><tt>d i</tt></th><th><tt>u o x X</tt></th><th><tt>f F e E g G a A</tt></th><th><tt>c</tt></th><th><tt>s</tt></th><th><tt>p</tt></th><th><tt>n</tt></th></tr>
  '''POST-incrementare: x = a++;''' <syntaxhighlight lang="C">
+
<tr><td><i>(none)</i></td><td class="yes"><tt>int</tt></td><td class="yes"><tt>unsigned int</tt></td><td class="yes"><tt>double</tt></td><td class="yes"><tt>int</tt></td><td class="yes"><tt>char*</tt></td><td class="yes"><tt>void*</tt></td><td class="yes"><tt>int*</tt></td></tr>
x = a;  
+
<tr style="background-color: yellow;"><td><tt>hh</tt></td><td class="yes"><tt>signed char</tt></td><td class="yes"><tt>unsigned char</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>signed char*</tt></td></tr>
a = a + 1;
+
<tr><td><tt>h</tt></td><td class="yes"><tt>short int</tt></td><td class="yes"><tt>unsigned short int</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>short int*</tt></td></tr>
</syntaxhighlight>
+
<tr><td><tt>l</tt></td><td class="yes"><tt>long int</tt></td><td class="yes"><tt>unsigned long int</tt></td><td class="no"></td><td class="yes"><tt>wint_t</tt></td><td class="yes"><tt>wchar_t*</tt></td><td class="no"></td><td class="yes"><tt>long int*</tt></td></tr>
 +
<tr style="background-color: yellow;"><td><tt>ll</tt></td><td class="yes"><tt>long long int</tt></td><td class="yes"><tt>unsigned long long int</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>long long int*</tt></td></tr>
 +
<tr style="background-color: yellow;"><td><tt>j</tt></td><td class="yes"><tt>intmax_t</tt></td><td class="yes"><tt>uintmax_t</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>intmax_t*</tt></td></tr>
 +
<tr style="background-color: yellow;"><td><tt>z</tt></td><td class="yes"><tt>size_t</tt></td><td class="yes"><tt>size_t</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>size_t*</tt></td></tr>
 +
<tr style="background-color: yellow;"><td><tt>t</tt></td><td class="yes"><tt>ptrdiff_t</tt></td><td class="yes"><tt>ptrdiff_t</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="yes"><tt>ptrdiff_t*</tt></td></tr>
 +
<tr><td><tt>L</tt></td><td class="no"></td><td class="no"></td><td class="yes"><tt>long double</tt></td><td class="no"></td><td class="no"></td><td class="no"></td><td class="no"></td></tr>
 +
</table>
  
  '''PRE-incremenatare: x = ++a;''' <syntaxhighlight lang="C">
+
<div class="sfat">'''<font color="blue">Observație:</font>''' Tabelele de mai sus pot fi înțelese abia după parcurgerea cursurilor și laboratoarelor legate de tipuri și dimensiuni de date. Ele vor fi folosite pe parcursul cursului drept referință și nu trebuie memorate.</div>
a = a + 1;
 
x = a;
 
</syntaxhighlight>
 
  
'''Operatori relaţionali şi logici''':
+
<div class="sfat">'''<font color="blue">Observație:</font>''' Rândurile marcate cu galben au fost introduse în standardul C99.</div>
  
* > - mai mare
+
== Scrierea pe '''stderr''' ==
* >= -mai mare egal
 
* <  -mai mic
 
* <= -mai mic egal
 
* = -egal
 
* != -diferit
 
* && -si
 
* || -sau
 
* !  -negare
 
  
=== Exemplu operatori relaționari și logici===
+
Scrierea pe stream-ul standard de eroare se face identic cu scrierea pe '''stdout''', specificând numele streamului ca prim argument al funcției <code style="color: green">fprintf</code>:
  
 
<syntaxhighlight lang="C">
 
<syntaxhighlight lang="C">
 +
fprintf(stderr, "[template]", val1, val2, ...);
 +
</syntaxhighlight>
  
int a=2,b=6,c=0,result;
 
 
result=!(a < c) && (a<b) || (b<=c);
 
  
printf("%d\n",result);
+
<div class="sfat">'''<font color="blue">Observație:</font>''' Nu se poate folosi funcția <code style="color: green">printf</code> pentru scrierea pe stream-ul standard de eroare.</div>
  
</syntaxhighlight>
+
== Citirea din '''stdin''' ==
  
 +
Pentru a citi de pe stream-ul standard de intrare se folosește generic funcția <code style="color: green">fscanf</code>:
  
'''Operatori la nivel de bit''':
+
Funcția <code style="color: green">fscanf</code> este definită în header-ul <code style="color: blue">stdio.h</code> (standard input/output) și se folosește în felul următor:
  
Avem şase operatori la nivel de bit,aceștia se aplică tipurilor char,int,long signed/unsigned,iar rezultatul este un întreg.
+
<syntaxhighlight lang="C">
Operatorii la nivel de bit sunt următorii.
+
fscanf(stdin, "[template]", val1, val2, ...);
* & - AND (şi logic)
+
</syntaxhighlight>
* | - OR (sau logic)
 
* ^ - XOR (sau exclusiv)
 
* << - deplasare la stânga
 
* >> - deplasare la dreapta
 
* ~ - negare la nivel de bit
 
  
=== Exemplu operator AND ===
 
Dorim să aflăm valoarea bitului de pe poziția 2 a numarului 20.Pentru a afla valorea acestui bit se poate realizare operația AND (&) între numărul dorit și un alt număr binar care are doar pe poziția dorită (în cazul nostru 2) valoarea 1,astfel dacă rezultatul este diferit de 0 atunci inițial am avut bitul 1, respectiv 0 dacă bitul a fost 0.
 
  
 +
<div class="sfat">'''<font color="blue">Observație:</font>''' Deoarece citirea de pe stream-ul standard este atât de utilizată, s-a creat o funcție specială pentru aceasta, numită <code style="color: green">scanf</code>:
 
<syntaxhighlight lang="C">
 
<syntaxhighlight lang="C">
 +
scanf("[template]", val1, val2, ...);
 +
</syntaxhighlight>
 +
</div>
  
int a=20; /*  1 0 1 0 0 */
+
''Template''-ul reprezintă modelul de date pe care ne așteptăm să-l primim de la tastatură. Acesta este un șir de caractere unde fiecare caracter este tratat în următorul fel:
int b=4; /*  0 0 1 0 0 */
+
* '''caracter alb''' - se va citi și ignora din stream orice caractere albe până la întâlnirea unui caracter care nu este alb (caracerele albe includ spații, linie nouă, caractere tab); un singur caracter alb în format validează oricâte caractere albe extrase din stream (inclusiv nici unul).
 +
* '''caracter care nu este alb, exceptând caracterul procent (%)''' - orice caracter care nu este alb (spațiu, linie nouă sau tab) sau care nu face parte dintr-un ''pattern'' (care începe cu %) face ca următorul caracter să fie citit din stream, comparat cu acest caracter din format, iar dacă sunt identice, ele sunt ignorate și se continuă cu următorul caracter; dacă nu sunt identice, <code style="color: green">fscanf</code> se va opri cu eroare;
 +
* '''pattern''' - o secvență care începe cu caracterul procent (<code>%</code>) indică un ''pattern'', utilizat pentru a indica tipul și formatul unei date care trebuie citită și stocată într-una din variabilele specificate în apelul lui <code style="color: green">fscanf</code>.
  
if(a & b) /*  0 0 1 0 0*/
+
''Pattern''-ul pentru <code style="color: green">fscanf</code> urmează următorul prototip:
printf("bitul are valoarea 1\n");
 
else
 
printf("bitul are valoarea 0\n");
 
  
</syntaxhighlight>
+
%[*][width][length]specifier
  
=== Exemplu operator de deplasare << ===
+
unde caracterul ''specifier'' de la sfârșit este componenta cea mai importantă deoarece definește ce caractere sunt extrase din stream, interpretarea lor, și tipul de date corect al argumentului corespunzător:
Operatorii de deplasare (<< şi >>) realizează deplasarea biţilor din operandul stâng cu atâtea
 
poziţii câte sunt indicate de operandul drept.
 
  
<syntaxhighlight lang="C">
+
{| class="wikitable"
 +
! ''specifier'' !! Descriere !! Caractere extrase din stream
 +
|-
 +
|i, u || întreg || Orice număr de cifre, opțional precedate de un semn (+ sau -). Baza zece este considerată implicit (0-9), dar un 0 ca prefix marchează baza ca fiind opt (0-7), iar "0x" ca prefix marchează baza șaisprezece (0-f).
 +
|-
 +
|d || întreg în baza 10 || Orice număr de cifre decimale (0-9), opțional precedate de un semn (+ sau -).
 +
|-
 +
|o || întreg în baza 8 || Orice număr de cifre octale (0-7), opțional precedate de un semn (+ sau -).
 +
|-
 +
|x || întreg în baza 16 || Orice număr de cifre hexazecimale (0-7, a-f, A-F), opțional precedate de "0x" sau "0X", și totul opțional precedat de un semn (+ sau -).
 +
|-
 +
|f, e, g, a (C99) || număr în virgulă mobilă || O serie de cifre decimale, conținând opțional un punct decimal, opțional precedat de un semn (+ sau -), opțional urmat de un caracter e sau E, și un întreg în baza 10. În standardul C99 se acceptă și numere în virgulă mobilă în baza hexazecimală, precedate de 0x sau 0X.
 +
|-
 +
|c || caracter || Următorul caracter. Dacă '''width''' este specificat, se vor citi exact '''width''' caractere și vor fi stocate în locații succesive în vectorul primit ca argument. Nu este adăugat terminator de șir la final.
 +
|-
 +
|s || șir de caractere ||Orice număr de caractere care nu sunt albe, oprindu-se la primul caracter alb înlâlnit. Este automat adăugat terminator de șir la final.
 +
|-
 +
|p || adresă în memorie ||O secvență de caractere reprezentând o adresă în memorie. Formatul folosit depinde de sistem și de implementarea bibliotecii, dar este identic cu cel folosit de formatul %p în <code style="color: green">fprintf</code>.
 +
|-
 +
|[characters] || set de caractere || Orice număr de caractere dintre cele specificate între parantezele drepte. O liniuță (-) care nu este pe prima poziție între paranteze poate produce comportamente nedefinite în unele implementări de bibliotecă standard.
 +
|-
 +
|[^characters] || set de caractere negat || Orice număr de caractere care nu sunt specificate între parantezele drepte.
 +
|-
 +
|n || număr caractere citite || Nu este consumat nimic din stream. Numărul de caractere citit până în momentul curent este stocat în variabila specificată.
 +
|-
 +
|% || % ||Un caracter % urmat de alt caracter % consumă un singur % din stream.
 +
|}
 +
În afară de <code>n</code>, cel puțin un caracter va fi consumat din stream de către oricare alt specificator. Altfel, operația eșuează și citirea se încheie în acel punct.
  
int a= 255; /* 0000 0000 1111 1111*/
+
''Pattern''-ul poate conține de asemenea și sub-specificatori: steluță (*), '''width''' și '''length''' (în această ordine), care sunt opționale și respectă următoarele specificații:
a=a<<3;  /* 0000 0111 1111 1000*/
 
  
</syntaxhighlight>
+
{| class="wikitable"
 +
! sub-specifcator !! Descriere
 +
|-
 +
| * || O steluță ca sub-specifiator indică faptul că data citită nu este stocată într-o variabilă ci este ignorată.
 +
|-
 +
| ''width'' || Specifică numărul maxim de caractere ce vor fi citite din stream de operația curentă.
 +
|-
 +
| ''length'' || Unul din hh, h, l, ll, j, z, t, L. Acest caracter modifică tipul de date așteptat ca argument. Comportamentul acestora este identic cu cel definit la capitolul [[#Scrierea pe stdout]]
 +
|}
  
<div class="regula">'''<font color="red"> Observatie:</font> '''Deplasarea la stânga (<<) completează la dreapta cu 0, deci biţii din stânga se vor pierde.</div>
+
<div class="regula">'''<font color="red">Atenție:</font>''' Variabilele care nu sunt de tip vector sau pointer și sunt folosite în apelul funcției <code style="color: green">fscanf</code> pentru a stoca date din stream, trebuie obligatoriu precedate de operatorul <code>&</code>. Explicația pentru această regulă va fi prezentată în cursul legat de pointeri și adrese de memorie.</div>
  
=== Exemplu operator de negare ~ ===
+
== Exemple ==
Operatorul de negare realizează complementarea biților,biții de 0 se pun 1,iar biții de 1 se pun 0.
 
  
 +
<ol>
 +
<li>
 +
Scrieți următorul program într-un fișier numit <code style="color: blue">exemplu_stdin.c</code>
 
<syntaxhighlight lang="C">
 
<syntaxhighlight lang="C">
 +
#include <stdio.h>
  
int a= 2200; /* 1000 1001 1000*/
+
int main(){
a=~a;       /* 0110 0110 0111*/
+
    int integerValue;
 
+
    fprintf(stdout, "Introduceti o valoare numerica: ");
 +
    fscanf(stdin, "%d", &integerValue);
 +
    printf("Valoarea introdusa este %d\n", integerValue);
 +
    return 0;
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
+
</li>
 +
</ol>
  
 
== Exerciții ==
 
== Exerciții ==
  
# Scrieți un program care să citească un număr în virgulă mobilă de la tastatură și se afișeze, pe linii diferite, în hexazecimal, semnul, mantisa și exponentul.
+
<ol>
# Scrieți un program care să citească de la tastatură un număr întreg și să-l afișeze pe ecran în baza doi, folosind operatori de shiftare și mascare.
+
<li>Citiți de la tastatură două numere întregi și afișați suma lor pe stream-ul standard de ieșire (operatorul pentru sumă este +).</li>
 +
<li>Citiți de la tastatură trei numere întregi și afișați produsul lor pe stream-ul standard de eroare (operatorul pentru produs este *).</li>
 +
<li>Citiți de la tastatură patru numere fracționare. Primele 3 sunt coeficienții unui polinom de gradul II. Al patrulea este valoarea unui punct în care se calculează valoarea polinomului. Afișați rezultatul cu două zecimale pe stream-ul standard de ieșire. </li>
 +
<li>Fie următoarea funcție: <code>f(x) = (3.6 * e<sup>x</sup> + cos(2*x)) / (x + 1)</code>. Scrieți un program care să citească o valoarea fracționară de la tastatură, să calculeze valoarea funcției în punctul respectiv și sa afișeze rezultatul pe stream-ul standard de eroare în format științific. Observație: pentru a utiliza funcțiile cos și exp (exponențială), trebuie să adăugați programului vostru, pe prima linie, directiva <code>#include <math.h></code> și să compilați programul adăugând comenzii de compliare flag-ul <code>-lm</code>. Pentru a calcula e<sup>2</sup> se folosește funcția exp: <code>exp(2)</code>.</li>
 +
</ol>

Versiunea curentă din 29 octombrie 2017 21:41

Obiective

În urma parcurgerii acestui laborator studentul va fi capabil să utilizeze stream-urile standard ale unui proces în Linux și în programe C;

Stream-urile standard ale unui proces

Standard streams.png

După cum se poate observa în schema de mai sus, un proces care rulează într-un sistem de operare dispune de trei stream-uri de intrare-ieșire implicite:

  • Stream-ul standard de intrare (stdin) - este utilizat pentru a introduce date în proces și este implicit atașat tastaturii;
  • Stream-ul standard de ieșire (stdout) - este utilizat pentru a extrage rezultate ale execuției procesului și este implicit atașat consolei;
  • Stream-ul standard de eroare (stderr) - este tradițional utilizat pentru a extrage informații legate de erorile apărute în execuția procesului și este implicit atașat consolei;

Ambele stream-uri de ieșire sunt implicit trimise către consolă, unde utilizatorul procesului le poate vedea. Motivul pentru care sunt două este acela că aceste stream-uri pot fi redirecționate către alte procese sau către fișiere, astfel se poate face o separare simplă și rapidă a mesajelor de eroare, în scopul depanării programului.

Toate aceste stream-uri sunt formatate pe octet (byte). În cele mai multe din cazuri, acești octeți reprezintă caractere codate ASCII.

Scrierea pe stdout

Pentru a scrie pe stream-ul standard de ieșire se folosește generic funcția fprintf:

Funcția fprintf este definită în header-ul stdio.h (standard input/output) și este implimentată în biblioteca standard a limbajului C. Pentru scrierea pe stream-ul standard de ieșire, se folosește în felul următor:

fprintf(stdout, "[template]", val1, val2, ...);


Observație: Deoarece scrierea pe stream-ul standard de ieșire este atât de utilizată, s-a creat o funcție specială pentru aceasta, numită printf:
printf("[template]", val1, val2, ...);

Template-ul reprezintă o secvență de caractere care specifică ce și cum dorim să afișam în consolă. Acest template poate conține două tipuri de caractere:

  1. Un caracter oarecare, diferit de caracterul procent (%), se afișează ca atare pe ecran.
  2. Un pattern, reprezintă o secvență de caractere care începe cu procent (%) și se termină cu un specificator de format.

Un pattern pentru fprintf, respectiv printf urmează următorul prototip:

%[flags][width][.precision][length]specifier 

unde caracterul specifier de la sfârșit este componenta cea mai importantă deoarece definește ce caractere sunt extrase din stream, interpretarea lor, și tipul de date corect al argumentului corespunzător:

specifier Afișare Exemplu
i, d întreg decimal cu semn -694
u întreg decimal fără semn 1659
o întreg în baza 8 254
x întreg în baza 16 (litere mici) 1af43
X întreg în baza 16 (litere mari) 1AF43
f număr în virgulă mobilă 23.65
e număr în virgulă mobilă, notație științifică (mantisa - exponent), litere mici 3.9265e+2
E număr în virgulă mobilă, notație științifică (mantisa - exponent), litere mari 3.9265E+2
g număr în virgulă mobilă, cea mai scurtă reprezentare (%f sau %e) 23.65
G număr în virgulă mobilă, cea mai scurtă reprezentare (%F sau %E) 23.65
a număr în virgulă mobilă în baza 16, litere mici -0xc.90fep-2
A număr în virgulă mobilă în baza 16, litere mari -0xc.90FEP-2
c caracter a
s șir de caracere castravete
p adresă în memorie (pointer) 00000000b8000000
n nu se printează nimic numărul de caractere citit până în momentul curent este stocat în variabila specificată
% % Un caracter % urmat de alt caracter % afișează un singur %.

Pattern-ul poate conține și sub-specificatori: flags, width, .precision și modifiers (în această ordine), care sunt opționale și respectă următoarele reguli:

flag Descriere
- Aliniere la stânga cu dimensiunea dată de câmpul width (implicit aliniera este la dreapta). Vezi descrierea lui width.
+ Forțează printarea unui semn (+ sau -), chiar și pentru valori pozitive (implicit se afișează semnul doar pentru numere negative).
(spațiu) Dacă semnul nu se afișează, în locul lui se inserează un spațiu.
# Folosit cu o, x, sau X, printează 0, 0x sau respectiv 0X în fața valorii, pentru numere diferite de 0. Folosit cu a, A, e, E, f, g sau G, forțează printarea punctului decimal, chiar dacă după el nu mai urmează alte cifre. Implicit, dacă după punct nu mai urmează alte cifre (numărul este întreg), punctul nu se mai afișează.
0 Când se specifică o dimensiune (width), numărul se completează la stânga cu 0 în loc de spațiu. Vezi descrierea lui width.
width Descriere
(număr) Numărul minim de caractere care trebuie scrise. Dacă lungimea numărului este mai mică decât această valoare, el este completat cu spații. Dacă lungimea este mai mare, numărul este afișat în întregime.
* Valoarea width nu este specificată în string-ul format, ci într-un argument suplimentar al funcției, care precede argumentul ce trebuie formatat.
.precision Descriere
(număr) Pentru valori întregi (d, i, o, u, x, X), precision specifică numărul minim de cifre care trebuie afișate. Dacă numărul ce trebuie afișat este mai scurt decât această valoare, rezultatul este completat la stânga cu cifra 0. Dacă numărul este mai lung, el este afișat în întregime. O precizie 0 înseamnă că pentru valoarea 0 nu se va afișa nimic. Pentru specificatorii a, A, e, E, f și F, aceasta este numărul de cifre ce a fi afișat după punct (implicit valoarea aceasta este 6). Pentru specificatorii g și G, acesta este numărul maxim de cifre semnificative ce trebuie afișat. Pentru s, acesta este numărul maxim de caractere ce trebuie afișat. Implicit toate caracterele sunt afișate, până la întâlnirea unui terminator de string. Dacă punctul este scris fără o valoare pentru precizie, aceasta se consideră implicit 0.
* Valoarea precision nu este specificată în string-ul format, ci într-un argument suplimentar al funcției, care precede argumentul ce trebuie formatat.

Specificatorul length modifică dimensunea tipului de date. Tabelul următor arată tipurile folosite pentru a interpreta argumentele corespunzătoare, cu și fără specificatorul length (dacă un alt tip este folosit, se realizează promovarea sau conversia tipului, dacă este posibil):

specifiers
lengthd iu o x Xf F e E g G a Acspn
(none)intunsigned intdoubleintchar*void*int*
hhsigned charunsigned charsigned char*
hshort intunsigned short intshort int*
llong intunsigned long intwint_twchar_t*long int*
lllong long intunsigned long long intlong long int*
jintmax_tuintmax_tintmax_t*
zsize_tsize_tsize_t*
tptrdiff_tptrdiff_tptrdiff_t*
Llong double
Observație: Tabelele de mai sus pot fi înțelese abia după parcurgerea cursurilor și laboratoarelor legate de tipuri și dimensiuni de date. Ele vor fi folosite pe parcursul cursului drept referință și nu trebuie memorate.
Observație: Rândurile marcate cu galben au fost introduse în standardul C99.

Scrierea pe stderr

Scrierea pe stream-ul standard de eroare se face identic cu scrierea pe stdout, specificând numele streamului ca prim argument al funcției fprintf:

fprintf(stderr, "[template]", val1, val2, ...);


Observație: Nu se poate folosi funcția printf pentru scrierea pe stream-ul standard de eroare.

Citirea din stdin

Pentru a citi de pe stream-ul standard de intrare se folosește generic funcția fscanf:

Funcția fscanf este definită în header-ul stdio.h (standard input/output) și se folosește în felul următor:

fscanf(stdin, "[template]", val1, val2, ...);


Observație: Deoarece citirea de pe stream-ul standard este atât de utilizată, s-a creat o funcție specială pentru aceasta, numită scanf:
scanf("[template]", val1, val2, ...);

Template-ul reprezintă modelul de date pe care ne așteptăm să-l primim de la tastatură. Acesta este un șir de caractere unde fiecare caracter este tratat în următorul fel:

  • caracter alb - se va citi și ignora din stream orice caractere albe până la întâlnirea unui caracter care nu este alb (caracerele albe includ spații, linie nouă, caractere tab); un singur caracter alb în format validează oricâte caractere albe extrase din stream (inclusiv nici unul).
  • caracter care nu este alb, exceptând caracterul procent (%) - orice caracter care nu este alb (spațiu, linie nouă sau tab) sau care nu face parte dintr-un pattern (care începe cu %) face ca următorul caracter să fie citit din stream, comparat cu acest caracter din format, iar dacă sunt identice, ele sunt ignorate și se continuă cu următorul caracter; dacă nu sunt identice, fscanf se va opri cu eroare;
  • pattern - o secvență care începe cu caracterul procent (%) indică un pattern, utilizat pentru a indica tipul și formatul unei date care trebuie citită și stocată într-una din variabilele specificate în apelul lui fscanf.

Pattern-ul pentru fscanf urmează următorul prototip:

%[*][width][length]specifier 

unde caracterul specifier de la sfârșit este componenta cea mai importantă deoarece definește ce caractere sunt extrase din stream, interpretarea lor, și tipul de date corect al argumentului corespunzător:

specifier Descriere Caractere extrase din stream
i, u întreg Orice număr de cifre, opțional precedate de un semn (+ sau -). Baza zece este considerată implicit (0-9), dar un 0 ca prefix marchează baza ca fiind opt (0-7), iar "0x" ca prefix marchează baza șaisprezece (0-f).
d întreg în baza 10 Orice număr de cifre decimale (0-9), opțional precedate de un semn (+ sau -).
o întreg în baza 8 Orice număr de cifre octale (0-7), opțional precedate de un semn (+ sau -).
x întreg în baza 16 Orice număr de cifre hexazecimale (0-7, a-f, A-F), opțional precedate de "0x" sau "0X", și totul opțional precedat de un semn (+ sau -).
f, e, g, a (C99) număr în virgulă mobilă O serie de cifre decimale, conținând opțional un punct decimal, opțional precedat de un semn (+ sau -), opțional urmat de un caracter e sau E, și un întreg în baza 10. În standardul C99 se acceptă și numere în virgulă mobilă în baza hexazecimală, precedate de 0x sau 0X.
c caracter Următorul caracter. Dacă width este specificat, se vor citi exact width caractere și vor fi stocate în locații succesive în vectorul primit ca argument. Nu este adăugat terminator de șir la final.
s șir de caractere Orice număr de caractere care nu sunt albe, oprindu-se la primul caracter alb înlâlnit. Este automat adăugat terminator de șir la final.
p adresă în memorie O secvență de caractere reprezentând o adresă în memorie. Formatul folosit depinde de sistem și de implementarea bibliotecii, dar este identic cu cel folosit de formatul %p în fprintf.
[characters] set de caractere Orice număr de caractere dintre cele specificate între parantezele drepte. O liniuță (-) care nu este pe prima poziție între paranteze poate produce comportamente nedefinite în unele implementări de bibliotecă standard.
[^characters] set de caractere negat Orice număr de caractere care nu sunt specificate între parantezele drepte.
n număr caractere citite Nu este consumat nimic din stream. Numărul de caractere citit până în momentul curent este stocat în variabila specificată.
% % Un caracter % urmat de alt caracter % consumă un singur % din stream.

În afară de n, cel puțin un caracter va fi consumat din stream de către oricare alt specificator. Altfel, operația eșuează și citirea se încheie în acel punct.

Pattern-ul poate conține de asemenea și sub-specificatori: steluță (*), width și length (în această ordine), care sunt opționale și respectă următoarele specificații:

sub-specifcator Descriere
* O steluță ca sub-specifiator indică faptul că data citită nu este stocată într-o variabilă ci este ignorată.
width Specifică numărul maxim de caractere ce vor fi citite din stream de operația curentă.
length Unul din hh, h, l, ll, j, z, t, L. Acest caracter modifică tipul de date așteptat ca argument. Comportamentul acestora este identic cu cel definit la capitolul #Scrierea pe stdout
Atenție: Variabilele care nu sunt de tip vector sau pointer și sunt folosite în apelul funcției fscanf pentru a stoca date din stream, trebuie obligatoriu precedate de operatorul &. Explicația pentru această regulă va fi prezentată în cursul legat de pointeri și adrese de memorie.

Exemple

  1. Scrieți următorul program într-un fișier numit exemplu_stdin.c
    #include <stdio.h>
    
    int main(){
        int integerValue;
        fprintf(stdout, "Introduceti o valoare numerica: ");
        fscanf(stdin, "%d", &integerValue);
        printf("Valoarea introdusa este %d\n", integerValue);
        return 0;
    }
    

Exerciții

  1. Citiți de la tastatură două numere întregi și afișați suma lor pe stream-ul standard de ieșire (operatorul pentru sumă este +).
  2. Citiți de la tastatură trei numere întregi și afișați produsul lor pe stream-ul standard de eroare (operatorul pentru produs este *).
  3. Citiți de la tastatură patru numere fracționare. Primele 3 sunt coeficienții unui polinom de gradul II. Al patrulea este valoarea unui punct în care se calculează valoarea polinomului. Afișați rezultatul cu două zecimale pe stream-ul standard de ieșire.
  4. Fie următoarea funcție: f(x) = (3.6 * ex + cos(2*x)) / (x + 1). Scrieți un program care să citească o valoarea fracționară de la tastatură, să calculeze valoarea funcției în punctul respectiv și sa afișeze rezultatul pe stream-ul standard de eroare în format științific. Observație: pentru a utiliza funcțiile cos și exp (exponențială), trebuie să adăugați programului vostru, pe prima linie, directiva #include <math.h> și să compilați programul adăugând comenzii de compliare flag-ul -lm. Pentru a calcula e2 se folosește funcția exp: exp(2).