manual c 2

280
Theory without practice is useless; practice without theory is blindRoger Bacon Acest manual este structurat astfel încît elementele limbajului C să fie prezentate într-o manieră unitară. Primul capitol face o scurtă introducere şi prezintă şase programe C. Următoarele cinci capitole descriu elementele primare ale limbajului C: tipurile fundamentale de date, definiţii de constante şi variabile, instrucţiuni, funcţii. Capitolul şapte descrie strategii de organizare a activităţii de programare pentru elaborarea programelor mari, compuse din mai multe module. Tot în acest capitol sînt prezentate şi directivele de compilare cele mai folosite. Capitolele opt şi nouă descriu elemente avansate ale limbajului C: pointeri şi structuri. Capitolele zece şi unsprezece trec în revistă funcţiile cele mai des utilizate definite în biblioteca standard, împreună cu cîteva programe demonstrative. Am selectat doar funcţiile definite de mai multe standarde (în primul rînd ANSI C), pentru a garanta o portabilitate cît mai mare. Acest manual a fost conceput pentru a servi ca document care să poată fi consultat de programatori în elaborarea proiectelor, şi nu pentru a fi memorat. Manualul nu este o prezentare exhaustivă a limbajului C; am selectat strictul necesar care să permită unui programator să elaboreze programe eficiente şi uşor de întreţinut. ______________________________________________________________________________________________________ _____________________ 3

description

nn

Transcript of manual c 2

Manual Limbaj C

Theory without practice is useless; practice without theory is blind

Roger Bacon

Acest manual este structurat astfel nct elementele limbajului C s fie prezentate ntr-o manier unitar. Primul capitol face o scurt introducere i prezint ase programe C.

Urmtoarele cinci capitole descriu elementele primare ale limbajului C: tipurile fundamentale de date, definiii de constante i variabile, instruciuni, funcii. Capitolul apte descrie strategii de organizare a activitii de programare pentru elaborarea programelor mari, compuse din mai multe module. Tot n acest capitol snt prezentate i directivele de compilare cele mai folosite. Capitolele opt i nou descriu elemente avansate ale limbajului C: pointeri i structuri.

Capitolele zece i unsprezece trec n revist funciile cele mai des utilizate definite n biblioteca standard, mpreun cu cteva programe demonstrative. Am selectat doar funciile definite de mai multe standarde (n primul rnd ANSI C), pentru a garanta o portabilitate ct mai mare.

Acest manual a fost conceput pentru a servi ca document care s poat fi consultat de programatori n elaborarea proiectelor, i nu pentru a fi memorat. Manualul nu este o prezentare exhaustiv a limbajului C; am selectat strictul necesar care s permit unui programator s elaboreze programe eficiente i uor de ntreinut.

Deoarece avem convingerea c cea mai bun explicaie este un program funcional, exemplele din acest manual pot fi rulate pe orice mediu de programare C i sub orice sistem de operare.

Ca o ultim observaie amintim recomandarea fcut de nii creatorii limbajului: cea mai bun metod de nvare este practica.

Dac constructorii ar fi construit cldiri aa cum scriu programatorii programe, atunci prima ciocnitoare care ar fi venit ar fi distrus civilizaia

(din Legile lui Murphy)

1. Generaliti asupra limbajului C

1.1. Introducere

Limbajul C a fost creat la nceputul anilor '70 de ctre Brian W Kernigham i Dennis M Ritchie de la Bell Laboratories New Jersey, fiind iniial destinat scrierii unei pri din sistemul de operare Unix. Lucrarea The C Programming Language a celor doi autori, aprut n mai multe versiuni, a rmas cartea de referin n domeniu, impunnd un standard minimal pentru orice implementare.

Caracteristicile distinctive ale limbajului au fost clar definite de la nceput, ele pstrndu-se n toate dezvoltrile ulterioare:

portabilitate maxim;

structurare;

posibilitatea efecturii operaiilor la nivelul mainii cu pstrarea caracteristicilor unui limbaj evoluat.

Limbajul C este un limbaj de programare universal, caracterizat printr-o exprimare concis, un control modern al fluxului execuiei, structuri de date, i un bogat set de operatori.

Limbajul C nu este un limbaj de nivel foarte nalt i nu este specializat pentru un anumit domeniu de aplicaii. Absena restriciilor i generalitatea sa l fac un limbaj mai convenabil i mai eficient dect multe alte limbaje mai puternice.

Limbajul C permite scrierea de programe bine structurate, datorit construciilor sale de control al fluxului: grupri de instruciuni, luri de decizii (if), cicluri cu testul de terminare naintea ciclului (while, for) sau dup ciclu (do) i selecia unui caz dintr-o mulime de cazuri (switch).

Limbajul C permite lucrul cu pointeri i are o aritmetic de adrese puternic. Permite de asemenea definirea de ctre programator a unor tipuri structurate de date, care pot fi orict de complexe.

Limbajul C nu are operaii care prelucreaz direct obiectele compuse cum snt irurile de caractere, mulimile, listele sau masivele, considerate fiecare ca o entitate. Limbajul C nu prezint faciliti de alocare a memoriei altele dect definiia static sau disciplina de stiv relativ la variabilele locale ale funciilor. n sfrit, limbajul C nu are faciliti de intrare / ieire i nici metode directe de acces la fiiere. Toate aceste mecanisme de nivel nalt snt realizate prin funcii explicite.

Dei limbajul C este, aadar, un limbaj de nivel relativ sczut, el este un limbaj agreabil, expresiv i elastic, care se preteaz la o gam larg de programe. C este un limbaj restrns i se nva relativ uor, iar subtilitile se rein pe msur ce crete experiena n programare.

1.2. Reprezentarea valorilor numerice

O valoare ntreag se reprezint n baza doi:

105(10) ( 01101001(2)32750(10) ( 0111111111101110(2)

O valoare ntreag negativ se reprezint n complement fa de doi astfel: dac pentru reprezentare se folosesc w bii, a (0(a(2w1) se reprezint ca i valoarea 2wa, din care se pstreaz cele mai puin semnificative w poziii. Astfel operaiile de adunare (cele mai frecvente operaii elementare) se efectueaz ntr-un timp foarte scurt, fr a fi nevoie de alte operaii auxiliare.

105(10) ( 10010111(2)32750(10) ( 1000000000010010(2)

Codificarea ASCII a caracterelor folosete 8 bii (un octet):

codul caracterului spaiu este 32;

codurile cifrelor snt valori ntre 48 i 57;

codurile literelor mari snt valori ntre 65 i 90;

codurile literelor mici snt valori ntre 97 i 122.

Exist dou convenii de memorare a valorilor ntregi care necesit mai muli octei:

Big Endian, octeii apar n ordinea cifrelor semnificative: valoarea 127AC450(16) se memoreaz astfel: 12, 7A, C4, 50;

Little Endian, octeii apar n ordine invers: valoarea 127AC450(16) se memoreaz astfel: 50, C4, 7A, 12.

Calculatoarele bazate pe procesoare Intel folosesc a doua convenie de reprezentare. Detalii despre acest subiect se afl la:

http://www.cs.umass.edu/~verts/cs32/endian.html

O valoare real nenul se reprezint normalizat n virgul mobil, descompus n dou elemente: o parte subunitar n intervalul [0.5,1) i o putere a bazei doi. Pentru semn se folosete o poziie distinct (0 pozitiv, 1 negativ).

Exemplu: 4.625 ( 100.101(2) ( 0.100101(2) ( 23Numrul de poziii rezervate pentru partea fracionar, respectiv pentru exponent depind de precizia aleas, care poate fi simpl, dubl sau extins.

1.3. Primele programe

n aceast seciune snt prezentate i explicate ase programe cu scopul de a asigura un suport de baz pentru prezentrile din capitolele urmtoare.

Prin tradiie primul program C este un mic exemplu din lucrarea devenit clasic The C programming language, de Brian W Kernigham i Dennis M Ritchie.

#include

int main() {

printf("Hello, world\n");

return 0;

}

Acest program afieaz un mesaj de salut.

Prima linie indic faptul c se folosesc funcii de intrare / ieire, i descrierea modului de utilizare (numele, tipul argumentelor, tipul valorii returnate etc) a acestora se afl n fiierul cu numele stdio.h .

A doua linie definete funcia main care va conine instruciunile programului. n acest caz singura instruciune este un apel al funciei printf care afieaz un mesaj la terminal. Mesajul este dat ntre ghilimele i se termin cu un caracter special new-line (\n).

Instruciunea return pred controlul sistemului de operare la terminarea programului i comunic acestuia codul 0 pentru terminare. Prin convenie aceast valoare semnific terminarea normal a programului adic nu au aprut erori n prelucrarea datelor.

Corpul funciei main apare ntre acolade. Orice program C trebuie s aib o funcie main.

Al doilea program ateapt de la terminal introducerea unor numere ntregi nenule i determin suma lor. n momentul n care se introduce o valoare zero, programul afieaz suma calculat.

#include

int main() {

int s,n;

s = 0;

do {

scanf("%d",&n);

s += n;

} while (n!=0);

printf("%d\n",s);

return 0;

}

n cadrul funciei main se definesc dou variabile s i n care vor memora valori ntregi. Variabila s (care va pstra suma numerelor introduse) este iniializat cu valoarea 0.

n continuare se repet (do) o secven de dou instruciuni, prima fiind o operaie de intrare i a doua o adunare.

Primul argument al funciei scanf formatul de introducere "%d" indic faptul c se ateapt introducerea unei valori ntregi n format zecimal de la terminal (consol). Al doilea argument indic unde se va depune n memorie valoarea citit; de aceea este necesar s se precizeze adresa variabilei n (cu ajutorul operatorului &).

n a doua instruciune la valoarea variabilei s se adun valoarea variabilei n. Operatorul += are semnificaia adun la.

Aceast secven se repet (do) ct timp (while) valoarea introdus (n) este nenul. Operatorul != are semnificaia diferit de.

n final funcia printf afieaz pe terminal valoarea variabilei s n format zecimal.

Al treilea program ateapt de la terminal introducerea unei valori naturale n, dup care mai ateapt introducerea a n valori reale (dubl precizie): a0, a1, ..., an(1. n continuare se afieaz media aritmetic a acestor valori, calculat n cadrul unei funcii care se definete n acest scop.

#include

double media(double v[], int n) {

double s;

int i;

for (s=i=0; i (mai mare), = (mai mare sau egal) produc valoarea 0 dac relaia specificat este fals i 1 dac ea este adevrat.

Tipul rezultatului este int. Se execut conversiile aritmetice obinuite. Aceti operatori au precedena mai mic dect operatorii aritmetici.

Expresie-egalitate:

expresie == expresie

expresie != expresieOperatorii == (egal cu) i != (diferit de) snt analogi cu operatorii relaionali, dar precedena lor este mai mic. Astfel c a0) ? f: neste de tip double indiferent dac n este pozitiv sau negativ. Parantezele nu snt necesare deoarece precedena operatorului ?: este mai mic, dar ele pot fi folosite pentru a face expresia condiional mai lizibil.4.8. Operatori de atribuireExist mai muli operatori de atribuire, care se grupeaz toi de la dreapta la stnga. Operandul stng este o valoare-stnga, operandul drept este o expresie. Tipul expresiei de atribuire este tipul operandului stng.Rezultatul este valoarea memorat n operandul stng dup ce atribuirea a avut loc. Cele dou pri ale unui operator de atribuire compus snt uniti lexicale (simboluri) distincte.Expresie-atribuire:valoare-stnga = expresievaloare-stnga op= expresieunde op poate fi unul din operatorii +, -, *, /, %, , &, ^, |.ntr-o atribuire simpl cu =, valoarea expresiei nlocuiete pe cea a obiectului referit de valoare-stnga. Dac ambii operanzi au tip aritmetic, atunci operandul drept este convertit la tipul operandului stng nainte de atribuire.Expresiile de forma E1op=E2 se interpreteaz ca fiind echivalente cu expresiile de forma E1=E1opE2; totui E1 este evaluat o singur dat.Exemplu: expresia x *= y+1 este echivalent cux = x * (y+1)i nu cux = x * y + 1Pentru operatorii += i -=, operandul stng poate fi i un pointer, n care caz operandul din dreapta este convertit la ntreg (capitolul opt). Toi operanzii din dreapta i toi operanzii din stnga care nu snt pointeri trebuie s fie de tip aritmetic.Atribuirea prescurtat este avantajoas n cazul cnd n membrul stng avem expresii complicate, deoarece ele se evalueaz o singur dat.4.9. Operatorul virgulExpresie-virgul:expresie, expresieO pereche de expresii separate prin virgul se evalueaz de la stnga la dreapta i valoarea expresiei din stnga se neglijeaz. Tipul i valoarea rezultatului snt cele ale operandului din dreapta. Aceti operatori se grupeaz de la stnga la dreapta. n contextele n care virgula are un sens special, (de exemplu ntr-o list de argumente reale ale unei funcii i lista de iniializare), operatorul virgul descris aici poate aprea numai n paranteze. De exemplu funcia:f(a,(t=3,t+2),c)are trei argumente, dintre care al doilea are valoarea 5. Expresia acestui argument este o expresie virgul. n calculul valorii lui se evalueaz nti expresia din stnga i se obine valoarea 3 pentru t, apoi cu aceast valoare se evalueaz a doua expresie i se obine t(5. Prima valoare a lui t se pierde.4.10. Precedena i ordinea de evaluareTabelul de la sfritul acestei seciuni constituie un rezumat al regulilor de preceden i asociativitate ale tuturor operatorilor.Operatorii din aceeai linie au aceeai preceden; liniile snt scrise n ordinea descresctoare a precedenei, astfel de exemplu operatorii *, / i % au toi aceeai preceden, care este mai mare dect aceea a operatorilor + i -.Dup cum s-a menionat deja, expresiile care conin unul dintre operatorii asociativi sau comutativi (*, +, &, ^, |) pot fi rearanjate de compilator chiar dac conin paranteze. n cele mai multe cazuri aceasta nu produce nici o diferen; n cazurile n care o asemenea diferen ar putea aprea pot fi utilizate variabile temporare explicite, pentru a fora ordinea de evaluare.Limbajul C, ca i multe alte limbaje, nu specific n ce ordine snt evaluai operanzii unui operator. Dm n continuare dou exemple:x = f() + g();a[i] = i++;n asemenea situaii trebuie memorate rezultate intermediare n variabile temporare pentru a garanta o execuie n ordinea dorit.Singurele cazuri n care se garanteaz evaluarea de la stnga la dreapta se refer la operatorii pentru expresii logice && i ||. n aceste cazuri se garanteaz n plus un numr minim de evaluri.OperatorAsociativitate() [] -> .stnga la dreapta! ++ -- - (tip) * & sizeofdreapta la stnga* / %stnga la dreapta+ -stnga la dreapta>stnga la dreapta< >=stnga la dreapta== !=stnga la dreapta&stnga la dreapta^stnga la dreapta|stnga la dreapta&&stnga la dreapta||stnga la dreapta?:dreapta la stnga= op=dreapta la stnga,stnga la dreapta5. Instruciunintr-un program scris n limbajul C instruciunile se execut secvenial, n afar de cazul n care se indic altfel.Instruciunile pot fi scrise cte una pe o linie pentru o lizibilitate mai bun, dar nu este obligatoriu.5.1. Instruciunea expresieCele mai multe instruciuni snt instruciuni expresie. O expresie devine instruciune dac ea este urmat de punct i virgul.a = b + c;n++;scanf(. . .);n limbajul C punct i virgula este un terminator de instruciune i este obligatoriu.O instruciune de atribuire poate aprea pe post de expresie ntr-o alt instruciune. Funcia strcpy copiaz irul s peste irul t.void strcpy(char t[], const char s[]) {int i = 0;while (t[i]=s[i]) i++;}Expresia t[i]=s[i] este o atribuire i are valoarea True sau False n funcie de valoarea s[i].5.2. Instruciunea compus sau bloculInstruciunea compus este o grupare de declaraii i instruciuni nchise ntre acolade. Aceasta a fost introdus cu scopul de a folosi mai multe instruciuni acolo unde sintaxa cere o instruciune. Instruciunea compus sau blocul este echivalent sintactic cu o singur instruciune.Format:{list-declaratorilist-instruciuni}Dac anumii identificatori din lista-declaratori au fost declarai anterior, atunci declaraia exterioar este ignorat pe durata blocului, dup care i reia sensul su.Orice iniializare pentru variabilele auto i register se efectueaz la fiecare intrare n bloc. Iniializrile pentru variabilele static se execut numai o singur dat cnd programul ncepe s se execute.Un bloc se termin cu o acolad dreapt care nu este urmat niciodat de caracterul punct i virgul.5.3. Instruciunea ifSintaxa instruciunii condiionale if admite dou formate:if (expresie)if (expresie) instruciune-1instruciune-1else instruciune-2Instruciunea if se folosete pentru a lua decizii. n ambele cazuri se evalueaz expresia i dac ea este adevrat (deci diferit de zero) se execut instruciune-1. Dac expresia este fals (are valoarea zero) i instruciunea if are i parte de else atunci se execut instruciune-2.n al doilea caz una i numai una dintre cele dou instruciuni se execut. Deoarece un if testeaz pur i simplu valoarea numeric a unei expresii, se admite o prescurtare i anume:if (expresie)n loc de:if (expresie != 0)Deoarece partea else a unei instruciuni if este opional, exist o ambiguitate cnd un else este omis dintr-o secven de if imbricat. Aceasta se rezolv asociind else cu ultimul if care nu are else.if (n>0)if (n>0) { if (a>b)if (a>b) z = a;z = a; else} z = b;elsez = b;n exemplul de mai sus, n primul caz partea else se asociaz if-ului din interior. Dac nu dorim acest lucru atunci folosim acoladele pentru a fora asocierea, ca n al doilea caz.Instruciunea condiional admite i construcia else-if:if (expresie-1) instruciune-1elseif (expresie-2) instruciune-2elseif (expresie-3) instruciune-3else instruciune-4Aceast cascad de if-uri se folosete frecvent n programe, ca mod de a exprima o decizie multipl.Expresiile se evalueaz n ordinea n care apar; dac se ntlnete o expresie adevrat, atunci se execut instruciunea asociat cu ea i astfel se termin ntregul lan.Oricare instruciune poate fi o instruciune simpl sau un grup de instruciuni ntre acolade.Instruciunea dup ultimul else se execut n cazul n care nici o expresie nu a fost adevrat.Dac n acest caz nu exist nici o aciune explicit de fcut, atunci ultima parte poate s lipseasc:else instruciune-4Pot exista un numr arbitrar de construcii:else if (expresie) instruciunegrupate ntre un if iniial i un else final.ntotdeauna un else se asociaz cu ultimul if ntlnit.5.4. Instruciunile while i doFormatul instruciunii while:while (expresie) instruciuneInstruciunea se execut repetat atta timp ct valoarea expresiei este diferit de zero. Testul are loc naintea fiecrei execuii a instruciunii. Prin urmare ciclul este urmtorul: se testeaz condiia din paranteze dac ea este adevrat (expresia din paranteze are o valoare diferit de zero) se execut corpul instruciunii while, se verific din nou condiia, dac ea este adevrat se execut din nou corpul instruciunii. Cnd condiia devine fals (valoarea expresiei din paranteze este zero) se continu execuia cu instruciunea de dup corpul instruciunii while, deci instruciunea while se termin.Formatul instruciunii do:do instruciunewhile (expresie);Instruciunea se execut repetat pn cnd valoarea expresiei devine zero. Testul are loc dup fiecare execuie a instruciunii.unsigned n, c = 0;unsigned n, c = 0;scanf("%u",&n);scanf("%u",&n);while (n>0) {do { n /= 10; c++; c++; n /= 10; } } while (n>0);printf("%u",c);printf("%u",c);Secvena din stnga afieaz numrul de cifre zecimale ale valorii lui n numai dac n(0, altfel afieaz zero. Secvena din dreapta afieaz numrul de cifre zecimale ale valorii lui n n orice condiii.5.5. Instruciunea forFormat:for (expresie-1; expresie-2; expresie-3) instruciuneAceast instruciune este echivalent cu secvena:expresie-1;while (expresie-2) {instruciune;expresie-3;}Expresie-1 constituie iniializarea ciclului i se execut o singur dat naintea ciclului. Expresie-2 specific testul care controleaz ciclul. El se execut naintea fiecrei iteraii. Dac condiia din test este adevrat atunci se execut corpul ciclului, dup care se execut expresie-3, care const de cele mai multe ori n modificarea valorii variabilei de control al ciclului. Se revine apoi la reevaluarea condiiei. Ciclul se termin cnd condiia devine fals.Oricare dintre expresiile instruciunii for sau chiar toate pot lipsi.Dac lipsete expresie-2, aceasta implic faptul c clauza while este echivalent cu while (1), ceea ce nseamn o condiie totdeauna adevrat. Alte omisiuni de expresii snt pur i simplu eliminate din expandarea de mai sus.Instruciunile while i for permit un lucru demn de observat, i anume: ele execut testul de control la nceputul ciclului i naintea intrrii n corpul instruciunii.Dac nu este nimic de fcut, nu se face nimic, cu riscul de a nu intra niciodat n corpul instruciunii.Exemple. 1) n primul capitol, al treilea i al patrulea program folosesc instruciunea for pentru a parcurge un ir de valori. S notm faptul c fiecare expresie poate fi orict de complex. Secvena urmtoare determin suma primelor n numere naturale.unsigned s,i;scanf("%u",&n);for (s=0,i=1; i i . ai structurilor, mpreun cu () (modificare prioriti, liste de argumente) i [] (indexare) se gsesc n vrful listei de preceden, fiind din acest punct de vedere foarte apropiai. Astfel, fie declaraia urmtoare care definete un pointer p la o structur:struct { int x,*y; } *p;Semnificaiile urmtoarelor expresii snt:++p->xIncrementeaz membrul x, nu pointerul p, deoarece operatorul -> are o preceden mai mare dect ++. Parantezele pot fi folosite pentru a modifica ordinea operatorilor dat de precedena.(++p)->xIncrementeaz mai nti pointerul p, apoi acceseaz membrul x, din structura indicat de noua valoare a lui p.(p++)->xAcceseaz mai nti membrul x, apoi incrementeaz pointerul p.*p->yIndic coninutul de la adresa memorat la y.*p->y++Acceseaz mai nti ceea ce indic y, apoi incrementeaz pe y.(*p->y)++Incrementeaz ceea ce indic y.*p++->yAcceseaz mai nti ceea ce indic y, apoi incrementeaz pointerul p.9.2. typedefStilul de utilizare a tipurilor de date structurate prezentat mai sus este foarte frecvent, i se ntlnete mai ales n programe care apeleaz funcii ale sistemelor de operare Linux.Considerm totui c a scrie declaraii de funcii care au ca i parametri date structurate, sau a defini variabile de tip structur, este destul de neplcut datorit cerinei de a preciza numele tipului precedat de cuvntul rezervat struct. De aceea, pe parcursul lucrrii vom folosi o facilitate (typedef) de a defini sinonime pentru tipuri de date existente, care pot fi fundamentale (predefinite) sau definite de programator.ntr-o declaraie typedef fiecare identificator care apare ca parte a unui declarator devine sintactic echivalent cu cuvntul cheie rezervat pentru tipul asociat cu identificatorul. De exemplu, declaraia urmtoare l face pe LENGTH sinonim cu int:typedef int LENGTH;Tipul LENGTH poate fi folosit ulterior n declaraii n acelai mod ca i tipul int:LENGTH len,maxlen,*lgm[];n mod similar, declaraia urmtoare l face pe STRING sinonim cu char*, adic pointer la caracter:typedef char *STRING;Tipul STRING poate fi folosit n declaraii ulterioare:STRING p,linptr[NLIN];Observm c tipul care se declar prin typedef apare pe poziia numelui de variabil, nu imediat dup cuvntul rezervat typedef.Relum definiia tipului P_func din capitolul precedent: typedef int (*P_func)(const char *, const char *);Aceast declaraie definete identificatorul P_func ca fiind un tip pointer la o funcie care returneaz un ntreg, i are ca argumente doi pointeri la caracter. Elementele celor dou masive nu pot fi modificate. Tipul P_func poate fi folosit n continuare pentru a construi noi declaraii.Rescriem definiiile de tipuri din seciunea precedent:typedef struct { int zi,luna,an; } T_data;O structur extern sau static poate fi iniializat, atand dup definiia ei o list de iniializatori pentru componente, de exemplu:T_data d = {4,7,1984};Structurile pot fi imbricate: un tip de nregistrare de stat de plat, de exemplu, poate fi definit astfel:typedef struct { long nrl; char num[LNUM],adr[LADR]; long sal; T_data dnast,dang; } T_pers;Funcia NrZile poate fi declarat astfel:int NrZile(const T_data *pd);Exist dou motive principale care impun folosirea declaraiilor typedef. Primul este legat de problemele de portabilitate. Cnd se folosesc declaraii typedef pentru tipuri de date care snt dependente de main, atunci pentru o compilare pe un alt sistem de calcul este necesar modificarea doar a acestor declaraii nu i a datelor din program. La sfritul acestui capitol vom da un exemplu n acest sens relund definirea tipului structurat dat calendaristic.Al doilea const n faptul c prin crearea de noi nume de tipuri se ofer posibilitatea folosirii unor nume mai sugestive n program, deci o mai rapid nelegere a programului.n continuare pe parcursul lucrrii vom folosi facilitatea typedef pentru a defini tipuri complexe de date i n special structuri.9.3. Masive de structuriStructurile snt n mod special utile pentru tratarea masivelor de variabile legate prin context. Pentru exemplificare vom considera un program care numr intrrile fiecrui cuvnt cheie dintr-un text. Pentru aceasta avem nevoie de un masiv de iruri de caractere pentru pstrarea cuvintelor cheie i un masiv de ntregi pentru a contoriza apariiile acestora. O posibilitate este de a folosi dou masive paralele keyw i keyc declarate prin:char *keyw[NKEYS];int keyc[NKEYS];respectiv unul de pointeri la iruri de caractere i cellalt de ntregi.Fiecrui cuvnt cheie i corespunde perechea (keyw,keyc) astfel nct putem considera cele dou masive ca fiind un masiv de perechi. Urmtoarea declaraie definete un tip structur T_key:typedef struct { char *keyw; int keyc; } T_key;Folosim acest tip structur pentru a defini un masiv keym, unde NKEYS este o constant definit cu ajutorul directivei #define:T_key keym[NKEYS];Fiecare element al masivului este o structur de acelai ablon ca i structura T_key.Deoarece masivul de structuri keym conine, n cazul nostru, o mulime constant de cuvinte cheie, este mai uor de iniializat o dat pentru totdeauna chiar n locul unde este definit. Iniializarea structurilor este o operaie analog cu iniializarea unui masiv n sensul c definiia este urmat de o list de iniializatori nchii n acolade.Atunci masivul de structuri keym va fi iniializat astfel:T_key keym[] = { "break",0, "case",0, "char",0, /* ... */ "while",0 };Iniializatorii snt perechi care corespund la membrii structurii. Iniializarea ar putea fi fcut i incluznd iniializatorii fiecrei structuri din masiv n acolade ca n:T_key keym[] = { {"break",0},{"case",0}, . . . }dar parantezele interioare nu mai snt necesare dac iniializatorii snt variabile simple sau iruri de caractere i dac toi iniializatorii snt prezeni.Dac masivul keym este global, eventualii membri pentru care nu se precizeaz o valoare iniial vor primi valoarea zero, dar n acest caz parantezele interioare snt necesare:T_key keym[] = { {"break"},{"case"}, . . . }Compilatorul va determina, pe baza iniializatorilor, dimensiunea masivului de structuri keym, motiv pentru care nu este necesar indicarea dimensiunii masivului la iniializare.Programul de numrare a cuvintelor cheie ncepe cu definirea masivului de structuri keym. Funcia principal main citete cte un cuvnt (ir de caractere) din textul de la intrarea standard prin apelul repetat al funciei scanf. Fiecare ir este apoi cutat n tabelul keym cu ajutorul unei funcii de cutare binary. Lista cuvintelor cheie trebuie s fie n ordine cresctoare pentru ca funcia binary s lucreze corect. Dac irul cercetat este un cuvnt cheie atunci funcia binary returneaz numrul de ordine al acestuia n tabelul cuvintelor cheie, altfel returneaz (1.int binary(char *word, const T_key kt[], int n) {int l,r,m,c;l = 0; r = n - 1;while (l0) l = m + 1; else r = m - 1; }return NULL;}int main() {/* numr cuvintele cheie, versiune cu pointeri */int t;char word[36];T_key *p;while (scanf("%s",word)!=EOF) if (p=binary(word,keym,NKEYS)) p->keyc++;for (p=keym; pkeyc>0) printf("%4d %s\n",p->keyc,p->keyw);}S observm cteva lucruri importante n acest exemplu. n primul rnd, declaraia funciei binary trebuie s indice c ea returneaz un pointer la o structur de acelai ablon cu structura T_key, n loc de un ntreg. Dac binary gsete un cuvnt n structura T_key, atunci returneaz un pointer la el; dac nu-1 gsete returneaz NULL. n funcie de aceste dou valori returnate, funcia main semnaleaz gsirea cuvntului prin incrementarea cmpului keyc corespunztor cuvntului sau trece direct la citirea urmtorului cuvnt.n al doilea rnd, toate operaiile de acces la elementele masivului de structuri keym se fac prin intermediul pointerilor. Acest lucru determin o modificare semnificativ n funcia binary. Calculul elementului mijlociu nu se mai poate face simplu prin:m = (l + r) / 2deoarece adunarea a doi pointeri este o operaie ilegal, nedefinit. Aceast instruciune trebuie modificat n:m = l + (r - l) / 2care face ca adresa memorat la m s indice elementul de la jumtatea distanei dintre l i r.S mai observm iniializarea pointerilor l i r, care este perfect legal, deoarece este posibil iniializarea unui pointer cu o adres a unui element deja definit.n funcia main avem urmtorul ciclu:for(p=keym; pnum)==0) return np; pp = &np->nxt; }return pp = NULL;}Fiecare nod din list are tipul T_list i conine, printre altele, i adresa urmtorului nod. Dar adresa primului nod e memorat ntr-un element care are alt tip dect T_list. De aceea avem nevoie s memorm adresa zonei unde este memorat adresa care ne intereseaz, de unde necesitatea definiiei globaleP_list *pp;Aceast definiie este echivalent cuT_list **pp;Dac i elementele masivului ar avea tipul T_list, nu am avea nevoie de dou nivele de indirectare, ar fi suficient s memorm adresa nodului precedent ntr-o variabil pointer la T_list.Rutina Install folosete funcia Lookup pentru a determina dac simbolul nou care trebuie introdus n lan este deja prezent sau nu. Dac mai exist o definiie anterioar pentru acest simbol, ea trebuie nlocuit cu definiia nou; altfel se creeaz o intrare nou pentru acest simbol, care se introduce la nceputul lanului. Funcia Install returneaz NULL dac din anumite motive nu exist suficient spaiu pentru crearea unui bloc nou.T_list *Install(char *num, char *def) {/* scrie (num, def) n htab */T_list *np;int hv;np = Lookup(num);if (np==NULL) {/* nu s-a gsit */ np = (T_list*)calloc(1,sizeof(*np)); if (np==NULL) return NULL;/* nu exist spaiu */ np->num = strdup(num); if (np==NULL) return NULL; hv = hashf(np->num); np->nxt = hasht[hv]; hasht[hv] = np; }else/* nodul exist deja */ free(np->def);/* elibereaz definiia veche */np->def = strdup(def);if (np->def==NULL) return NULL;return np;}Rutina Remove elimin din list ultimul element consultat.void Remove() {T_list *np;if (!pp) return;free(np=(*pp)->nxt);*pp = np; pp = NULL;}Cum eliberm toate zonele ocupate de o list? Trebuie s fim foarte ateni deoarece, nainte de a elibera memoria ocupat de nodul curent, trebuie eliberat memoria ocupat de nodul care urmeaz celui curent. Prezentm mai nti varianta recursiv:void FreeList(T_list *np) {if (np) { FreeList(np->nxt); free(np); }}Definiia recursiv a funciei FreeList consum destul de mult memorie din stiv, de aceea redm n continuare i varianta nerecursiv, mai eficient.void FreeList(T_list *np) {T_list *nx;while (np) { nx = np->nxt; free(np); np = nx; }}Funcia main, care utilizeaz aceste rutine, nu are nevoie s iniializeze elementele masivului hasht cu NULL, deoarece acesta este declarat global i este automat iniializat corespunztor. De la intrarea standard se ateapt introducerea unor comenzi:+ nume valoare nume? numeComanda ( insereaz n list simbolul nume cu valoarea dat. Dac numele introdus nu exist n listele hasht atunci se afieaz un mesaj corespunztor, altfel se afieaz vechea valoare care este apoi nlocuit de noua valoare.Comanda ? afieaz valoarea simbolului nume, dac acesta exist n listele hasht.Comanda elimin simbolul nume, dac acesta exist n liste.Dup epuizarea datelor introduse memoria ocupat de cele NHASH liste este eliberat.int main() {char num[36],def[36],op;T_list *np;int i;while (scanf(" %c",&op)!=EOF) switch (op) { case '+': scanf("%s %s",num,def); np = Lookup(num); if (!np) puts("New name\n"); else printf("Old value: %s\n",np->def); Install(num,def); break; case '-': case '?': scanf("%s",num); np = Lookup(num); if (!np) puts("Not in list"); else switch (op) { case '-': Remove(); puts("Removed"); break; case '?': printf("Value: %s\n",np->def); break; } }for (i=0; iw); if (c==0) (*p)->c++; else if (cl,w); else/* noul cuvnt mai mare, mergem spre dreapta */ TreeInsert(&(*p)->r,w); }else { *p = (P_node)calloc(1,sizeof(T_node)); (*p)->w = strdup(w); (*p)->c = 1;}Am considerat c este mai comod s solicitm alocare de memorie pentru un nod apelnd funcia calloc:void calloc(unsigned ne, unsigned sz);Argumentele acestei funcii snt ne: numrul de elemente pe care le va avea zona, i sz: mrimea n octei a unui element, care se poate obine cu operatorul sizeof. Coninutul zonei de memorie este pus la zero, spre deosebire de funcia malloc care pstreaz neschimbat acest coninut (valori reziduale).La adugarea unui nod nou, funcia calloc iniializeaz pointerii spre cei doi descendeni cu NULL. Rmne s crem o copie a irului dat cu funcia strdup, deja cunoscut, i s iniializm contorul cu 1.Rutina TreePrint afieaz arborele astfel nct pentru fiecare nod se afieaz sub-arborele lui stng, adic toate cuvintele mai mici dect cuvntul curent, apoi cuvntul curent i la sfrit sub-arborele drept, adic toate cuvintele mai mari dect cuvntul curent. Funcia TreePrint este una din cele mai tipice rutine recursive.void TreePrint(P_node p) {if (p) { Treeprint(p->l); printf("%5d %s\n",p->c,p->w); TreePrint(p->r); }}Este important de reinut faptul c n algoritmul de cutare n arbore, pentru a ajunge la un anumit nod, se parcurg toate nodurile precedente pe ramura respectiv (stng sau dreapt), ncepnd ntotdeauna cu nodul rdcin. Dup fiecare ieire din rutina TreeInsert, datorit recursivitii se parcurge acelai drum, de data aceasta de la nodul gsit spre rdcina arborelui, refcndu-se toi pointerii drumului parcurs.O observaie legat de acest exemplu: dac arborele este nebalansat, adic cuvintele nu sosesc n ordine aleatoare din punct de vedere lexicografic, atunci timpul de execuie al programului poate deveni foarte mare. Cazul limit n acest sens este acela n care cuvintele de la intrare snt deja n ordine (cresctoare sau descresctoare), caz n care programul nostru simuleaz o cutare liniar ntr-un mod destul de costisitor.Rutina TreeFree elibereaz memoria ocupat de nodurile arborelui. Ordinea n care se face eliberarea este urmtoarea: mai nti sub-arborele stng, apoi sub-arborele drept, i n final nodul curent. i aici eliberarea memoriei ocupate de nod trebuie s respecte o ordine: mai nti memoria ocupat de irul de caractere, apoi memoria ocupat de nodul propriu-zis.void TreeFree(P_node p) {if (p) { TreeFree(p->l); TreeFree(p->r); free(p->w); free(p); }}9.7. CmpuriUn cmp se definete ca fiind o mulime de bii consecutivi dintr-un cuvnt sau ntreg. Concret, din motive de economie a spaiului de memorie, este util mpachetarea unor obiecte ntr-un singur cuvnt main. Un caz frecvent de acest tip este utilizarea unui set de flaguri, fiecare pe un bit, pentru tabela de simboluri a unui compilator.Fiecare simbol dintr-un program are anumite informaii asociate lui, cum snt de exemplu clasa de memorie, tipul, dac este sau nu cuvnt cheie .a.m.d. Cel mai compact mod de a codifica aceste informaii este folosirea unui set de flaguri, de cte un bit, ntr-un singur ntreg sau caracter.Modul cel mai uzual pentru a face acest lucru este de a defini un set de mti, fiecare masc fiind corespunztoare poziiei bitului n interiorul caracterului sau cuvntului. De exemplu:#define KEYWORD 01#define EXTERNAL 02#define STATIC 04definesc mtile KEYWORD, EXTERNAL i STATIC care se refer la biii 0, 1 i respectiv 2 din caracter sau cuvnt. Atunci accesarea acestor bii se realizeaz cu ajutorul operaiilor de deplasare, mascare i complementare, descrii ntr-un capitol anterior. Numerele trebuie s fie puteri ale lui 2.Expresii de forma urmtoare apar frecvent i ele seteaz biii 1 i 2 din caracterul sau ntregul flags (n exemplul nostru):flags | = EXTERNAL | STATIC;Expresia urmtoare selecteaz biii 1 i 2 din flags:flags &= EXTERNAL | STATIC;Expresia urmtoare este adevrat cnd cel puin unul din biii 1 sau 2 din flags este unu:if (flags & (EXTERNAL | STATIC)) . . .Expresia urmtoare este adevrat cnd biii 1 i 2 din flags snt ambii zero:if (!(flags & (EXTERNAL | STATIC))) . . .Limbajul C ofer aceste expresii ca o alternativ pentru posibilitatea de a defini i de a accesa biii dintr-un cuvnt n mod direct, folosind operatorii logici pe bii.Sintaxa definiiei cmpului i a accesului la el se bazeaz pe structuri. De exemplu construciile #define din exemplul de mai sus pot fi nlocuite prin definirea a trei cmpuri:struct {unsigned is_keyword:1;unsigned is_external:1;unsigned is_static:1;} flags;Aceast construcie definete variabila flags care conine 3 cmpuri, fiecare de cte un bit. Numrul care urmeaz dup : (dou puncte) reprezint lungimea cmpului n bii. Cmpurile snt declarate unsigned pentru a sublinia c ele snt cantiti fr semn. Pentru a ne referi la un cmp individual din variabila flags folosim o notaie similar cu notaia folosit pentru membrii structurilor.flags.is_keywordflags.is_staticCmpurile se comport ca nite ntregi mici fr semn i pot participa n expresii aritmetice ca orice ali ntregi. Astfel, expresiile anterioare pot fi scrise mai natural sub forma urmtoare:flags.is_extern = flags.is_static = 1;pentru setarea biilor 1 i 2 din variabila flags,flags.is_extern = flags.is_static = 0;pentru tergerea biilor, iar:if (flags.is_extern==0 && flags.is_static==0)pentru testarea lor.Un cmp nu trebuie s depeasc limitele unui cuvnt (16 sau 32 de bii, n funcie de sistemul de calcul). n caz contrar, cmpul se aliniaz la limita urmtorului cuvnt. Cmpurile nu necesit s fie denumite. Un cmp fr nume, descris numai prin caracterul : i lungimea lui n bii, este folosit pentru a rezerva spaiu n vederea alinierii urmtorului cmp. Lungimea zero a unui cmp poate fi folosit pentru forarea alinierii urmtorului cmp la limita unui nou cuvnt, el fiind presupus a conine tot cmpuri i nu un membru obinuit al structuri, deoarece n acest ultim caz alinierea se face n mod automat. Nici un cmp nu poate fi mai lung dect un cuvnt. Cmpurile se atribuie de la dreapta la stnga, de la poziiile cele mai puin semnificative la cele mai semnificative.Cmpurile nu pot constitui masive, nu au adrese, astfel nct operatorul & nu se poate aplica asupra lor.9.8. ReuniuniO reuniune este o variabil care poate conine, la momente diferite, obiecte de diferite tipuri i dimensiuni; compilatorul este cel care ine evidena dimensiunilor i aliniamentului.Reuniunile ofer posibilitatea ca mai multe tipuri diferite de date s fie tratate ntr-o singur zon de memorie.S relum exemplul tabelei de simboluri a unui compilator, presupunnd c se gestioneaz constante de tip int, float sau iruri de caractere.Valoarea unei constante particulare ar putea fi memorat ntr-o variabil de tip corespunztor, dar este mai convenabil, pentru gestiunea tabelei de simboluri, ca valoarea s fie memorat n aceeai zon de memorie, indiferent de tipul ei. Acesta este scopul unei reuniuni: de a furniza o singur variabil care s poat conine oricare dintre valorile unor tipuri de date. Ca i n cazul cmpurilor, sintaxa definiiei i accesului la o reuniune se bazeaz pe structuri. Fie definiia:union u_tag { int ival;float fval;char *pval;} uval;Variabila uval va fi suficient de mare ca s poat pstra pe cea mai mare dintre cele trei tipuri de componente. Oricare dintre tipurile de mai sus poate fi atribuit variabilei uval i apoi folosit n expresii n mod corespunztor, adic tipul n uval este tipul ultim atribuit. Utilizatorul este cel care ine evidena tipului curent memorat ntr-o reuniune.Sintactic, membrii unei reuniuni snt accesibili printr-o construcie de forma:nume-reuniune. membrupointer-la-reuniune->membruDac variabila utype este utilizat pentru a ine evidena tipului curent memorat n uval, atunci putem avea urmtorul cod:switch (utype) { case INT: printf ("%d\n",uval.ival); break; case FLOAT: printf("%f\n",uval.fval); break; case STRING: printf("%s\n",uval.pval); break; default: printf("tip incorect %d in utype\n",utype); break; }Reuniunile pot aprea n structuri i masive i invers. Sintaxa pentru accesarea unui membru al unei reuniuni dintr-o structur (sau invers) este identic cu cea pentru structurile imbricate. De exemplu, n masivul de structuri symtab[NSYM] definit de:struct {char *name;int flags,utype;union {int ival;float fval;char *pval;} uval;} symtab[NSYM];variabila ival se refer prin:symtab[i].uval.ivaliar primul caracter al irului indicat de pval prin:*symtab[i].uval.pvalTehnic, o reuniune este o structur n care toi membrii au deplasamentul zero, structura fiind suficient de mare pentru a putea pstra pe cel mai mare membru. Alinierea este corespunztoare pentru toate tipurile reuniunii. Pointerii la reuniuni pot fi folosii n mod similar cu pointerii la structuri.Urmtorul exemplu trebuie studiat cu atenie, deoarece implementarea este dependent de arhitectura sistemului de calcul. De aceea vom prezenta dou declaraii de tip, corespunztoare celor dou moduri de reprezentare a valorilor ntregi: pentru arhitecturi Big Endian:typedef struct { short an; char luna, zi; } T_struc; pentru arhitecturi Little Endian:typedef struct { char luna, zi; short an; } T_struc;n continuare putem defini un tip dat calendaristic:typedef union { T_struc ds; long dn; } T_data;Fie urmtoarele definiii de variabile:T_data dc1, dc2;n continuare putem interpreta coninutul variabilelor dc1 i dc2 fie ca structuri fie ca ntregi lungi.O variabil de acest tip se iniializeaz astfel:dc1.ds.an = . . .;dc1.ds.luna = . . .;dc1.ds.zi = . . .;Dou date calendaristice se compar ct se poate de natural:dc1.dn < dc2.dnCum putem depista automat ce fel de arhitectur are sistemul de calcul?union { long l; char c[4]; } tst = 0x12345678;if (tst.c[0]==0x12) puts("Big Endian"); else puts("Little Endian");10. Operaii de intrare / ieirentruct limbajul C nu a fost dezvoltat pentru un sistem particular de operare, i datorit faptului c s-a dorit realizarea unei portabiliti ct mai mari, att a unui compilator C, ct i a programelor scrise n acest limbaj, el nu posed faciliti de intrare / ieire.Exist totui un sistem de intrare / ieire constituit dintr-un numr de subprograme care realizeaz funcii de intrare / ieire pentru programe scrise n C, dar care nu fac parte din limbajul C. Aceste subprograme se gsesc n biblioteca C.Scopul acestui capitol este de a descrie cele mai utilizate subprograme de intrare / ieire i interfaa lor cu programele scrise n limbajul C.10.1. Intrri i ieiri standard; fiiereSistemul I/O ofer utilizatorului trei fiiere standard de lucru. Cuvntul fiier a fost pus ntre ghilimele, deoarece limbajul nu definete acest tip de dat i pentru c fiierele reprezint mai degrab nite fluxuri de intrare / ieire standard puse la dispoziia utilizatorului. Aceste fiiere snt: fiierul standard de intrare (stdin); fiierul standard de ieire (stdout); fiierul standard de afiare a mesajelor (stderr).Toate aceste trei fiiere snt secveniale i n momentul execuiei unui program C snt implicit definite i deschise.stdin i stdout snt asociate n mod normal terminalului de la care a fost lansat programul n execuie. Sistemul I/O permite redirectarea acestor fiiere pe alte periferice i nchiderea lor la ncheierea execuiei programului. Redirectarea fiierului stdin se specific prin construcia:specificator-fiier-ieiren linia de comand prin care a fost lansat programul.Redirectarea fiierului stdout pe un alt periferic, n scopul efecturii unei operaii de adugare (append) se specific prin construcia:>>specificator-fiier-ieirestderr este ntotdeauna asociat terminalului de la care a fost lansat programul n execuie i nu poate fi redirectat.Astfel c o linie de comand a unui program poate arta astfel:Prog ieire-standard ali parametriProg < intrare-standard >> ieire-standard ali parametriFiecare din parametri poate lipsi; n lipsa unui specificator de intrare sau ieire standard se folosete terminalul curent. Asocierea unui fiier cu intrarea sau ieirea standard este fcut de sistemul de operare, programul primind doar informaii despre ceilali parametri din linia de comand.Pentru a se putea face o referire la fiiere orice program C trebuie s conin fiierul stdio.h, care se include printr-o linie de forma:#include Pentru claritatea i lizibilitatea programelor scrise n C, ct i pentru crearea unei imagini sugestive asupra lucrului cu fiiere, n fiierul de definiii standard stdio.h s-a definit un nou nume de tip de dat i anume FILE care este o structur. Pentru a referi un fiier, este necesar o declaraie de forma:FILE *fp;unde fp va fi numele de dat cu care se va referi fiierul n orice operaie de intrare / ieire asociat. Iat cteva informaii pstrate de structura FILE: un identificator de fiier pe care sistemul de operare l asociaz fluxului pe durata prelucrrii; acesta poate fi aflat cu ajutorul funciei fileno; adresele zonelor tampon asociate; poziia curent n aceste zone; indicatorii de sfrit de fiier i de eroare; alte informaii.10.2. Accesul la fiiere; deschidere i nchidereNumefopen - deschide un fluxDeclaraieFILE *fopen(const char *num, const char *mod);DescriereFuncia fopen deschide fiierul al crui nume este un ir indicat de num i i asociaz un flux.Argumentul mod indic un ir care ncepe cu una din secvenele urmtoare:rdeschide un fiier pentru citire;r+deschide pentru citire i scriere;wtrunchiaz fiierul la lungime zero sau creeaz un fiier pentru scriere;w+deschide pentru adugare la sfrit, n citire i scriere; fiierul este creat dac nu exist, altfel este trunchiat;adeschide pentru adugare la sfrit, n scriere; fiierul este creat dac nu exist;a+deschide pentru adugare la sfrit, n citire i scriere; fiierul este creat dac nu exist;Dup deschidere, n primele patru cazuri indicatorul poziiei n flux este la nceputul fiierului, n ultimele dou la sfritul acestuia.irul mod include de asemenea litera b (deschide un fiier binar) sau t (deschide un fiier text) fie pe ultima poziie fie pe cea din mijloc.Operaiile de citire i scriere pot alterna n cazul fluxurilor read / write n orice ordine. S reinem c standardul ANSI C cere s existe o funcie de poziionare ntre o operaie de intrare i una de ieire, sau ntre o operaie de ieire i una de intrare, cu excepia cazului cnd o operaie de citire detecteaz sfritul de fiier. Aceast operaie poate fi inefectiv cum ar fi fseek(flux, 0L, SEEK_CUR) apelat cu scop de sincronizare.Valori returnaten caz de succes se returneaz un pointer de tip FILE. n caz de eroare se returneaz NULL i variabila global errno indic codul erorii.Numefclose - nchide un fluxDeclaraieint fclose( FILE *flux);DescriereFuncia fclose nchide fiierul asociat fluxului flux. Dac flux a fost deschis pentru ieire, orice date aflate n zone tampon snt scrise n fiier n prealabil cu un apel fflush.Valori returnaten caz de succes se returneaz 0. n caz de eroare se returneaz EOF i variabila global errno indic codul erorii.Numetmpfile - creeaz un fiier temporarDeclaraieFILE *tmpfile();DescriereFuncia tmpfile genereaz un nume unic de fiier temporar. Acesta este deschis n mod binar pentru scriere / citire ("wb+"). Fiierul va fi ters automat la nchidere sau la terminarea programului.Valoare returnatFuncia returneaz un descriptor de flux n caz de succes, sau NULL dac nu poate fi generat un nume unic de fiier sau dac fiierul nu poate fi deschis. n caz de eroare variabila global errno indic codul erorii.Numefflush - foreaz scrierea n fluxDeclaraieint fflush(FILE *flux);DescriereFuncia fflush foreaz o scriere a tuturor datelor aflate n zone tampon ale fluxului flux. Fluxul rmne deschis.Valori returnaten caz de succes se returneaz 0. n caz de eroare se returneaz EOF i variabila global errno indic codul erorii.Numefseek, ftell, rewind - repoziioneaz un fluxDeclaraieint fseek(FILE *flux, long ofs, int reper);long ftell(FILE *flux);void rewind(FILE *flux);DescriereFuncia fseek seteaz indicatorul de poziie pentru fiierul asociat fluxului flux. Noua poziie, dat n octei, se obine adunnd ofs octei (offset) la poziia specificat de reper. Dac reper este SEEK_SET, SEEK_CUR, sau SEEK_END, ofs este relativ la nceputul fiierului, poziia curent a indicatorului, respectiv sfritul fiierului. Funcia fseek terge indicatorul de sfrit de fiier.Funcia ftell obine valoarea curent a indicatorului de poziie pentru fiierul asociat fluxului flux.Funcia rewind poziioneaz indicatorul de poziie pentru fiierul asociat fluxului flux la nceputul fiierului. Este echivalent cu:(void)fseek(flux, 0L, SEEK_SET)cu completarea c funcia rewind terge i indicatorul de eroare al fluxului.Valori returnateFuncia rewind nu returneaz nici o valoare. n caz de succes, fseek returneaz 0, i ftell returneaz offset-ul curent. n caz de eroare se returneaz EOF i variabila global errno indic codul erorii.Numefileno returneaz descriptorul asociat fluxuluiDeclaraieint fileno(FILE *flux);DescriereFuncia fileno examineaz argumentul flux i returneaz descriptorul asociat de sistemul de operare acestui flux.10.3. Citire i scriere fr formatNumefgets - citete un ir de caractere dintr-un flux textDeclaraiechar *fgets(char *s, int size, FILE *flux);DescriereFuncia fgets citete cel mult size-1 caractere din flux i le memoreaz n zona indicat de s. Citirea se oprete la detectarea sfritului de fiier sau new-line. Dac se citete caracterul new-line acesta este memorat n s. Dup ultimul caracter se memoreaz null.Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale altor funcii de intrare din bibliotec (fscanf, de exemplu) pentru un acelai flux de intrare.Valori returnateFuncia returneaz adresa s n caz de succes, sau NULL n caz de eroare sau la ntlnirea sfritului de fiier dac nu s-a citit nici un caracter.Numefputs - scrie un ir de caractere ntr-un flux textDeclaraieint fputs(const char *s, FILE *flux);DescriereFuncia fputs scrie irul s n flux fr caracterul terminator null.Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale altor funcii de ieire din bibliotec (fprintf, de exemplu) pentru un acelai flux de ieire.Valori returnateFuncia returneaz o valoare non-negativ n caz de succes, sau EOF n caz de eroare.Numefread, fwrite - intrri / ieiri pentru fluxuri binareDeclaraieunsigned fread(void *ptr, unsigned size, unsigned nel, FILE *flux);unsigned fwrite(const void *ptr, unsigned size, unsigned nel, FILE *flux);DescriereFuncia fread citete nel elemente, fiecare avnd mrimea size octei, din fluxul indicat de flux, i le memoreaz n zona indicat de ptr.Funcia fwrite scrie nel elemente, fiecare avnd mrimea size octei, din fluxul indicat de flux, pe care le ia din zona indicat de ptr.Valori returnateFunciile returneaz numrul de elemente citite sau scrise cu succes (i nu numrul de caractere). Dac apare o eroare sau se ntlnete sfritul de fiier, valoarea returnat este mai mic dect nel (posibil zero).10.4. Citire cu formatNumescanf, fscanf, sscanf - citire cu formatDeclaraieint scanf(const char *fmt, ...);int fscanf(FILE *flux, const char *fmt, ...);int sscanf(char *str, const char *fmt, ...);DescriereFunciile din familia ...scanf scaneaz intrarea n concordan cu irul de caractere fmt dup cum se descrie mai jos. Acest format poate conine specificatori de conversie; rezultatele unor astfel de conversii (dac se efectueaz) se memoreaz prin intermediul argumentelor pointer. Funcia scanf citete irul de intrare din fluxul standard stdin, fscanf din flux, i sscanf din irul indicat de str.Fiecare argument pointer trebuie s corespund n ordine ca tip cu fiecare specificator de conversie (dar a se vedea suprimarea mai jos). Dac argumentele nu snt suficiente comportamentul programului este imprevizibil. Toate conversiile snt introduse de caracterul %. irul format poate conine i alte caractere. Spaii albe (blanc, tab, sau new-line) din irul format se potrivesc cu orice spaiu alb n orice numr (inclusiv nici unul) din irul de intrare. Orice alte caractere trebuie s se potriveasc exact. Scanarea se oprete atunci cnd un caracter din irul de intrare nu se potrivete cu cel din format. Scanarea se oprete de asemenea atunci cnd o conversie nu se mai poate efectua (a se vedea mai jos).ConversiiDup caracterul % care introduce o conversie poate urma un numr de caractere indicatori, dup cum urmeaz:*Suprim atribuirea. Conversia care urmeaz se face n mod obinuit, dar nu se folosete nici un argument pointer; rezultatul conversiei este pur i simplu abandonat.hConversia este de tip d, i, o, u, x sau n i argumentul asociat este un pointer la short (n loc de int).lConversia este de tip d, i, o, u, x sau n i argumentul asociat este un pointer la long (n loc de int), sau conversia este de tip e, f, g i argumentul asociat este un pointer la double (n loc de float).LConversia este de tip e, f, g i argumentul asociat este un pointer la long double.n completare la aceti indicatori poate exista o mrime w maxim opional pentru cmp, exprimat ca un ntreg zecimal, ntre caracterul % i cel de conversie, i naintea indicatorului. Dac nu este dat o mrime maxim se folosete mrimea implicit infinit (cu o excepie la conversia de tip c); n caz contrar se scaneaz cel mult un numr de w caractere n timpul conversiei. nainte de a ncepe o conversie, majoritatea conversiilor ignor spaiile albe; acestea nu snt contorizate n mrimea cmpului.Snt disponibile urmtoarele conversii:%Potrivire cu un caracter %. Cu alte cuvinte, %% n irul format trebuie s se potriveasc cu un caracter %. Nu se efectueaz nici o conversie i nici o atribuire.dPotrivire cu un ntreg zecimal (eventual cu semn); argumentul asociat trebuie s fie un pointer la int.iPotrivire cu un ntreg (eventual cu semn); argumentul asociat trebuie s fie un pointer la int. Valoarea ntreag este citit n baza 16 dac ncepe cu 0x sau 0X, n baza 8 dac ncepe cu 0, i n baza 10 n caz contrar. Snt folosite numai caracterele care corespund bazei respective.oPotrivire cu un ntreg octal fr semn; argumentul asociat trebuie s fie un pointer la unsigned.uPotrivire cu un ntreg zecimal fr semn; argumentul asociat trebuie s fie un pointer la unsigned.xPotrivire cu un ntreg hexazecimal fr semn; argumentul asociat trebuie s fie un pointer la unsigned.fPotrivire cu un numr n virgul mobil (eventual cu semn); argumentul asociat trebuie s fie un pointer la float.e,g Echivalent cu f.sPotrivire cu o secven de caractere diferite de spaiu alb; argumentul asociat trebuie s fie un pointer la char, i zona trebuie s fie suficient de mare pentru a putea primi toat secvena i caracterul terminator null. irul de intrare se termin la un spaiu alb sau la atingerea mrimii maxime a cmpului (prima condiie ntlnit).cPotrivire cu o secven de caractere de mrime w (dac aceasta este specificat; prin lips se ia w(1); argumentul asociat trebuie s fie un pointer la char, i zona trebuie s fie suficient de mare pentru a putea primi toat secvena (nu se adaug terminator null). Nu se ignor ca de obicei spaiile albe din fa. Pentru a ignora mai nti spaiile albe se indic un spaiu explicit n format.[Potrivire cu o secven nevid de caractere din setul specificat de caractere acceptate; argumentul asociat trebuie s fie un pointer la char, i zona trebuie s fie suficient de mare pentru a putea primi toat secvena i caracterul terminator null. Nu se ignor ca de obicei spaiile albe din fa. irul de intrare va fi format din caractere aflate n (sau care nu se afl n) setul specificat n format; setul este definit de caracterele aflate ntre [ i ]. Setul exclude acele caractere dac primul caracter dup [ este ^. Pentru a include caracterul ] n set, acesta trebuie s fie primul caracter dup [ sau ^; caracterul ] aflat n orice alt poziie nchide setul. Caracterul - are i el un rol special: plasat ntre dou alte caractere adaug toate celelalte caractere aflate n intervalul respectiv la set. Pentru a include caracterul - acesta trebuie s fie ultimul caracter nainte de ]. De exemplu, "%[^]0-9-]" semnific setul orice caracter cu excepia ], 0 pn la 9, i -. irul se termin la apariia unui caracter care nu se afl (sau, dac se precizeaz ^, care se afl) n set sau dac se atinge mrimea maxim specificat.nNu se prelucreaz nimic din irul de intrare; n schimb, numrul de caractere consumate pn la acest punct din irul de intrare este memorat la argumentul asociat, care trebuie s fie un pointer la int.Valori returnateFunciile returneaz numrul de valori atribuite, care poate fi mai mic dect numrul de argumente pointer, sau chiar zero, n cazul n care apar nepotriviri ntre format i irul de intrare. Zero indic faptul c, chiar dac avem un ir de intrare disponibil, nu s-a efectuat nici o conversie (i atribuire); aceast situaie apare atunci cnd un caracter din irul de intrare este invalid, cum ar fi un caracter alfabetic pentru o conversie %d. Valoarea EOF este returnat dac apare o eroare nainte de prima conversie, cum ar fi detectarea sfritului de fiier. Dac o eroare sau un sfrit de fiier apare dup ce o conversie a nceput, se returneaz numrul de conversii efectuate cu succes, i se poziioneaz bitul corespunztor din structura FILE, care poate fi testat.10.5. Scriere cu formatNumeprintf, fprintf, sprintf - scriere cu formatDeclaraieint printf(const char *fmt, ...);int fprintf(FILE *flux, const char *fmt, ...);int sprintf(char *str, const char *fmt, ...);DescriereFunciile din familia ...printf genereaz o ieire n concordan cu format dup cum se descrie mai jos. Funcia printf afieaz ieirea la fluxul standard stdout; fprintf scrie ieirea la flux; sprintf scrie ieirea n irul de caractere str.Aceste funcii genereaz ieirea sub controlul irului format care specific cum se convertesc argumentele pentru ieire.irul de formatareirul fmt este un ir de caractere, printre care se pot afla zero sau mai multe directive: caractere obinuite (diferite de %) care snt copiate aa cum snt n fluxul de ieire, i specificaii de conversie, fiecare dintre ele rezultnd din ncrcarea a zero sau mai multe argumente. Fiecare specificaie de conversie este introdus de caracterul % i se termin cu un specificator de conversie. ntre acestea pot fi (n aceast ordine) zero sau mai muli indicatori, o mrime minim a cmpului opional, o precizie opional i un modificator opional de lungime.Argumentele trebuie s corespund n ordine ca tip cu specificatorii de conversie. Acestea snt folosite n ordinea dat, unde fiecare caracter * i fiecare specificator de conversie solicit urmtorul argument. Dac argumentele nu snt suficiente comportamentul programului este imprevizibil.Caractere indicatoriCaracterul % este urmat de zero, unul sau mai muli indicatori:0Valoarea numeric este convertit cu zerouri la stnga. Pentru conversii de tip d, i, o, u, x, X, e, E, f, F, g i G, valoarea convertit este completat cu zerouri la stnga n loc de blanc. Dac apar indicatorii 0 i - mpreun, indicatorul 0 este ignorat. Dac pentru o conversie numeric (d, i, o, u, x, X) este dat o precizie, indicatorul 0 este ignorat. Pentru alte conversii rezultatul este nedefinit.-Valoarea convertit este aliniat la stnga (implicit alinierea se face la dreapta). Cu excepia conversiilor de tip n, valoarea convertit este completat la dreapta cu blanc, n loc s fie completat la stnga cu blanc sau zero. Dac apar indicatorii 0 i - mpreun, indicatorul 0 este ignorat.Sp(spaiu) n cazul unui rezultat al unei conversii cu semn, naintea unui numr pozitiv sau ir vid se pune un blanc.+Semnul (+ sau -) este plasat naintea numrului generat de o conversie cu semn. Implicit semnul este folosit numai pentru numere negative. Dac apar indicatorii + i Sp mpreun, indicatorul Sp este ignorat.Limea cmpuluiUn ir de cifre zecimale (cu prima cifr nenul) specific o lime minim pentru cmp. Dac valoarea convertit are mai puine caractere dect limea specificat, va fi completat cu spaii la stnga (sau dreapta, dac s-a specificat aliniere la stnga). n locul unui numr zecimal se poate folosi * pentru a specifica faptul c limea cmpului este dat de argumentul corespondent, care trebuie s fie de tip int. O valoare negativ pentru lime este considerat un indicator - urmat de o valoare pozitiv pentru lime. n nici un caz nu se va trunchia cmpul; dac rezultatul conversiei este mai mare dect limea cmpului, cmpul este expandat pentru a conine rezultatul conversiei.PreciziaPrecizia (opional) este dat de caracterul . urmat de un ir de cifre zecimale. n locul irului de cifre zecimale se poate scrie * pentru a specifica faptul c precizia este dat de argumentul corespondent, care trebuie s fie de tip int. Dac precizia este dat doar de caracterul . sau dac precizia este negativ, atunci aceasta se consider zero. Precizia d numrul minim de cifre care apar pentru conversii de tip d, i, o, u, x, X, numrul de cifre care apar dup punctul zecimal pentru conversii de tip e, E, f, F, numrul maxim de cifre semnificative pentru conversii de tip g i G, sau numrul maxim de caractere generate pentru conversii de tip s.Dac se folosete * pentru lime sau precizie (sau ambele), argumentele se iau n ordine: lime, precizie, valoare de scris.Modificator de lungimen acest caz prin conversie ntreag nelegem conversie de tip d, i, o, u, x, X.hConversia ntreag care urmeaz corespunde unui argument short sau unsigned short, sau urmtoarea conversie de tip n corespunde unui argument de tip pointer la short.lConversia care urmeaz corespunde unui argument de tip:long sau unsigned long, dac este o conversie de tip d, i, o, u, x, X sau n;double, dac este o conversie de tip f.LUrmtoarea conversie de tip e, E, f, F, g, G corespunde unui argument long double.Specificator de conversieUn caracter care specific tipul conversiei care se va face. Specificatorii de conversie i semnificaia lor snt:d,iArgumentul de tip int este convertit la notaia zecimal cu semn. Precizia, dac este dat, d numrul minim de cifre care trebuie s apar; dac valoarea convertit necesit mai puine cifre, aceasta este completat la stnga cu zerouri. Precizia implicit este 1. Dac valoarea 0 este afiat cu precizie explicit 0, ieirea este vid.o,u,x,XArgumentul de tip unsigned este convertit la notaie octal fr semn (o), zecimal fr semn (u), sau hexazecimal fr semn (x i X). Literele abcdef se folosesc pentru conversii de tip x, literele ABCDEF pentru conversii de tip X. Precizia, dac este dat, d numrul minim de cifre care trebuie s apar; dac valoarea convertit necesit mai puine cifre, aceasta este completat la stnga cu zerouri. Precizia implicit este 1. Dac valoarea 0 este afiat cu precizie explicit 0, ieirea este vid.e,EArgumentul de tip flotant este rotunjit i convertit n stil [-]d.dddedd unde avem o cifr nainte de punctul zecimal i numrul de cifre dup acesta este egal cu precizia; dac aceasta lipsete se consider 6; dac precizia este zero, punctul zecimal nu apare. O conversie de tip E folosete litera E (n loc de e) pentru a introduce exponentul. Exponentul are ntotdeauna cel puin dou cifre; dac valoarea este zero, exponentul este 00.f,FArgumentul de tip flotant este rotunjit i convertit n notaie zecimal n stil [-]ddd.ddd, unde numrul de cifre dup punctul zecimal este egal cu precizia specificat. Dac precizia lipsete se consider 6; dac precizia este explicit zero, punctul zecimal nu apare. Dac punctul zecimal apare, cel puin o cifr apare naintea acestuia.g,GArgumentul de tip flotant este convertit n stil f sau e (sau E pentru conversii de tip G). Precizia specific numrul de cifre semnificative. Dac precizia lipsete se consider 6; dac precizia este zero se consider 1. Stilul e este folosit dac exponentul rezultat n urma conversiei este mai mic dect (4 ori mai mare sau egal cu precizia. Zerourile finale snt eliminate din partea fracionar a rezultatului; punctul zecimal apare numai dac este urmat de cel puin o cifr.cArgumentul de tip int este convertit la unsigned char i se scrie caracterul rezultat.sArgumentul de tip const char * este un pointer la un ir de caractere. Caracterele din ir snt scrise pn la (fr a include) caracterul terminator null; dac precizia este specificat, nu se scrie un numr mai mare dect cel specificat. Dac precizia este dat, nu e nevoie de caracterul null; dac precizia nu este specificat, irul trebuie s conin un caracter terminator null.pArgumentul de tip pointer este scris n hexazecimal; formatul este specific sistemului de calcul.nNumrul de caractere scrise pn n acest moment este memorat la argumentul de tip int *. Nu se face nici o conversie.%Se scrie un caracter %. Nu se face nici o conversie. Specificaia complet este %%.Valoare returnatFunciile returneaz numrul de caractere generate (nu se include caracterul terminator null pentru sprintf).10.6. Tratarea erorilorNumeperror - afieaz un mesaj de eroare sistemDeclaraievoid perror(const char *s);#include const char *sys_errlist[];int sys_nerr;DescriereRutina perror afieaz un mesaj la ieirea standard de eroare, care descrie ultima eroare ntlnit la ultimul apel sistem sau funcie de bibliotec. Mai nti se afieaz argumentul s, apoi virgula i blanc, i n final mesajul de eroare i new-line. Se recomand (mai ales pentru depanare) ca argumentul s s includ numele funciei n care a aprut eroarea. Codul erorii se ia din variabila extern errno.Lista global de erori sys_errlist[] indexat cu errno poate fi folosit pentru a obine mesajul de eroare fr new-line. Ultimul indice de mesaj din list este sys_nerr-1. Se recomand o atenie deosebit n cazul accesului direct la list deoarece unele coduri noi de eroare pot lipsi din sys_errlist[].Dac un apel sistem eueaz variabila errno indic codul erorii. Aceste valori pot fi gsite n . Funcia perror servete la afiarea acestui cod de eroare ntr-o form lizibil. Dac un apel terminat cu eroare nu este imediat urmat de un apel perror, valoarea variabilei errno se poate pierde dac nu e salvat.Numeclearerr, feof, ferror - verific i reseteaz stareafluxuluiDeclaraievoid clearerr(FILE *flux);int feof(FILE *flux);int ferror(FILE *flux);DescriereFuncia clearerr terge indicatorii de sfrit de fiier i eroare ai fluxului.Funcia feof testeaz indicatorul de sfrit de fiier al fluxului, i returneaz non-zero dac este setat. Acesta este setat dac o operaie de citire a detectat sfritul de fiier.Funcia ferror testeaz indicatorul de eroare al fluxului, i returneaz non-zero dac este setat. Acesta este setat dac o operaie de citire sau scriere a detectat o eroare (datorat de exemplu hardware-ului).Funciile de citire (cu sau fr format) nu fac distincie ntre sfrit de fiier i eroare, astfel c trebuie apelate funciile feof i ferror pentru a determina cauza terminrii.Atenie! Este foarte frecvent folosirea incorect a funciei feof pentru a testa dac s-a ajuns la sfritul fiierului. Nu se recomand n nici un caz acest stil de programare:#define LSIR 80char lin[LSIR];FILE *fi,*fo;fi=fopen(nume-fiier-intrare,"rt");fo=fopen(nume-fiier-ieire,"wt");while (!feof(fi)) {/* greit! */ fgets(lin,LSIR,fi); fputs(lin,fo); }fclose(fi); fclose(fo);n aceast secven, dac i ultima linie a fiierului text de intrare este terminat cu new-line, aceasta va fi scris de dou ori n fiierul de ieire. De ce? Dup ce se citete ultima linie nc nu este poziionat indicatorul de sfrit de fiier, deci funcia fgets nc returneaz succes. La reluarea ciclului se ncearc un nou fgets i abia acum se depisteaz sfritul de fiier, fapt marcat n zona rezervat fluxului fi. Astfel coninutul irului lin rmne nemodificat i este scris a doua oar n fiierul de ieire. Abia la o nou reluare a ciclului funcia feof ne spune c s-a depistat sfritul de fiier.n acest manual snt prezentate mai multe programe care efectueaz diferite prelucrri asupra unor fiiere text. Pentru simplitate toate programele presupun c nu apar erori la citire sau la scriere.10.7. Operaii cu directoareFunciile de parcurgere a cataloagelor de fiiere descrise n aceast seciune (opendir, readdir, closedir) snt definite de mai multe medii de programare C (printre care i GNU C pentru sistemul de operare Linux), precum i de standardul POSIX. Aceste funcii snt declarate n .Funciile de redenumire i tergere a unor fiiere snt descrise n .Numeopendir - deschide un directorDeclaraieDIR *opendir(const char *nume);DescriereFuncia opendir deschide un flux pentru directorul cu numele nume, i returneaz un pointer la fluxul deschis. Fluxul este poziionat pe prima intrare din director.Valoare returnatFuncia returneaz un pointer la flux n caz de succes, sau NULL n caz de eroare i variabila global errno indic codul erorii.Cteva erori posibileEACCESAcces interzisENOTDIRnume nu este un directorNumereaddir - citete dintr-un directorDeclaraiestruct dirent *readdir(DIR *dir);DescriereFuncia readdir returneaz un pointer la o structur de tip dirent care reprezint urmtoarea intrare din directorul indicat de fluxul dir. Returneaz NULL dac s-a depistat sfritul de director sau dac a aprut o eroare.Structura de tip dirent conine un cmp char d_name[]. Utilizarea altor cmpuri din structur reduce portabilitatea programelor.Valoare returnatFuncia returneaz un pointer la o structur de tip dirent, sau NULL dac s-a depistat sfritul de director sau dac a aprut o eroare.Numeclosedir - nchide un directorDeclaraieint closedir(DIR *dir);DescriereFuncia closedir nchide fluxul dir.Valoare returnatFuncia returneaz 0 n caz de succes sau EOF n caz de eroare.Numerename - redenumete un fiierremove - terge un fiierDeclaraieint rename(const char *old, const char *new);int remove(const char *name);DescriereFuncia rename schimb numele unui fiier din old n new. Dac a fost precizat un periferic n new, acesta trebuie s coincid cu cel din old. Directoarele din old i new pot s fie diferite, astfel c rename poate fi folosit pentru a muta un fiier dintr-un director n altul. Nu se permit specificatori generici (wildcards).Funcia remove terge fiierul specificat prin name.Valoare returnatn caz de succes se returneaz 0. n caz de eroare se returneaz EOF i variabila global errno indic codul erorii.10.8. Programe demonstrativePrimele trei programe primesc ca parametri n linia de comand numele fiierelor pe care le vor prelucra. Ultimul program primete ca parametru n linia de comand numele directorului al crui coninut va fi afiat.1) Determinarea mrimii unui fiier#include FILE *f;int main(int ac, char **av) {if (ac!=2) { fputs("Un argument!\n",stderr); return 1; }f = fopen(av[1],"rb");if (!f) { perror("Eroare la deschidere"); return 1; }fseek(f,0,SEEK_END);fprintf(stderr,"File %s, size %ld\n",ftell(f));fclose(f);return 0;}2) Copierea unui fiierFunciile fgets i fputs se folosesc pentru fluxuri deschise n mod text. Cum se utilizeaz pentru copierea unui fiier text?#include #define LSIR 80char lin[LSIR];FILE *fi, *fo;int main(int ac, char **av) {if (ac!=3) { fputs("Doua argumente!\n",stderr); }fi=fopen(av[1],"rt"); fo=fopen(av[2],"wt");if (!fi || !fo) { perror("Eroare la deschidere"); return 1; }while (fgets(lin,LSIR,fi)) fputs(lin,fo);fclose(fi); fclose(fo);return 0;}Funciile fread i fwrite se folosesc pentru fluxuri deschise n mod binar. Cum se utilizeaz pentru copierea unui fiier binar?#include #define LZON 4096char zon[LZON];FILE *fi, *fo;unsigned k;int main(int ac, char **av) {if (ac!=3) { fputs("Doua argumente!\n",stderr); return 1; }fi=fopen(av[1],"rb"); fo=fopen(av[2],"wb");if (!fi || !fo) { perror("Eroare la deschidere"); return 1; }while (k=fread(zon,1,LZON,fi)) fwrite(zon,1,k,fo);fclose(fi); fclose(fo);return 0;}3) Prelucrarea unui fiier textProgramul prezentat n continuare citete un fiier text care conine pe fiecare linie un ir de caractere (fr spaii) i trei valori ntregi, i afieaz pe terminal numele pe 12 poziii aliniat la stnga i media aritmetic a celor trei valori ntregi.#include FILE *fi;char num[10];int a,b,c;double m;int main(int ac, char **av) {if (ac!=2) { fputs("Un argument!\n",stderr); return 1; }fi=fopen(av[1],"rt");if (!fi) { perror("Eroare la deschidere"); return 1; }while (fscanf(fi,"%s %d %d %d",num,&a,&b,&c)!=EOF) { m=(a+b+c)/3.0; printf("%-12s%6.2lf\n",num,m); }fclose(fi);return 0;}4) Afiarea coninutului unui director#include #include DIR *dir;struct dirent *ent;int main(int ac, char **av) {if (ac!=2) { printf("Un parametru\n"); return 1; }dir = opendir(av[1]);if (!dir) { perror("Eroare open dir"); return 1; }while (ent=readdir(dir)) printf("%s\n",ent->d_name);return 0;}11. Alte rutine din biblioteca standardn acest capitol snt descrise funcii care rezolv probleme legate de alocarea dinamic a memoriei, sortare i cutare, clasificare, operaii cu blocuri de memorie i iruri de caractere, funcii matematice.11.1. Alocarea dinamic a memorieiNumecalloc, malloc, realloc - aloc memoria n mod dinamicfree - elibereaz memoria alocat n mod dinamicDeclaraie#include void *calloc(unsigned nel, unsigned size);void *malloc(unsigned size);void *realloc(void *ptr, unsigned size);void free(void *ptr);DescriereFuncia calloc aloc memorie pentru un masiv de nel elemente, fiecare de mrime size octei i returneaz un pointer la memoria alocat. Coninutul memoriei este pus la zero.Funcia malloc aloc size octei i returneaz un pointer la memoria alocat. Coninutul memoriei nu este ters.Funcia free elibereaz spaiul de memorie indicat de ptr, care trebuie s fi fost returnat de un apel anterior malloc, calloc sau realloc. n caz contrar, sau dac a existat deja un apel anterior free(ptr), comportamentul programului este imprevizibil.Funcia realloc modific mrimea blocului de memorie indicat de ptr la size octei. Coninutul rmne neschimbat la mrimea minim dintre mrimea veche i cea nou; noul spaiu de memorie care este eventual alocat este neiniializat. Dac ptr este NULL apelul este echivalent cu malloc(size); dac size este egal cu zero apelul este echivalent cu free(ptr). Cu excepia cazului cnd ptr este NULL, acesta trebuie s fi fost returnat de un apel precedent malloc, calloc sau realloc.Valori returnatePentru calloc i malloc valoarea returnat este un pointer la memoria alocat, aliniat n mod corespunztor pentru orice tip de variabile, sau NULL dac nu exist suficient memorie continu.Funcia free nu returneaz nimic.Funcia realloc returneaz un pointer la noua zon de memorie alocat, aliniat n mod corespunztor pentru orice tip de variabile. Valoarea acestuia poate fi diferit de ptr, poate fi NULL dac nu exist suficient memorie continu sau dac valoarea size este egal cu 0. Dac realloc eueaz, blocul original rmne neatins nu este nici eliberat nici mutat.11.2. Sortare i cutareNumeqsort - sorteaz un masivbsearch - cutare binar ntr-un masiv sortatDeclaraie#include void qsort(void *base, unsigned nel, unsigned size, int (*comp) (const void *, const void *));void *bsearch(const void *key, const void *base, unsigned nel, unsigned size, int (*comp)(const void *, const void *));DescriereFuncia qsort sorteaz un masiv de nel elemente, fiecare de mrime size. Argumentul base indic spre nceputul masivului.Elementele masivului snt sortate n ordine cresctoare n concordan cu funcia de comparare referit de comp, apelat cu dou argumente care indic spre obiectele ce se compar. Funcia de comparare trebuie s returneze un ntreg mai mic dect, egal cu, sau mai mare dect zero dac primul argument este considerat a fi mai mic dect, egal cu, respectiv mai mare dect al doilea. Dac cele dou elemente comparate snt egale, ordinea n masivul sortat este nedefinit.Funcia bsearch caut ntr-un masiv de nel elemente, fiecare de mrime size, un membru care coincide cu obiectul indicat de key. Argumentul base indic spre nceputul masivului.Coninutul masivului trebuie s fie sortat cresctor n concordan cu funcia de comparare referit de comp, apelat cu dou argumente care indic spre obiectele ce se compar. Funcia de comparare se definete ca n cazul qsort. Primul argument este adresa cheii, al doilea este adresa unui element din masiv.Valoare returnatFuncia bsearch returneaz un pointer la un membru al masivului care coincide cu obiectul indicat de key, sau NULL dac nu se gsete nici un membru. Dac exist mai multe elemente care coincid cu key, poate fi returnat oricare element cu aceast proprietate.11.3. Rutine de clasificareNumeisalnum, isalpha, isascii, iscntrl, isdigit,isgraph, islower, isprint, ispunct, isspace,isupper, isxdigit- rutine de clasificaretolower - conversie n liter mictoupper - conversie n liter mareDeclaraie#include int isalnum(int c);int isalpha(int c);int isascii(int c);int iscntrl(int c);int isdigit(int c);int isgraph(int c);int islower(int c);int isprint(int c);int ispunct(int c);int isspace(int c);int isupper(int c);int isxdigit(int c);int tolower(int c);int toupper(int c);DescrierePrimele 12 funcii verific dac c, care trebuie s fie o valoare de tip unsigned char sau EOF, se afl n una din clasele de caractere enumerate mai sus.isalnumVerific dac c este alfanumeric (liter sau cifr).isalphaVerific dac c este alfabetic (liter mare sau mic).isasciiVerific dac c este o valoare pe 7 bii din setul de caractere ASCII.iscntrlVerific dac c este un caracter de control.isdigitVerific dac c este o cifr (ntre 0 i 9).isgraphVerific dac c este un caracter afiabil cu excepia spaiului.islowerVerific dac c este o liter mic.isprintVerific dac c este un caracter afiabil inclusiv spaiu.ispunctVerific dac c este un caracter diferit de spaiu i non-alfanumeric.isspaceVerific dac c este un spaiu alb.isupperVerific dac c este o liter mare.isxdigitVerific dac c este o cifr hexazecimal din setul:0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E FtolowerConvertete caracterul c, dac este o liter, la litera mic corespunztoare.toupperConvertete caracterul c, dac este o liter, la litera mare corespunztoare.Valoare returnatValoarea returnat de funciile is... este nenul dac caracterul c se afl n clasa testat, i zero n caz contrar.Valoarea returnat de funciile to... este litera convertit dac caracterul c este o liter, i nedefinit n caz contrar.11.4. Operaii cu blocuri de memoriePentru majoritatea funciilor din aceast categorie compilatorul expandeaz codul acestora folosind instruciuni pe iruri de caractere. Aceste funcii snt declarate n Numememcpy - copiaz o zon de memorieDeclaraievoid *memcpy(void *dest, const void *src, unsigned n);void *memmove(void *dest, const void *src, unsigned n);DescriereFuncia memcpy copiaz n octei din zona de memorie src n zona de memorie dest. Zonele de memorie nu trebuie s se suprapun. Dac exist acest risc se utilizeaz memmove.Valoare returnatFunciile returneaz un pointer la dest.Numememcmp - compar dou zone de memorieDeclaraieint memcmp(const void *s1, const void *s2, unsigned n);DescriereFuncia memcmp compar primii n octei ai zonelor de memorie s1 i s2.Valoare returnatReturneaz un ntreg mai mic dect, egal cu, sau mai mare dect zero dac s1 este mai mic dect, coincide, respectiv este mai mare dect s2.Numememset - umple o zon de memorie cu o constant pe un octetDeclaraievoid *memset(void *s, int c, unsigned n);DescriereFuncia memset umple primii n octei ai zonei de memorie indicat de s cu constanta c pe un octet.Valoare returnatFuncia returneaz un pointer la zona de memorie s.Numememchr - caut n memorie un caracterDeclaraievoid *memchr(const void *s, int c, unsigned n);DescriereFuncia memchr caut caracterul c n primii n octei de memorie indicai de s. Cutarea se oprete la primul octet care are valoarea c (interpretat ca unsigned char).Valoare returnatFuncia returneaz un pointer la octetul gsit sau NULL dac valoarea nu exist n zona de memorie.11.5. Operaii cu iruri de caracterePentru majoritatea funciilor din aceast categorie compilatorul expandeaz codul acestora folosind instruciuni pe iruri de caractere. Aceste funcii snt declarate n Numestrlen - calculeaz lungimea unui irDeclaraieunsigned strlen(const char *s);DescriereFuncia strlen calculeaz lungimea irului s, fr a include caracterul terminator null.Valoare returnatFuncia returneaz numrul de caractere din s.Numestrcpy, strncpy - copiaz un ir de caractereDeclaraiechar *strcpy(char *dest, const char *src);char *strncpy(char *dest, const char *src, unsigned n);DescriereFuncia strcpy copiaz irul indicat de src (inclusiv caracterul terminator null) n zona indicat de dest. irurile nu trebuie s se suprapun, i n plus zona dest trebuie s fie suficient de mare pentru a primi copia.Funcia strncpy este similar, cu excepia faptului c nu se copiaz mai mult de n octei din src. Astfel, dac caracterul terminator null nu se afl n primii n octei din src, rezultatul nu va fi terminat cu null. n cazul n care lungimea lui src este mai mic dect n, restul octeilor din dest primesc valoarea null.Valoare returnatFunciile returneaz un pointer la irul dest.Numestrdup - duplic un irDeclaraiechar *strdup(const char *s);DescriereFuncia strdup returneaz un pointer la un nou ir care este un duplicat al irului s. Memoria pentru noul ir se obine cu malloc, i poate fi eliberat cu free.Valoare returnatFuncia returneaz un pointer la irul duplicat, sau NULL dac nu exist memorie suficient disponibil.Numestrcat, strncat - concateneaz dou iruriDeclaraiechar *strcat(char *dest, const char *src);char *strncat(char *dest, const char *src, unsigned n);DescriereFuncia strcat adaug irul src la irul dest suprascriind caracterul null de la sfritul lui dest, i la sfrit adaug un caracter terminator null. irurile nu trebuie s se suprapun, i n plus irul dest trebuie s aib suficient spaiu pentru a pstra rezultatul.Funcia strncat este similar, cu excepia faptului c numai primele n caractere din src se adaug la dest.Valoare returnatFunciile returneaz un pointer la irul rezultat dest.Numestrcmp - compar dou iruri de caractereDeclaraieint strcmp(const char *s1, const char *s2);DescriereFuncia strcmp compar cele dou iruri s1 i s2.Valoare returnatFuncia returneaz un ntreg mai mic dect, egal cu, sau mai mare dect zero dac s1 este mai mic dect, coincide, respectiv este mai mare dect s2.Numestrchr, strrchr - localizeaz un caracterDeclaraiechar *strchr(const char *s, int c);char *strrchr(const char *s, int c);DescriereFuncia strchr returneaz un pointer la prima apariie a caracterului c n irul s.Funcia strrchr returneaz un pointer la ultima apariie a caracterului c n irul s.Valoare returnatFunciile returneaz un pointer la caracterul gsit sau NULL dac valoarea nu a fost gsit.Numestrstr - localizeaz un subirDeclaraiechar *strstr(const char *sir, const char *subs);DescriereFuncia strstr gsete prima apariie a subirului subs n irul sir. Caracterul terminator null nu este luat n considerare.Valoare returnatFuncia returneaz un pointer la nceputul subirului, sau NULL dac subirul nu este gsit.Numestrspn, strcspn - caut un set de caractere ntr-un irDeclaraie unsigned strspn(const char *s, const char *acc); unsigned strcspn(const char *s, const char *rej);DescriereFuncia strspn determin lungimea segmentului iniial din s format n ntregime numai cu caractere din acc.Funcia strcspn determin lungimea segmentului iniial din s format n ntregime numai cu caractere care nu se gsesc n rej.Valori returnateFuncia strspn returneaz poziia primului caracter din s care nu se afl n acc.Funcia strcspn returneaz poziia primului caracter din s care se afl n rej.11.6. Biblioteca matematic1) Funciile din prima categorie snt declarate n Numerand, srand - generarea numerelor pseudo-aleatoareDeclaraieint rand(void);void srand(unsigned int seed);DescriereFuncia rand returneaz un ntreg pseudo-aleator ntre 0 i RAND_MAX (pentru majoritatea mediilor de programare C aceast constant este egal cu valoarea maxim cu semn reprezentabil pe un cuvnt al sistemului de calcul).Funcia srand iniializeaz generatorul cu valoarea seed pentru o nou secven de valori ntregi pseudo-aleatoare care vor fi returnate de rand. Aceste secvene se repet dac srand se apeleaz cu aceeai valoare seed.Se obinuiete ca generatorul s fie iniializat cu o valoare dat de ceasul sistemului de calcul, ca n exemplul de mai jos:#include srand(time(NULL));Valoare returnatFuncia rand returneaz o valoare ntre 0 i RAND_MAX.Observaien lucrarea Numerical Recipes in C: The Art of Scientific Computing - William H Press, Brian P Flannery, Saul A Teukolsky, William T Vetterling / New York: Cambridge University Press, 1990 (1st ed, p 207), se face urmtorul comentariu:Dac dorii s generai o valoare aleatoare ntreag ntre 1 i 10, se recomand s folosii secvenaj=1+(int)(10.0*rand()/(RAND_MAX+1.0));i nu o secven de tipulj=1+(int)(1000000.0*rand())%10;care folosete biii de rang inferior.Tot n fiierul snt descrise i urmtoarele funcii:int abs(int i); valoare absolutlong labs(long i); valoare absolutint atoi(char *s); conversie din ASCII n ntreglong atol(char *s); conversie din ASCII n ntreg lungdouble atof(char *s); conversie din ASCII n dubl precizieUrmtoarele funcii de conversie necesit o discuie detaliat:double strtod(char *str, char **end);conversie din ASCII n dubl precizielong strtol(char *str, char **end, int base);conversie din ASCII n ntreg lungunsigned long strtoul(char *str, char **end, int base);conversie din ASCII n ntreg lung fr semnirul de caractere str este convertit n reprezentare binar, ca i n cazul funciilor atoi, atol, atof. Pentru conversii de tip ntreg se precizeaz i baza de numeraie folosit, care poate fi ntre 2 i 36. Pentru baze mai mari dect zece se folosesc litere mici sau mari. n plus, dac parametrul end indic o adres valid (nu este null), la ieire primete adresa primului caracter invalid din str, care nu respect regulile de scriere a unei valori numerice de tipul ateptat.2) Funciile din a doua categorie snt declarate n double fabs(double x); valoare absolutdouble floor(double x); parte ntreag inferioardouble ceil(double x); parte ntreag superioardouble sqrt(double x); double sin(double x); sin(x)double cos(double x); cos(x)double tan(double x); tg(x)double asin(double x); arcsin(x)double acos(double x); arccos(x)double atan(double x); arctg(x) n [-(/2,(/2]double atan2(double y, double x); arctg(y/x) n [(,(]double exp(double x); exdouble log(double x); ln(x)double pow(double x, double y); xydouble sinh(double x); sinh(x)double cosh(double x); cosh(x)double tanh(double x); tgh(x)double ldexp(double x, int e); x ( 2edouble fmod(double x, double y); x modulo yFuncia fmod returneaz o valoare f definit astfel:x ( a ( y ( f a este o valoare ntreag (dat de x/y) i 0 ( |f | < y; f are semnul lui x.Urmtoarele dou funcii returneaz dou valori: una este valoarea returnat de funcie (de tip double), i cealalt returnat prin intermediul unui argument de tip pointer la int respectiv double.double frexp(double x, int *e);Funcia frexp desparte valoarea x n dou pri: o parte fracionar normalizat (f ( [0.5,1)) i un exponent e. Dac x este 0 atunci f(0 i e(0. Valoarea returnat este f.double modf(double x, double *n);Funcia modf desparte valoarea x n dou pri: o parte fracionar subunitar f i o parte ntreag n. Valorile f i n au acelai semn ca i x. Valoarea returnat este f.11.7. Funcii pentru timp i dat calendaristicFunciile din aceast categorie, mpreun cu tipurile i structurile de date folosite, snt declarate n Funcia timeData i ora calendaristic se reprezint n format compact printr-o valoare de tip time_t. Majoritatea mediilor de programare definesc acest tip ca un sinonim pentru unsigned long. Aceast valoare se obine prin apelul funciei time.time_t time(time_t *tims);Funcia returneaz data i ora curent n coordonate universale (UTC), care reprezint numrul de secunde de la nceputul erei Unix (1 ianuarie 1970, ora 0:00:00).Valoarea returnat este memorat la adresa indicat de argumentul tims, dac acesta nu este NULL. Dac argumentul tims este NULL, trebuie s memorm valoarea returnat.Structura tm (dat calendaristic defalcat)Funcia gmtimeO valoare de tip time_t poate fi defalcat pe componente i memorat ntr-o structur:struct tm {int tm_sec;/* secunde, ntre 0 i 59 */int tm_min;/* minute, ntre 0 i 59 */int tm_hour;/* ore, ntre 0 i 23 */int tm_mday;/* ziua din lun, ntre 1 i 31 */int tm_mon;/* luna, ntre 0 i 11 */int tm_year;/* anul curent minus 1900 */int tm_wday;/* ziua din sptmn, duminica: 0 */int tm_yday;/* ziua din an, ntre 0 i 365 */int tm_isdst;/* opiunea daylight saving time */};Observaie. Alegerea numelui tm pentru aceast structur a fost ct se poate de neinspirat. Programatorii trebuie s fie deosebit de ateni ca, atunci cnd folosesc funcii din aceast categorie, s nu foloseasc n nici un caz numele tm pentru definiii proprii.Funcia gmtime efectueaz aceast defalcare. Aceast funcie returneaz adresa unei zone unde se afl data calendaristic defalcat. Informaiile din aceast zon trebuie salvate n cazul n care se efectueaz prelucrri ulterioare. Un apel ulterior al funciei gmtime le va suprascrie, zona fiind alocat static n interiorul acesteia.struct tm *gmtime(const time_t *tims);Funcia mktimetime_t mktime(struct tm *datc);Aceast funcie efectueaz conversia invers, de la o structur defalcat la o valoare compact. Este suficient s completm valorile tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec. Dac o valoare se afl n afara limitelor permise, acestea snt automat normalizate. De exemplu, ora 25:00:00 este echivalat cu ora 1:00:00 a zilei urmtoare. Funcia corecteaz (dac e nevoie) toate valorile structurii. Dac data calendaristic nu poate fi reprezentat ca o valoare de tip time_t, funcia returneaz valoarea (time_t)(1).Funciile ctime i asctimechar *ctime(const time_t *tims);char *asctime(const struct *datc);Aceste funcii convertesc data calendaristic n reprezentare ASCII. Aceste funcii returneaz adresa unui ir de forma:"Sun Jun 05 10:00:00 2005"Informaiile din aceast zon trebuie salvate n cazul n care se efectueaz prelucrri ulterioare. Un apel ulterior al funciei le va suprascrie, zona fiind alocat static n interiorul acesteia.Funcia clockclock_t clock(void);Funcia clock returneaz o aproximare a timpului pe care procesorul l-a folosit pentru program pn la momentul apelului. Tipul clock_t este definit de majoritatea mediilor de programare ca un sinonim pentru unsigned long. Numrul de secunde de la lansarea programului se obine astfel:(double)clock()/CLOCKS_PER_SECONDConstanta CLOCKS_PER_SECOND este specific mediului de programare folosit. De exemplu, biblioteca GNU C Compiler pentru sistemul de operare Linux definete valoarea 1000000. O consecin a acestui fapt este c pe un sistem de calcul pe 32 de bii funcia clock va returna aceeai valoare la aproximativ fiecare 72 de minute.Observaie. Standardul C nu impune ca aceast cronometrare s nceap de la zero la lansarea programului. De aceea, pentru a garanta un maxim de portabilitate, cronometrarea se face astfel:main() {double stm;stm = clock();. . .printf("Time:%.3lf\n",(clock()-stm)/ CLOCKS_PER_SECOND);}Unele sisteme de operare, de exemplu Linux, permit cronometrarea exact a timpului de rulare a unui program (evident, cu limitarea la 72 de minute), incluznd aici i timpul ocupat de sistemul de operare pentru efectuarea uno